Merge "Add art's fuzzer to the visibility" into main
diff --git a/Android.bp b/Android.bp
index b7ed308..9e8eca6 100644
--- a/Android.bp
+++ b/Android.bp
@@ -51,6 +51,9 @@
     errorprone: {
         javacflags: [
             "-Xep:MissingOverride:OFF", // Ignore missing @Override.
+            "-Xep:BoxedPrimitiveEquality:WARN",
+            "-Xep:DoubleBraceInitialization:WARN",
+            "-Xep:HashtableContains:WARN",
         ],
     },
 }
@@ -65,13 +68,6 @@
     lint: {
         warning_checks: ["SuspiciousIndentation"],
     },
-    errorprone: {
-        javacflags: [
-            "-Xep:BoxedPrimitiveEquality:WARN",
-            "-Xep:DoubleBraceInitialization:WARN",
-            "-Xep:HashtableContains:WARN",
-        ],
-    },
 }
 
 // The src files for bouncycastle, used to generate core platform / intra-core
@@ -191,6 +187,7 @@
     "//packages/apps/RemoteProvisioner/tests/unittests",
     "//packages/modules/Connectivity/tests/cts/net",
     "//packages/modules/RemoteKeyProvisioning/app/tests/unit",
+    "//packages/modules/Virtualization/tests/vm_attestation",
     "//packages/modules/Wifi/service",
     "//packages/modules/Wifi/service/tests/wifitests",
     "//libcore",
@@ -342,6 +339,7 @@
 //Excludes directories not needed.
 java_library {
     name: "bouncycastle-uwb",
+    defaults: ["bouncycastle-errorprone-defaults"],
     visibility: [
         "//packages/modules/Uwb/service",
     ],
diff --git a/bcpkix/src/main/java/org/bouncycastle/cert/AttributeCertificateHolder.java b/bcpkix/src/main/java/org/bouncycastle/cert/AttributeCertificateHolder.java
index d0b1259..df8b1d6 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cert/AttributeCertificateHolder.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cert/AttributeCertificateHolder.java
@@ -323,9 +323,9 @@
 
                 digOut.close();
 
-                if (!Arrays.areEqual(digCalc.getDigest(), getObjectDigest()))
+                if (Arrays.areEqual(digCalc.getDigest(), getObjectDigest()))
                 {
-                    return false;
+                    return true;
                 }
             }
             catch (Exception e)
diff --git a/bcpkix/src/main/java/org/bouncycastle/cert/CertUtils.java b/bcpkix/src/main/java/org/bouncycastle/cert/CertUtils.java
index c71be36..55bc072 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cert/CertUtils.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cert/CertUtils.java
@@ -12,6 +12,7 @@
 import java.util.List;
 import java.util.Set;
 
+import org.bouncycastle.asn1.ASN1BitString;
 import org.bouncycastle.asn1.ASN1Encodable;
 import org.bouncycastle.asn1.ASN1EncodableVector;
 import org.bouncycastle.asn1.ASN1Encoding;
@@ -19,9 +20,12 @@
 import org.bouncycastle.asn1.ASN1Object;
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
 import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
 import org.bouncycastle.asn1.DERBitString;
 import org.bouncycastle.asn1.DERNull;
 import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.DERTaggedObject;
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 import org.bouncycastle.asn1.x509.AttributeCertificate;
 import org.bouncycastle.asn1.x509.AttributeCertificateInfo;
@@ -77,28 +81,6 @@
         }
     }
 
-    static X509CRLHolder generateFullCRL(ContentSigner signer, TBSCertList tbsCertList)
-    {
-        try
-        {
-            return new X509CRLHolder(generateCRLStructure(tbsCertList, signer.getAlgorithmIdentifier(), generateSig(signer, tbsCertList)));
-        }
-        catch (IOException e)
-        {
-            throw new IllegalStateException("cannot produce certificate signature");
-        }
-    }
-
-    private static byte[] generateSig(ContentSigner signer, ASN1Object tbsObj)
-        throws IOException
-    {
-        OutputStream sOut = signer.getOutputStream();
-        tbsObj.encodeTo(sOut, ASN1Encoding.DER);
-        sOut.close();
-
-        return signer.getSignature();
-    }
-
     private static Certificate generateStructure(TBSCertificate tbsCert, AlgorithmIdentifier sigAlgId, byte[] signature)
     {
         ASN1EncodableVector v = new ASN1EncodableVector();
@@ -197,12 +179,12 @@
         }
     }
 
-    static boolean[] bitStringToBoolean(DERBitString bitString)
+    static boolean[] bitStringToBoolean(ASN1BitString bitString)
     {
         if (bitString != null)
         {
-            byte[]          bytes = bitString.getBytes();
-            boolean[]       boolId = new boolean[bytes.length * 8 - bitString.getPadBits()];
+            byte[] bytes = bitString.getBytes();
+            boolean[] boolId = new boolean[bytes.length * 8 - bitString.getPadBits()];
 
             for (int i = 0; i != boolId.length; i++)
             {
@@ -234,40 +216,40 @@
              return false;
          }
 
-         if (Properties.isOverrideSet("org.bouncycastle.x509.allow_absent_equiv_NULL"))
-         {
-             if (id1.getParameters() == null)
-             {
-                 if (id2.getParameters() != null && !id2.getParameters().equals(DERNull.INSTANCE))
-                 {
-                     return false;
-                 }
+        if (Properties.isOverrideSet("org.bouncycastle.x509.allow_absent_equiv_NULL"))
+        {
+            if (id1.getParameters() == null)
+            {
+                if (id2.getParameters() != null && !id2.getParameters().equals(DERNull.INSTANCE))
+                {
+                    return false;
+                }
 
-                 return true;
-             }
+                return true;
+            }
 
-             if (id2.getParameters() == null)
-             {
-                 if (id1.getParameters() != null && !id1.getParameters().equals(DERNull.INSTANCE))
-                 {
-                     return false;
-                 }
+            if (id2.getParameters() == null)
+            {
+                if (id1.getParameters() != null && !id1.getParameters().equals(DERNull.INSTANCE))
+                {
+                    return false;
+                }
 
-                 return true;
-             }
-         }
+                return true;
+            }
+        }
 
-         if (id1.getParameters() != null)
-         {
-             return id1.getParameters().equals(id2.getParameters());
-         }
+        if (id1.getParameters() != null)
+        {
+            return id1.getParameters().equals(id2.getParameters());
+        }
 
-         if (id2.getParameters() != null)
-         {
-             return id2.getParameters().equals(id1.getParameters());
-         }
+        if (id2.getParameters() != null)
+        {
+            return id2.getParameters().equals(id1.getParameters());
+        }
 
-         return true;
+        return true;
     }
 
     static ExtensionsGenerator doReplaceExtension(ExtensionsGenerator extGenerator, Extension ext)
@@ -276,7 +258,7 @@
         Extensions exts = extGenerator.generate();
         extGenerator = new ExtensionsGenerator();
 
-        for (Enumeration en = exts.oids(); en.hasMoreElements();)
+        for (Enumeration en = exts.oids(); en.hasMoreElements(); )
         {
             ASN1ObjectIdentifier extOid = (ASN1ObjectIdentifier)en.nextElement();
 
@@ -299,13 +281,13 @@
         return extGenerator;
     }
 
-    static ExtensionsGenerator doRemoveExtension(ExtensionsGenerator extGenerator, ASN1ObjectIdentifier  oid)
+    static ExtensionsGenerator doRemoveExtension(ExtensionsGenerator extGenerator, ASN1ObjectIdentifier oid)
     {
         boolean isRemoved = false;
         Extensions exts = extGenerator.generate();
         extGenerator = new ExtensionsGenerator();
 
-        for (Enumeration en = exts.oids(); en.hasMoreElements();)
+        for (Enumeration en = exts.oids(); en.hasMoreElements(); )
         {
             ASN1ObjectIdentifier extOid = (ASN1ObjectIdentifier)en.nextElement();
 
@@ -326,4 +308,31 @@
 
         return extGenerator;
     }
+
+    private static byte[] generateSig(ContentSigner signer, ASN1Object tbsObj)
+        throws IOException
+    {
+        OutputStream sOut = signer.getOutputStream();
+        tbsObj.encodeTo(sOut, ASN1Encoding.DER);
+        sOut.close();
+
+        return signer.getSignature();
+    }
+
+    static ASN1TaggedObject trimExtensions(int tagNo, Extensions exts)
+    {
+        ASN1Sequence extSeq = ASN1Sequence.getInstance(exts.toASN1Primitive());
+        ASN1EncodableVector extV = new ASN1EncodableVector();
+        for (int i = 0; i != extSeq.size(); i++)
+        {
+            ASN1Sequence ext = ASN1Sequence.getInstance(extSeq.getObjectAt(i));
+
+            if (!Extension.altSignatureValue.equals(ext.getObjectAt(0)))
+            {
+                extV.add(ext);
+            }
+        }
+
+        return new DERTaggedObject(true, tagNo, new DERSequence(extV));
+    }
 }
diff --git a/bcpkix/src/main/java/org/bouncycastle/cert/DeltaCertificateTool.java b/bcpkix/src/main/java/org/bouncycastle/cert/DeltaCertificateTool.java
new file mode 100644
index 0000000..f599a52
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/cert/DeltaCertificateTool.java
@@ -0,0 +1,185 @@
+package org.bouncycastle.cert;
+
+import java.io.IOException;
+
+import org.bouncycastle.asn1.ASN1BitString;
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Encoding;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERBitString;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.DERTaggedObject;
+import org.bouncycastle.asn1.x509.Certificate;
+import org.bouncycastle.asn1.x509.Extension;
+import org.bouncycastle.asn1.x509.ExtensionsGenerator;
+
+/**
+ * General tool for handling the extension described in: https://datatracker.ietf.org/doc/draft-bonnell-lamps-chameleon-certs/
+ */
+public class DeltaCertificateTool
+{
+    public static Extension makeDeltaCertificateExtension(boolean isCritical, X509CertificateHolder deltaCert)
+        throws IOException
+    {
+        ASN1EncodableVector deltaV = new ASN1EncodableVector();
+
+        deltaV.add(new ASN1Integer(deltaCert.getSerialNumber()));
+        deltaV.add(new DERTaggedObject(false, 0, deltaCert.getSignatureAlgorithm()));
+        deltaV.add(new DERTaggedObject(false, 1, deltaCert.getIssuer()));
+
+        ASN1EncodableVector validity = new ASN1EncodableVector(2);
+        validity.add(deltaCert.toASN1Structure().getStartDate());
+        validity.add(deltaCert.toASN1Structure().getEndDate());
+
+        deltaV.add(new DERTaggedObject(false, 2, new DERSequence(validity)));
+        deltaV.add(new DERTaggedObject(false, 3, deltaCert.getSubject()));
+        deltaV.add(deltaCert.getSubjectPublicKeyInfo());
+        if (deltaCert.getExtensions() != null)
+        {
+            deltaV.add(new DERTaggedObject(false, 4, deltaCert.getExtensions()));
+        }
+        deltaV.add(new DERBitString(deltaCert.getSignature()));
+
+        return new Extension(Extension.deltaCertificateDescriptor, isCritical, new DERSequence(deltaV).getEncoded(ASN1Encoding.DER));
+    }
+
+    public static X509CertificateHolder extractDeltaCertificate(X509CertificateHolder originCert)
+    {
+        ASN1ObjectIdentifier deltaExtOid = Extension.deltaCertificateDescriptor;
+        Extension deltaExt = originCert.getExtension(deltaExtOid);
+
+        ASN1Sequence seq = ASN1Sequence.getInstance(deltaExt.getParsedValue());
+//        *      version          [ 0 ]  Version DEFAULT v1(0),
+//        *      serialNumber            CertificateSerialNumber,
+//        *      signature               AlgorithmIdentifier,
+//        *      issuer                  Name,
+//        *      validity                Validity,
+//        *      subject                 Name,
+//        *      subjectPublicKeyInfo    SubjectPublicKeyInfo,
+//        *      issuerUniqueID    [ 1 ] IMPLICIT UniqueIdentifier OPTIONAL,
+//        *      subjectUniqueID   [ 2 ] IMPLICIT UniqueIdentifier OPTIONAL,
+//        *      extensions        [ 3 ] Extensions OPTIONAL
+        ASN1Sequence originTbs = ASN1Sequence.getInstance(originCert.toASN1Structure().getTBSCertificate().toASN1Primitive());
+        int idx = 0;
+        ASN1Encodable[] extracted = originTbs.toArray();
+
+        extracted[0] = originTbs.getObjectAt(0);
+        extracted[1] = ASN1Integer.getInstance(seq.getObjectAt(idx++));
+
+        ASN1Encodable next = seq.getObjectAt(idx++);
+        while (next instanceof ASN1TaggedObject)
+        {
+            ASN1TaggedObject tagged = ASN1TaggedObject.getInstance(next);
+            switch (tagged.getTagNo())
+            {
+            case 0:
+                extracted[2] = ASN1Sequence.getInstance(tagged, false);
+                break;
+            case 1:
+                extracted[3] = ASN1Sequence.getInstance(tagged, true);   // issuer
+                break;
+            case 2:
+                extracted[4] = ASN1Sequence.getInstance(tagged, false);
+                break;
+            case 3:
+                extracted[5] = ASN1Sequence.getInstance((ASN1TaggedObject)next, true);   // subject
+                break;
+            }
+            next = seq.getObjectAt(idx++);
+        }
+
+        extracted[6] = next;  // subjectPublicKey
+
+        if (extracted[2] == null)
+        {
+            extracted[2] = originTbs.getObjectAt(2);
+        }
+
+        if (extracted[3] == null)
+        {
+            extracted[3] = originTbs.getObjectAt(3);
+        }
+
+        if (extracted[4] == null)
+        {
+            extracted[4] = originTbs.getObjectAt(4);
+        }
+
+        if (extracted[5] == null)
+        {
+            extracted[5] = originTbs.getObjectAt(5);
+        }
+
+        ExtensionsGenerator extGen = extractExtensions(originTbs);
+
+        if (idx < (seq.size() - 1))  // last element is the signature
+        {
+            next = seq.getObjectAt(idx++);
+            ASN1TaggedObject tagged = ASN1TaggedObject.getInstance(next);
+            if (tagged.getTagNo() != 4)
+            {
+                throw new IllegalArgumentException("malformed delta extension");
+            }
+
+            ASN1Sequence deltaExts = ASN1Sequence.getInstance(tagged, false);
+
+            for (int i = 0; i != deltaExts.size(); i++)
+            {
+                Extension ext = Extension.getInstance(deltaExts.getObjectAt(i));
+
+                extGen.replaceExtension(ext);
+            }
+
+            extracted[7] = new DERTaggedObject(3, extGen.generate());
+        }
+        else
+        {
+            if (!extGen.isEmpty())
+            {
+                extracted[7] = new DERTaggedObject(3, extGen.generate());
+            }
+            else
+            {
+                extracted[7] = null;
+            }
+        }
+
+        ASN1EncodableVector tbsDeltaCertV = new ASN1EncodableVector(7);
+        for (int i = 0; i != extracted.length; i++)
+        {
+            if (extracted[i] != null)
+            {
+                tbsDeltaCertV.add(extracted[i]);
+            }
+        }
+
+        ASN1EncodableVector certV = new ASN1EncodableVector();
+        certV.add(new DERSequence(tbsDeltaCertV));
+        certV.add(ASN1Sequence.getInstance(extracted[2]));
+        certV.add(ASN1BitString.getInstance(seq.getObjectAt(seq.size() - 1)));
+
+        return new X509CertificateHolder(Certificate.getInstance(new DERSequence(certV)));
+    }
+
+    private static ExtensionsGenerator extractExtensions(ASN1Sequence originTbs)
+    {
+        ASN1ObjectIdentifier deltaExtOid = Extension.deltaCertificateDescriptor;
+        ASN1Sequence originExt = ASN1Sequence.getInstance(ASN1TaggedObject.getInstance(originTbs.getObjectAt(originTbs.size() - 1)), true);
+        ExtensionsGenerator extGen = new ExtensionsGenerator();
+
+        for (int i = 0; i != originExt.size(); i++)
+        {
+            Extension ext = Extension.getInstance(originExt.getObjectAt(i));
+            if (!deltaExtOid.equals(ext.getExtnId()))
+            {
+                extGen.addExtension(ext);
+            }
+        }
+
+        return extGen;
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/cert/X509CRLHolder.java b/bcpkix/src/main/java/org/bouncycastle/cert/X509CRLHolder.java
index 93b8645..7651bbe 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cert/X509CRLHolder.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cert/X509CRLHolder.java
@@ -15,11 +15,18 @@
 import java.util.List;
 import java.util.Set;
 
+import org.bouncycastle.asn1.ASN1EncodableVector;
 import org.bouncycastle.asn1.ASN1Encoding;
 import org.bouncycastle.asn1.ASN1InputStream;
+import org.bouncycastle.asn1.ASN1Integer;
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
 import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERSequence;
 import org.bouncycastle.asn1.x500.X500Name;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.asn1.x509.AltSignatureAlgorithm;
+import org.bouncycastle.asn1.x509.AltSignatureValue;
 import org.bouncycastle.asn1.x509.CertificateList;
 import org.bouncycastle.asn1.x509.Extension;
 import org.bouncycastle.asn1.x509.Extensions;
@@ -39,7 +46,7 @@
     implements Encodable, Serializable
 {
     private static final long serialVersionUID = 20170722001L;
-    
+
     private transient CertificateList x509CRL;
     private transient boolean isIndirect;
     private transient Extensions extensions;
@@ -162,7 +169,7 @@
     public X509CRLEntryHolder getRevokedCertificate(BigInteger serialNumber)
     {
         GeneralNames currentCA = issuerName;
-        for (Enumeration en = x509CRL.getRevokedCertificateEnumeration(); en.hasMoreElements();)
+        for (Enumeration en = x509CRL.getRevokedCertificateEnumeration(); en.hasMoreElements(); )
         {
             TBSCertList.CRLEntry entry = (TBSCertList.CRLEntry)en.nextElement();
 
@@ -197,7 +204,7 @@
         List l = new ArrayList(entries.length);
         GeneralNames currentCA = issuerName;
 
-        for (Enumeration en = x509CRL.getRevokedCertificateEnumeration(); en.hasMoreElements();)
+        for (Enumeration en = x509CRL.getRevokedCertificateEnumeration(); en.hasMoreElements(); )
         {
             TBSCertList.CRLEntry entry = (TBSCertList.CRLEntry)en.nextElement();
             X509CRLEntryHolder crlEntry = new X509CRLEntryHolder(entry, isIndirect, currentCA);
@@ -209,7 +216,7 @@
 
         return l;
     }
-    
+
     /**
      * Return whether or not the holder's CRL contains extensions.
      *
@@ -224,7 +231,6 @@
      * Look up the extension associated with the passed in OID.
      *
      * @param oid the OID of the extension of interest.
-     *
      * @return the extension if present, null otherwise.
      */
     public Extension getExtension(ASN1ObjectIdentifier oid)
@@ -325,6 +331,51 @@
         return verifier.verify(x509CRL.getSignature().getOctets());
     }
 
+    public boolean isAlternativeSignatureValid(ContentVerifierProvider verifierProvider)
+        throws CertException
+    {
+        TBSCertList tbsCrList = x509CRL.getTBSCertList();
+        AltSignatureAlgorithm altSigAlg = AltSignatureAlgorithm.fromExtensions(tbsCrList.getExtensions());
+        AltSignatureValue altSigValue = AltSignatureValue.fromExtensions(tbsCrList.getExtensions());
+
+        ContentVerifier verifier;
+
+        try
+        {
+            verifier = verifierProvider.get(AlgorithmIdentifier.getInstance(altSigAlg.toASN1Primitive()));
+
+            OutputStream sOut = verifier.getOutputStream();
+
+            ASN1Sequence tbsSeq = ASN1Sequence.getInstance(tbsCrList.toASN1Primitive());
+            ASN1EncodableVector v = new ASN1EncodableVector();
+
+            int start = 1;    //  want to skip signature field
+            if (tbsSeq.getObjectAt(0) instanceof ASN1Integer)
+            {
+                v.add(tbsSeq.getObjectAt(0));
+                start++;
+            }
+
+            for (int i = start; i != tbsSeq.size() - 1; i++)
+            {
+                v.add(tbsSeq.getObjectAt(i));
+            }
+            
+            v.add(CertUtils.trimExtensions(0, tbsCrList.getExtensions()));
+
+            new DERSequence(v).encodeTo(sOut, ASN1Encoding.DER);
+
+            sOut.close();
+        }
+        catch (Exception e)
+        {
+            throw new CertException("unable to process signature: " + e.getMessage(), e);
+        }
+
+        return verifier.verify(altSigValue.getSignature().getOctets());
+    }
+
+
     public boolean equals(
         Object o)
     {
diff --git a/bcpkix/src/main/java/org/bouncycastle/cert/X509CertificateHolder.java b/bcpkix/src/main/java/org/bouncycastle/cert/X509CertificateHolder.java
index d78b6a5..bc328fd 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cert/X509CertificateHolder.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cert/X509CertificateHolder.java
@@ -10,10 +10,15 @@
 import java.util.List;
 import java.util.Set;
 
+import org.bouncycastle.asn1.ASN1EncodableVector;
 import org.bouncycastle.asn1.ASN1Encoding;
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERSequence;
 import org.bouncycastle.asn1.x500.X500Name;
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.asn1.x509.AltSignatureAlgorithm;
+import org.bouncycastle.asn1.x509.AltSignatureValue;
 import org.bouncycastle.asn1.x509.Certificate;
 import org.bouncycastle.asn1.x509.Extension;
 import org.bouncycastle.asn1.x509.Extensions;
@@ -243,9 +248,9 @@
     }
 
     /**
-     * Return the bytes making up the signature associated with this attribute certificate.
+     * Return the bytes making up the signature associated with this certificate.
      *
-     * @return the attribute certificate signature bytes.
+     * @return the certificate signature bytes.
      */
     public byte[] getSignature()
     {
@@ -298,6 +303,53 @@
         return verifier.verify(this.getSignature());
     }
 
+    /**
+     * Validate the signature on the certificate in this holder.
+     *
+     * @param verifierProvider a ContentVerifierProvider that can generate a verifier for the signature.
+     * @return true if the signature is valid, false otherwise.
+     * @throws CertException if the signature cannot be processed or is inappropriate.
+     */
+    public boolean isAlternativeSignatureValid(ContentVerifierProvider verifierProvider)
+        throws CertException
+    {
+        TBSCertificate tbsCert = x509Certificate.getTBSCertificate();
+        AltSignatureAlgorithm altSigAlg = AltSignatureAlgorithm.fromExtensions(tbsCert.getExtensions());
+        AltSignatureValue altSigValue = AltSignatureValue.fromExtensions(tbsCert.getExtensions());
+
+        ContentVerifier verifier;
+
+        try
+        {
+            verifier = verifierProvider.get(AlgorithmIdentifier.getInstance(altSigAlg.toASN1Primitive()));
+
+            OutputStream sOut = verifier.getOutputStream();
+
+            ASN1Sequence tbsSeq = ASN1Sequence.getInstance(tbsCert.toASN1Primitive());
+            ASN1EncodableVector v = new ASN1EncodableVector();
+
+            for (int i = 0; i != tbsSeq.size() - 1; i++)
+            {
+                if (i != 2) // signature field - must be ver 3 so version always present
+                {
+                    v.add(tbsSeq.getObjectAt(i));
+                }
+            }
+
+            v.add(CertUtils.trimExtensions(3, tbsCert.getExtensions()));
+
+            new DERSequence(v).encodeTo(sOut, ASN1Encoding.DER);
+
+            sOut.close();
+        }
+        catch (Exception e)
+        {
+            throw new CertException("unable to process signature: " + e.getMessage(), e);
+        }
+
+        return verifier.verify(altSigValue.getSignature().getOctets());
+    }
+
     public boolean equals(
         Object o)
     {
diff --git a/bcpkix/src/main/java/org/bouncycastle/cert/X509v3CertificateBuilder.java b/bcpkix/src/main/java/org/bouncycastle/cert/X509v3CertificateBuilder.java
index 3562cbf..403f561 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cert/X509v3CertificateBuilder.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cert/X509v3CertificateBuilder.java
@@ -14,10 +14,12 @@
 import org.bouncycastle.asn1.ASN1Object;
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
 import org.bouncycastle.asn1.DERBitString;
+import org.bouncycastle.asn1.DERNull;
 import org.bouncycastle.asn1.DERSequence;
 import org.bouncycastle.asn1.x500.X500Name;
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 import org.bouncycastle.asn1.x509.Certificate;
+import org.bouncycastle.asn1.x509.DeltaCertificateDescriptor;
 import org.bouncycastle.asn1.x509.Extension;
 import org.bouncycastle.asn1.x509.Extensions;
 import org.bouncycastle.asn1.x509.ExtensionsGenerator;
@@ -26,6 +28,7 @@
 import org.bouncycastle.asn1.x509.Time;
 import org.bouncycastle.asn1.x509.V3TBSCertificateGenerator;
 import org.bouncycastle.operator.ContentSigner;
+import org.bouncycastle.util.Exceptions;
 
 
 /**
@@ -112,7 +115,16 @@
 
         for (Enumeration en = exts.oids(); en.hasMoreElements();)
         {
-            extGenerator.addExtension(exts.getExtension((ASN1ObjectIdentifier)en.nextElement()));
+            ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier)en.nextElement();
+            // we remove the altSignatureAlgorithm, altSignatureValue, and subjectAltPublicKeyInfo
+            // extensions as they probably need to be regenerated.
+            if (Extension.subjectAltPublicKeyInfo.equals(oid)
+                || Extension.altSignatureAlgorithm.equals(oid)
+                || Extension.altSignatureValue.equals(oid))
+            {
+                continue;
+            }
+            extGenerator.addExtension(exts.getExtension(oid));
         }
     }
 
@@ -140,6 +152,11 @@
 
     private Extension doGetExtension(ASN1ObjectIdentifier oid)
     {
+        if (extGenerator.isEmpty())
+        {
+            return null;
+        }
+        
         Extensions exts = extGenerator.generate();
 
         return exts.getExtension(oid);
@@ -363,6 +380,21 @@
 
         if (!extGenerator.isEmpty())
         {
+            if (extGenerator.hasExtension(Extension.deltaCertificateDescriptor))
+            {
+                Extension deltaExt = extGenerator.getExtension(Extension.deltaCertificateDescriptor);
+                DeltaCertificateDescriptor deltaDesc = DeltaCertificateDescriptor.getInstance(deltaExt.getParsedValue());
+
+                try
+                {
+                    extGenerator.replaceExtension(Extension.deltaCertificateDescriptor, deltaExt.isCritical(),
+                        deltaDesc.trimTo(tbsGen.generateTBSCertificate(), extGenerator.generate()));
+                }
+                catch (IOException e)
+                {
+                    throw new IllegalStateException("unable to replace deltaCertificateDescriptor: " + e.getMessage()) ;
+                }
+            }
             tbsGen.setExtensions(extGenerator.generate());
         }
 
@@ -373,7 +405,75 @@
         }
         catch (IOException e)
         {
-            throw new IllegalArgumentException("cannot produce certificate signature");
+            throw Exceptions.illegalArgumentException("cannot produce certificate signature", e);
+        }
+    }
+
+    /**
+     * Generate an X.509 certificate, based on the current issuer and subject
+     * using the passed in signer and containing altSignatureAlgorithm and altSignatureValue extensions
+     * based on the passed altSigner.
+     *
+     * @param signer the content signer to be used to generate the signature validating the certificate.
+     * @param altSigner the content signer used to create the altSignatureAlgorithm and altSignatureValue extension.
+     * @return a holder containing the resulting signed certificate.
+     */
+    public X509CertificateHolder build(
+        ContentSigner signer,
+        boolean isCritical,
+        ContentSigner altSigner)
+    {
+        try
+        {
+            extGenerator.addExtension(Extension.altSignatureAlgorithm, isCritical, altSigner.getAlgorithmIdentifier());
+        }
+        catch (IOException e)
+        {
+            throw Exceptions.illegalStateException("cannot add altSignatureAlgorithm extension", e);
+        }
+
+        if (extGenerator.hasExtension(Extension.deltaCertificateDescriptor))
+        {
+            tbsGen.setSignature(signer.getAlgorithmIdentifier());
+            
+            Extension deltaExt = extGenerator.getExtension(Extension.deltaCertificateDescriptor);
+            DeltaCertificateDescriptor deltaDesc = DeltaCertificateDescriptor.getInstance(deltaExt.getParsedValue());
+
+            try
+            {
+                // the altSignatureValue is not present yet, but it must be in the deltaCertificate and
+                // it must be different (by definition!). We add a dummy one to trigger inclusion.
+                ExtensionsGenerator tmpExtGen = new ExtensionsGenerator();
+                tmpExtGen.addExtension(extGenerator.generate());
+                tmpExtGen.addExtension(Extension.altSignatureValue, false, DERNull.INSTANCE);
+
+                extGenerator.replaceExtension(Extension.deltaCertificateDescriptor, deltaExt.isCritical(),
+                    deltaDesc.trimTo(tbsGen.generateTBSCertificate(), tmpExtGen.generate()));
+            }
+            catch (IOException e)
+            {
+                throw new IllegalStateException("unable to replace deltaCertificateDescriptor: " + e.getMessage());
+            }
+        }
+
+        tbsGen.setSignature(null);
+
+        tbsGen.setExtensions(extGenerator.generate());
+
+        try
+        {
+            extGenerator.addExtension(Extension.altSignatureValue, isCritical, new DERBitString(generateSig(altSigner, tbsGen.generatePreTBSCertificate())));
+
+            tbsGen.setSignature(signer.getAlgorithmIdentifier());
+
+            tbsGen.setExtensions(extGenerator.generate());
+            
+            TBSCertificate tbsCert = tbsGen.generateTBSCertificate();
+            return new X509CertificateHolder(generateStructure(tbsCert, signer.getAlgorithmIdentifier(), generateSig(signer, tbsCert)));
+        }
+        catch (IOException e)
+        {
+            throw Exceptions.illegalArgumentException("cannot produce certificate signature", e);
         }
     }
 
diff --git a/bcpkix/src/main/java/org/bouncycastle/cert/ocsp/BasicOCSPRespBuilder.java b/bcpkix/src/main/java/org/bouncycastle/cert/ocsp/BasicOCSPRespBuilder.java
index 5695907..76b8df6 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cert/ocsp/BasicOCSPRespBuilder.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cert/ocsp/BasicOCSPRespBuilder.java
@@ -35,7 +35,7 @@
     private Extensions  responseExtensions = null;
     private RespID          responderID;
 
-    private class ResponseObject
+    private static class ResponseObject
     {
         CertificateID         certId;
         CertStatus            certStatus;
diff --git a/bcpkix/src/main/java/org/bouncycastle/cert/ocsp/OCSPReqBuilder.java b/bcpkix/src/main/java/org/bouncycastle/cert/ocsp/OCSPReqBuilder.java
index 3f3c954..389952c 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cert/ocsp/OCSPReqBuilder.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cert/ocsp/OCSPReqBuilder.java
@@ -26,7 +26,7 @@
     private GeneralName     requestorName = null;
     private Extensions  requestExtensions = null;
     
-    private class RequestObject
+    private static class RequestObject
     {
         CertificateID   certId;
         Extensions  extensions;
diff --git a/bcpkix/src/main/java/org/bouncycastle/cert/ocsp/RevokedStatus.java b/bcpkix/src/main/java/org/bouncycastle/cert/ocsp/RevokedStatus.java
index d349f07..7124f38 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cert/ocsp/RevokedStatus.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cert/ocsp/RevokedStatus.java
@@ -19,7 +19,12 @@
     {
         this.info = info;
     }
-    
+
+    public RevokedStatus(Date revocationDate)
+    {
+        this.info = new RevokedInfo(new ASN1GeneralizedTime(revocationDate));
+    }
+
     public RevokedStatus(
         Date        revocationDate,
         int         reason)
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/CMSPatchKit.java b/bcpkix/src/main/java/org/bouncycastle/cms/CMSPatchKit.java
new file mode 100644
index 0000000..1237988
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/CMSPatchKit.java
@@ -0,0 +1,71 @@
+package org.bouncycastle.cms;
+
+import java.io.IOException;
+
+import org.bouncycastle.asn1.ASN1Encoding;
+import org.bouncycastle.asn1.cms.SignerInfo;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+
+/**
+ * Toolkit methods for dealing with common errors in CMS
+ * classes.
+ */
+public class CMSPatchKit
+{
+    /**
+     * Create a SignerInformation based on original which uses definite-length
+     * rather than DER encoding for verifying the signature on the signed attributes.
+     *
+     * @param original the source SignerInformation
+     */
+    public static SignerInformation createNonDERSignerInfo(
+        SignerInformation original)
+    {
+        return new DLSignerInformation(original);
+    }
+
+    /**
+     * Create a SignerInformation based on original has it's signatureAlgorithm replaced
+     * with the passed in AlgorithmIdentifier.
+     *
+     * @param original the source SignerInformation
+     */
+    public static SignerInformation createWithSignatureAlgorithm(
+        SignerInformation original,
+        AlgorithmIdentifier signatureAlgorithm)
+    {
+         return new ModEncAlgSignerInformation(original, signatureAlgorithm);
+    }
+
+    private static class DLSignerInformation
+        extends SignerInformation
+    {
+        protected DLSignerInformation(SignerInformation baseInfo)
+        {
+            super(baseInfo);
+        }
+
+        public byte[] getEncodedSignedAttributes()
+            throws IOException
+        {
+            return signedAttributeSet.getEncoded(ASN1Encoding.DL);
+        }
+    }
+
+    private static class ModEncAlgSignerInformation
+        extends SignerInformation
+    {
+        protected ModEncAlgSignerInformation(
+            SignerInformation baseInfo,
+            AlgorithmIdentifier signatureAlgorithm)
+        {
+            super(baseInfo, editEncAlg(baseInfo.info, signatureAlgorithm));
+        }
+
+        private static SignerInfo editEncAlg(SignerInfo info, AlgorithmIdentifier signatureAlgorithm)
+        {
+            return new SignerInfo(info.getSID(), info.getDigestAlgorithm(), info.getAuthenticatedAttributes(),
+                signatureAlgorithm, info.getEncryptedDigest(), info.getUnauthenticatedAttributes());
+        }
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/CMSSignedData.java b/bcpkix/src/main/java/org/bouncycastle/cms/CMSSignedData.java
index 3110fa6..ef1e3d6 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cms/CMSSignedData.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/CMSSignedData.java
@@ -21,7 +21,6 @@
 import org.bouncycastle.asn1.ASN1Sequence;
 import org.bouncycastle.asn1.ASN1Set;
 import org.bouncycastle.asn1.BERSequence;
-import org.bouncycastle.asn1.DERSet;
 import org.bouncycastle.asn1.DLSet;
 import org.bouncycastle.asn1.cms.ContentInfo;
 import org.bouncycastle.asn1.cms.SignedData;
@@ -30,15 +29,17 @@
 import org.bouncycastle.cert.X509AttributeCertificateHolder;
 import org.bouncycastle.cert.X509CRLHolder;
 import org.bouncycastle.cert.X509CertificateHolder;
+import org.bouncycastle.operator.DefaultDigestAlgorithmIdentifierFinder;
+import org.bouncycastle.operator.DigestAlgorithmIdentifierFinder;
 import org.bouncycastle.operator.OperatorCreationException;
 import org.bouncycastle.util.Encodable;
 import org.bouncycastle.util.Store;
 
 /**
  * general class for handling a pkcs7-signature message.
- *
+ * <p>
  * A simple example of usage - note, in the example below the validity of
- * the certificate isn't verified, just the fact that one of the certs 
+ * the certificate isn't verified, just the fact that one of the certs
  * matches the given signer...
  *
  * <pre>
@@ -46,7 +47,7 @@
  *  SignerInformationStore  signers = s.getSignerInfos();
  *  Collection              c = signers.getSigners();
  *  Iterator                it = c.iterator();
- *  
+ *
  *  while (it.hasNext())
  *  {
  *      SignerInformation   signer = (SignerInformation)it.next();
@@ -54,11 +55,11 @@
  *
  *      Iterator              certIt = certCollection.iterator();
  *      X509CertificateHolder cert = (X509CertificateHolder)certIt.next();
- *  
+ *
  *      if (signer.verify(new JcaSimpleSignerInfoVerifierBuilder().setProvider("BC").build(cert)))
  *      {
  *          verified++;
- *      }   
+ *      }
  *  }
  * </pre>
  */
@@ -66,16 +67,18 @@
     implements Encodable
 {
     private static final CMSSignedHelper HELPER = CMSSignedHelper.INSTANCE;
-    
-    SignedData              signedData;
-    ContentInfo             contentInfo;
-    CMSTypedData            signedContent;
-    SignerInformationStore  signerInfoStore;
+    private static final DefaultDigestAlgorithmIdentifierFinder DIGEST_ALG_ID_FINDER =
+        new DefaultDigestAlgorithmIdentifierFinder();
 
-    private Map             hashes;
+    SignedData signedData;
+    ContentInfo contentInfo;
+    CMSTypedData signedContent;
+    SignerInformationStore signerInfoStore;
+
+    private Map hashes;
 
     private CMSSignedData(
-        CMSSignedData   c)
+        CMSSignedData c)
     {
         this.signedData = c.signedData;
         this.contentInfo = c.contentInfo;
@@ -84,15 +87,15 @@
     }
 
     public CMSSignedData(
-        byte[]      sigBlock)
+        byte[] sigBlock)
         throws CMSException
     {
         this(CMSUtils.readContentInfo(sigBlock));
     }
 
     public CMSSignedData(
-        CMSProcessable  signedContent,
-        byte[]          sigBlock)
+        CMSProcessable signedContent,
+        byte[] sigBlock)
         throws CMSException
     {
         this(signedContent, CMSUtils.readContentInfo(sigBlock));
@@ -101,12 +104,12 @@
     /**
      * Content with detached signature, digests precomputed
      *
-     * @param hashes a map of precomputed digests for content indexed by name of hash.
+     * @param hashes   a map of precomputed digests for content indexed by name of hash.
      * @param sigBlock the signature object.
      */
     public CMSSignedData(
-        Map     hashes,
-        byte[]  sigBlock)
+        Map hashes,
+        byte[] sigBlock)
         throws CMSException
     {
         this(hashes, CMSUtils.readContentInfo(sigBlock));
@@ -116,11 +119,11 @@
      * base constructor - content with detached signature.
      *
      * @param signedContent the content that was signed.
-     * @param sigData the signature object.
+     * @param sigData       the signature object.
      */
     public CMSSignedData(
-        CMSProcessable  signedContent,
-        InputStream     sigData)
+        CMSProcessable signedContent,
+        InputStream sigData)
         throws CMSException
     {
         this(signedContent, CMSUtils.readContentInfo(new ASN1InputStream(sigData)));
@@ -137,8 +140,8 @@
     }
 
     public CMSSignedData(
-        final CMSProcessable  signedContent,
-        ContentInfo     sigData)
+        final CMSProcessable signedContent,
+        ContentInfo sigData)
         throws CMSException
     {
         if (signedContent instanceof CMSTypedData)
@@ -172,8 +175,8 @@
     }
 
     public CMSSignedData(
-        Map             hashes,
-        ContentInfo     sigData)
+        Map hashes,
+        ContentInfo sigData)
         throws CMSException
     {
         this.hashes = hashes;
@@ -244,8 +247,8 @@
     {
         if (signerInfoStore == null)
         {
-            ASN1Set         s = signedData.getSignerInfos();
-            List            signerInfos = new ArrayList();
+            ASN1Set s = signedData.getSignerInfos();
+            List signerInfos = new ArrayList();
 
             for (int i = 0; i != s.size(); i++)
             {
@@ -328,7 +331,6 @@
      * this SignedData structure.
      *
      * @param otherRevocationInfoFormat OID of the format type been looked for.
-     *
      * @return a Store of ASN1Encodable objects representing any objects of otherRevocationInfoFormat found.
      *
     public Store getOtherRevocationInfo(ASN1ObjectIdentifier otherRevocationInfoFormat)
@@ -345,9 +347,9 @@
      */
     public Set<AlgorithmIdentifier> getDigestAlgorithmIDs()
     {
-        Set<AlgorithmIdentifier> digests = new HashSet<AlgorithmIdentifier>(signedData.getDigestAlgorithms().size());
+        Set<AlgorithmIdentifier> digests = new HashSet<AlgorithmIdentifier>();
 
-        for (Enumeration en = signedData.getDigestAlgorithms().getObjects(); en.hasMoreElements();)
+        for (Enumeration en = signedData.getDigestAlgorithms().getObjects(); en.hasMoreElements(); )
         {
             digests.add(AlgorithmIdentifier.getInstance(en.nextElement()));
         }
@@ -358,14 +360,14 @@
     /**
      * Return the a string representation of the OID associated with the
      * encapsulated content info structure carried in the signed data.
-     * 
+     *
      * @return the OID for the content type.
      */
     public String getSignedContentTypeOID()
     {
         return signedData.getEncapContentInfo().getContentType().getId();
     }
-    
+
     public CMSTypedData getSignedContent()
     {
         return signedContent;
@@ -394,7 +396,18 @@
      * return the ASN.1 encoded representation of this object using the specified encoding.
      *
      * @param encoding the ASN.1 encoding format to use ("BER", "DL", or "DER").
-     */
+     *
+    public byte[] getEncoded(String encoding)
+        throws IOException
+    {
+        return contentInfo.getEncoded(encoding);
+    }
+
+    /**
+     * return the ASN.1 encoded representation of this object using the specified encoding.
+     *
+     * @param encoding the ASN.1 encoding format to use ("BER", "DL", or "DER").
+     *
     public byte[] getEncoded(String encoding)
         throws IOException
     {
@@ -405,9 +418,9 @@
      * Verify all the SignerInformation objects and their associated counter signatures attached
      * to this CMS SignedData object.
      *
-     * @param verifierProvider  a provider of SignerInformationVerifier objects.
+     * @param verifierProvider a provider of SignerInformationVerifier objects.
      * @return true if all verify, false otherwise.
-     * @throws CMSException  if an exception occurs during the verification process.
+     * @throws CMSException if an exception occurs during the verification process.
      *
     public boolean verifySignatures(SignerInformationVerifierProvider verifierProvider)
         throws CMSException
@@ -419,17 +432,17 @@
      * Verify all the SignerInformation objects and optionally their associated counter signatures attached
      * to this CMS SignedData object.
      *
-     * @param verifierProvider  a provider of SignerInformationVerifier objects.
+     * @param verifierProvider        a provider of SignerInformationVerifier objects.
      * @param ignoreCounterSignatures if true don't check counter signatures. If false check counter signatures as well.
      * @return true if all verify, false otherwise.
-     * @throws CMSException  if an exception occurs during the verification process.
+     * @throws CMSException if an exception occurs during the verification process.
      *
     public boolean verifySignatures(SignerInformationVerifierProvider verifierProvider, boolean ignoreCounterSignatures)
         throws CMSException
     {
         Collection signers = this.getSignerInfos().getSigners();
 
-        for (Iterator it = signers.iterator(); it.hasNext();)
+        for (Iterator it = signers.iterator(); it.hasNext(); )
         {
             SignerInformation signer = (SignerInformation)it.next();
 
@@ -446,7 +459,7 @@
                 {
                     Collection counterSigners = signer.getCounterSignatures().getSigners();
 
-                    for  (Iterator cIt = counterSigners.iterator(); cIt.hasNext();)
+                    for (Iterator cIt = counterSigners.iterator(); cIt.hasNext(); )
                     {
                         if (!verifyCounterSignature((SignerInformation)cIt.next(), verifierProvider))
                         {
@@ -475,7 +488,7 @@
         }
 
         Collection counterSigners = counterSigner.getCounterSignatures().getSigners();
-        for  (Iterator cIt = counterSigners.iterator(); cIt.hasNext();)
+        for (Iterator cIt = counterSigners.iterator(); cIt.hasNext(); )
         {
             if (!verifyCounterSignature((SignerInformation)cIt.next(), verifierProvider))
             {
@@ -489,24 +502,119 @@
     // END Android-removed: Unknown reason
 
     /**
+     * Return a new CMSSignedData which guarantees to have the passed in digestAlgorithm
+     * in it. Uses the current DigestAlgorithmIdentifierFinder for creating the digest sets.
+     *
+     * @param signedData      the signed data object to be used as a base.
+     * @param digestAlgorithm the digest algorithm to be added to the signed data.
+     * @return a new signed data object.
+     */
+    public static CMSSignedData addDigestAlgorithm(CMSSignedData signedData, AlgorithmIdentifier digestAlgorithm)
+    {
+        return addDigestAlgorithm(signedData, digestAlgorithm, DIGEST_ALG_ID_FINDER);
+    }
+
+    /**
+     * Return a new CMSSignedData which guarantees to have the passed in digestAlgorithm
+     * in it. Uses the passed in DigestAlgorithmIdentifierFinder for creating the digest sets.
+     *
+     * @param signedData      the signed data object to be used as a base.
+     * @param digestAlgorithm the digest algorithm to be added to the signed data.
+     * @param digestAlgIdFinder      the digest algorithmID map to generate the digest set with.
+     * @return a new signed data object.
+     */
+    public static CMSSignedData addDigestAlgorithm(CMSSignedData signedData, AlgorithmIdentifier digestAlgorithm,
+        DigestAlgorithmIdentifierFinder digestAlgIdFinder)
+    {
+        Set<AlgorithmIdentifier> digestAlgorithms = signedData.getDigestAlgorithmIDs();
+        AlgorithmIdentifier digestAlg = HELPER.fixDigestAlgID(digestAlgorithm, digestAlgIdFinder);
+
+        //
+        // if the algorithm is already present there is no need to add it.
+        //
+        if (digestAlgorithms.contains(digestAlg))
+        {
+            return signedData;
+        }
+
+        //
+        // copy
+        //
+        CMSSignedData cms = new CMSSignedData(signedData);
+
+        //
+        // build up the new set
+        //
+        Set<AlgorithmIdentifier> digestAlgs = new HashSet<AlgorithmIdentifier>();
+
+        Iterator it = digestAlgorithms.iterator();
+        while (it.hasNext())
+        {
+            digestAlgs.add(HELPER.fixDigestAlgID((AlgorithmIdentifier)it.next(), digestAlgIdFinder));
+        }
+        digestAlgs.add(digestAlg);
+
+        ASN1Set digestSet = CMSUtils.convertToDlSet(digestAlgs);
+        ASN1Sequence sD = (ASN1Sequence)signedData.signedData.toASN1Primitive();
+
+        //
+        // signers are the last item in the sequence.
+        //
+        ASN1EncodableVector vec = new ASN1EncodableVector(sD.size());
+        vec.add(sD.getObjectAt(0)); // version
+        vec.add(digestSet);
+
+        for (int i = 2; i != sD.size(); i++)
+        {
+            vec.add(sD.getObjectAt(i));
+        }
+
+        cms.signedData = SignedData.getInstance(new BERSequence(vec));
+
+        //
+        // replace the contentInfo with the new one
+        //
+        cms.contentInfo = new ContentInfo(cms.contentInfo.getContentType(), cms.signedData);
+
+        return cms;
+    }
+
+    /**
      * Replace the SignerInformation store associated with this
-     * CMSSignedData object with the new one passed in. You would
-     * probably only want to do this if you wanted to change the unsigned 
+     * CMSSignedData object with the new one passed in using the current
+     * DigestAlgorithmIdentifierFinder for creating the digest sets. You would
+     * probably only want to do this if you wanted to change the unsigned
      * attributes associated with a signer, or perhaps delete one.
-     * 
-     * @param signedData the signed data object to be used as a base.
+     *
+     * @param signedData             the signed data object to be used as a base.
      * @param signerInformationStore the new signer information store to use.
      * @return a new signed data object.
      */
-    public static CMSSignedData replaceSigners(
-        CMSSignedData           signedData,
-        SignerInformationStore  signerInformationStore)
+    public static CMSSignedData replaceSigners(CMSSignedData signedData, SignerInformationStore signerInformationStore)
+    {
+        return replaceSigners(signedData, signerInformationStore, DIGEST_ALG_ID_FINDER);
+    }
+
+    /**
+     * Replace the SignerInformation store associated with this
+     * CMSSignedData object with the new one passed in using the passed in
+     * DigestAlgorithmIdentifierFinder for creating the digest sets. You would
+     * probably only want to do this if you wanted to change the unsigned
+     * attributes associated with a signer, or perhaps delete one.
+     *
+     * @param signedData             the signed data object to be used as a base.
+     * @param signerInformationStore the new signer information store to use.
+     * @param digestAlgIdFinder      the digest algorithmID map to generate the digest set with.
+     * @return a new signed data object.
+     */
+    public static CMSSignedData replaceSigners(CMSSignedData signedData, SignerInformationStore signerInformationStore,
+        DigestAlgorithmIdentifierFinder digestAlgIdFinder)
     {
         //
         // copy
         //
-        CMSSignedData   cms = new CMSSignedData(signedData);
-        
+        CMSSignedData cms = new CMSSignedData(signedData);
+
         //
         // replace the store
         //
@@ -515,43 +623,44 @@
         //
         // replace the signers in the SignedData object
         //
-        ASN1EncodableVector digestAlgs = new ASN1EncodableVector();
-        ASN1EncodableVector vec = new ASN1EncodableVector();
-        
-        Iterator    it = signerInformationStore.getSigners().iterator();
+        Set<AlgorithmIdentifier> digestAlgs = new HashSet<AlgorithmIdentifier>();
+
+        Collection<SignerInformation> signers = signerInformationStore.getSigners();
+        ASN1EncodableVector vec = new ASN1EncodableVector(signers.size());
+
+        Iterator it = signers.iterator();
         while (it.hasNext())
         {
             SignerInformation signer = (SignerInformation)it.next();
-            digestAlgs.add(CMSSignedHelper.INSTANCE.fixAlgID(signer.getDigestAlgorithmID()));
+            CMSUtils.addDigestAlgs(digestAlgs, signer, digestAlgIdFinder);
             vec.add(signer.toASN1Structure());
         }
 
-        ASN1Set             digests = new DERSet(digestAlgs);
-        ASN1Set             signers = new DLSet(vec);
-        ASN1Sequence        sD = (ASN1Sequence)signedData.signedData.toASN1Primitive();
+        ASN1Set digestSet = CMSUtils.convertToDlSet(digestAlgs);
+        ASN1Set signerSet = new DLSet(vec);
+        ASN1Sequence sD = (ASN1Sequence)signedData.signedData.toASN1Primitive();
 
-        vec = new ASN1EncodableVector();
-        
         //
         // signers are the last item in the sequence.
         //
+        vec = new ASN1EncodableVector(sD.size());
         vec.add(sD.getObjectAt(0)); // version
-        vec.add(digests);
+        vec.add(digestSet);
 
         for (int i = 2; i != sD.size() - 1; i++)
         {
             vec.add(sD.getObjectAt(i));
         }
-        
-        vec.add(signers);
-        
+
+        vec.add(signerSet);
+
         cms.signedData = SignedData.getInstance(new BERSequence(vec));
-        
+
         //
         // replace the contentInfo with the new one
         //
         cms.contentInfo = new ContentInfo(cms.contentInfo.getContentType(), cms.signedData);
-        
+
         return cms;
     }
 
@@ -559,24 +668,24 @@
      * Replace the certificate and CRL information associated with this
      * CMSSignedData object with the new one passed in.
      *
-     * @param signedData the signed data object to be used as a base.
+     * @param signedData   the signed data object to be used as a base.
      * @param certificates the new certificates to be used.
-     * @param attrCerts the new attribute certificates to be used.
-     * @param revocations the new CRLs to be used - a collection of X509CRLHolder objects, OtherRevocationInfoFormat, or both.
+     * @param attrCerts    the new attribute certificates to be used.
+     * @param revocations  the new CRLs to be used - a collection of X509CRLHolder objects, OtherRevocationInfoFormat, or both.
      * @return a new signed data object.
-     * @exception CMSException if there is an error processing the CertStore
+     * @throws CMSException if there is an error processing the CertStore
      */
     public static CMSSignedData replaceCertificatesAndCRLs(
-        CMSSignedData   signedData,
-        Store           certificates,
-        Store           attrCerts,
-        Store           revocations)
+        CMSSignedData signedData,
+        Store certificates,
+        Store attrCerts,
+        Store revocations)
         throws CMSException
     {
         //
         // copy
         //
-        CMSSignedData   cms = new CMSSignedData(signedData);
+        CMSSignedData cms = new CMSSignedData(signedData);
 
         //
         // replace the certs and revocations in the SignedData object
@@ -594,7 +703,7 @@
             }
             if (attrCerts != null)
             {
-                certs.addAll(CMSUtils.getAttributeCertificatesFromStore(attrCerts));   
+                certs.addAll(CMSUtils.getAttributeCertificatesFromStore(attrCerts));
             }
 
             ASN1Set set = CMSUtils.createBerSetFromList(certs);
@@ -619,10 +728,10 @@
         // replace the CMS structure.
         //
         cms.signedData = new SignedData(signedData.signedData.getDigestAlgorithms(),
-                                   signedData.signedData.getEncapContentInfo(),
-                                   certSet,
-                                   crlSet,
-                                   signedData.signedData.getSignerInfos());
+            signedData.signedData.getEncapContentInfo(),
+            certSet,
+            crlSet,
+            signedData.signedData.getSignerInfos());
 
         //
         // replace the contentInfo with the new one
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/CMSSignedDataGenerator.java b/bcpkix/src/main/java/org/bouncycastle/cms/CMSSignedDataGenerator.java
index 5417ce4..ab63c1d 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cms/CMSSignedDataGenerator.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/CMSSignedDataGenerator.java
@@ -5,18 +5,23 @@
 import java.io.OutputStream;
 import java.util.ArrayList;
 import java.util.Iterator;
+import java.util.LinkedHashSet;
 import java.util.List;
+import java.util.Set;
 
 import org.bouncycastle.asn1.ASN1EncodableVector;
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
 import org.bouncycastle.asn1.ASN1OctetString;
 import org.bouncycastle.asn1.ASN1Set;
 import org.bouncycastle.asn1.BEROctetString;
+import org.bouncycastle.asn1.DEROctetString;
 import org.bouncycastle.asn1.DERSet;
 import org.bouncycastle.asn1.cms.CMSObjectIdentifiers;
 import org.bouncycastle.asn1.cms.ContentInfo;
 import org.bouncycastle.asn1.cms.SignedData;
 import org.bouncycastle.asn1.cms.SignerInfo;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.operator.DigestAlgorithmIdentifierFinder;
 
 /**
  * general class for generating a pkcs7-signature message.
@@ -48,6 +53,7 @@
     extends CMSSignedGenerator
 {
     private List signerInfs = new ArrayList();
+    private boolean isDefiniteLength = false;
 
     /**
      * base constructor
@@ -57,6 +63,24 @@
     }
 
     /**
+     * base constructor with a custom DigestAlgorithmIdentifierFinder
+     */
+    public CMSSignedDataGenerator(DigestAlgorithmIdentifierFinder digestAlgIdFinder)
+    {
+        super(digestAlgIdFinder);
+    }
+
+    /**
+     * Specify use of definite length rather than indefinite length encoding.
+     *
+     * @param isDefiniteLength true use definite length, false use indefinite (default false).
+     */
+    public void setDefiniteLengthEncoding(boolean isDefiniteLength)
+    {
+        this.isDefiniteLength = isDefiniteLength;
+    }
+
+    /**
      * Generate a CMS Signed Data object carrying a detached CMS signature.
      *
      * @param content the content to be signed.
@@ -116,7 +140,7 @@
 //            // TODO signedAttrs must be present for all signers
 //        }
 
-        ASN1EncodableVector  digestAlgs = new ASN1EncodableVector();
+        Set<AlgorithmIdentifier> digestAlgs = new LinkedHashSet<AlgorithmIdentifier>();
         ASN1EncodableVector  signerInfos = new ASN1EncodableVector();
 
         digests.clear();  // clear the current preserved digest state
@@ -127,8 +151,7 @@
         for (Iterator it = _signers.iterator(); it.hasNext();)
         {
             SignerInformation signer = (SignerInformation)it.next();
-            digestAlgs.add(CMSSignedHelper.INSTANCE.fixAlgID(signer.getDigestAlgorithmID()));
-
+            CMSUtils.addDigestAlgs(digestAlgs, signer, digestAlgIdFinder);
             // TODO Verify the content type and calculated digest match the precalculated SignerInfo
             signerInfos.add(signer.toASN1Structure());
         }
@@ -167,7 +190,14 @@
 
             if (encapsulate)
             {
-                octs = new BEROctetString(bOut.toByteArray());
+                if (isDefiniteLength)
+                {
+                    octs = new DEROctetString(bOut.toByteArray());
+                }
+                else
+                {
+                    octs = new BEROctetString(bOut.toByteArray());
+                }
             }
         }
 
@@ -191,20 +221,34 @@
 
         if (certs.size() != 0)
         {
-            certificates = CMSUtils.createBerSetFromList(certs);
+            if (isDefiniteLength)
+            {
+                certificates = CMSUtils.createDlSetFromList(certs);
+            }
+            else
+            {
+                certificates = CMSUtils.createBerSetFromList(certs);
+            }
         }
 
         ASN1Set certrevlist = null;
 
         if (crls.size() != 0)
         {
-            certrevlist = CMSUtils.createBerSetFromList(crls);
+            if (isDefiniteLength)
+            {
+                certrevlist = CMSUtils.createDlSetFromList(crls);
+            }
+            else
+            {
+                certrevlist = CMSUtils.createBerSetFromList(crls);
+            }
         }
 
         ContentInfo encInfo = new ContentInfo(contentTypeOID, octs);
 
         SignedData  sd = new SignedData(
-                                 new DERSet(digestAlgs),
+                                 CMSUtils.convertToDlSet(digestAlgs),
                                  encInfo,
                                  certificates,
                                  certrevlist,
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/CMSSignedGenerator.java b/bcpkix/src/main/java/org/bouncycastle/cms/CMSSignedGenerator.java
index 995de4f..79d776e 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cms/CMSSignedGenerator.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/CMSSignedGenerator.java
@@ -11,8 +11,8 @@
 import org.bouncycastle.asn1.ASN1Encodable;
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
 import org.bouncycastle.asn1.DERTaggedObject;
-import org.bouncycastle.asn1.cms.CMSObjectIdentifiers;
 // Android-removed: Unsupported algorithms
+// import org.bouncycastle.asn1.cms.CMSObjectIdentifiers;
 // import org.bouncycastle.asn1.cms.OtherRevocationInfoFormat;
 // import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers;
 import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
@@ -26,6 +26,8 @@
 import org.bouncycastle.cert.X509AttributeCertificateHolder;
 import org.bouncycastle.cert.X509CRLHolder;
 import org.bouncycastle.cert.X509CertificateHolder;
+import org.bouncycastle.operator.DefaultDigestAlgorithmIdentifierFinder;
+import org.bouncycastle.operator.DigestAlgorithmIdentifierFinder;
 import org.bouncycastle.util.Arrays;
 import org.bouncycastle.util.Store;
 
@@ -34,7 +36,6 @@
     /**
      * Default type for the signed data.
      */
-    public static final String  DATA = CMSObjectIdentifiers.data.getId();
     
     public static final String  DIGEST_SHA1 = OIWObjectIdentifiers.idSHA1.getId();
     public static final String  DIGEST_SHA224 = NISTObjectIdentifiers.id_sha224.getId();
@@ -43,6 +44,7 @@
     public static final String  DIGEST_SHA512 = NISTObjectIdentifiers.id_sha512.getId();
     public static final String  DIGEST_MD5 = PKCSObjectIdentifiers.md5.getId();
     // BEGIN Android-removed: Unsupported algorithms
+    // public static final String  DATA = CMSObjectIdentifiers.data.getId();
     // public static final String  DIGEST_GOST3411 = CryptoProObjectIdentifiers.gostR3411.getId();
     // public static final String  DIGEST_RIPEMD128 = TeleTrusTObjectIdentifiers.ripemd128.getId();
     // public static final String  DIGEST_RIPEMD160 = TeleTrusTObjectIdentifiers.ripemd160.getId();
@@ -92,11 +94,19 @@
     protected List signerGens = new ArrayList();
     protected Map digests = new HashMap();
 
+    protected DigestAlgorithmIdentifierFinder digestAlgIdFinder;
+
     /**
      * base constructor
      */
     protected CMSSignedGenerator()
     {
+        this(new DefaultDigestAlgorithmIdentifierFinder());
+    }
+
+    protected CMSSignedGenerator(DigestAlgorithmIdentifierFinder digestAlgIdFinder)
+    {
+        this.digestAlgIdFinder = digestAlgIdFinder;
     }
 
     protected Map getBaseParameters(ASN1ObjectIdentifier contentType, AlgorithmIdentifier digAlgId, byte[] hash)
@@ -195,7 +205,9 @@
         ASN1ObjectIdentifier   otherRevocationInfoFormat,
         ASN1Encodable          otherRevocationInfo)
     {
-        crls.add(new DERTaggedObject(false, 1, new OtherRevocationInfoFormat(otherRevocationInfoFormat, otherRevocationInfo)));
+        OtherRevocationInfoFormat infoFormat = new OtherRevocationInfoFormat(otherRevocationInfoFormat, otherRevocationInfo);
+        CMSUtils.validateInfoFormat(infoFormat);
+        crls.add(new DERTaggedObject(false, 1, infoFormat));
     }
 
     /**
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/CMSSignedHelper.java b/bcpkix/src/main/java/org/bouncycastle/cms/CMSSignedHelper.java
index e71d796..0d43ecd 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cms/CMSSignedHelper.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/CMSSignedHelper.java
@@ -12,11 +12,12 @@
 import org.bouncycastle.asn1.ASN1Sequence;
 import org.bouncycastle.asn1.ASN1Set;
 import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.BERTags;
 import org.bouncycastle.asn1.DERNull;
 // Android-removed: Unsupported algorithms
 // import org.bouncycastle.asn1.cms.OtherRevocationInfoFormat;
 // import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers;
-import org.bouncycastle.asn1.eac.EACObjectIdentifiers;
+// import org.bouncycastle.asn1.eac.EACObjectIdentifiers;
 import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
 import org.bouncycastle.asn1.oiw.OIWObjectIdentifiers;
 import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
@@ -31,6 +32,7 @@
 import org.bouncycastle.cert.X509AttributeCertificateHolder;
 import org.bouncycastle.cert.X509CRLHolder;
 import org.bouncycastle.cert.X509CertificateHolder;
+import org.bouncycastle.operator.DigestAlgorithmIdentifierFinder;
 import org.bouncycastle.util.CollectionStore;
 import org.bouncycastle.util.Store;
 
@@ -99,9 +101,6 @@
         addEntries(NISTObjectIdentifiers.id_ecdsa_with_sha3_256,  "ECDSA");
         addEntries(NISTObjectIdentifiers.id_ecdsa_with_sha3_384, "ECDSA");
         addEntries(NISTObjectIdentifiers.id_ecdsa_with_sha3_512,  "ECDSA");
-        */
-        // END Android-removed: Unsupported algorithms
-        addEntries(X9ObjectIdentifiers.id_dsa_with_sha1,  "DSA");
         addEntries(EACObjectIdentifiers.id_TA_ECDSA_SHA_1,  "ECDSA");
         addEntries(EACObjectIdentifiers.id_TA_ECDSA_SHA_224,  "ECDSA");
         addEntries(EACObjectIdentifiers.id_TA_ECDSA_SHA_256,  "ECDSA");
@@ -111,6 +110,9 @@
         addEntries(EACObjectIdentifiers.id_TA_RSA_v1_5_SHA_256, "RSA");
         addEntries(EACObjectIdentifiers.id_TA_RSA_PSS_SHA_1,  "RSAandMGF1");
         addEntries(EACObjectIdentifiers.id_TA_RSA_PSS_SHA_256, "RSAandMGF1");
+        */
+        // END Android-removed: Unsupported algorithms
+        addEntries(X9ObjectIdentifiers.id_dsa_with_sha1,  "DSA");
 
         addEntries(X9ObjectIdentifiers.id_dsa, "DSA");
         addEntries(PKCSObjectIdentifiers.rsaEncryption, "RSA");
@@ -152,14 +154,17 @@
         return encryptionAlgOID;
     }
 
-    AlgorithmIdentifier fixAlgID(AlgorithmIdentifier algId)
+    AlgorithmIdentifier fixDigestAlgID(AlgorithmIdentifier algId, DigestAlgorithmIdentifierFinder dgstAlgFinder)
     {
-        if (algId.getParameters() == null)
+        ASN1Encodable params = algId.getParameters();
+        if (params == null || DERNull.INSTANCE.equals(params))
         {
-            return new AlgorithmIdentifier(algId.getAlgorithm(), DERNull.INSTANCE);
+            return dgstAlgFinder.find(algId.getAlgorithm());
         }
-
-        return algId;
+        else
+        {
+            return algId;
+        }
     }
 
     void setSigningEncryptionAlgorithmMapping(ASN1ObjectIdentifier oid, String algorithmName)
@@ -201,7 +206,18 @@
 
                 if (obj instanceof ASN1TaggedObject)
                 {
-                    certList.add(new X509AttributeCertificateHolder(AttributeCertificate.getInstance(((ASN1TaggedObject)obj).getObject())));
+                    ASN1TaggedObject tObj = (ASN1TaggedObject)obj;
+
+                    // CertificateChoices ::= CHOICE {
+                    //     certificate Certificate,
+                    //     extendedCertificate [0] IMPLICIT ExtendedCertificate,  -- Obsolete
+                    //     v1AttrCert [1] IMPLICIT AttributeCertificateV1,        -- Obsolete
+                    //     v2AttrCert [2] IMPLICIT AttributeCertificateV2,
+                    //     other [3] IMPLICIT OtherCertificateFormat }
+                    if (tObj.getTagNo() == 1 || tObj.getTagNo() == 2)
+                    {
+                        certList.add(new X509AttributeCertificateHolder(AttributeCertificate.getInstance(tObj.getBaseUniversal(false, BERTags.SEQUENCE))));
+                    }
                 }
             }
 
@@ -249,7 +265,7 @@
                 {
                     ASN1TaggedObject tObj = ASN1TaggedObject.getInstance(obj);
 
-                    if (tObj.getTagNo() == 1)
+                    if (tObj.hasContextTag(1))
                     {
                         OtherRevocationInfoFormat other = OtherRevocationInfoFormat.getInstance(tObj, false);
 
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/CMSUtils.java b/bcpkix/src/main/java/org/bouncycastle/cms/CMSUtils.java
index 2d38b53..46006d6 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cms/CMSUtils.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/CMSUtils.java
@@ -21,6 +21,7 @@
 import org.bouncycastle.asn1.DERNull;
 import org.bouncycastle.asn1.DERSet;
 import org.bouncycastle.asn1.DERTaggedObject;
+import org.bouncycastle.asn1.DLSet;
 import org.bouncycastle.asn1.cms.CMSObjectIdentifiers;
 import org.bouncycastle.asn1.cms.ContentInfo;
 // Android-removed: Unsupported algorithms
@@ -37,6 +38,7 @@
 import org.bouncycastle.cert.X509AttributeCertificateHolder;
 import org.bouncycastle.cert.X509CRLHolder;
 import org.bouncycastle.cert.X509CertificateHolder;
+import org.bouncycastle.operator.DigestAlgorithmIdentifierFinder;
 import org.bouncycastle.operator.DigestCalculator;
 import org.bouncycastle.util.Store;
 import org.bouncycastle.util.Strings;
@@ -59,7 +61,6 @@
         /*
         des.add(OIWObjectIdentifiers.desCBC.getId());
         des.add(PKCSObjectIdentifiers.des_EDE3_CBC.getId());
-        des.add(PKCSObjectIdentifiers.des_EDE3_CBC.getId());
         des.add(PKCSObjectIdentifiers.id_alg_CMS3DESwrap.getId());
 
         mqvAlgs.add(X9ObjectIdentifiers.mqvSinglePass_sha1kdf_scheme);
@@ -155,6 +156,23 @@
         return readContentInfo(new ASN1InputStream(input));
     }
 
+    static ASN1Set convertToDlSet(Set<AlgorithmIdentifier> digestAlgs)
+    {
+        return new DLSet((AlgorithmIdentifier[])digestAlgs.toArray(new AlgorithmIdentifier[digestAlgs.size()]));
+    }
+
+    static void addDigestAlgs(Set<AlgorithmIdentifier> digestAlgs, SignerInformation signer, DigestAlgorithmIdentifierFinder dgstAlgFinder)
+    {
+        digestAlgs.add(CMSSignedHelper.INSTANCE.fixDigestAlgID(signer.getDigestAlgorithmID(), dgstAlgFinder));
+        SignerInformationStore counterSignaturesStore = signer.getCounterSignatures();
+        Iterator<SignerInformation> counterSignatureIt = counterSignaturesStore.iterator();
+        while (counterSignatureIt.hasNext())
+        {
+            SignerInformation counterSigner = (SignerInformation)counterSignatureIt.next();
+            digestAlgs.add(CMSSignedHelper.INSTANCE.fixDigestAlgID(counterSigner.getDigestAlgorithmID(), dgstAlgFinder));
+        }
+    }
+
     static List getCertificatesFromStore(Store certStore)
         throws CMSException
     {
@@ -243,7 +261,7 @@
 
     // BEGIN Android-removed: OtherRevocationInfoFormat isn't supported
     /*
-    private static void validateInfoFormat(OtherRevocationInfoFormat infoFormat)
+    static void validateInfoFormat(OtherRevocationInfoFormat infoFormat)
     {
         if (CMSObjectIdentifiers.id_ri_ocsp_response.equals(infoFormat.getInfoFormat()))
         {
@@ -287,6 +305,18 @@
         return new BERSet(v);
     }
 
+    static ASN1Set createDlSetFromList(List derObjects)
+    {
+        ASN1EncodableVector v = new ASN1EncodableVector();
+
+        for (Iterator it = derObjects.iterator(); it.hasNext(); )
+        {
+            v.add((ASN1Encodable)it.next());
+        }
+
+        return new DLSet(v);
+    }
+
     static ASN1Set createDerSetFromList(List derObjects)
     {
         ASN1EncodableVector v = new ASN1EncodableVector();
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/DefaultCMSSignatureAlgorithmNameGenerator.java b/bcpkix/src/main/java/org/bouncycastle/cms/DefaultCMSSignatureAlgorithmNameGenerator.java
index f2e1377..2ef8476 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cms/DefaultCMSSignatureAlgorithmNameGenerator.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/DefaultCMSSignatureAlgorithmNameGenerator.java
@@ -5,11 +5,14 @@
 
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
 // Android-removed: Unsupported algorithms
+// import org.bouncycastle.asn1.bc.BCObjectIdentifiers;
 // import org.bouncycastle.asn1.bsi.BSIObjectIdentifiers;
+// import org.bouncycastle.asn1.cms.CMSObjectIdentifiers;
 // import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers;
-import org.bouncycastle.asn1.eac.EACObjectIdentifiers;
+// import org.bouncycastle.asn1.eac.EACObjectIdentifiers;
 // import org.bouncycastle.asn1.gm.GMObjectIdentifiers;
 // import org.bouncycastle.asn1.edec.EdECObjectIdentifiers;
+// import org.bouncycastle.asn1.misc.MiscObjectIdentifiers;
 import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
 import org.bouncycastle.asn1.oiw.OIWObjectIdentifiers;
 import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
@@ -24,6 +27,7 @@
 {
     private final Map encryptionAlgs = new HashMap();
     private final Map     digestAlgs = new HashMap();
+    private final Map     simpleAlgs = new HashMap();
 
     private void addEntries(ASN1ObjectIdentifier alias, String digest, String encryption)
     {
@@ -70,12 +74,22 @@
         addEntries(PKCSObjectIdentifiers.sha256WithRSAEncryption, "SHA256", "RSA");
         addEntries(PKCSObjectIdentifiers.sha384WithRSAEncryption, "SHA384", "RSA");
         addEntries(PKCSObjectIdentifiers.sha512WithRSAEncryption, "SHA512", "RSA");
+        addEntries(PKCSObjectIdentifiers.sha512_224WithRSAEncryption, "SHA512(224)", "RSA");
+        addEntries(PKCSObjectIdentifiers.sha512_256WithRSAEncryption, "SHA512(256)", "RSA");
+        addEntries(NISTObjectIdentifiers.id_rsassa_pkcs1_v1_5_with_sha3_224, "SHA3-224", "RSA");
+        addEntries(NISTObjectIdentifiers.id_rsassa_pkcs1_v1_5_with_sha3_256, "SHA3-256", "RSA");
+        addEntries(NISTObjectIdentifiers.id_rsassa_pkcs1_v1_5_with_sha3_384, "SHA3-384", "RSA");
+        addEntries(NISTObjectIdentifiers.id_rsassa_pkcs1_v1_5_with_sha3_512, "SHA3-512", "RSA");
 
         // BEGIN Android-removed: Unsupported algorithms
         /*
+        addEntries(CMSObjectIdentifiers.id_RSASSA_PSS_SHAKE128, "SHAKE128", "RSAPSS");
+        addEntries(CMSObjectIdentifiers.id_RSASSA_PSS_SHAKE256, "SHAKE256", "RSAPSS");
         addEntries(TeleTrusTObjectIdentifiers.rsaSignatureWithripemd128, "RIPEMD128", "RSA");
         addEntries(TeleTrusTObjectIdentifiers.rsaSignatureWithripemd160, "RIPEMD160", "RSA");
         addEntries(TeleTrusTObjectIdentifiers.rsaSignatureWithripemd256, "RIPEMD256", "RSA");
+        addEntries(CMSObjectIdentifiers.id_ecdsa_with_shake128, "SHAKE128", "ECDSA");
+        addEntries(CMSObjectIdentifiers.id_ecdsa_with_shake256, "SHAKE256", "ECDSA");
         */
         // END Android-removed: Unsupported algorithms
 
@@ -85,6 +99,8 @@
         addEntries(X9ObjectIdentifiers.ecdsa_with_SHA384, "SHA384", "ECDSA");
         addEntries(X9ObjectIdentifiers.ecdsa_with_SHA512, "SHA512", "ECDSA");
         addEntries(X9ObjectIdentifiers.id_dsa_with_sha1, "SHA1", "DSA");
+        // BEGIN Android-removed: Unsupported algorithms
+        /*
         addEntries(EACObjectIdentifiers.id_TA_ECDSA_SHA_1, "SHA1", "ECDSA");
         addEntries(EACObjectIdentifiers.id_TA_ECDSA_SHA_224, "SHA224", "ECDSA");
         addEntries(EACObjectIdentifiers.id_TA_ECDSA_SHA_256, "SHA256", "ECDSA");
@@ -94,14 +110,31 @@
         addEntries(EACObjectIdentifiers.id_TA_RSA_v1_5_SHA_256, "SHA256", "RSA");
         addEntries(EACObjectIdentifiers.id_TA_RSA_PSS_SHA_1, "SHA1", "RSAandMGF1");
         addEntries(EACObjectIdentifiers.id_TA_RSA_PSS_SHA_256, "SHA256", "RSAandMGF1");
-        // BEGIN Android-removed: Unsupported algorithms
-        /*
         addEntries(BSIObjectIdentifiers.ecdsa_plain_SHA1, "SHA1", "PLAIN-ECDSA");
         addEntries(BSIObjectIdentifiers.ecdsa_plain_SHA224, "SHA224", "PLAIN-ECDSA");
         addEntries(BSIObjectIdentifiers.ecdsa_plain_SHA256, "SHA256", "PLAIN-ECDSA");
         addEntries(BSIObjectIdentifiers.ecdsa_plain_SHA384, "SHA384", "PLAIN-ECDSA");
         addEntries(BSIObjectIdentifiers.ecdsa_plain_SHA512, "SHA512", "PLAIN-ECDSA");
         addEntries(BSIObjectIdentifiers.ecdsa_plain_RIPEMD160, "RIPEMD160", "PLAIN-ECDSA");
+        addEntries(BSIObjectIdentifiers.ecdsa_plain_SHA3_224, "SHA3-224", "PLAIN-ECDSA");
+        addEntries(BSIObjectIdentifiers.ecdsa_plain_SHA3_256, "SHA3-256", "PLAIN-ECDSA");
+        addEntries(BSIObjectIdentifiers.ecdsa_plain_SHA3_384, "SHA3-384", "PLAIN-ECDSA");
+        addEntries(BSIObjectIdentifiers.ecdsa_plain_SHA3_512, "SHA3-512", "PLAIN-ECDSA");
+
+//        addEntries(GMObjectIdentifiers.sm2sign_with_rmd160, "RIPEMD160", "SM2");
+//        addEntries(GMObjectIdentifiers.sm2sign_with_sha1, "SHA1", "SM2");
+//        addEntries(GMObjectIdentifiers.sm2sign_with_sha224, "SHA224", "SM2");
+        addEntries(GMObjectIdentifiers.sm2sign_with_sha256, "SHA256", "SM2");
+//        addEntries(GMObjectIdentifiers.sm2sign_with_sha384, "SHA384", "SM2");
+//        addEntries(GMObjectIdentifiers.sm2sign_with_sha512, "SHA512", "SM2");
+        addEntries(GMObjectIdentifiers.sm2sign_with_sm3, "SM3", "SM2");
+
+        addEntries(BCObjectIdentifiers.sphincs256_with_SHA512, "SHA512", "SPHINCS256");
+        addEntries(BCObjectIdentifiers.sphincs256_with_SHA3_512, "SHA3-512", "SPHINCS256");
+
+        addEntries(BCObjectIdentifiers.picnic_with_shake256, "SHAKE256", "Picnic");
+        addEntries(BCObjectIdentifiers.picnic_with_sha512, "SHA512", "Picnic");
+        addEntries(BCObjectIdentifiers.picnic_with_sha3_512, "SHA3-512", "Picnic");
 
         addEntries(GMObjectIdentifiers.sm2sign_with_rmd160, "RIPEMD160", "SM2");
         addEntries(GMObjectIdentifiers.sm2sign_with_sha1, "SHA1", "SM2");
@@ -130,6 +163,7 @@
         encryptionAlgs.put(CryptoProObjectIdentifiers.gostR3411_94_with_gostR3410_94, "GOST3410");
         encryptionAlgs.put(RosstandartObjectIdentifiers.id_tc26_signwithdigest_gost_3410_12_256, "ECGOST3410-2012-256");
         encryptionAlgs.put(RosstandartObjectIdentifiers.id_tc26_signwithdigest_gost_3410_12_512, "ECGOST3410-2012-512");
+        encryptionAlgs.put(X9ObjectIdentifiers.id_ecPublicKey, "ECDSA");
 
         digestAlgs.put(PKCSObjectIdentifiers.md2, "MD2");
         digestAlgs.put(PKCSObjectIdentifiers.md4, "MD4");
@@ -143,6 +177,10 @@
         digestAlgs.put(NISTObjectIdentifiers.id_sha512, "SHA512");
         // BEGIN Android-removed: Unsupported algorithms
         /*
+        digestAlgs.put(NISTObjectIdentifiers.id_sha512_224, "SHA512(224)");
+        digestAlgs.put(NISTObjectIdentifiers.id_sha512_256, "SHA512(256)");
+        digestAlgs.put(NISTObjectIdentifiers.id_shake128, "SHAKE128");
+        digestAlgs.put(NISTObjectIdentifiers.id_shake256, "SHAKE256");
         digestAlgs.put(NISTObjectIdentifiers.id_sha3_224, "SHA3-224");
         digestAlgs.put(NISTObjectIdentifiers.id_sha3_256, "SHA3-256");
         digestAlgs.put(NISTObjectIdentifiers.id_sha3_384, "SHA3-384");
@@ -155,6 +193,34 @@
         digestAlgs.put(RosstandartObjectIdentifiers.id_tc26_gost_3411_12_256,  "GOST3411-2012-256");
         digestAlgs.put(RosstandartObjectIdentifiers.id_tc26_gost_3411_12_512,  "GOST3411-2012-512");
         digestAlgs.put(GMObjectIdentifiers.sm3,  "SM3");
+
+        simpleAlgs.put(EdECObjectIdentifiers.id_Ed25519, "Ed25519");
+        simpleAlgs.put(EdECObjectIdentifiers.id_Ed448, "Ed448");
+        simpleAlgs.put(PKCSObjectIdentifiers.id_alg_hss_lms_hashsig, "LMS");
+
+        simpleAlgs.put(MiscObjectIdentifiers.id_alg_composite, "COMPOSITE");
+        simpleAlgs.put(BCObjectIdentifiers.falcon_512, "Falcon-512");
+        simpleAlgs.put(BCObjectIdentifiers.falcon_1024, "Falcon-1024");
+        simpleAlgs.put(BCObjectIdentifiers.dilithium2, "Dilithium2");
+        simpleAlgs.put(BCObjectIdentifiers.dilithium3, "Dilithium3");
+        simpleAlgs.put(BCObjectIdentifiers.dilithium5, "Dilithium5");
+        simpleAlgs.put(BCObjectIdentifiers.sphincsPlus_sha2_128s, "SPHINCS+-SHA2-128s");
+        simpleAlgs.put(BCObjectIdentifiers.sphincsPlus_sha2_128f, "SPHINCS+-SHA2-128f");
+        simpleAlgs.put(BCObjectIdentifiers.sphincsPlus_sha2_192s, "SPHINCS+-SHA2-192s");
+        simpleAlgs.put(BCObjectIdentifiers.sphincsPlus_sha2_192f, "SPHINCS+-SHA2-192f");
+        simpleAlgs.put(BCObjectIdentifiers.sphincsPlus_sha2_256s, "SPHINCS+-SHA2-256s");
+        simpleAlgs.put(BCObjectIdentifiers.sphincsPlus_sha2_256f, "SPHINCS+-SHA2-256f");
+        simpleAlgs.put(BCObjectIdentifiers.sphincsPlus_shake_128s, "SPHINCS+-SHAKE-128s");
+        simpleAlgs.put(BCObjectIdentifiers.sphincsPlus_shake_128f, "SPHINCS+-SHAKE-128f");
+        simpleAlgs.put(BCObjectIdentifiers.sphincsPlus_shake_192s, "SPHINCS+-SHAKE-192s");
+        simpleAlgs.put(BCObjectIdentifiers.sphincsPlus_shake_192f, "SPHINCS+-SHAKE-192f");
+        simpleAlgs.put(BCObjectIdentifiers.sphincsPlus_shake_256s, "SPHINCS+-SHAKE-256s");
+        simpleAlgs.put(BCObjectIdentifiers.sphincsPlus_shake_256f, "SPHINCS+-SHAKE-256f");
+        simpleAlgs.put(BCObjectIdentifiers.dilithium2, "Dilithium2");
+        simpleAlgs.put(BCObjectIdentifiers.dilithium3, "Dilithium3");
+        simpleAlgs.put(BCObjectIdentifiers.dilithium5, "Dilithium5");
+
+        simpleAlgs.put(BCObjectIdentifiers.picnic_signature, "Picnic");
         */
         // END Android-removed: Unsupported algorithms
     }
@@ -230,16 +296,29 @@
         {
             return "Ed448";
         }
+
+        // if (encryptionAlgOID.on(BCObjectIdentifiers.sphincsPlus))
+        // {
+        //     return "SPHINCSPlus";
+        // }
         */
         // END Android-removed: unsupported algorithms
 
-        String digestName = getDigestAlgName(encryptionAlg.getAlgorithm());
+        ASN1ObjectIdentifier encryptionAlgOID = encryptionAlg.getAlgorithm();
 
-        if (!digestName.equals(encryptionAlg.getAlgorithm().getId()))
+        String simpleAlgName = (String)simpleAlgs.get(encryptionAlgOID);
+        if (simpleAlgName != null)
         {
-            return digestName + "with" + getEncryptionAlgName(encryptionAlg.getAlgorithm());
+            return simpleAlgName;
         }
 
-        return getDigestAlgName(digestAlg.getAlgorithm()) + "with" + getEncryptionAlgName(encryptionAlg.getAlgorithm());
+        String digestName = getDigestAlgName(encryptionAlgOID);
+
+        if (!digestName.equals(encryptionAlgOID.getId()))
+        {
+            return digestName + "with" + getEncryptionAlgName(encryptionAlgOID);
+        }
+
+        return getDigestAlgName(digestAlg.getAlgorithm()) + "with" + getEncryptionAlgName(encryptionAlgOID);
     }
 }
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/DefaultCMSSignatureEncryptionAlgorithmFinder.java b/bcpkix/src/main/java/org/bouncycastle/cms/DefaultCMSSignatureEncryptionAlgorithmFinder.java
index 4e3c967..58dce8f 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cms/DefaultCMSSignatureEncryptionAlgorithmFinder.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/DefaultCMSSignatureEncryptionAlgorithmFinder.java
@@ -16,8 +16,8 @@
 public class DefaultCMSSignatureEncryptionAlgorithmFinder
     implements CMSSignatureEncryptionAlgorithmFinder
 {
-    private static final Set RSA_PKCS1d5 = new HashSet();
-    private static final Map GOST_ENC = new HashMap();
+    protected static final Set RSA_PKCS1d5 = new HashSet();
+    protected static final Map GOST_ENC = new HashMap();
 
     static
     {
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/KEMRecipient.java b/bcpkix/src/main/java/org/bouncycastle/cms/KEMRecipient.java
new file mode 100644
index 0000000..0134be4
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/KEMRecipient.java
@@ -0,0 +1,10 @@
+package org.bouncycastle.cms;
+
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+
+public interface KEMRecipient
+    extends Recipient
+{
+    RecipientOperator getRecipientOperator(AlgorithmIdentifier keyEncAlg, AlgorithmIdentifier contentEncryptionAlgorithm, byte[] encryptedContentKey)
+        throws CMSException;
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/SignerInfoGenerator.java b/bcpkix/src/main/java/org/bouncycastle/cms/SignerInfoGenerator.java
index b26dddb..3acb69c 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cms/SignerInfoGenerator.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/SignerInfoGenerator.java
@@ -19,11 +19,7 @@
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 import org.bouncycastle.cert.X509CertificateHolder;
 import org.bouncycastle.operator.ContentSigner;
-import org.bouncycastle.operator.DefaultDigestAlgorithmIdentifierFinder;
-import org.bouncycastle.operator.DigestAlgorithmIdentifierFinder;
 import org.bouncycastle.operator.DigestCalculator;
-import org.bouncycastle.operator.DigestCalculatorProvider;
-import org.bouncycastle.operator.OperatorCreationException;
 import org.bouncycastle.util.Arrays;
 import org.bouncycastle.util.io.TeeOutputStream;
 
@@ -34,7 +30,7 @@
     private final CMSAttributeTableGenerator unsAttrGen;
     private final ContentSigner signer;
     private final DigestCalculator digester;
-    private final DigestAlgorithmIdentifierFinder digAlgFinder = new DefaultDigestAlgorithmIdentifierFinder();
+    private final AlgorithmIdentifier digestAlgorithm;
     private final CMSSignatureEncryptionAlgorithmFinder sigEncAlgFinder;
 
     private byte[] calculatedDigest = null;
@@ -43,44 +39,32 @@
     SignerInfoGenerator(
         SignerIdentifier signerIdentifier,
         ContentSigner signer,
-        DigestCalculatorProvider digesterProvider,
+        AlgorithmIdentifier digesterAlgorithm,
         CMSSignatureEncryptionAlgorithmFinder sigEncAlgFinder)
-        throws OperatorCreationException
     {
-        this(signerIdentifier, signer, digesterProvider, sigEncAlgFinder, false);
+        this.signerIdentifier = signerIdentifier;
+        this.signer = signer;
+        this.digestAlgorithm = digesterAlgorithm;
+        this.digester = null;
+        this.sAttrGen = null;
+        this.unsAttrGen = null;
+        this.sigEncAlgFinder = sigEncAlgFinder;
     }
 
     SignerInfoGenerator(
         SignerIdentifier signerIdentifier,
         ContentSigner signer,
-        DigestCalculatorProvider digesterProvider,
+        DigestCalculator digester,
         CMSSignatureEncryptionAlgorithmFinder sigEncAlgFinder,
-        boolean isDirectSignature)
-        throws OperatorCreationException
+        CMSAttributeTableGenerator sAttrGen,
+        CMSAttributeTableGenerator unsAttrGen)
     {
         this.signerIdentifier = signerIdentifier;
         this.signer = signer;
-
-        if (digesterProvider != null)
-        {
-            this.digester = digesterProvider.get(digAlgFinder.find(signer.getAlgorithmIdentifier()));
-        }
-        else
-        {
-            this.digester = null;
-        }
-
-        if (isDirectSignature)
-        {
-            this.sAttrGen = null;
-            this.unsAttrGen = null;
-        }
-        else
-        {
-            this.sAttrGen = new DefaultSignedAttributeTableGenerator();
-            this.unsAttrGen = null;
-        }
-
+        this.digestAlgorithm = digester.getAlgorithmIdentifier();
+        this.digester = digester;
+        this.sAttrGen = sAttrGen;
+        this.unsAttrGen = unsAttrGen;
         this.sigEncAlgFinder = sigEncAlgFinder;
     }
 
@@ -91,38 +75,13 @@
     {
         this.signerIdentifier = original.signerIdentifier;
         this.signer = original.signer;
+        this.digestAlgorithm = original.digestAlgorithm;
         this.digester = original.digester;
         this.sigEncAlgFinder = original.sigEncAlgFinder;
         this.sAttrGen = sAttrGen;
         this.unsAttrGen = unsAttrGen;
     }
 
-    SignerInfoGenerator(
-        SignerIdentifier signerIdentifier,
-        ContentSigner signer,
-        DigestCalculatorProvider digesterProvider,
-        CMSSignatureEncryptionAlgorithmFinder sigEncAlgFinder,
-        CMSAttributeTableGenerator sAttrGen,
-        CMSAttributeTableGenerator unsAttrGen)
-        throws OperatorCreationException
-    {
-        this.signerIdentifier = signerIdentifier;
-        this.signer = signer;
-
-        if (digesterProvider != null)
-        {
-            this.digester = digesterProvider.get(digAlgFinder.find(signer.getAlgorithmIdentifier()));
-        }
-        else
-        {
-            this.digester = null;
-        }
-
-        this.sAttrGen = sAttrGen;
-        this.unsAttrGen = unsAttrGen;
-        this.sigEncAlgFinder = sigEncAlgFinder;
-    }
-
     public SignerIdentifier getSID()
     {
         return signerIdentifier;
@@ -145,12 +104,7 @@
     
     public AlgorithmIdentifier getDigestAlgorithm()
     {
-        if (digester != null)
-        {
-            return digester.getAlgorithmIdentifier();
-        }
-
-        return digAlgFinder.find(signer.getAlgorithmIdentifier());
+        return digestAlgorithm;
     }
     
     public OutputStream getCalculatingOutputStream()
@@ -207,14 +161,13 @@
             }
             else
             {
+                digestAlg = digestAlgorithm;
                 if (digester != null)
                 {
-                    digestAlg = digester.getAlgorithmIdentifier();
                     calculatedDigest = digester.getDigest();
                 }
                 else
                 {
-                    digestAlg = digAlgFinder.find(signer.getAlgorithmIdentifier());
                     calculatedDigest = null;
                 }
             }
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/SignerInfoGeneratorBuilder.java b/bcpkix/src/main/java/org/bouncycastle/cms/SignerInfoGeneratorBuilder.java
index ccb6e2c..b10a6e3 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cms/SignerInfoGeneratorBuilder.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/SignerInfoGeneratorBuilder.java
@@ -3,8 +3,12 @@
 import org.bouncycastle.asn1.DEROctetString;
 import org.bouncycastle.asn1.cms.IssuerAndSerialNumber;
 import org.bouncycastle.asn1.cms.SignerIdentifier;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 import org.bouncycastle.cert.X509CertificateHolder;
 import org.bouncycastle.operator.ContentSigner;
+import org.bouncycastle.operator.DefaultDigestAlgorithmIdentifierFinder;
+import org.bouncycastle.operator.DigestAlgorithmIdentifierFinder;
+import org.bouncycastle.operator.DigestCalculator;
 import org.bouncycastle.operator.DigestCalculatorProvider;
 import org.bouncycastle.operator.OperatorCreationException;
 
@@ -13,11 +17,14 @@
  */
 public class SignerInfoGeneratorBuilder
 {
+    private final DigestAlgorithmIdentifierFinder digAlgFinder = new DefaultDigestAlgorithmIdentifierFinder();
+
     private DigestCalculatorProvider digestProvider;
     private boolean directSignature;
     private CMSAttributeTableGenerator signedGen;
     private CMSAttributeTableGenerator unsignedGen;
     private CMSSignatureEncryptionAlgorithmFinder sigEncAlgFinder;
+    private AlgorithmIdentifier contentDigest;
 
     /**
      *  Base constructor.
@@ -55,6 +62,18 @@
     }
 
     /**
+     * Set the algorithm identifier for the contentDigest to be used for processing the data.
+     *
+     * @return the builder object
+     */
+    public SignerInfoGeneratorBuilder setContentDigest(AlgorithmIdentifier contentDigest)
+    {
+        this.contentDigest = contentDigest;
+
+        return this;
+    }
+
+    /**
      *  Provide a custom signed attribute generator.
      *
      * @param signedGen a generator of signed attributes.
@@ -120,9 +139,19 @@
     private SignerInfoGenerator createGenerator(ContentSigner contentSigner, SignerIdentifier sigId)
         throws OperatorCreationException
     {
+        DigestCalculator digester;
+        if (contentDigest != null)
+        {
+            digester = digestProvider.get(contentDigest);
+        }
+        else
+        {
+            digester = digestProvider.get(digAlgFinder.find(contentSigner.getAlgorithmIdentifier()));
+        }
+
         if (directSignature)
         {
-            return new SignerInfoGenerator(sigId, contentSigner, digestProvider, sigEncAlgFinder, true);
+            return new SignerInfoGenerator(sigId, contentSigner, digester.getAlgorithmIdentifier(), sigEncAlgFinder);
         }
 
         if (signedGen != null || unsignedGen != null)
@@ -132,9 +161,9 @@
                 signedGen = new DefaultSignedAttributeTableGenerator();
             }
 
-            return new SignerInfoGenerator(sigId, contentSigner, digestProvider, sigEncAlgFinder, signedGen, unsignedGen);
+            return new SignerInfoGenerator(sigId, contentSigner, digester, sigEncAlgFinder, signedGen, unsignedGen);
         }
         
-        return new SignerInfoGenerator(sigId, contentSigner, digestProvider, sigEncAlgFinder);
+        return new SignerInfoGenerator(sigId, contentSigner, digester, sigEncAlgFinder, new DefaultSignedAttributeTableGenerator(), null);
     }
 }
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/SignerInformation.java b/bcpkix/src/main/java/org/bouncycastle/cms/SignerInformation.java
index 96440a5..06a444d 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cms/SignerInformation.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/SignerInformation.java
@@ -24,6 +24,8 @@
 import org.bouncycastle.asn1.cms.SignerIdentifier;
 import org.bouncycastle.asn1.cms.SignerInfo;
 import org.bouncycastle.asn1.cms.Time;
+import org.bouncycastle.asn1.oiw.OIWObjectIdentifiers;
+import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 import org.bouncycastle.asn1.x509.DigestInfo;
 import org.bouncycastle.cert.X509CertificateHolder;
@@ -39,34 +41,34 @@
  */
 public class SignerInformation
 {
-    private final SignerId                sid;
-    private final CMSProcessable          content;
-    private final byte[]                  signature;
-    private final ASN1ObjectIdentifier    contentType;
-    private final boolean                 isCounterSignature;
+    private final SignerId sid;
+    private final CMSProcessable content;
+    private final byte[] signature;
+    private final ASN1ObjectIdentifier contentType;
+    private final boolean isCounterSignature;
 
     // Derived
-    private AttributeTable                signedAttributeValues;
-    private AttributeTable                unsignedAttributeValues;
-    private byte[]                        resultDigest;
+    private AttributeTable signedAttributeValues;
+    private AttributeTable unsignedAttributeValues;
+    private byte[] resultDigest;
 
-    protected final SignerInfo            info;
-    protected final AlgorithmIdentifier   digestAlgorithm;
-    protected final AlgorithmIdentifier   encryptionAlgorithm;
-    protected final ASN1Set               signedAttributeSet;
-    protected final ASN1Set               unsignedAttributeSet;
+    protected final SignerInfo info;
+    protected final AlgorithmIdentifier digestAlgorithm;
+    protected final AlgorithmIdentifier encryptionAlgorithm;
+    protected final ASN1Set signedAttributeSet;
+    protected final ASN1Set unsignedAttributeSet;
 
     SignerInformation(
-        SignerInfo          info,
+        SignerInfo info,
         ASN1ObjectIdentifier contentType,
-        CMSProcessable      content,
-        byte[]              resultDigest)
+        CMSProcessable content,
+        byte[] resultDigest)
     {
         this.info = info;
         this.contentType = contentType;
         this.isCounterSignature = contentType == null;
 
-        SignerIdentifier   s = info.getSID();
+        SignerIdentifier s = info.getSID();
 
         if (s.isTagged())
         {
@@ -76,7 +78,7 @@
         }
         else
         {
-            IssuerAndSerialNumber   iAnds = IssuerAndSerialNumber.getInstance(s.getId());
+            IssuerAndSerialNumber iAnds = IssuerAndSerialNumber.getInstance(s.getId());
 
             sid = new SignerId(iAnds.getName(), iAnds.getSerialNumber().getValue());
         }
@@ -109,7 +111,7 @@
      * that by also tweaking the SignerInfo so that these issues can be dealt with.
      *
      * @param baseInfo the SignerInformation to base this one on.
-     * @param info the SignerInfo to associate with the existing baseInfo data.
+     * @param info     the SignerInfo to associate with the existing baseInfo data.
      */
     protected SignerInformation(SignerInformation baseInfo, SignerInfo info)
     {
@@ -124,8 +126,8 @@
         this.signature = info.getEncryptedDigest().getOctets();
         this.content = baseInfo.content;
         this.resultDigest = baseInfo.resultDigest;
-        this.signedAttributeValues = baseInfo.signedAttributeValues;
-        this.unsignedAttributeValues = baseInfo.unsignedAttributeValues;
+        this.signedAttributeValues = getSignedAttributes();
+        this.unsignedAttributeValues = getUnsignedAttributes();
     }
 
     public boolean isCounterSignature()
@@ -139,7 +141,7 @@
     }
 
     private byte[] encodeObj(
-        ASN1Encodable    obj)
+        ASN1Encodable obj)
         throws IOException
     {
         if (obj != null)
@@ -200,10 +202,10 @@
         {
             throw new IllegalStateException("method can only be called after verify.");
         }
-        
+
         return Arrays.clone(resultDigest);
     }
-    
+
     /**
      * return the object identifier for the signature.
      */
@@ -226,7 +228,7 @@
         {
             throw new RuntimeException("exception getting encryption parameters " + e);
         }
-    }  
+    }
 
     /**
      * return a table of the signed attributes - indexed by
@@ -276,7 +278,7 @@
         The countersignature attribute MUST be an unsigned attribute; it MUST
         NOT be a signed attribute, an authenticated attribute, an
         unauthenticated attribute, or an unprotected attribute.
-        */        
+        */
         AttributeTable unsignedAttributeTable = getUnsignedAttributes();
         if (unsignedAttributeTable == null)
         {
@@ -307,7 +309,7 @@
                 // TODO Throw an appropriate exception?
             }
 
-            for (Enumeration en = values.getObjects(); en.hasMoreElements();)
+            for (Enumeration en = values.getObjects(); en.hasMoreElements(); )
             {
                 /*
                 Countersignature values have the same meaning as SignerInfo values
@@ -331,9 +333,10 @@
 
         return new SignerInformationStore(counterSignatures);
     }
-    
+
     /**
      * return the DER encoding of the signed attributes.
+     *
      * @throws IOException if an encoding error occurs.
      */
     public byte[] getEncodedSignedAttributes()
@@ -351,12 +354,14 @@
         SignerInformationVerifier verifier)
         throws CMSException
     {
-        String          encName = CMSSignedHelper.INSTANCE.getEncryptionAlgName(this.getEncryptionAlgOID());
+        String encName = CMSSignedHelper.INSTANCE.getEncryptionAlgName(this.getEncryptionAlgOID());
+        AlgorithmIdentifier realDigestAlgorithm = signedAttributeSet != null ?
+            info.getDigestAlgorithm() : translateBrokenRSAPkcs7(encryptionAlgorithm, info.getDigestAlgorithm());
         ContentVerifier contentVerifier;
 
         try
         {
-            contentVerifier = verifier.getContentVerifier(encryptionAlgorithm, info.getDigestAlgorithm());
+            contentVerifier = verifier.getContentVerifier(encryptionAlgorithm, realDigestAlgorithm);
         }
         catch (OperatorCreationException e)
         {
@@ -369,10 +374,10 @@
 
             if (resultDigest == null)
             {
-                DigestCalculator calc = verifier.getDigestCalculator(this.getDigestAlgorithmID());
+                DigestCalculator calc = verifier.getDigestCalculator(realDigestAlgorithm);
                 if (content != null)
                 {
-                    OutputStream      digOut = calc.getOutputStream();
+                    OutputStream digOut = calc.getOutputStream();
 
                     if (signedAttributeSet == null)
                     {
@@ -436,128 +441,18 @@
         }
 
         // RFC 3852 11.1 Check the content-type attribute is correct
-        {
-            ASN1Primitive validContentType = getSingleValuedSignedAttribute(
-                CMSAttributes.contentType, "content-type");
-            if (validContentType == null)
-            {
-                if (!isCounterSignature && signedAttributeSet != null)
-                {
-                    throw new CMSException("The content-type attribute type MUST be present whenever signed attributes are present in signed-data");
-                }
-            }
-            else
-            {
-                if (isCounterSignature)
-                {
-                    throw new CMSException("[For counter signatures,] the signedAttributes field MUST NOT contain a content-type attribute");
-                }
-
-                if (!(validContentType instanceof ASN1ObjectIdentifier))
-                {
-                    throw new CMSException("content-type attribute value not of ASN.1 type 'OBJECT IDENTIFIER'");
-                }
-
-                ASN1ObjectIdentifier signedContentType = (ASN1ObjectIdentifier)validContentType;
-
-                if (!signedContentType.equals(contentType))
-                {
-                    throw new CMSException("content-type attribute value does not match eContentType");
-                }
-            }
-        }
+        verifyContentTypeAttributeValue();
 
         AttributeTable signedAttrTable = this.getSignedAttributes();
 
         // RFC 6211 Validate Algorithm Identifier protection attribute if present
-        {
-            AttributeTable unsignedAttrTable = this.getUnsignedAttributes();
-            if (unsignedAttrTable != null && unsignedAttrTable.getAll(CMSAttributes.cmsAlgorithmProtect).size() > 0)
-            {
-                throw new CMSException("A cmsAlgorithmProtect attribute MUST be a signed attribute");
-            }
-            if (signedAttrTable != null)
-            {
-                ASN1EncodableVector protectionAttributes = signedAttrTable.getAll(CMSAttributes.cmsAlgorithmProtect);
-                if (protectionAttributes.size() > 1)
-                {
-                    throw new CMSException("Only one instance of a cmsAlgorithmProtect attribute can be present");
-                }
-
-                if (protectionAttributes.size() > 0)
-                {
-                    Attribute attr = Attribute.getInstance(protectionAttributes.get(0));
-                    if (attr.getAttrValues().size() != 1)
-                    {
-                        throw new CMSException("A cmsAlgorithmProtect attribute MUST contain exactly one value");
-                    }
-
-                    CMSAlgorithmProtection algorithmProtection = CMSAlgorithmProtection.getInstance(attr.getAttributeValues()[0]);
-
-                    if (!CMSUtils.isEquivalent(algorithmProtection.getDigestAlgorithm(), info.getDigestAlgorithm()))
-                    {
-                        throw new CMSException("CMS Algorithm Identifier Protection check failed for digestAlgorithm");
-                    }
-
-                    if (!CMSUtils.isEquivalent(algorithmProtection.getSignatureAlgorithm(), info.getDigestEncryptionAlgorithm()))
-                    {
-                        throw new CMSException("CMS Algorithm Identifier Protection check failed for signatureAlgorithm");
-                    }
-                }
-            }
-        }
+        verifyAlgorithmIdentifierProtectionAttribute(signedAttrTable);
 
         // RFC 3852 11.2 Check the message-digest attribute is correct
-        {
-            ASN1Primitive validMessageDigest = getSingleValuedSignedAttribute(
-                CMSAttributes.messageDigest, "message-digest");
-            if (validMessageDigest == null)
-            {
-                if (signedAttributeSet != null)
-                {
-                    throw new CMSException("the message-digest signed attribute type MUST be present when there are any signed attributes present");
-                }
-            }
-            else
-            {
-                if (!(validMessageDigest instanceof ASN1OctetString))
-                {
-                    throw new CMSException("message-digest attribute value not of ASN.1 type 'OCTET STRING'");
-                }
-
-                ASN1OctetString signedMessageDigest = (ASN1OctetString)validMessageDigest;
-
-                if (!Arrays.constantTimeAreEqual(resultDigest, signedMessageDigest.getOctets()))
-                {
-                    throw new CMSSignerDigestMismatchException("message-digest attribute value does not match calculated value");
-                }
-            }
-        }
+        verifyMessageDigestAttribute();
 
         // RFC 3852 11.4 Validate countersignature attribute(s)
-        {
-            if (signedAttrTable != null
-                && signedAttrTable.getAll(CMSAttributes.counterSignature).size() > 0)
-            {
-                throw new CMSException("A countersignature attribute MUST NOT be a signed attribute");
-            }
-
-            AttributeTable unsignedAttrTable = this.getUnsignedAttributes();
-            if (unsignedAttrTable != null)
-            {
-                ASN1EncodableVector csAttrs = unsignedAttrTable.getAll(CMSAttributes.counterSignature);
-                for (int i = 0; i < csAttrs.size(); ++i)
-                {
-                    Attribute csAttr = Attribute.getInstance(csAttrs.get(i));
-                    if (csAttr.getAttrValues().size() < 1)
-                    {
-                        throw new CMSException("A countersignature attribute MUST contain at least one AttributeValue");
-                    }
-
-                    // Note: We don't recursively validate the countersignature value
-                }
-            }
-        }
+        verifyCounterSignatureAttribute(signedAttrTable);
 
         try
         {
@@ -569,7 +464,7 @@
 
                     if (encName.equals("RSA"))
                     {
-                        DigestInfo digInfo = new DigestInfo(new AlgorithmIdentifier(digestAlgorithm.getAlgorithm(), DERNull.INSTANCE), resultDigest);
+                        DigestInfo digInfo = new DigestInfo(new AlgorithmIdentifier(realDigestAlgorithm.getAlgorithm(), DERNull.INSTANCE), resultDigest);
 
                         return rawVerifier.verify(digInfo.getEncoded(ASN1Encoding.DER), this.getSignature());
                     }
@@ -587,6 +482,154 @@
     }
 
     /**
+     * RFC 3852 11.1 Check the content-type attribute is correct
+     *
+     * @throws CMSException when content-type was invalid.
+     */
+    private void verifyContentTypeAttributeValue()
+        throws CMSException
+    {
+        ASN1Primitive validContentType = getSingleValuedSignedAttribute(
+            CMSAttributes.contentType, "content-type");
+        if (validContentType == null)
+        {
+            if (!isCounterSignature && signedAttributeSet != null)
+            {
+                throw new CMSException("The content-type attribute type MUST be present whenever signed attributes are present in signed-data");
+            }
+        }
+        else
+        {
+            if (isCounterSignature)
+            {
+                throw new CMSException("[For counter signatures,] the signedAttributes field MUST NOT contain a content-type attribute");
+            }
+
+            if (!(validContentType instanceof ASN1ObjectIdentifier))
+            {
+                throw new CMSException("content-type attribute value not of ASN.1 type 'OBJECT IDENTIFIER'");
+            }
+
+            ASN1ObjectIdentifier signedContentType = (ASN1ObjectIdentifier)validContentType;
+
+            if (!signedContentType.equals(contentType))
+            {
+                throw new CMSException("content-type attribute value does not match eContentType");
+            }
+        }
+    }
+
+    /**
+     * RFC 3852 11.2 Check the message-digest attribute is correct
+     *
+     * @throws CMSException when message-digest attribute was rejected
+     */
+    private void verifyMessageDigestAttribute()
+        throws CMSException
+    {
+        ASN1Primitive validMessageDigest = getSingleValuedSignedAttribute(
+            CMSAttributes.messageDigest, "message-digest");
+        if (validMessageDigest == null)
+        {
+            if (signedAttributeSet != null)
+            {
+                throw new CMSException("the message-digest signed attribute type MUST be present when there are any signed attributes present");
+            }
+        }
+        else
+        {
+            if (!(validMessageDigest instanceof ASN1OctetString))
+            {
+                throw new CMSException("message-digest attribute value not of ASN.1 type 'OCTET STRING'");
+            }
+
+            ASN1OctetString signedMessageDigest = (ASN1OctetString)validMessageDigest;
+
+            if (!Arrays.constantTimeAreEqual(resultDigest, signedMessageDigest.getOctets()))
+            {
+                throw new CMSSignerDigestMismatchException("message-digest attribute value does not match calculated value");
+            }
+        }
+    }
+
+    /**
+     * RFC 6211 Validate Algorithm Identifier protection attribute if present
+     *
+     * @param signedAttrTable signed attributes
+     * @throws CMSException when cmsAlgorihmProtect attribute was rejected
+     */
+    private void verifyAlgorithmIdentifierProtectionAttribute(AttributeTable signedAttrTable)
+        throws CMSException
+    {
+        AttributeTable unsignedAttrTable = this.getUnsignedAttributes();
+        if (unsignedAttrTable != null && unsignedAttrTable.getAll(CMSAttributes.cmsAlgorithmProtect).size() > 0)
+        {
+            throw new CMSException("A cmsAlgorithmProtect attribute MUST be a signed attribute");
+        }
+        if (signedAttrTable != null)
+        {
+            ASN1EncodableVector protectionAttributes = signedAttrTable.getAll(CMSAttributes.cmsAlgorithmProtect);
+            if (protectionAttributes.size() > 1)
+            {
+                throw new CMSException("Only one instance of a cmsAlgorithmProtect attribute can be present");
+            }
+
+            if (protectionAttributes.size() > 0)
+            {
+                Attribute attr = Attribute.getInstance(protectionAttributes.get(0));
+                if (attr.getAttrValues().size() != 1)
+                {
+                    throw new CMSException("A cmsAlgorithmProtect attribute MUST contain exactly one value");
+                }
+
+                CMSAlgorithmProtection algorithmProtection = CMSAlgorithmProtection.getInstance(attr.getAttributeValues()[0]);
+
+                if (!CMSUtils.isEquivalent(algorithmProtection.getDigestAlgorithm(), info.getDigestAlgorithm()))
+                {
+                    throw new CMSException("CMS Algorithm Identifier Protection check failed for digestAlgorithm");
+                }
+
+                if (!CMSUtils.isEquivalent(algorithmProtection.getSignatureAlgorithm(), info.getDigestEncryptionAlgorithm()))
+                {
+                    throw new CMSException("CMS Algorithm Identifier Protection check failed for signatureAlgorithm");
+                }
+            }
+        }
+    }
+
+    /**
+     * RFC 3852 11.4 Validate countersignature attribute(s)
+     *
+     * @param signedAttrTable signed attributes
+     * @throws CMSException when countersignature attribute was rejected
+     */
+    private void verifyCounterSignatureAttribute(AttributeTable signedAttrTable)
+        throws CMSException
+    {
+        if (signedAttrTable != null
+            && signedAttrTable.getAll(CMSAttributes.counterSignature).size() > 0)
+        {
+            throw new CMSException("A countersignature attribute MUST NOT be a signed attribute");
+        }
+
+        AttributeTable unsignedAttrTable = this.getUnsignedAttributes();
+        if (unsignedAttrTable != null)
+        {
+            ASN1EncodableVector csAttrs = unsignedAttrTable.getAll(CMSAttributes.counterSignature);
+            for (int i = 0; i < csAttrs.size(); ++i)
+            {
+                Attribute csAttr = Attribute.getInstance(csAttrs.get(i));
+                if (csAttr.getAttrValues().size() < 1)
+                {
+                    throw new CMSException("A countersignature attribute MUST contain at least one AttributeValue");
+                }
+
+                // Note: We don't recursively validate the countersignature value
+            }
+        }
+    }
+
+    /**
      * Verify that the given verifier can successfully verify the signature on
      * this SignerInformation object.
      *
@@ -647,27 +690,28 @@
         ASN1EncodableVector v = signedAttrTable.getAll(attrOID);
         switch (v.size())
         {
-            case 0:
-                return null;
-            case 1:
+        case 0:
+            return null;
+        case 1:
+        {
+            Attribute t = (Attribute)v.get(0);
+            ASN1Set attrValues = t.getAttrValues();
+            if (attrValues.size() != 1)
             {
-                Attribute t = (Attribute)v.get(0);
-                ASN1Set attrValues = t.getAttrValues();
-                if (attrValues.size() != 1)
-                {
-                    throw new CMSException("A " + printableName
-                        + " attribute MUST have a single attribute value");
-                }
-
-                return attrValues.getObjectAt(0).toASN1Primitive();
+                throw new CMSException("A " + printableName
+                    + " attribute MUST have a single attribute value");
             }
-            default:
-                throw new CMSException("The SignedAttributes in a signerInfo MUST NOT include multiple instances of the "
-                    + printableName + " attribute");
+
+            return attrValues.getObjectAt(0).toASN1Primitive();
+        }
+        default:
+            throw new CMSException("The SignedAttributes in a signerInfo MUST NOT include multiple instances of the "
+                + printableName + " attribute");
         }
     }
 
-    private Time getSigningTime() throws CMSException
+    private Time getSigningTime()
+        throws CMSException
     {
         ASN1Primitive validSigningTime = getSingleValuedSignedAttribute(
             CMSAttributes.signingTime, "signing-time");
@@ -691,27 +735,27 @@
      * Return a signer information object with the passed in unsigned
      * attributes replacing the ones that are current associated with
      * the object passed in.
-     * 
-     * @param signerInformation the signerInfo to be used as the basis.
+     *
+     * @param signerInformation  the signerInfo to be used as the basis.
      * @param unsignedAttributes the unsigned attributes to add.
      * @return a copy of the original SignerInformationObject with the changed attributes.
      */
     public static SignerInformation replaceUnsignedAttributes(
-        SignerInformation   signerInformation,
-        AttributeTable      unsignedAttributes)
+        SignerInformation signerInformation,
+        AttributeTable unsignedAttributes)
     {
-        SignerInfo  sInfo = signerInformation.info;
-        ASN1Set     unsignedAttr = null;
-        
+        SignerInfo sInfo = signerInformation.info;
+        ASN1Set unsignedAttr = null;
+
         if (unsignedAttributes != null)
         {
             unsignedAttr = new DERSet(unsignedAttributes.toASN1EncodableVector());
         }
-        
+
         return new SignerInformation(
-                new SignerInfo(sInfo.getSID(), sInfo.getDigestAlgorithm(),
-                    sInfo.getAuthenticatedAttributes(), sInfo.getDigestEncryptionAlgorithm(), sInfo.getEncryptedDigest(), unsignedAttr),
-                    signerInformation.contentType, signerInformation.content, null);
+            new SignerInfo(sInfo.getSID(), sInfo.getDigestAlgorithm(),
+                sInfo.getAuthenticatedAttributes(), sInfo.getDigestEncryptionAlgorithm(), sInfo.getEncryptedDigest(), unsignedAttr),
+            signerInformation.contentType, signerInformation.content, null);
     }
 
     /**
@@ -719,17 +763,17 @@
      * signatures attached as an unsigned attribute.
      *
      * @param signerInformation the signerInfo to be used as the basis.
-     * @param counterSigners signer info objects carrying counter signature.
+     * @param counterSigners    signer info objects carrying counter signature.
      * @return a copy of the original SignerInformationObject with the changed attributes.
      */
     public static SignerInformation addCounterSigners(
-        SignerInformation        signerInformation,
-        SignerInformationStore   counterSigners)
+        SignerInformation signerInformation,
+        SignerInformationStore counterSigners)
     {
         // TODO Perform checks from RFC 3852 11.4
 
-        SignerInfo          sInfo = signerInformation.info;
-        AttributeTable      unsignedAttr = signerInformation.getUnsignedAttributes();
+        SignerInfo sInfo = signerInformation.info;
+        AttributeTable unsignedAttr = signerInformation.getUnsignedAttributes();
         ASN1EncodableVector v;
 
         if (unsignedAttr != null)
@@ -743,7 +787,7 @@
 
         ASN1EncodableVector sigs = new ASN1EncodableVector();
 
-        for (Iterator it = counterSigners.getSigners().iterator(); it.hasNext();)
+        for (Iterator it = counterSigners.getSigners().iterator(); it.hasNext(); )
         {
             sigs.add(((SignerInformation)it.next()).toASN1Structure());
         }
@@ -751,8 +795,23 @@
         v.add(new Attribute(CMSAttributes.counterSignature, new DERSet(sigs)));
 
         return new SignerInformation(
-                new SignerInfo(sInfo.getSID(), sInfo.getDigestAlgorithm(),
-                    sInfo.getAuthenticatedAttributes(), sInfo.getDigestEncryptionAlgorithm(), sInfo.getEncryptedDigest(), new DERSet(v)),
-                    signerInformation.contentType, signerInformation.content, null);
+            new SignerInfo(sInfo.getSID(), sInfo.getDigestAlgorithm(),
+                sInfo.getAuthenticatedAttributes(), sInfo.getDigestEncryptionAlgorithm(), sInfo.getEncryptedDigest(), new DERSet(v)),
+            signerInformation.contentType, signerInformation.content, null);
+    }
+
+    private static AlgorithmIdentifier translateBrokenRSAPkcs7(AlgorithmIdentifier encryptionAlgorithm, AlgorithmIdentifier digestAlgorithm)
+    {
+        if (PKCSObjectIdentifiers.rsaEncryption.equals(encryptionAlgorithm.getAlgorithm()))
+        {
+            // Yes, some people really did this.
+            if (OIWObjectIdentifiers.sha1WithRSA.equals(digestAlgorithm.getAlgorithm())
+                || PKCSObjectIdentifiers.sha1WithRSAEncryption.equals(digestAlgorithm.getAlgorithm()))
+            {
+                return new AlgorithmIdentifier(OIWObjectIdentifiers.idSHA1, DERNull.INSTANCE);
+            }
+        }
+
+        return digestAlgorithm;
     }
 }
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/bc/package.html b/bcpkix/src/main/java/org/bouncycastle/cms/bc/package.html
deleted file mode 100644
index 7182ae3..0000000
--- a/bcpkix/src/main/java/org/bouncycastle/cms/bc/package.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<html>
-<body bgcolor="#ffffff">
-CMS operator implementations for doing message encryption, signing, digesting, and MACing operations using the BC lightweight API.
-</body>
-</html>
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/JcaSignerInfoGeneratorBuilder.java b/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/JcaSignerInfoGeneratorBuilder.java
index 17a2f09..d39a7a9 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/JcaSignerInfoGeneratorBuilder.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/JcaSignerInfoGeneratorBuilder.java
@@ -3,6 +3,7 @@
 import java.security.cert.CertificateEncodingException;
 import java.security.cert.X509Certificate;
 
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 import org.bouncycastle.cert.X509CertificateHolder;
 import org.bouncycastle.cert.jcajce.JcaX509CertificateHolder;
 import org.bouncycastle.cms.CMSAttributeTableGenerator;
@@ -52,6 +53,13 @@
         return this;
     }
 
+    public JcaSignerInfoGeneratorBuilder setContentDigest(AlgorithmIdentifier contentDigest)
+    {
+        builder.setContentDigest(contentDigest);
+
+        return this;
+    }
+
     public JcaSignerInfoGeneratorBuilder setSignedAttributeGenerator(CMSAttributeTableGenerator signedGen)
     {
         builder.setSignedAttributeGenerator(signedGen);
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/JcaSignerInfoVerifierBuilder.java b/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/JcaSignerInfoVerifierBuilder.java
index a805839..0fd0c07 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/JcaSignerInfoVerifierBuilder.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/JcaSignerInfoVerifierBuilder.java
@@ -81,7 +81,7 @@
         return new SignerInformationVerifier(sigAlgNameGen, sigAlgIDFinder, helper.createContentVerifierProvider(pubKey), digestProvider);
     }
 
-    private class Helper
+    private static class Helper
     {
         ContentVerifierProvider createContentVerifierProvider(PublicKey publicKey)
             throws OperatorCreationException
@@ -108,7 +108,7 @@
         }
     }
 
-    private class NamedHelper
+    private static class NamedHelper
         extends Helper
     {
         private final String providerName;
@@ -143,7 +143,7 @@
         }
     }
 
-    private class ProviderHelper
+    private static class ProviderHelper
         extends Helper
     {
         private final Provider provider;
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/JcaSimpleSignerInfoVerifierBuilder.java b/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/JcaSimpleSignerInfoVerifierBuilder.java
index 441f27d..0a589ec 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/JcaSimpleSignerInfoVerifierBuilder.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/JcaSimpleSignerInfoVerifierBuilder.java
@@ -51,7 +51,7 @@
         return new SignerInformationVerifier(new DefaultCMSSignatureAlgorithmNameGenerator(), new DefaultSignatureAlgorithmIdentifierFinder(), helper.createContentVerifierProvider(pubKey), helper.createDigestCalculatorProvider());
     }
 
-    private class Helper
+    private static class Helper
     {
         ContentVerifierProvider createContentVerifierProvider(PublicKey publicKey)
             throws OperatorCreationException
@@ -78,7 +78,7 @@
         }
     }
 
-    private class NamedHelper
+    private static class NamedHelper
         extends Helper
     {
         private final String providerName;
@@ -113,7 +113,7 @@
         }
     }
 
-    private class ProviderHelper
+    private static class ProviderHelper
         extends Helper
     {
         private final Provider provider;
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/JceAADStream.java b/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/JceAADStream.java
new file mode 100644
index 0000000..23e5785
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/JceAADStream.java
@@ -0,0 +1,31 @@
+package org.bouncycastle.cms.jcajce;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+import javax.crypto.Cipher;
+
+class JceAADStream
+    extends OutputStream
+{
+     private static final byte[] SINGLE_BYTE = new byte[1];
+     private Cipher cipher;
+
+     JceAADStream(Cipher cipher)
+     {
+         this.cipher = cipher;
+     }
+
+     public void write(byte[] buf, int off, int len)
+         throws IOException
+     {
+         cipher.updateAAD(buf, off, len);
+     }
+
+     public void write(int b)
+         throws IOException
+     {
+         SINGLE_BYTE[0] = (byte)b;
+         cipher.updateAAD(SINGLE_BYTE, 0, 1);
+     }
+ }
diff --git a/bcpkix/src/main/java/org/bouncycastle/openssl/CertificateTrustBlock.java b/bcpkix/src/main/java/org/bouncycastle/openssl/CertificateTrustBlock.java
index 9a76084..7c6b65c 100644
--- a/bcpkix/src/main/java/org/bouncycastle/openssl/CertificateTrustBlock.java
+++ b/bcpkix/src/main/java/org/bouncycastle/openssl/CertificateTrustBlock.java
@@ -11,6 +11,7 @@
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
 import org.bouncycastle.asn1.ASN1Sequence;
 import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.ASN1UTF8String;
 import org.bouncycastle.asn1.DERSequence;
 import org.bouncycastle.asn1.DERTaggedObject;
 import org.bouncycastle.asn1.DERUTF8String;
@@ -54,9 +55,9 @@
             {
                 this.prohibitions = ASN1Sequence.getInstance((ASN1TaggedObject)obj, false);
             }
-            else if (obj instanceof DERUTF8String)
+            else if (obj instanceof ASN1UTF8String)
             {
-                this.alias = DERUTF8String.getInstance(obj).getString();
+                this.alias = ASN1UTF8String.getInstance(obj).getString();
             }
         }
     }
diff --git a/bcpkix/src/main/java/org/bouncycastle/operator/AADProcessor.java b/bcpkix/src/main/java/org/bouncycastle/operator/AADProcessor.java
new file mode 100644
index 0000000..a663421
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/operator/AADProcessor.java
@@ -0,0 +1,24 @@
+package org.bouncycastle.operator;
+
+import java.io.OutputStream;
+
+/**
+ * Base interface for extra methods required for handling associated data in AEAD ciphers.
+ */
+public interface AADProcessor
+{
+    /**
+     * Return a stream to write associated data to in order to have it incorporated into the
+     * AEAD cipher's MAC.
+     *
+     * @return a stream for collecting associated data.
+     */
+    OutputStream getAADStream();
+
+    /**
+     * Return the final value of AEAD cipher's MAC.
+     *
+     * @return MAC value for the AEAD cipher.
+     */
+    byte[] getMAC();
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/operator/AlgorithmNameFinder.java b/bcpkix/src/main/java/org/bouncycastle/operator/AlgorithmNameFinder.java
new file mode 100644
index 0000000..bacb359
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/operator/AlgorithmNameFinder.java
@@ -0,0 +1,35 @@
+package org.bouncycastle.operator;
+
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+
+/**
+ * General finder for converting OIDs and AlgorithmIdentifiers into strings.
+ */
+public interface AlgorithmNameFinder
+{
+    /**
+     * Return true if the passed in objectIdentifier has a "human friendly" name associated with it.
+     *
+     * @param objectIdentifier the OID of interest.
+     * @return true if a name lookup exists for the OID, false otherwise.
+     */
+    boolean hasAlgorithmName(ASN1ObjectIdentifier objectIdentifier);
+
+    /**
+     * Return a string representation of the passed in objectIdentifier.
+     *
+     * @param objectIdentifier the OID of interest.
+     * @return a "human friendly" representation of the OID, the OID as a string if none available.
+     */
+    String getAlgorithmName(ASN1ObjectIdentifier objectIdentifier);
+
+    /**
+     * Return a string representation of the passed in AlgorithmIdentifier, based on the OID in the AlgorithmField, with the parameters
+     * included where appropriate.
+     *
+     * @param algorithmIdentifier the AlgorithmIdentifier of interest.
+     * @return a "human friendly" representation of the algorithmIdentifier, the identifiers OID as a string if none available.
+     */
+    String getAlgorithmName(AlgorithmIdentifier algorithmIdentifier);
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/operator/DefaultDigestAlgorithmIdentifierFinder.java b/bcpkix/src/main/java/org/bouncycastle/operator/DefaultDigestAlgorithmIdentifierFinder.java
index 119d1b1..a5e4fbb 100644
--- a/bcpkix/src/main/java/org/bouncycastle/operator/DefaultDigestAlgorithmIdentifierFinder.java
+++ b/bcpkix/src/main/java/org/bouncycastle/operator/DefaultDigestAlgorithmIdentifierFinder.java
@@ -1,7 +1,9 @@
 package org.bouncycastle.operator;
 
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Map;
+import java.util.Set;
 
 import org.bouncycastle.asn1.ASN1Integer;
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
@@ -9,6 +11,7 @@
 // Android-removed: Unsupported algorithms
 // import org.bouncycastle.asn1.bc.BCObjectIdentifiers;
 // import org.bouncycastle.asn1.bsi.BSIObjectIdentifiers;
+// import org.bouncycastle.asn1.cms.CMSObjectIdentifiers;
 // import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers;
 // import org.bouncycastle.asn1.eac.EACObjectIdentifiers;
 // import org.bouncycastle.asn1.gm.GMObjectIdentifiers;
@@ -27,6 +30,9 @@
 {
     private static Map digestOids = new HashMap();
     private static Map digestNameToOids = new HashMap();
+    private static Map digestOidToAlgIds = new HashMap();
+
+    private static Set shake256oids = new HashSet();  // signatures that use SHAKE-256
 
     static
     {
@@ -37,14 +43,18 @@
         // digestOids.put(OIWObjectIdentifiers.md4WithRSAEncryption, PKCSObjectIdentifiers.md4);
         // digestOids.put(OIWObjectIdentifiers.md4WithRSA, PKCSObjectIdentifiers.md4);
         // digestOids.put(OIWObjectIdentifiers.dsaWithSHA1, OIWObjectIdentifiers.idSHA1);
+        // digestOids.put(OIWObjectIdentifiers.md5WithRSA, PKCSObjectIdentifiers.md5);
         // END Android-removed: Unsupported algorithms
         digestOids.put(OIWObjectIdentifiers.sha1WithRSA, OIWObjectIdentifiers.idSHA1);
 
+        digestOids.put(PKCSObjectIdentifiers.rsaEncryption, NISTObjectIdentifiers.id_sha256);
         digestOids.put(PKCSObjectIdentifiers.sha224WithRSAEncryption, NISTObjectIdentifiers.id_sha224);
         digestOids.put(PKCSObjectIdentifiers.sha256WithRSAEncryption, NISTObjectIdentifiers.id_sha256);
         digestOids.put(PKCSObjectIdentifiers.sha384WithRSAEncryption, NISTObjectIdentifiers.id_sha384);
         digestOids.put(PKCSObjectIdentifiers.sha512WithRSAEncryption, NISTObjectIdentifiers.id_sha512);
         // BEGIN Android-removed: Unsupported algorithms
+        // digestOids.put(PKCSObjectIdentifiers.sha512_224WithRSAEncryption, NISTObjectIdentifiers.id_sha512_224);
+        // digestOids.put(PKCSObjectIdentifiers.sha512_256WithRSAEncryption, NISTObjectIdentifiers.id_sha512_256);
         // digestOids.put(PKCSObjectIdentifiers.md2WithRSAEncryption, PKCSObjectIdentifiers.md2);
         // digestOids.put(PKCSObjectIdentifiers.md4WithRSAEncryption, PKCSObjectIdentifiers.md4);
         // END Android-removed: Unsupported algorithms
@@ -65,6 +75,10 @@
         digestOids.put(BSIObjectIdentifiers.ecdsa_plain_SHA256, NISTObjectIdentifiers.id_sha256);
         digestOids.put(BSIObjectIdentifiers.ecdsa_plain_SHA384, NISTObjectIdentifiers.id_sha384);
         digestOids.put(BSIObjectIdentifiers.ecdsa_plain_SHA512, NISTObjectIdentifiers.id_sha512);
+        digestOids.put(BSIObjectIdentifiers.ecdsa_plain_SHA3_224, NISTObjectIdentifiers.id_sha3_224);
+        digestOids.put(BSIObjectIdentifiers.ecdsa_plain_SHA3_256, NISTObjectIdentifiers.id_sha3_256);
+        digestOids.put(BSIObjectIdentifiers.ecdsa_plain_SHA3_384, NISTObjectIdentifiers.id_sha3_384);
+        digestOids.put(BSIObjectIdentifiers.ecdsa_plain_SHA3_512, NISTObjectIdentifiers.id_sha3_512);
         digestOids.put(BSIObjectIdentifiers.ecdsa_plain_RIPEMD160, TeleTrusTObjectIdentifiers.ripemd160);
 
         digestOids.put(EACObjectIdentifiers.id_TA_ECDSA_SHA_1, OIWObjectIdentifiers.idSHA1);
@@ -114,6 +128,54 @@
         digestOids.put(GMObjectIdentifiers.sm2sign_with_sha384, NISTObjectIdentifiers.id_sha384);
         digestOids.put(GMObjectIdentifiers.sm2sign_with_sha512, NISTObjectIdentifiers.id_sha512);
         digestOids.put(GMObjectIdentifiers.sm2sign_with_sm3, GMObjectIdentifiers.sm3);
+
+        digestOids.put(BCObjectIdentifiers.sphincsPlus_sha2_128s_r3, NISTObjectIdentifiers.id_sha256);
+        digestOids.put(BCObjectIdentifiers.sphincsPlus_sha2_128f_r3, NISTObjectIdentifiers.id_sha256);
+        digestOids.put(BCObjectIdentifiers.sphincsPlus_shake_128s_r3, NISTObjectIdentifiers.id_shake256);
+        digestOids.put(BCObjectIdentifiers.sphincsPlus_shake_128f_r3, NISTObjectIdentifiers.id_shake256);
+        digestOids.put(BCObjectIdentifiers.sphincsPlus_sha2_192s_r3, NISTObjectIdentifiers.id_sha256);
+        digestOids.put(BCObjectIdentifiers.sphincsPlus_sha2_192f_r3, NISTObjectIdentifiers.id_sha256);
+        digestOids.put(BCObjectIdentifiers.sphincsPlus_shake_192s_r3, NISTObjectIdentifiers.id_shake256);
+        digestOids.put(BCObjectIdentifiers.sphincsPlus_shake_192f_r3, NISTObjectIdentifiers.id_shake256);
+        digestOids.put(BCObjectIdentifiers.sphincsPlus_sha2_256s_r3, NISTObjectIdentifiers.id_sha256);
+        digestOids.put(BCObjectIdentifiers.sphincsPlus_sha2_256f_r3, NISTObjectIdentifiers.id_sha256);
+        digestOids.put(BCObjectIdentifiers.sphincsPlus_shake_256s_r3, NISTObjectIdentifiers.id_shake256);
+        digestOids.put(BCObjectIdentifiers.sphincsPlus_shake_256f_r3, NISTObjectIdentifiers.id_shake256);
+
+        digestOids.put(BCObjectIdentifiers.sphincsPlus_sha2_128s_r3_simple, NISTObjectIdentifiers.id_sha256);
+        digestOids.put(BCObjectIdentifiers.sphincsPlus_sha2_128f_r3_simple, NISTObjectIdentifiers.id_sha256);
+        digestOids.put(BCObjectIdentifiers.sphincsPlus_shake_128s_r3_simple, NISTObjectIdentifiers.id_shake256);
+        digestOids.put(BCObjectIdentifiers.sphincsPlus_shake_128f_r3_simple, NISTObjectIdentifiers.id_shake256);
+        digestOids.put(BCObjectIdentifiers.sphincsPlus_sha2_192s_r3_simple, NISTObjectIdentifiers.id_sha256);
+        digestOids.put(BCObjectIdentifiers.sphincsPlus_sha2_192f_r3_simple, NISTObjectIdentifiers.id_sha256);
+        digestOids.put(BCObjectIdentifiers.sphincsPlus_shake_192s_r3_simple, NISTObjectIdentifiers.id_shake256);
+        digestOids.put(BCObjectIdentifiers.sphincsPlus_shake_192f_r3_simple, NISTObjectIdentifiers.id_shake256);
+        digestOids.put(BCObjectIdentifiers.sphincsPlus_sha2_256s_r3_simple, NISTObjectIdentifiers.id_sha256);
+        digestOids.put(BCObjectIdentifiers.sphincsPlus_sha2_256f_r3_simple, NISTObjectIdentifiers.id_sha256);
+        digestOids.put(BCObjectIdentifiers.sphincsPlus_shake_256s_r3_simple, NISTObjectIdentifiers.id_shake256);
+        digestOids.put(BCObjectIdentifiers.sphincsPlus_shake_256f_r3_simple, NISTObjectIdentifiers.id_shake256);
+
+        digestOids.put(BCObjectIdentifiers.falcon, NISTObjectIdentifiers.id_shake256);
+        digestOids.put(BCObjectIdentifiers.falcon_512, NISTObjectIdentifiers.id_shake256);
+        digestOids.put(BCObjectIdentifiers.falcon_1024, NISTObjectIdentifiers.id_shake256);
+
+        digestOids.put(BCObjectIdentifiers.picnic_signature, NISTObjectIdentifiers.id_shake256);
+        digestOids.put(BCObjectIdentifiers.picnic_with_sha512, NISTObjectIdentifiers.id_sha512);
+        digestOids.put(BCObjectIdentifiers.picnic_with_sha3_512, NISTObjectIdentifiers.id_sha3_512);
+        digestOids.put(BCObjectIdentifiers.picnic_with_shake256, NISTObjectIdentifiers.id_shake256);
+
+//        digestOids.put(GMObjectIdentifiers.sm2sign_with_rmd160, TeleTrusTObjectIdentifiers.ripemd160);
+//        digestOids.put(GMObjectIdentifiers.sm2sign_with_sha1, OIWObjectIdentifiers.idSHA1);
+//        digestOids.put(GMObjectIdentifiers.sm2sign_with_sha224, NISTObjectIdentifiers.id_sha224);
+        digestOids.put(GMObjectIdentifiers.sm2sign_with_sha256, NISTObjectIdentifiers.id_sha256);
+//        digestOids.put(GMObjectIdentifiers.sm2sign_with_sha384, NISTObjectIdentifiers.id_sha384);
+//        digestOids.put(GMObjectIdentifiers.sm2sign_with_sha512, NISTObjectIdentifiers.id_sha512);
+        digestOids.put(GMObjectIdentifiers.sm2sign_with_sm3, GMObjectIdentifiers.sm3);
+
+        digestOids.put(CMSObjectIdentifiers.id_RSASSA_PSS_SHAKE128, NISTObjectIdentifiers.id_shake128);
+        digestOids.put(CMSObjectIdentifiers.id_RSASSA_PSS_SHAKE256, NISTObjectIdentifiers.id_shake256);
+        digestOids.put(CMSObjectIdentifiers.id_ecdsa_with_shake128, NISTObjectIdentifiers.id_shake128);
+        digestOids.put(CMSObjectIdentifiers.id_ecdsa_with_shake256, NISTObjectIdentifiers.id_shake256);
         */
         // END Android-removed: Unsupported algorithms
 
@@ -140,6 +202,8 @@
         digestNameToOids.put("SHA3-384", NISTObjectIdentifiers.id_sha3_384);
         digestNameToOids.put("SHA3-512", NISTObjectIdentifiers.id_sha3_512);
 
+        digestNameToOids.put("SHAKE128", NISTObjectIdentifiers.id_shake128);
+        digestNameToOids.put("SHAKE256", NISTObjectIdentifiers.id_shake256);
         digestNameToOids.put("SHAKE-128", NISTObjectIdentifiers.id_shake128);
         digestNameToOids.put("SHAKE-256", NISTObjectIdentifiers.id_shake256);
 
@@ -160,20 +224,106 @@
         digestNameToOids.put("RIPEMD256", TeleTrusTObjectIdentifiers.ripemd256);
 
         digestNameToOids.put("SM3", GMObjectIdentifiers.sm3);
+
+        // IETF RFC 3370
+        addDigestAlgId(OIWObjectIdentifiers.idSHA1, true);
+        // IETF RFC 5754
+        addDigestAlgId(NISTObjectIdentifiers.id_sha224, false);
+        addDigestAlgId(NISTObjectIdentifiers.id_sha256, false);
+        addDigestAlgId(NISTObjectIdentifiers.id_sha384, false);
+        addDigestAlgId(NISTObjectIdentifiers.id_sha512, false);
+        addDigestAlgId(NISTObjectIdentifiers.id_sha512_224, false);
+        addDigestAlgId(NISTObjectIdentifiers.id_sha512_256, false);
+
+        // NIST CSOR
+        addDigestAlgId(NISTObjectIdentifiers.id_sha3_224, false);
+        addDigestAlgId(NISTObjectIdentifiers.id_sha3_256, false);
+        addDigestAlgId(NISTObjectIdentifiers.id_sha3_384, false);
+        addDigestAlgId(NISTObjectIdentifiers.id_sha3_512, false);
+
+        // RFC 8702
+        addDigestAlgId(NISTObjectIdentifiers.id_shake128, false);
+        addDigestAlgId(NISTObjectIdentifiers.id_shake256, false);
+
+        // RFC 4357
+        addDigestAlgId(CryptoProObjectIdentifiers.gostR3411, true);
+
+        // draft-deremin-rfc4491
+        addDigestAlgId(RosstandartObjectIdentifiers.id_tc26_gost_3411_12_256, false);
+        addDigestAlgId(RosstandartObjectIdentifiers.id_tc26_gost_3411_12_512, false);
+
+        // IETF RFC 1319
+        addDigestAlgId(PKCSObjectIdentifiers.md2, true);
+        // IETF RFC 1320
+        addDigestAlgId(PKCSObjectIdentifiers.md4, true);
+        // IETF RFC 1321
+        addDigestAlgId(PKCSObjectIdentifiers.md5, true);
+
+        // found no standard which specified the handle of AlgorithmIdentifier.parameters,
+        // so let it as before.
+        addDigestAlgId(TeleTrusTObjectIdentifiers.ripemd128, true);
+        addDigestAlgId(TeleTrusTObjectIdentifiers.ripemd160, true);
+        addDigestAlgId(TeleTrusTObjectIdentifiers.ripemd256, true);
+
+        shake256oids.add(EdECObjectIdentifiers.id_Ed448);
+
+        shake256oids.add(BCObjectIdentifiers.dilithium2);
+        shake256oids.add(BCObjectIdentifiers.dilithium3);
+        shake256oids.add(BCObjectIdentifiers.dilithium5);
+        shake256oids.add(BCObjectIdentifiers.dilithium2_aes);
+        shake256oids.add(BCObjectIdentifiers.dilithium3_aes);
+        shake256oids.add(BCObjectIdentifiers.dilithium5_aes);
+
+        shake256oids.add(BCObjectIdentifiers.falcon_512);
+        shake256oids.add(BCObjectIdentifiers.falcon_1024);
         */
         // END Android-removed: Unsupported algorithms
     }
 
+    private static void addDigestAlgId(ASN1ObjectIdentifier oid, boolean withNullParams)
+    {
+        AlgorithmIdentifier algId;
+        if (withNullParams)
+        {
+            algId = new AlgorithmIdentifier(oid, DERNull.INSTANCE);
+        }
+        else
+        {
+            algId = new AlgorithmIdentifier(oid);
+        }
+        digestOidToAlgIds.put(oid, algId);
+    }
+
     public AlgorithmIdentifier find(AlgorithmIdentifier sigAlgId)
     {
-        AlgorithmIdentifier digAlgId;
+        ASN1ObjectIdentifier sigAlgOid = sigAlgId.getAlgorithm();
 
-        if (sigAlgId.getAlgorithm().equals(PKCSObjectIdentifiers.id_RSASSA_PSS))
+        if (shake256oids.contains(sigAlgOid))
         {
-            digAlgId = RSASSAPSSparams.getInstance(sigAlgId.getParameters()).getHashAlgorithm();
+            // TODO: it seems it's very hard to find people accepting SHAKE256-len at the moment...
+            // Android-removed: unsupported
+            // if (!sigAlgOid.equals(EdECObjectIdentifiers.id_Ed448))
+            // {
+            //     return new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha256);
+            // }
+
+            return new AlgorithmIdentifier(NISTObjectIdentifiers.id_shake256_len, new ASN1Integer(512));
+        }
+
+        ASN1ObjectIdentifier digAlgOid;
+        if (sigAlgOid.equals(PKCSObjectIdentifiers.id_RSASSA_PSS))
+        {
+            digAlgOid = RSASSAPSSparams.getInstance(sigAlgId.getParameters()).getHashAlgorithm().getAlgorithm();
+        }
+        else if (sigAlgOid.equals(PKCSObjectIdentifiers.id_alg_hss_lms_hashsig))
+        {
+            digAlgOid = NISTObjectIdentifiers.id_sha256;
         }
         // BEGIN Android-removed: Unsupported algorithms
-        /*
+        /*        // else if (sigAlgOid.equals(EdECObjectIdentifiers.id_Ed25519))
+        {
+            digAlgOid = NISTObjectIdentifiers.id_sha512;
+        }
         else if (sigAlgId.getAlgorithm().equals(EdECObjectIdentifiers.id_Ed25519))
         {
             digAlgId = new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha512);
@@ -186,14 +336,47 @@
         // END Android-removed: Unsupported algorithms
         else
         {
-            digAlgId = new AlgorithmIdentifier((ASN1ObjectIdentifier)digestOids.get(sigAlgId.getAlgorithm()), DERNull.INSTANCE);
+            digAlgOid = (ASN1ObjectIdentifier)digestOids.get(sigAlgOid);
         }
 
-        return digAlgId;
+        return find(digAlgOid);
+    }
+
+    public AlgorithmIdentifier find(ASN1ObjectIdentifier digAlgOid)
+    {
+        if (digAlgOid == null)
+        {
+            throw new NullPointerException("digest OID is null");
+        }
+
+        AlgorithmIdentifier digAlgId = (AlgorithmIdentifier)digestOidToAlgIds.get(digAlgOid);
+        if (digAlgId == null)
+        {
+            return new AlgorithmIdentifier(digAlgOid);
+        }
+        else
+        {
+            return digAlgId;
+        }
     }
 
     public AlgorithmIdentifier find(String digAlgName)
     {
-        return new AlgorithmIdentifier((ASN1ObjectIdentifier)digestNameToOids.get(digAlgName), DERNull.INSTANCE);
+        ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier)digestNameToOids.get(digAlgName);
+        if (oid != null)
+        {
+            return find(oid);
+        }
+
+        try
+        {
+            return find(new ASN1ObjectIdentifier(digAlgName));
+        }
+        catch (RuntimeException e)
+        {
+            // ignore - tried it but it didn't work...
+        }
+
+        return null;
     }
-}
\ No newline at end of file
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/operator/DefaultMacAlgorithmIdentifierFinder.java b/bcpkix/src/main/java/org/bouncycastle/operator/DefaultMacAlgorithmIdentifierFinder.java
new file mode 100644
index 0000000..75acfed
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/operator/DefaultMacAlgorithmIdentifierFinder.java
@@ -0,0 +1,38 @@
+package org.bouncycastle.operator;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.bouncycastle.asn1.DERNull;
+import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
+import org.bouncycastle.asn1.oiw.OIWObjectIdentifiers;
+import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.util.Strings;
+
+public class DefaultMacAlgorithmIdentifierFinder
+    implements MacAlgorithmIdentifierFinder
+{
+    private static Map macNameToAlgIds = new HashMap();
+
+    static
+    {
+        macNameToAlgIds.put("HMACSHA1", new AlgorithmIdentifier(OIWObjectIdentifiers.idSHA1));
+        macNameToAlgIds.put("HMACSHA224", new AlgorithmIdentifier(PKCSObjectIdentifiers.id_hmacWithSHA224, DERNull.INSTANCE));
+        macNameToAlgIds.put("HMACSHA256", new AlgorithmIdentifier(PKCSObjectIdentifiers.id_hmacWithSHA256, DERNull.INSTANCE));
+        macNameToAlgIds.put("HMACSHA384", new AlgorithmIdentifier(PKCSObjectIdentifiers.id_hmacWithSHA384, DERNull.INSTANCE));
+        macNameToAlgIds.put("HMACSHA512", new AlgorithmIdentifier(PKCSObjectIdentifiers.id_hmacWithSHA512, DERNull.INSTANCE));
+        macNameToAlgIds.put("HMACSHA512-224", new AlgorithmIdentifier(PKCSObjectIdentifiers.id_hmacWithSHA512_224, DERNull.INSTANCE));
+        macNameToAlgIds.put("HMACSHA512-256", new AlgorithmIdentifier(PKCSObjectIdentifiers.id_hmacWithSHA512_256, DERNull.INSTANCE));
+
+        macNameToAlgIds.put("HMACSHA3-224", new AlgorithmIdentifier(NISTObjectIdentifiers.id_hmacWithSHA3_224));
+        macNameToAlgIds.put("HMACSHA3-256", new AlgorithmIdentifier(NISTObjectIdentifiers.id_hmacWithSHA3_256));
+        macNameToAlgIds.put("HMACSHA3-384", new AlgorithmIdentifier(NISTObjectIdentifiers.id_hmacWithSHA3_384));
+        macNameToAlgIds.put("HMACSHA3-512", new AlgorithmIdentifier(NISTObjectIdentifiers.id_hmacWithSHA3_512));
+    }
+
+    public AlgorithmIdentifier find(String macAlgName)
+    {
+        return (AlgorithmIdentifier)macNameToAlgIds.get(Strings.toUpperCase(macAlgName));
+    }
+}
\ No newline at end of file
diff --git a/bcpkix/src/main/java/org/bouncycastle/operator/DefaultSignatureAlgorithmIdentifierFinder.java b/bcpkix/src/main/java/org/bouncycastle/operator/DefaultSignatureAlgorithmIdentifierFinder.java
index 9c567b4..8f362af 100644
--- a/bcpkix/src/main/java/org/bouncycastle/operator/DefaultSignatureAlgorithmIdentifierFinder.java
+++ b/bcpkix/src/main/java/org/bouncycastle/operator/DefaultSignatureAlgorithmIdentifierFinder.java
@@ -12,11 +12,13 @@
 // Android-removed: Unsupported algorithms
 // import org.bouncycastle.asn1.bc.BCObjectIdentifiers;
 // import org.bouncycastle.asn1.bsi.BSIObjectIdentifiers;
+// import org.bouncycastle.asn1.cms.CMSObjectIdentifiers;
 // import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers;
 // import org.bouncycastle.asn1.eac.EACObjectIdentifiers;
 // import org.bouncycastle.asn1.gm.GMObjectIdentifiers;
 // import org.bouncycastle.asn1.edec.EdECObjectIdentifiers;
 // import org.bouncycastle.asn1.isara.IsaraObjectIdentifiers;
+// import org.bouncycastle.asn1.misc.MiscObjectIdentifiers;
 import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
 import org.bouncycastle.asn1.oiw.OIWObjectIdentifiers;
 import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
@@ -50,6 +52,7 @@
     static
     {
         // BEGIN Android-removed: Unsupported algorithms
+        // algorithms.put("COMPOSITE", MiscObjectIdentifiers.id_alg_composite);
         // algorithms.put("MD2WITHRSAENCRYPTION", PKCSObjectIdentifiers.md2WithRSAEncryption);
         // algorithms.put("MD2WITHRSA", PKCSObjectIdentifiers.md2WithRSAEncryption);
         // END Android-removed: Unsupported algorithms
@@ -65,6 +68,10 @@
         algorithms.put("SHA384WITHRSA", PKCSObjectIdentifiers.sha384WithRSAEncryption);
         algorithms.put("SHA512WITHRSAENCRYPTION", PKCSObjectIdentifiers.sha512WithRSAEncryption);
         algorithms.put("SHA512WITHRSA", PKCSObjectIdentifiers.sha512WithRSAEncryption);
+        algorithms.put("SHA512(224)WITHRSAENCRYPTION", PKCSObjectIdentifiers.sha512_224WithRSAEncryption);
+        algorithms.put("SHA512(224)WITHRSA", PKCSObjectIdentifiers.sha512_224WithRSAEncryption);
+        algorithms.put("SHA512(256)WITHRSAENCRYPTION", PKCSObjectIdentifiers.sha512_256WithRSAEncryption);
+        algorithms.put("SHA512(256)WITHRSA", PKCSObjectIdentifiers.sha512_256WithRSAEncryption);
         algorithms.put("SHA1WITHRSAANDMGF1", PKCSObjectIdentifiers.id_RSASSA_PSS);
         algorithms.put("SHA224WITHRSAANDMGF1", PKCSObjectIdentifiers.id_RSASSA_PSS);
         algorithms.put("SHA256WITHRSAANDMGF1", PKCSObjectIdentifiers.id_RSASSA_PSS);
@@ -132,12 +139,7 @@
         algorithms.put("GOST3411-2012-512WITHECGOST3410-2012-512", RosstandartObjectIdentifiers.id_tc26_signwithdigest_gost_3410_12_512);
         algorithms.put("GOST3411-2012-256WITHGOST3410-2012-256", RosstandartObjectIdentifiers.id_tc26_signwithdigest_gost_3410_12_256);
         algorithms.put("GOST3411-2012-512WITHGOST3410-2012-512", RosstandartObjectIdentifiers.id_tc26_signwithdigest_gost_3410_12_512);
-        algorithms.put("SHA1WITHPLAIN-ECDSA", BSIObjectIdentifiers.ecdsa_plain_SHA1);
-        algorithms.put("SHA224WITHPLAIN-ECDSA", BSIObjectIdentifiers.ecdsa_plain_SHA224);
-        algorithms.put("SHA256WITHPLAIN-ECDSA", BSIObjectIdentifiers.ecdsa_plain_SHA256);
-        algorithms.put("SHA384WITHPLAIN-ECDSA", BSIObjectIdentifiers.ecdsa_plain_SHA384);
-        algorithms.put("SHA512WITHPLAIN-ECDSA", BSIObjectIdentifiers.ecdsa_plain_SHA512);
-        algorithms.put("RIPEMD160WITHPLAIN-ECDSA", BSIObjectIdentifiers.ecdsa_plain_RIPEMD160);
+
         algorithms.put("SHA1WITHCVC-ECDSA", EACObjectIdentifiers.id_TA_ECDSA_SHA_1);
         algorithms.put("SHA224WITHCVC-ECDSA", EACObjectIdentifiers.id_TA_ECDSA_SHA_224);
         algorithms.put("SHA256WITHCVC-ECDSA", EACObjectIdentifiers.id_TA_ECDSA_SHA_256);
@@ -155,12 +157,33 @@
         algorithms.put("SHA256WITHSM2", GMObjectIdentifiers.sm2sign_with_sha256);
         algorithms.put("SHA384WITHSM2", GMObjectIdentifiers.sm2sign_with_sha384);
         algorithms.put("SHA512WITHSM2", GMObjectIdentifiers.sm2sign_with_sha512);
+        algorithms.put("SHA1WITHPLAIN-ECDSA", BSIObjectIdentifiers.ecdsa_plain_SHA1);
+        algorithms.put("RIPEMD160WITHPLAIN-ECDSA", BSIObjectIdentifiers.ecdsa_plain_RIPEMD160);
+        algorithms.put("SHA224WITHPLAIN-ECDSA", BSIObjectIdentifiers.ecdsa_plain_SHA224);
+        algorithms.put("SHA256WITHPLAIN-ECDSA", BSIObjectIdentifiers.ecdsa_plain_SHA256);
+        algorithms.put("SHA384WITHPLAIN-ECDSA", BSIObjectIdentifiers.ecdsa_plain_SHA384);
+        algorithms.put("SHA512WITHPLAIN-ECDSA", BSIObjectIdentifiers.ecdsa_plain_SHA512);
+        algorithms.put("SHA3-224WITHPLAIN-ECDSA", BSIObjectIdentifiers.ecdsa_plain_SHA3_224);
+        algorithms.put("SHA3-256WITHPLAIN-ECDSA", BSIObjectIdentifiers.ecdsa_plain_SHA3_256);
+        algorithms.put("SHA3-384WITHPLAIN-ECDSA", BSIObjectIdentifiers.ecdsa_plain_SHA3_384);
+        algorithms.put("SHA3-512WITHPLAIN-ECDSA", BSIObjectIdentifiers.ecdsa_plain_SHA3_512);
+
+        // RFC 8702
+        algorithms.put("SHAKE128WITHRSAPSS", CMSObjectIdentifiers.id_RSASSA_PSS_SHAKE128);
+        algorithms.put("SHAKE256WITHRSAPSS", CMSObjectIdentifiers.id_RSASSA_PSS_SHAKE256);
+        algorithms.put("SHAKE128WITHRSASSA-PSS", CMSObjectIdentifiers.id_RSASSA_PSS_SHAKE128);
+        algorithms.put("SHAKE256WITHRSASSA-PSS", CMSObjectIdentifiers.id_RSASSA_PSS_SHAKE256);
+        algorithms.put("SHAKE128WITHECDSA", CMSObjectIdentifiers.id_ecdsa_with_shake128);
+        algorithms.put("SHAKE256WITHECDSA", CMSObjectIdentifiers.id_ecdsa_with_shake256);
+
         algorithms.put("SM3WITHSM2", GMObjectIdentifiers.sm2sign_with_sm3);
 
         algorithms.put("SHA256WITHXMSS", BCObjectIdentifiers.xmss_SHA256ph);
         algorithms.put("SHA512WITHXMSS", BCObjectIdentifiers.xmss_SHA512ph);
         algorithms.put("SHAKE128WITHXMSS", BCObjectIdentifiers.xmss_SHAKE128ph);
         algorithms.put("SHAKE256WITHXMSS", BCObjectIdentifiers.xmss_SHAKE256ph);
+        algorithms.put("SHAKE128(512)WITHXMSS", BCObjectIdentifiers.xmss_SHAKE128_512ph);
+        algorithms.put("SHAKE256(1024)WITHXMSS", BCObjectIdentifiers.xmss_SHAKE256_1024ph);
 
         algorithms.put("SHA256WITHXMSSMT", BCObjectIdentifiers.xmss_mt_SHA256ph);
         algorithms.put("SHA512WITHXMSSMT", BCObjectIdentifiers.xmss_mt_SHA512ph);
@@ -176,6 +199,8 @@
         algorithms.put("SHA512WITHXMSSMT-SHA512", BCObjectIdentifiers.xmss_mt_SHA512ph);
         algorithms.put("SHAKE128WITHXMSSMT-SHAKE128", BCObjectIdentifiers.xmss_mt_SHAKE128ph);
         algorithms.put("SHAKE256WITHXMSSMT-SHAKE256", BCObjectIdentifiers.xmss_mt_SHAKE256ph);
+        algorithms.put("SHAKE128(512)WITHXMSSMT-SHAKE128", BCObjectIdentifiers.xmss_mt_SHAKE128_512ph);
+        algorithms.put("SHAKE256(1024)WITHXMSSMT-SHAKE256", BCObjectIdentifiers.xmss_mt_SHAKE256_1024ph);
 
         algorithms.put("LMS", PKCSObjectIdentifiers.id_alg_hss_lms_hashsig);
 
@@ -193,6 +218,47 @@
 
         algorithms.put("QTESLA-P-I", BCObjectIdentifiers.qTESLA_p_I);
         algorithms.put("QTESLA-P-III", BCObjectIdentifiers.qTESLA_p_III);
+        algorithms.put("SPHINCS+", BCObjectIdentifiers.sphincsPlus);
+        algorithms.put("SPHINCSPLUS", BCObjectIdentifiers.sphincsPlus);
+        algorithms.put("SPHINCS+-SHA2-128S", BCObjectIdentifiers.sphincsPlus_sha2_128s);
+        algorithms.put("SPHINCS+-SHA2-128F", BCObjectIdentifiers.sphincsPlus_sha2_128f);
+        algorithms.put("SPHINCS+-SHA2-192S", BCObjectIdentifiers.sphincsPlus_sha2_192s);
+        algorithms.put("SPHINCS+-SHA2-192F", BCObjectIdentifiers.sphincsPlus_sha2_192f);
+        algorithms.put("SPHINCS+-SHA2-256S", BCObjectIdentifiers.sphincsPlus_sha2_256s);
+        algorithms.put("SPHINCS+-SHA2-256F", BCObjectIdentifiers.sphincsPlus_sha2_256f);
+        algorithms.put("SPHINCS+-SHAKE-128S", BCObjectIdentifiers.sphincsPlus_shake_128s);
+        algorithms.put("SPHINCS+-SHAKE-128F", BCObjectIdentifiers.sphincsPlus_shake_128f);
+        algorithms.put("SPHINCS+-SHAKE-192S", BCObjectIdentifiers.sphincsPlus_shake_192s);
+        algorithms.put("SPHINCS+-SHAKE-192F", BCObjectIdentifiers.sphincsPlus_shake_192f);
+        algorithms.put("SPHINCS+-SHAKE-256S", BCObjectIdentifiers.sphincsPlus_shake_256s);
+        algorithms.put("SPHINCS+-SHAKE-256F", BCObjectIdentifiers.sphincsPlus_shake_256f);
+        algorithms.put("SPHINCS+-HARAKA-128S-ROBUST", BCObjectIdentifiers.sphincsPlus_haraka_128s_r3);
+        algorithms.put("SPHINCS+-HARAKA-128F-ROBUST", BCObjectIdentifiers.sphincsPlus_haraka_128f_r3);
+        algorithms.put("SPHINCS+-HARAKA-192S-ROBUST", BCObjectIdentifiers.sphincsPlus_haraka_192s_r3);
+        algorithms.put("SPHINCS+-HARAKA-192F-ROBUST", BCObjectIdentifiers.sphincsPlus_haraka_192f_r3);
+        algorithms.put("SPHINCS+-HARAKA-256S-ROBUST", BCObjectIdentifiers.sphincsPlus_haraka_256s_r3);
+        algorithms.put("SPHINCS+-HARAKA-256F-ROBUST", BCObjectIdentifiers.sphincsPlus_haraka_256f_r3);
+        algorithms.put("SPHINCS+-HARAKA-128S-SIMPLE", BCObjectIdentifiers.sphincsPlus_haraka_128s_r3_simple);
+        algorithms.put("SPHINCS+-HARAKA-128F-SIMPLE", BCObjectIdentifiers.sphincsPlus_haraka_128f_r3_simple);
+        algorithms.put("SPHINCS+-HARAKA-192S-SIMPLE", BCObjectIdentifiers.sphincsPlus_haraka_192s_r3_simple);
+        algorithms.put("SPHINCS+-HARAKA-192F-SIMPLE", BCObjectIdentifiers.sphincsPlus_haraka_192f_r3_simple);
+        algorithms.put("SPHINCS+-HARAKA-256S-SIMPLE", BCObjectIdentifiers.sphincsPlus_haraka_256s_r3_simple);
+        algorithms.put("SPHINCS+-HARAKA-256F-SIMPLE", BCObjectIdentifiers.sphincsPlus_haraka_256f_r3_simple);
+        algorithms.put("SPHINCSPLUS", BCObjectIdentifiers.sphincsPlus);
+        algorithms.put("DILITHIUM2", BCObjectIdentifiers.dilithium2);
+        algorithms.put("DILITHIUM3", BCObjectIdentifiers.dilithium3);
+        algorithms.put("DILITHIUM5", BCObjectIdentifiers.dilithium5);
+        algorithms.put("DILITHIUM2-AES", BCObjectIdentifiers.dilithium2_aes);
+        algorithms.put("DILITHIUM3-AES", BCObjectIdentifiers.dilithium3_aes);
+        algorithms.put("DILITHIUM5-AES", BCObjectIdentifiers.dilithium5_aes);
+
+        algorithms.put("FALCON-512", BCObjectIdentifiers.falcon_512);
+        algorithms.put("FALCON-1024", BCObjectIdentifiers.falcon_1024);
+
+        algorithms.put("PICNIC", BCObjectIdentifiers.picnic_signature);
+        algorithms.put("SHA512WITHPICNIC", BCObjectIdentifiers.picnic_with_sha512);
+        algorithms.put("SHA3-512WITHPICNIC", BCObjectIdentifiers.picnic_with_sha3_512);
+        algorithms.put("SHAKE256WITHPICNIC", BCObjectIdentifiers.picnic_with_shake256);
         */
         // END Android-removed: Unsupported algorithms
 
@@ -224,6 +290,15 @@
         noParams.add(NISTObjectIdentifiers.id_ecdsa_with_sha3_384);
         noParams.add(NISTObjectIdentifiers.id_ecdsa_with_sha3_512);
 
+        noParams.add(BSIObjectIdentifiers.ecdsa_plain_SHA224);
+        noParams.add(BSIObjectIdentifiers.ecdsa_plain_SHA256);
+        noParams.add(BSIObjectIdentifiers.ecdsa_plain_SHA384);
+        noParams.add(BSIObjectIdentifiers.ecdsa_plain_SHA512);
+        noParams.add(BSIObjectIdentifiers.ecdsa_plain_SHA3_224);
+        noParams.add(BSIObjectIdentifiers.ecdsa_plain_SHA3_256);
+        noParams.add(BSIObjectIdentifiers.ecdsa_plain_SHA3_384);
+        noParams.add(BSIObjectIdentifiers.ecdsa_plain_SHA3_512);
+
         //
         // RFC 4491
         //
@@ -239,6 +314,67 @@
         noParams.add(BCObjectIdentifiers.sphincs256_with_SHA3_512);
 
         //
+        // SPHINCS-PLUS
+        //
+        noParams.add(BCObjectIdentifiers.sphincsPlus);
+        noParams.add(BCObjectIdentifiers.sphincsPlus_sha2_128s_r3);
+        noParams.add(BCObjectIdentifiers.sphincsPlus_sha2_128f_r3);
+        noParams.add(BCObjectIdentifiers.sphincsPlus_shake_128s_r3);
+        noParams.add(BCObjectIdentifiers.sphincsPlus_shake_128f_r3);
+        noParams.add(BCObjectIdentifiers.sphincsPlus_haraka_128s_r3);
+        noParams.add(BCObjectIdentifiers.sphincsPlus_haraka_128f_r3);
+        noParams.add(BCObjectIdentifiers.sphincsPlus_sha2_192s_r3);
+        noParams.add(BCObjectIdentifiers.sphincsPlus_sha2_192f_r3);
+        noParams.add(BCObjectIdentifiers.sphincsPlus_shake_192s_r3);
+        noParams.add(BCObjectIdentifiers.sphincsPlus_shake_192f_r3);
+        noParams.add(BCObjectIdentifiers.sphincsPlus_haraka_192s_r3);
+        noParams.add(BCObjectIdentifiers.sphincsPlus_haraka_192f_r3);
+        noParams.add(BCObjectIdentifiers.sphincsPlus_sha2_256s_r3);
+        noParams.add(BCObjectIdentifiers.sphincsPlus_sha2_256f_r3);
+        noParams.add(BCObjectIdentifiers.sphincsPlus_shake_256s_r3);
+        noParams.add(BCObjectIdentifiers.sphincsPlus_shake_256f_r3);
+        noParams.add(BCObjectIdentifiers.sphincsPlus_haraka_256s_r3);
+        noParams.add(BCObjectIdentifiers.sphincsPlus_haraka_256f_r3);
+        noParams.add(BCObjectIdentifiers.sphincsPlus_sha2_128s);
+        noParams.add(BCObjectIdentifiers.sphincsPlus_sha2_128f);
+        noParams.add(BCObjectIdentifiers.sphincsPlus_shake_128s);
+        noParams.add(BCObjectIdentifiers.sphincsPlus_shake_128f);
+        noParams.add(BCObjectIdentifiers.sphincsPlus_sha2_192s);
+        noParams.add(BCObjectIdentifiers.sphincsPlus_sha2_192f);
+        noParams.add(BCObjectIdentifiers.sphincsPlus_shake_192s);
+        noParams.add(BCObjectIdentifiers.sphincsPlus_shake_192f);
+        noParams.add(BCObjectIdentifiers.sphincsPlus_sha2_256s);
+        noParams.add(BCObjectIdentifiers.sphincsPlus_sha2_256f);
+        noParams.add(BCObjectIdentifiers.sphincsPlus_shake_256s);
+        noParams.add(BCObjectIdentifiers.sphincsPlus_shake_256f);
+
+        //
+        // Dilithium
+        //
+        noParams.add(BCObjectIdentifiers.dilithium);
+        noParams.add(BCObjectIdentifiers.dilithium2);
+        noParams.add(BCObjectIdentifiers.dilithium3);
+        noParams.add(BCObjectIdentifiers.dilithium5);
+        noParams.add(BCObjectIdentifiers.dilithium2_aes);
+        noParams.add(BCObjectIdentifiers.dilithium3_aes);
+        noParams.add(BCObjectIdentifiers.dilithium5_aes);
+
+        //
+        // Falcon
+        //
+        noParams.add(BCObjectIdentifiers.falcon);
+        noParams.add(BCObjectIdentifiers.falcon_512);
+        noParams.add(BCObjectIdentifiers.falcon_1024);
+
+        //
+        // Picnic
+        //
+        noParams.add(BCObjectIdentifiers.picnic_signature);
+        noParams.add(BCObjectIdentifiers.picnic_with_sha512);
+        noParams.add(BCObjectIdentifiers.picnic_with_sha3_512);
+        noParams.add(BCObjectIdentifiers.picnic_with_shake256);
+
+        //
         // XMSS
         //
         noParams.add(BCObjectIdentifiers.xmss_SHA256ph);
@@ -249,6 +385,8 @@
         noParams.add(BCObjectIdentifiers.xmss_mt_SHA512ph);
         noParams.add(BCObjectIdentifiers.xmss_mt_SHAKE128ph);
         noParams.add(BCObjectIdentifiers.xmss_mt_SHAKE256ph);
+        noParams.add(BCObjectIdentifiers.xmss_mt_SHAKE128ph);
+        noParams.add(BCObjectIdentifiers.xmss_mt_SHAKE256ph);
 
         noParams.add(BCObjectIdentifiers.xmss_SHA256);
         noParams.add(BCObjectIdentifiers.xmss_SHA512);
@@ -282,6 +420,16 @@
         noParams.add(EdECObjectIdentifiers.id_Ed25519);
         noParams.add(EdECObjectIdentifiers.id_Ed448);
         */
+
+        // EdDSA
+        // noParams.add(EdECObjectIdentifiers.id_Ed25519);
+        // noParams.add(EdECObjectIdentifiers.id_Ed448);
+
+        // RFC 8702
+        // noParams.add(CMSObjectIdentifiers.id_RSASSA_PSS_SHAKE128);
+        // noParams.add(CMSObjectIdentifiers.id_RSASSA_PSS_SHAKE256);
+        // noParams.add(CMSObjectIdentifiers.id_ecdsa_with_shake128);
+        // noParams.add(CMSObjectIdentifiers.id_ecdsa_with_shake256);
         // END Android-removed: Unsupported algorithms
 
         //
@@ -294,6 +442,8 @@
         pkcs15RsaEncryption.add(PKCSObjectIdentifiers.sha512WithRSAEncryption);
         // BEGIN Android-removed: Unsupported algorithms
         /*
+        pkcs15RsaEncryption.add(PKCSObjectIdentifiers.sha512_224WithRSAEncryption);
+        pkcs15RsaEncryption.add(PKCSObjectIdentifiers.sha512_256WithRSAEncryption);
         pkcs15RsaEncryption.add(TeleTrusTObjectIdentifiers.rsaSignatureWithripemd128);
         pkcs15RsaEncryption.add(TeleTrusTObjectIdentifiers.rsaSignatureWithripemd160);
         pkcs15RsaEncryption.add(TeleTrusTObjectIdentifiers.rsaSignatureWithripemd256);
@@ -357,6 +507,8 @@
         // digestOids.put(TeleTrusTObjectIdentifiers.rsaSignatureWithripemd256, TeleTrusTObjectIdentifiers.ripemd256);
         // digestOids.put(CryptoProObjectIdentifiers.gostR3411_94_with_gostR3410_94, CryptoProObjectIdentifiers.gostR3411);
         // digestOids.put(CryptoProObjectIdentifiers.gostR3411_94_with_gostR3410_2001, CryptoProObjectIdentifiers.gostR3411);
+        // digestOids.put(PKCSObjectIdentifiers.sha512_224WithRSAEncryption, NISTObjectIdentifiers.id_sha512_224);
+        // digestOids.put(PKCSObjectIdentifiers.sha512_256WithRSAEncryption, NISTObjectIdentifiers.id_sha512_256);
         // END Android-removed: Unsupported algorithms
         digestOids.put(NISTObjectIdentifiers.dsa_with_sha224, NISTObjectIdentifiers.id_sha224);
         digestOids.put(NISTObjectIdentifiers.dsa_with_sha256, NISTObjectIdentifiers.id_sha256);
@@ -400,37 +552,54 @@
         digestOids.put(GMObjectIdentifiers.sm2sign_with_sha384, NISTObjectIdentifiers.id_sha384);
         digestOids.put(GMObjectIdentifiers.sm2sign_with_sha512, NISTObjectIdentifiers.id_sha512);
         digestOids.put(GMObjectIdentifiers.sm2sign_with_sm3, GMObjectIdentifiers.sm3);
+
+        digestOids.put(BCObjectIdentifiers.sphincsPlus_sha2_128s_r3, NISTObjectIdentifiers.id_sha256);
+        digestOids.put(BCObjectIdentifiers.sphincsPlus_sha2_128f_r3, NISTObjectIdentifiers.id_sha256);
+        digestOids.put(BCObjectIdentifiers.sphincsPlus_shake_128s_r3, NISTObjectIdentifiers.id_shake256);
+        digestOids.put(BCObjectIdentifiers.sphincsPlus_shake_128f_r3, NISTObjectIdentifiers.id_shake256);
+        digestOids.put(BCObjectIdentifiers.sphincsPlus_sha2_192s_r3, NISTObjectIdentifiers.id_sha256);
+        digestOids.put(BCObjectIdentifiers.sphincsPlus_sha2_192f_r3, NISTObjectIdentifiers.id_sha256);
+        digestOids.put(BCObjectIdentifiers.sphincsPlus_shake_192s_r3, NISTObjectIdentifiers.id_shake256);
+        digestOids.put(BCObjectIdentifiers.sphincsPlus_shake_192f_r3, NISTObjectIdentifiers.id_shake256);
+        digestOids.put(BCObjectIdentifiers.sphincsPlus_sha2_256s_r3, NISTObjectIdentifiers.id_sha256);
+        digestOids.put(BCObjectIdentifiers.sphincsPlus_sha2_256f_r3, NISTObjectIdentifiers.id_sha256);
+        digestOids.put(BCObjectIdentifiers.sphincsPlus_shake_256s_r3, NISTObjectIdentifiers.id_shake256);
+        digestOids.put(BCObjectIdentifiers.sphincsPlus_shake_256f_r3, NISTObjectIdentifiers.id_shake256);
+
+        digestOids.put(BCObjectIdentifiers.sphincsPlus_sha2_128s_r3_simple, NISTObjectIdentifiers.id_sha256);
+        digestOids.put(BCObjectIdentifiers.sphincsPlus_sha2_128f_r3_simple, NISTObjectIdentifiers.id_sha256);
+        digestOids.put(BCObjectIdentifiers.sphincsPlus_shake_128s_r3_simple, NISTObjectIdentifiers.id_shake256);
+        digestOids.put(BCObjectIdentifiers.sphincsPlus_shake_128f_r3_simple, NISTObjectIdentifiers.id_shake256);
+        digestOids.put(BCObjectIdentifiers.sphincsPlus_sha2_192s_r3_simple, NISTObjectIdentifiers.id_sha256);
+        digestOids.put(BCObjectIdentifiers.sphincsPlus_sha2_192f_r3_simple, NISTObjectIdentifiers.id_sha256);
+        digestOids.put(BCObjectIdentifiers.sphincsPlus_shake_192s_r3_simple, NISTObjectIdentifiers.id_shake256);
+        digestOids.put(BCObjectIdentifiers.sphincsPlus_shake_192f_r3_simple, NISTObjectIdentifiers.id_shake256);
+        digestOids.put(BCObjectIdentifiers.sphincsPlus_sha2_256s_r3_simple, NISTObjectIdentifiers.id_sha256);
+        digestOids.put(BCObjectIdentifiers.sphincsPlus_sha2_256f_r3_simple, NISTObjectIdentifiers.id_sha256);
+        digestOids.put(BCObjectIdentifiers.sphincsPlus_shake_256s_r3_simple, NISTObjectIdentifiers.id_shake256);
+        digestOids.put(BCObjectIdentifiers.sphincsPlus_shake_256f_r3_simple, NISTObjectIdentifiers.id_shake256);
+
+        digestOids.put(BCObjectIdentifiers.sphincsPlus_sha2_128s, NISTObjectIdentifiers.id_sha256);
+        digestOids.put(BCObjectIdentifiers.sphincsPlus_sha2_128f, NISTObjectIdentifiers.id_sha256);
+        digestOids.put(BCObjectIdentifiers.sphincsPlus_shake_128s, NISTObjectIdentifiers.id_shake256);
+        digestOids.put(BCObjectIdentifiers.sphincsPlus_shake_128f, NISTObjectIdentifiers.id_shake256);
+        digestOids.put(BCObjectIdentifiers.sphincsPlus_sha2_192s, NISTObjectIdentifiers.id_sha256);
+        digestOids.put(BCObjectIdentifiers.sphincsPlus_sha2_192f, NISTObjectIdentifiers.id_sha256);
+        digestOids.put(BCObjectIdentifiers.sphincsPlus_shake_192s, NISTObjectIdentifiers.id_shake256);
+        digestOids.put(BCObjectIdentifiers.sphincsPlus_shake_192f, NISTObjectIdentifiers.id_shake256);
+        digestOids.put(BCObjectIdentifiers.sphincsPlus_sha2_256s, NISTObjectIdentifiers.id_sha256);
+        digestOids.put(BCObjectIdentifiers.sphincsPlus_sha2_256f, NISTObjectIdentifiers.id_sha256);
+        digestOids.put(BCObjectIdentifiers.sphincsPlus_shake_256s, NISTObjectIdentifiers.id_shake256);
+        digestOids.put(BCObjectIdentifiers.sphincsPlus_shake_256f, NISTObjectIdentifiers.id_shake256);
+
+        digestOids.put(CMSObjectIdentifiers.id_RSASSA_PSS_SHAKE128, NISTObjectIdentifiers.id_shake128);
+        digestOids.put(CMSObjectIdentifiers.id_RSASSA_PSS_SHAKE256, NISTObjectIdentifiers.id_shake256);
+        digestOids.put(CMSObjectIdentifiers.id_ecdsa_with_shake128, NISTObjectIdentifiers.id_shake128);
+        digestOids.put(CMSObjectIdentifiers.id_ecdsa_with_shake256, NISTObjectIdentifiers.id_shake256);
         */
         // END Android-removed: Unsupported algorithms
     }
 
-    private static AlgorithmIdentifier generate(String signatureAlgorithm)
-    {
-        AlgorithmIdentifier sigAlgId;
-
-        String algorithmName = Strings.toUpperCase(signatureAlgorithm);
-        ASN1ObjectIdentifier sigOID = (ASN1ObjectIdentifier)algorithms.get(algorithmName);
-        if (sigOID == null)
-        {
-            throw new IllegalArgumentException("Unknown signature type requested: " + algorithmName);
-        }
-
-        if (noParams.contains(sigOID))
-        {
-            sigAlgId = new AlgorithmIdentifier(sigOID);
-        }
-        else if (params.containsKey(algorithmName))
-        {
-            sigAlgId = new AlgorithmIdentifier(sigOID, (ASN1Encodable)params.get(algorithmName));
-        }
-        else
-        {
-            sigAlgId = new AlgorithmIdentifier(sigOID, DERNull.INSTANCE);
-        }
-
-        return sigAlgId;
-    }
-
     private static RSASSAPSSparams createPSSParams(AlgorithmIdentifier hashAlgId, int saltSize)
     {
         return new RSASSAPSSparams(
@@ -442,6 +611,26 @@
 
     public AlgorithmIdentifier find(String sigAlgName)
     {
-        return generate(sigAlgName);
+        String algorithmName = Strings.toUpperCase(sigAlgName);
+        ASN1ObjectIdentifier sigOID = (ASN1ObjectIdentifier)algorithms.get(algorithmName);
+        if (sigOID == null)
+        {
+            throw new IllegalArgumentException("Unknown signature type requested: " + sigAlgName);
+        }
+
+        AlgorithmIdentifier sigAlgId;
+        if (noParams.contains(sigOID))
+        {
+            sigAlgId = new AlgorithmIdentifier(sigOID);
+        }
+        else if (params.containsKey(algorithmName))
+        {
+            sigAlgId = new AlgorithmIdentifier(sigOID, (ASN1Encodable)params.get(algorithmName));
+        }
+        else
+        {
+            sigAlgId = new AlgorithmIdentifier(sigOID, DERNull.INSTANCE);
+        }
+        return sigAlgId;
     }
-}
\ No newline at end of file
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/operator/DefaultSignatureNameFinder.java b/bcpkix/src/main/java/org/bouncycastle/operator/DefaultSignatureNameFinder.java
new file mode 100644
index 0000000..3bfc50d
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/operator/DefaultSignatureNameFinder.java
@@ -0,0 +1,170 @@
+package org.bouncycastle.operator;
+
+import java.util.HashMap;
+import java.util.Map;
+
+// BEGIN Android-removed: unsupported algorithms
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.DERNull;
+// import org.bouncycastle.asn1.bsi.BSIObjectIdentifiers;
+// import org.bouncycastle.asn1.cms.CMSObjectIdentifiers;
+// import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers;
+// import org.bouncycastle.asn1.eac.EACObjectIdentifiers;
+// import org.bouncycastle.asn1.edec.EdECObjectIdentifiers;
+import org.bouncycastle.asn1.isara.IsaraObjectIdentifiers;
+import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
+import org.bouncycastle.asn1.oiw.OIWObjectIdentifiers;
+import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.bouncycastle.asn1.pkcs.RSASSAPSSparams;
+// import org.bouncycastle.asn1.rosstandart.RosstandartObjectIdentifiers;
+import org.bouncycastle.asn1.teletrust.TeleTrusTObjectIdentifiers;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
+// END Android-removed: unsupported algorithms
+
+/**
+ * Class for return signature names from OIDs or AlgorithmIdentifiers
+ */
+public class DefaultSignatureNameFinder
+    implements AlgorithmNameFinder
+{
+    private static final Map oids = new HashMap();
+    private static final Map digests = new HashMap();
+
+    static
+    {
+        //
+        // reverse mappings
+        //
+        // BEGIN Android-removed: unsupported algorithms
+        oids.put(PKCSObjectIdentifiers.id_RSASSA_PSS, "RSASSA-PSS");
+        // oids.put(EdECObjectIdentifiers.id_Ed25519, "ED25519");
+        // oids.put(EdECObjectIdentifiers.id_Ed448, "ED448");
+        oids.put(new ASN1ObjectIdentifier("1.2.840.113549.1.1.5"), "SHA1WITHRSA");
+        oids.put(PKCSObjectIdentifiers.sha224WithRSAEncryption, "SHA224WITHRSA");
+        oids.put(PKCSObjectIdentifiers.sha256WithRSAEncryption, "SHA256WITHRSA");
+        oids.put(PKCSObjectIdentifiers.sha384WithRSAEncryption, "SHA384WITHRSA");
+        oids.put(PKCSObjectIdentifiers.sha512WithRSAEncryption, "SHA512WITHRSA");
+        // oids.put(CMSObjectIdentifiers.id_RSASSA_PSS_SHAKE128, "SHAKE128WITHRSAPSS");
+        // oids.put(CMSObjectIdentifiers.id_RSASSA_PSS_SHAKE256, "SHAKE256WITHRSAPSS");
+        // oids.put(CryptoProObjectIdentifiers.gostR3411_94_with_gostR3410_94, "GOST3411WITHGOST3410");
+        // oids.put(CryptoProObjectIdentifiers.gostR3411_94_with_gostR3410_2001, "GOST3411WITHECGOST3410");
+        // oids.put(RosstandartObjectIdentifiers.id_tc26_signwithdigest_gost_3410_12_256, "GOST3411-2012-256WITHECGOST3410-2012-256");
+        // oids.put(RosstandartObjectIdentifiers.id_tc26_signwithdigest_gost_3410_12_512, "GOST3411-2012-512WITHECGOST3410-2012-512");
+        // oids.put(BSIObjectIdentifiers.ecdsa_plain_SHA1, "SHA1WITHPLAIN-ECDSA");
+        // oids.put(BSIObjectIdentifiers.ecdsa_plain_SHA224, "SHA224WITHPLAIN-ECDSA");
+        // oids.put(BSIObjectIdentifiers.ecdsa_plain_SHA256, "SHA256WITHPLAIN-ECDSA");
+        // oids.put(BSIObjectIdentifiers.ecdsa_plain_SHA384, "SHA384WITHPLAIN-ECDSA");
+        // oids.put(BSIObjectIdentifiers.ecdsa_plain_SHA512, "SHA512WITHPLAIN-ECDSA");
+        // oids.put(BSIObjectIdentifiers.ecdsa_plain_SHA3_224, "SHA3-224WITHPLAIN-ECDSA");
+        // oids.put(BSIObjectIdentifiers.ecdsa_plain_SHA3_256, "SHA3-256WITHPLAIN-ECDSA");
+        // oids.put(BSIObjectIdentifiers.ecdsa_plain_SHA3_384, "SHA3-384WITHPLAIN-ECDSA");
+        // oids.put(BSIObjectIdentifiers.ecdsa_plain_SHA3_512, "SHA3-512WITHPLAIN-ECDSA");
+        // oids.put(BSIObjectIdentifiers.ecdsa_plain_RIPEMD160, "RIPEMD160WITHPLAIN-ECDSA");
+        // oids.put(EACObjectIdentifiers.id_TA_ECDSA_SHA_1, "SHA1WITHCVC-ECDSA");
+        // oids.put(EACObjectIdentifiers.id_TA_ECDSA_SHA_224, "SHA224WITHCVC-ECDSA");
+        // oids.put(EACObjectIdentifiers.id_TA_ECDSA_SHA_256, "SHA256WITHCVC-ECDSA");
+        // oids.put(EACObjectIdentifiers.id_TA_ECDSA_SHA_384, "SHA384WITHCVC-ECDSA");
+        // oids.put(EACObjectIdentifiers.id_TA_ECDSA_SHA_512, "SHA512WITHCVC-ECDSA");
+        oids.put(IsaraObjectIdentifiers.id_alg_xmss, "XMSS");
+        oids.put(IsaraObjectIdentifiers.id_alg_xmssmt, "XMSSMT");
+        oids.put(TeleTrusTObjectIdentifiers.rsaSignatureWithripemd128, "RIPEMD128WITHRSA");
+        oids.put(TeleTrusTObjectIdentifiers.rsaSignatureWithripemd160, "RIPEMD160WITHRSA");
+        oids.put(TeleTrusTObjectIdentifiers.rsaSignatureWithripemd256, "RIPEMD256WITHRSA");
+        oids.put(new ASN1ObjectIdentifier("1.2.840.113549.1.1.4"), "MD5WITHRSA");
+        oids.put(new ASN1ObjectIdentifier("1.2.840.113549.1.1.2"), "MD2WITHRSA");
+        oids.put(new ASN1ObjectIdentifier("1.2.840.10040.4.3"), "SHA1WITHDSA");
+        oids.put(X9ObjectIdentifiers.ecdsa_with_SHA1, "SHA1WITHECDSA");
+        oids.put(X9ObjectIdentifiers.ecdsa_with_SHA224, "SHA224WITHECDSA");
+        oids.put(X9ObjectIdentifiers.ecdsa_with_SHA256, "SHA256WITHECDSA");
+        oids.put(X9ObjectIdentifiers.ecdsa_with_SHA384, "SHA384WITHECDSA");
+        oids.put(X9ObjectIdentifiers.ecdsa_with_SHA512, "SHA512WITHECDSA");
+        // oids.put(CMSObjectIdentifiers.id_ecdsa_with_shake128, "SHAKE128WITHECDSA");
+        // oids.put(CMSObjectIdentifiers.id_ecdsa_with_shake256, "SHAKE256WITHECDSA");
+        oids.put(OIWObjectIdentifiers.sha1WithRSA, "SHA1WITHRSA");
+        oids.put(OIWObjectIdentifiers.dsaWithSHA1, "SHA1WITHDSA");
+        oids.put(NISTObjectIdentifiers.dsa_with_sha224, "SHA224WITHDSA");
+        oids.put(NISTObjectIdentifiers.dsa_with_sha256, "SHA256WITHDSA");
+        // END Android-removed: unsupported algorithms
+
+        digests.put(OIWObjectIdentifiers.idSHA1, "SHA1");
+        digests.put(NISTObjectIdentifiers.id_sha224, "SHA224");
+        digests.put(NISTObjectIdentifiers.id_sha256, "SHA256");
+        digests.put(NISTObjectIdentifiers.id_sha384, "SHA384");
+        digests.put(NISTObjectIdentifiers.id_sha512, "SHA512");
+        digests.put(NISTObjectIdentifiers.id_sha3_224, "SHA3-224");
+        digests.put(NISTObjectIdentifiers.id_sha3_256, "SHA3-256");
+        digests.put(NISTObjectIdentifiers.id_sha3_384, "SHA3-384");
+        digests.put(NISTObjectIdentifiers.id_sha3_512, "SHA3-512");
+        digests.put(TeleTrusTObjectIdentifiers.ripemd128, "RIPEMD128");
+        digests.put(TeleTrusTObjectIdentifiers.ripemd160, "RIPEMD160");
+        digests.put(TeleTrusTObjectIdentifiers.ripemd256, "RIPEMD256");
+    }
+
+    public boolean hasAlgorithmName(ASN1ObjectIdentifier objectIdentifier)
+    {
+        return oids.containsKey(objectIdentifier);
+    }
+
+    public String getAlgorithmName(ASN1ObjectIdentifier objectIdentifier)
+    {
+        String name = (String)oids.get(objectIdentifier);
+        if (name != null)
+        {
+            return name;
+        }
+        return objectIdentifier.getId();
+    }
+
+    /**
+     * Return the signature name for the passed in algorithm identifier. For signatures
+     * that require parameters, like RSASSA-PSS, this is the best one to use.
+     *
+     * @param algorithmIdentifier the AlgorithmIdentifier of interest.
+     * @return a string representation of the name.
+     */
+    public String getAlgorithmName(AlgorithmIdentifier algorithmIdentifier)
+    {
+        ASN1Encodable params = algorithmIdentifier.getParameters();
+        if (params != null && !DERNull.INSTANCE.equals(params))
+        {
+            if (algorithmIdentifier.getAlgorithm().equals(PKCSObjectIdentifiers.id_RSASSA_PSS))
+            {
+                RSASSAPSSparams rsaParams = RSASSAPSSparams.getInstance(params);
+                AlgorithmIdentifier mgfAlg = rsaParams.getMaskGenAlgorithm();
+                if (mgfAlg.getAlgorithm().equals(PKCSObjectIdentifiers.id_mgf1))
+                {
+                    AlgorithmIdentifier digAlg = rsaParams.getHashAlgorithm();
+                    ASN1ObjectIdentifier mgfHashOid = AlgorithmIdentifier.getInstance(mgfAlg.getParameters()).getAlgorithm();
+                    if (mgfHashOid.equals(digAlg.getAlgorithm()))
+                    {
+                        return getDigestName(digAlg.getAlgorithm()) + "WITHRSAANDMGF1";
+                    }
+                    else
+                    {
+                        return getDigestName(digAlg.getAlgorithm()) + "WITHRSAANDMGF1USING" + getDigestName(mgfHashOid);
+                    }
+                }
+                return getDigestName(rsaParams.getHashAlgorithm().getAlgorithm()) + "WITHRSAAND" + mgfAlg.getAlgorithm().getId();
+            }
+        }
+
+        if (oids.containsKey(algorithmIdentifier.getAlgorithm()))
+        {
+            return (String)oids.get(algorithmIdentifier.getAlgorithm());
+        }
+
+        return algorithmIdentifier.getAlgorithm().getId();
+    }
+
+    private static String getDigestName(ASN1ObjectIdentifier oid)
+    {
+        String name = (String)digests.get(oid);
+        if (name != null)
+        {
+            return name;
+        }
+        return oid.getId();
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/operator/DigestAlgorithmIdentifierFinder.java b/bcpkix/src/main/java/org/bouncycastle/operator/DigestAlgorithmIdentifierFinder.java
index b2d57c6..de2d680 100644
--- a/bcpkix/src/main/java/org/bouncycastle/operator/DigestAlgorithmIdentifierFinder.java
+++ b/bcpkix/src/main/java/org/bouncycastle/operator/DigestAlgorithmIdentifierFinder.java
@@ -1,5 +1,6 @@
 package org.bouncycastle.operator;
 
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 
 public interface DigestAlgorithmIdentifierFinder
@@ -15,6 +16,15 @@
 
     /**
      * Find the algorithm identifier that matches with
+     * the passed in digest OID.
+     *
+     * @param digestOid the OID of the digest algorithm of interest.
+     * @return an algorithm identifier for the digest signature.
+     */
+    AlgorithmIdentifier find(ASN1ObjectIdentifier digestOid);
+
+    /**
+     * Find the algorithm identifier that matches with
      * the passed in digest name.
      *
      * @param digAlgName the name of the digest algorithm of interest.
diff --git a/bcpkix/src/main/java/org/bouncycastle/operator/InputAEADDecryptor.java b/bcpkix/src/main/java/org/bouncycastle/operator/InputAEADDecryptor.java
new file mode 100644
index 0000000..88adef4
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/operator/InputAEADDecryptor.java
@@ -0,0 +1,9 @@
+package org.bouncycastle.operator;
+
+/**
+ * Base interface for an input consuming AEAD Decryptor supporting associated text.
+ */
+public interface InputAEADDecryptor
+    extends InputDecryptor, AADProcessor
+{
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/operator/MacAlgorithmIdentifierFinder.java b/bcpkix/src/main/java/org/bouncycastle/operator/MacAlgorithmIdentifierFinder.java
new file mode 100644
index 0000000..7a09504
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/operator/MacAlgorithmIdentifierFinder.java
@@ -0,0 +1,15 @@
+package org.bouncycastle.operator;
+
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+
+public interface MacAlgorithmIdentifierFinder
+{
+    /**
+     * Find the algorithm identifier that matches with
+     * the passed in digest name.
+     *
+     * @param macAlgName the name of the digest algorithm of interest.
+     * @return an algorithm identifier for the MAC.
+     */
+    AlgorithmIdentifier find(String macAlgName);
+}
\ No newline at end of file
diff --git a/bcpkix/src/main/java/org/bouncycastle/operator/MacCaptureStream.java b/bcpkix/src/main/java/org/bouncycastle/operator/MacCaptureStream.java
new file mode 100644
index 0000000..ae44153
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/operator/MacCaptureStream.java
@@ -0,0 +1,67 @@
+package org.bouncycastle.operator;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+import org.bouncycastle.util.Arrays;
+
+/**
+ * A generic class for capturing the mac data at the end of a encrypted data stream.
+ * <p>
+ * Note: this class will not close the underlying stream.
+ * </p>
+ */
+public class MacCaptureStream
+    extends OutputStream
+{
+    private final OutputStream cOut;
+    private final byte[] mac;
+
+    int macIndex = 0;
+
+    public MacCaptureStream(OutputStream cOut, int macLength)
+    {
+        this.cOut = cOut;
+        this.mac = new byte[macLength];
+    }
+
+    public void write(byte[] buf, int off, int len)
+        throws IOException
+    {
+        if (len >= mac.length)
+        {
+            cOut.write(mac, 0, macIndex);
+            macIndex = mac.length;
+            System.arraycopy(buf, off + len - mac.length, mac, 0, mac.length);
+            cOut.write(buf, off, len - mac.length);
+        }
+        else
+        {
+            for (int i = 0; i != len; i++)
+            {
+                write(buf[off + i]);
+            }
+        }
+    }
+
+    public void write(int b)
+        throws IOException
+    {
+        if (macIndex == mac.length)
+        {
+             byte b1 = mac[0];
+             System.arraycopy(mac, 1, mac, 0, mac.length - 1);
+             mac[mac.length - 1] = (byte)b;
+             cOut.write(b1);
+        }
+        else
+        {
+            mac[macIndex++] = (byte)b;
+        }
+    }
+
+    public byte[] getMac()
+    {
+        return Arrays.clone(mac);
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/operator/PBEMacCalculatorProvider.java b/bcpkix/src/main/java/org/bouncycastle/operator/PBEMacCalculatorProvider.java
new file mode 100644
index 0000000..b6b9f61
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/operator/PBEMacCalculatorProvider.java
@@ -0,0 +1,9 @@
+package org.bouncycastle.operator;
+
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+
+public interface PBEMacCalculatorProvider
+{
+    MacCalculator get(AlgorithmIdentifier algorithm, char[] password)
+        throws OperatorCreationException;
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/operator/bc/BcDefaultDigestProvider.java b/bcpkix/src/main/java/org/bouncycastle/operator/bc/BcDefaultDigestProvider.java
index a5f0a7b..d0e8aa55 100644
--- a/bcpkix/src/main/java/org/bouncycastle/operator/bc/BcDefaultDigestProvider.java
+++ b/bcpkix/src/main/java/org/bouncycastle/operator/bc/BcDefaultDigestProvider.java
@@ -6,6 +6,9 @@
 
 // Android-removed: Unsupported algorithms
 // import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers;
+// import org.bouncycastle.asn1.ASN1Integer;
+// import org.bouncycastle.asn1.gm.GMObjectIdentifiers;
+// import org.bouncycastle.asn1.misc.MiscObjectIdentifiers;
 import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
 import org.bouncycastle.asn1.oiw.OIWObjectIdentifiers;
 // import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
@@ -14,6 +17,25 @@
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 import org.bouncycastle.crypto.ExtendedDigest;
 import org.bouncycastle.crypto.digests.*;
+// import org.bouncycastle.crypto.Xof;
+// import org.bouncycastle.crypto.digests.Blake3Digest;
+// import org.bouncycastle.crypto.digests.GOST3411Digest;
+// import org.bouncycastle.crypto.digests.GOST3411_2012_256Digest;
+// import org.bouncycastle.crypto.digests.GOST3411_2012_512Digest;
+// import org.bouncycastle.crypto.digests.MD2Digest;
+// import org.bouncycastle.crypto.digests.MD4Digest;
+// import org.bouncycastle.crypto.digests.MD5Digest;
+// import org.bouncycastle.crypto.digests.RIPEMD128Digest;
+// import org.bouncycastle.crypto.digests.RIPEMD160Digest;
+// import org.bouncycastle.crypto.digests.RIPEMD256Digest;
+// import org.bouncycastle.crypto.digests.SHA1Digest;
+// import org.bouncycastle.crypto.digests.SHA224Digest;
+// import org.bouncycastle.crypto.digests.SHA256Digest;
+// import org.bouncycastle.crypto.digests.SHA384Digest;
+// import org.bouncycastle.crypto.digests.SHA3Digest;
+// import org.bouncycastle.crypto.digests.SHA512Digest;
+// import org.bouncycastle.crypto.digests.SHAKEDigest;
+// import org.bouncycastle.crypto.digests.SM3Digest;
 import org.bouncycastle.operator.OperatorCreationException;
 
 public class BcDefaultDigestProvider
@@ -90,6 +112,34 @@
                 return new SHA3Digest(512);
             }
         });
+        table.put(NISTObjectIdentifiers.id_shake128, new BcDigestProvider()
+        {
+            public ExtendedDigest get(AlgorithmIdentifier digestAlgorithmIdentifier)
+            {
+                return new SHAKEDigest(128);
+            }
+        });
+        table.put(NISTObjectIdentifiers.id_shake256, new BcDigestProvider()
+        {
+            public ExtendedDigest get(AlgorithmIdentifier digestAlgorithmIdentifier)
+            {
+                return new SHAKEDigest(256);
+            }
+        });
+        table.put(NISTObjectIdentifiers.id_shake128_len, new BcDigestProvider()
+        {
+            public ExtendedDigest get(AlgorithmIdentifier digestAlgorithmIdentifier)
+            {
+                return new AdjustedXof(new SHAKEDigest(128), ASN1Integer.getInstance(digestAlgorithmIdentifier.getParameters()).intValueExact());
+            }
+        });
+        table.put(NISTObjectIdentifiers.id_shake256_len, new BcDigestProvider()
+        {
+            public ExtendedDigest get(AlgorithmIdentifier digestAlgorithmIdentifier)
+            {
+                return new AdjustedXof(new SHAKEDigest(256), ASN1Integer.getInstance(digestAlgorithmIdentifier.getParameters()).intValueExact());
+            }
+        });
         table.put(PKCSObjectIdentifiers.md5, new BcDigestProvider()
         {
             public ExtendedDigest get(AlgorithmIdentifier digestAlgorithmIdentifier)
@@ -153,6 +203,20 @@
                 return new RIPEMD256Digest();
             }
         });
+        table.put(GMObjectIdentifiers.sm3, new BcDigestProvider()
+        {
+            public ExtendedDigest get(AlgorithmIdentifier digestAlgorithmIdentifier)
+            {
+                return new SM3Digest();
+            }
+        });
+        table.put(MiscObjectIdentifiers.blake3_256, new BcDigestProvider()
+        {
+            public ExtendedDigest get(AlgorithmIdentifier digestAlgorithmIdentifier)
+            {
+                return new Blake3Digest(256);
+            }
+        });
         */
         // END Android-removed: Unsupported algorithms
 
@@ -178,4 +242,69 @@
 
         return extProv.get(digestAlgorithmIdentifier);
     }
+
+    /**
+     * -len OIDs for SHAKE include an integer representing the bitlength in of the output.
+     */
+    // BEGIN Android-removed: unused
+    /*
+    private static class AdjustedXof
+        implements Xof
+    {
+        private final Xof xof;
+        private final int length;
+
+        AdjustedXof(Xof xof, int length)
+        {
+            this.xof = xof;
+            this.length = length;
+        }
+
+        public String getAlgorithmName()
+        {
+            return xof.getAlgorithmName() + "-" + length;
+        }
+
+        public int getDigestSize()
+        {
+            return (length + 7) / 8;
+        }
+
+        public void update(byte in)
+        {
+            xof.update(in);
+        }
+
+        public void update(byte[] in, int inOff, int len)
+        {
+            xof.update(in, inOff, len);
+        }
+
+        public int doFinal(byte[] out, int outOff)
+        {
+            return doFinal(out, outOff, getDigestSize());
+        }
+
+        public void reset()
+        {
+            xof.reset();
+        }
+
+        public int getByteLength()
+        {
+            return xof.getByteLength();
+        }
+
+        public int doFinal(byte[] out, int outOff, int outLen)
+        {
+            return xof.doFinal(out, outOff, outLen);
+        }
+
+        public int doOutput(byte[] out, int outOff, int outLen)
+        {
+            return xof.doOutput(out, outOff, outLen);
+        }
+    }
+    */
+    // END Android-removed: unused
 }
diff --git a/bcpkix/src/main/java/org/bouncycastle/operator/bc/BcDigestCalculatorProvider.java b/bcpkix/src/main/java/org/bouncycastle/operator/bc/BcDigestCalculatorProvider.java
index b01b370..61901a9 100644
--- a/bcpkix/src/main/java/org/bouncycastle/operator/bc/BcDigestCalculatorProvider.java
+++ b/bcpkix/src/main/java/org/bouncycastle/operator/bc/BcDigestCalculatorProvider.java
@@ -40,7 +40,7 @@
         };
     }
 
-    private class DigestOutputStream
+    private static class DigestOutputStream
         extends OutputStream
     {
         private Digest dig;
diff --git a/bcpkix/src/main/java/org/bouncycastle/operator/jcajce/JcaContentSignerBuilder.java b/bcpkix/src/main/java/org/bouncycastle/operator/jcajce/JcaContentSignerBuilder.java
index f219b11..d3e2b92 100644
--- a/bcpkix/src/main/java/org/bouncycastle/operator/jcajce/JcaContentSignerBuilder.java
+++ b/bcpkix/src/main/java/org/bouncycastle/operator/jcajce/JcaContentSignerBuilder.java
@@ -11,16 +11,20 @@
 import java.security.spec.AlgorithmParameterSpec;
 import java.security.spec.MGF1ParameterSpec;
 import java.security.spec.PSSParameterSpec;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 
 import org.bouncycastle.asn1.ASN1EncodableVector;
 import org.bouncycastle.asn1.ASN1Encoding;
 import org.bouncycastle.asn1.ASN1Integer;
 import org.bouncycastle.asn1.ASN1Sequence;
 import org.bouncycastle.asn1.DERBitString;
+import org.bouncycastle.asn1.DERNull;
 import org.bouncycastle.asn1.DERSequence;
 import org.bouncycastle.asn1.misc.MiscObjectIdentifiers;
 import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
 import org.bouncycastle.asn1.pkcs.RSASSAPSSparams;
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 import org.bouncycastle.jcajce.CompositePrivateKey;
@@ -36,21 +40,31 @@
 import org.bouncycastle.operator.OperatorCreationException;
 import org.bouncycastle.operator.RuntimeOperatorException;
 import org.bouncycastle.operator.SignatureAlgorithmIdentifierFinder;
+import org.bouncycastle.util.Strings;
 import org.bouncycastle.util.io.TeeOutputStream;
 
 public class JcaContentSignerBuilder
 {
+    private static final Set isAlgIdFromPrivate = new HashSet();
+
+    static
+    {
+        isAlgIdFromPrivate.add("DILITHIUM");
+        isAlgIdFromPrivate.add("SPHINCS+");
+        isAlgIdFromPrivate.add("SPHINCSPlus");
+    }
+
+    private final String signatureAlgorithm;
+
     private OperatorHelper helper = new OperatorHelper(new DefaultJcaJceHelper());
     private SecureRandom random;
-    private String signatureAlgorithm;
+
     private AlgorithmIdentifier sigAlgId;
     private AlgorithmParameterSpec sigAlgSpec;
 
     public JcaContentSignerBuilder(String signatureAlgorithm)
     {
         this.signatureAlgorithm = signatureAlgorithm;
-        this.sigAlgId = new DefaultSignatureAlgorithmIdentifierFinder().find(signatureAlgorithm);
-        this.sigAlgSpec = null;
     }
 
     public JcaContentSignerBuilder(String signatureAlgorithm, AlgorithmParameterSpec sigParamSpec)
@@ -111,8 +125,21 @@
         
         try
         {
-            final Signature sig = helper.createSignature(sigAlgId);
+            if (sigAlgSpec == null)
+            {
+                if (isAlgIdFromPrivate.contains(Strings.toUpperCase(signatureAlgorithm)))
+                {
+                    sigAlgId = PrivateKeyInfo.getInstance(privateKey.getEncoded()).getPrivateKeyAlgorithm();
+                    this.sigAlgSpec = null;
+                }
+                else
+                {
+                    this.sigAlgId = new DefaultSignatureAlgorithmIdentifierFinder().find(signatureAlgorithm);
+                    this.sigAlgSpec = null;
+                }
+            }
             final AlgorithmIdentifier signatureAlgId = sigAlgId;
+            final Signature sig = helper.createSignature(sigAlgId);
 
             if (random != null)
             {
@@ -234,8 +261,16 @@
     private static RSASSAPSSparams createPSSParams(PSSParameterSpec pssSpec)
     {
         DigestAlgorithmIdentifierFinder digFinder = new DefaultDigestAlgorithmIdentifierFinder();
-           AlgorithmIdentifier digId = digFinder.find(pssSpec.getDigestAlgorithm());
-           AlgorithmIdentifier mgfDig = digFinder.find(((MGF1ParameterSpec)pssSpec.getMGFParameters()).getDigestAlgorithm());
+        AlgorithmIdentifier digId = digFinder.find(pssSpec.getDigestAlgorithm());
+        if (digId.getParameters() == null)
+        {
+            digId = new AlgorithmIdentifier(digId.getAlgorithm(), DERNull.INSTANCE);
+        }
+        AlgorithmIdentifier mgfDig = digFinder.find(((MGF1ParameterSpec)pssSpec.getMGFParameters()).getDigestAlgorithm());
+        if (mgfDig.getParameters() == null)
+        {
+            mgfDig = new AlgorithmIdentifier(mgfDig.getAlgorithm(), DERNull.INSTANCE);
+        }
 
         return new RSASSAPSSparams(
             digId,
diff --git a/bcpkix/src/main/java/org/bouncycastle/operator/jcajce/JcaContentVerifierProviderBuilder.java b/bcpkix/src/main/java/org/bouncycastle/operator/jcajce/JcaContentVerifierProviderBuilder.java
index 8f2102b..c44a24a 100644
--- a/bcpkix/src/main/java/org/bouncycastle/operator/jcajce/JcaContentVerifierProviderBuilder.java
+++ b/bcpkix/src/main/java/org/bouncycastle/operator/jcajce/JcaContentVerifierProviderBuilder.java
@@ -11,8 +11,8 @@
 import java.security.cert.X509Certificate;
 import java.util.List;
 
+import org.bouncycastle.asn1.ASN1BitString;
 import org.bouncycastle.asn1.ASN1Sequence;
-import org.bouncycastle.asn1.DERBitString;
 import org.bouncycastle.asn1.misc.MiscObjectIdentifiers;
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
@@ -278,7 +278,7 @@
         return rawSig;
     }
 
-    private class SigVerifier
+    private static class SigVerifier
         implements ContentVerifier
     {
         private final AlgorithmIdentifier algorithm;
@@ -321,7 +321,7 @@
         }
     }
 
-    private class RawSigVerifier
+    private static class RawSigVerifier
         extends SigVerifier
         implements RawContentVerifier
     {
@@ -382,7 +382,7 @@
         }
     }
 
-    private class CompositeVerifier
+    private static class CompositeVerifier
         implements ContentVerifier
     {
         private Signature[] sigs;
@@ -433,7 +433,7 @@
                 {
                     if (sigs[i] != null)
                     {
-                        if (!sigs[i].verify(DERBitString.getInstance(sigSeq.getObjectAt(i)).getBytes()))
+                        if (!sigs[i].verify(ASN1BitString.getInstance(sigSeq.getObjectAt(i)).getBytes()))
                         {
                             failed = true;
                         }
diff --git a/bcpkix/src/main/java/org/bouncycastle/operator/jcajce/JcaDigestCalculatorProviderBuilder.java b/bcpkix/src/main/java/org/bouncycastle/operator/jcajce/JcaDigestCalculatorProviderBuilder.java
index e356796..6494bf3 100644
--- a/bcpkix/src/main/java/org/bouncycastle/operator/jcajce/JcaDigestCalculatorProviderBuilder.java
+++ b/bcpkix/src/main/java/org/bouncycastle/operator/jcajce/JcaDigestCalculatorProviderBuilder.java
@@ -8,6 +8,7 @@
 
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 import org.bouncycastle.jcajce.util.DefaultJcaJceHelper;
+import org.bouncycastle.jcajce.util.JcaJceHelper;
 import org.bouncycastle.jcajce.util.NamedJcaJceHelper;
 import org.bouncycastle.jcajce.util.ProviderJcaJceHelper;
 import org.bouncycastle.operator.DigestCalculator;
@@ -22,6 +23,14 @@
     {
     }
 
+
+    public JcaDigestCalculatorProviderBuilder setHelper(JcaJceHelper helper)
+    {
+        this.helper = new OperatorHelper(helper);
+
+        return this;
+    }
+
     public JcaDigestCalculatorProviderBuilder setProvider(Provider provider)
     {
         this.helper = new OperatorHelper(new ProviderJcaJceHelper(provider));
@@ -63,7 +72,7 @@
                     {
                         return algorithm;
                     }
-                    
+
                     public OutputStream getOutputStream()
                     {
                         return stream;
@@ -78,7 +87,7 @@
         };
     }
 
-    private class DigestOutputStream
+    private static class DigestOutputStream
         extends OutputStream
     {
         private MessageDigest dig;
@@ -97,13 +106,13 @@
         public void write(byte[] bytes)
             throws IOException
         {
-           dig.update(bytes);
+            dig.update(bytes);
         }
 
         public void write(int b)
             throws IOException
         {
-           dig.update((byte)b);
+            dig.update((byte)b);
         }
 
         byte[] getDigest()
diff --git a/bcpkix/src/main/java/org/bouncycastle/operator/jcajce/OperatorHelper.java b/bcpkix/src/main/java/org/bouncycastle/operator/jcajce/OperatorHelper.java
index eb8b2d5..c3d2c85 100644
--- a/bcpkix/src/main/java/org/bouncycastle/operator/jcajce/OperatorHelper.java
+++ b/bcpkix/src/main/java/org/bouncycastle/operator/jcajce/OperatorHelper.java
@@ -44,12 +44,12 @@
 // import org.bouncycastle.asn1.teletrust.TeleTrusTObjectIdentifiers;
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
-import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
 import org.bouncycastle.cert.X509CertificateHolder;
 import org.bouncycastle.cms.CMSException;
 import org.bouncycastle.jcajce.util.AlgorithmParametersUtils;
 import org.bouncycastle.jcajce.util.JcaJceHelper;
 import org.bouncycastle.jcajce.util.MessageDigestUtils;
+import org.bouncycastle.operator.DefaultSignatureNameFinder;
 import org.bouncycastle.operator.OperatorCreationException;
 import org.bouncycastle.util.Integers;
 
@@ -61,54 +61,10 @@
     private static final Map symmetricKeyAlgNames = new HashMap();
     private static final Map symmetricWrapperKeySizes = new HashMap();
 
+    private static DefaultSignatureNameFinder sigFinder = new DefaultSignatureNameFinder();
+
     static
     {
-        //
-        // reverse mappings
-        //
-        oids.put(new ASN1ObjectIdentifier("1.2.840.113549.1.1.5"), "SHA1WITHRSA");
-        oids.put(PKCSObjectIdentifiers.sha224WithRSAEncryption, "SHA224WITHRSA");
-        oids.put(PKCSObjectIdentifiers.sha256WithRSAEncryption, "SHA256WITHRSA");
-        oids.put(PKCSObjectIdentifiers.sha384WithRSAEncryption, "SHA384WITHRSA");
-        oids.put(PKCSObjectIdentifiers.sha512WithRSAEncryption, "SHA512WITHRSA");
-        // BEGIN Android-removed: Unsupported algorithms
-        /*
-        oids.put(EdECObjectIdentifiers.id_Ed25519, "Ed25519");
-        oids.put(EdECObjectIdentifiers.id_Ed448, "Ed448");
-        oids.put(CryptoProObjectIdentifiers.gostR3411_94_with_gostR3410_94, "GOST3411WITHGOST3410");
-        oids.put(CryptoProObjectIdentifiers.gostR3411_94_with_gostR3410_2001, "GOST3411WITHECGOST3410");
-        oids.put(RosstandartObjectIdentifiers.id_tc26_signwithdigest_gost_3410_12_256, "GOST3411-2012-256WITHECGOST3410-2012-256");
-        oids.put(RosstandartObjectIdentifiers.id_tc26_signwithdigest_gost_3410_12_512, "GOST3411-2012-512WITHECGOST3410-2012-512");
-        oids.put(BSIObjectIdentifiers.ecdsa_plain_SHA1, "SHA1WITHPLAIN-ECDSA");
-        oids.put(BSIObjectIdentifiers.ecdsa_plain_SHA224, "SHA224WITHPLAIN-ECDSA");
-        oids.put(BSIObjectIdentifiers.ecdsa_plain_SHA256, "SHA256WITHPLAIN-ECDSA");
-        oids.put(BSIObjectIdentifiers.ecdsa_plain_SHA384, "SHA384WITHPLAIN-ECDSA");
-        oids.put(BSIObjectIdentifiers.ecdsa_plain_SHA512, "SHA512WITHPLAIN-ECDSA");
-        oids.put(BSIObjectIdentifiers.ecdsa_plain_RIPEMD160, "RIPEMD160WITHPLAIN-ECDSA");
-        oids.put(EACObjectIdentifiers.id_TA_ECDSA_SHA_1, "SHA1WITHCVC-ECDSA");
-        oids.put(EACObjectIdentifiers.id_TA_ECDSA_SHA_224, "SHA224WITHCVC-ECDSA");
-        oids.put(EACObjectIdentifiers.id_TA_ECDSA_SHA_256, "SHA256WITHCVC-ECDSA");
-        oids.put(EACObjectIdentifiers.id_TA_ECDSA_SHA_384, "SHA384WITHCVC-ECDSA");
-        oids.put(EACObjectIdentifiers.id_TA_ECDSA_SHA_512, "SHA512WITHCVC-ECDSA");
-        oids.put(IsaraObjectIdentifiers.id_alg_xmss, "XMSS");
-        oids.put(IsaraObjectIdentifiers.id_alg_xmssmt, "XMSSMT");
-        */
-        // END Android-removed: Unsupported algorithms
-
-        oids.put(new ASN1ObjectIdentifier("1.2.840.113549.1.1.4"), "MD5WITHRSA");
-        // BEGIN Android-removed: Unsupported algorithms
-        // oids.put(new ASN1ObjectIdentifier("1.2.840.113549.1.1.2"), "MD2WITHRSA");
-        // END Android-removed: Unsupported algorithms
-        oids.put(new ASN1ObjectIdentifier("1.2.840.10040.4.3"), "SHA1WITHDSA");
-        oids.put(X9ObjectIdentifiers.ecdsa_with_SHA1, "SHA1WITHECDSA");
-        oids.put(X9ObjectIdentifiers.ecdsa_with_SHA224, "SHA224WITHECDSA");
-        oids.put(X9ObjectIdentifiers.ecdsa_with_SHA256, "SHA256WITHECDSA");
-        oids.put(X9ObjectIdentifiers.ecdsa_with_SHA384, "SHA384WITHECDSA");
-        oids.put(X9ObjectIdentifiers.ecdsa_with_SHA512, "SHA512WITHECDSA");
-        oids.put(OIWObjectIdentifiers.sha1WithRSA, "SHA1WITHRSA");
-        oids.put(OIWObjectIdentifiers.dsaWithSHA1, "SHA1WITHDSA");
-        oids.put(NISTObjectIdentifiers.dsa_with_sha224, "SHA224WITHDSA");
-        oids.put(NISTObjectIdentifiers.dsa_with_sha256, "SHA256WITHDSA");
 
         oids.put(OIWObjectIdentifiers.idSHA1, "SHA1");
         oids.put(NISTObjectIdentifiers.id_sha224, "SHA224");
@@ -124,6 +80,8 @@
         // END Android-removed: Unsupported algorithms
 
         asymmetricWrapperAlgNames.put(PKCSObjectIdentifiers.rsaEncryption, "RSA/ECB/PKCS1Padding");
+        asymmetricWrapperAlgNames.put(OIWObjectIdentifiers.elGamalAlgorithm, "Elgamal/ECB/PKCS1Padding");
+        asymmetricWrapperAlgNames.put(PKCSObjectIdentifiers.id_RSAES_OAEP, "RSA/ECB/OAEPPadding");
 
         // Android-removed: Unsupported algorithms
         // asymmetricWrapperAlgNames.put(CryptoProObjectIdentifiers.gostR3410_2001, "ECGOST3410");
@@ -321,24 +279,43 @@
     AlgorithmParameters createAlgorithmParameters(AlgorithmIdentifier cipherAlgId)
         throws OperatorCreationException
     {
-        AlgorithmParameters parameters;
+        AlgorithmParameters parameters = null;
 
         if (cipherAlgId.getAlgorithm().equals(PKCSObjectIdentifiers.rsaEncryption))
         {
             return null;
         }
 
-        try
+        if (cipherAlgId.getAlgorithm().equals(PKCSObjectIdentifiers.id_RSAES_OAEP))
         {
-            parameters = helper.createAlgorithmParameters(cipherAlgId.getAlgorithm().getId());
+            try
+            {
+                parameters = helper.createAlgorithmParameters("OAEP");
+            }
+            catch (NoSuchAlgorithmException e)
+            {
+                // try below
+            }
+            catch (NoSuchProviderException e)
+            {
+                throw new OperatorCreationException("cannot create algorithm parameters: " + e.getMessage(), e);
+            }
         }
-        catch (NoSuchAlgorithmException e)
+
+        if (parameters == null)
         {
-            return null;   // There's a good chance there aren't any!
-        }
-        catch (NoSuchProviderException e)
-        {
-            throw new OperatorCreationException("cannot create algorithm parameters: " + e.getMessage(), e);
+            try
+            {
+                parameters = helper.createAlgorithmParameters(cipherAlgId.getAlgorithm().getId());
+            }
+            catch (NoSuchAlgorithmException e)
+            {
+                return null;   // There's a good chance there aren't any!
+            }
+            catch (NoSuchProviderException e)
+            {
+                throw new OperatorCreationException("cannot create algorithm parameters: " + e.getMessage(), e);
+            }
         }
 
         try
@@ -364,6 +341,10 @@
             {
                 dig = helper.createMessageDigest("SHAKE256-" + ASN1Integer.getInstance(digAlgId.getParameters()).getValue());
             }
+            else if (digAlgId.getAlgorithm().equals(NISTObjectIdentifiers.id_shake128_len))
+            {
+                dig = helper.createMessageDigest("SHAKE128-" + ASN1Integer.getInstance(digAlgId.getParameters()).getValue());
+            }
             else
             {
                 dig = helper.createMessageDigest(MessageDigestUtils.getDigestName(digAlgId.getAlgorithm()));
@@ -411,12 +392,6 @@
 
                 sig = helper.createSignature(signatureAlgorithm);
             }
-            else if (oids.get(sigAlgId.getAlgorithm()) != null)
-            {
-                String signatureAlgorithm = (String)oids.get(sigAlgId.getAlgorithm());
-
-                sig = helper.createSignature(signatureAlgorithm);
-            }
             else
             {
                 throw e;
@@ -447,7 +422,7 @@
         return sig;
     }
 
-    public Signature createRawSignature(AlgorithmIdentifier algorithm)
+    Signature createRawSignature(AlgorithmIdentifier algorithm)
     {
         Signature sig;
 
@@ -483,27 +458,11 @@
     private static String getSignatureName(
         AlgorithmIdentifier sigAlgId)
     {
-        ASN1Encodable params = sigAlgId.getParameters();
-
-        if (params != null && !DERNull.INSTANCE.equals(params))
-        {
-            if (sigAlgId.getAlgorithm().equals(PKCSObjectIdentifiers.id_RSASSA_PSS))
-            {
-                RSASSAPSSparams rsaParams = RSASSAPSSparams.getInstance(params);
-                return getDigestName(rsaParams.getHashAlgorithm().getAlgorithm()) + "WITHRSAANDMGF1";
-            }
-        }
-
-        if (oids.containsKey(sigAlgId.getAlgorithm()))
-        {
-            return (String)oids.get(sigAlgId.getAlgorithm());
-        }
-
-        return sigAlgId.getAlgorithm().getId();
+        return sigFinder.getAlgorithmName(sigAlgId);
     }
 
     // we need to remove the - to create a correct signature name
-    private static String getDigestName(ASN1ObjectIdentifier oid)
+    static String getDigestName(ASN1ObjectIdentifier oid)
     {
         String name = MessageDigestUtils.getDigestName(oid);
 
diff --git a/bcpkix/src/main/java/org/bouncycastle/pkcs/DeltaCertAttributeUtils.java b/bcpkix/src/main/java/org/bouncycastle/pkcs/DeltaCertAttributeUtils.java
new file mode 100644
index 0000000..627780b
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/pkcs/DeltaCertAttributeUtils.java
@@ -0,0 +1,65 @@
+package org.bouncycastle.pkcs;
+
+import java.io.IOException;
+import java.util.Enumeration;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.DERSet;
+import org.bouncycastle.asn1.DERTaggedObject;
+import org.bouncycastle.asn1.pkcs.Attribute;
+import org.bouncycastle.asn1.pkcs.CertificationRequest;
+import org.bouncycastle.asn1.pkcs.CertificationRequestInfo;
+import org.bouncycastle.asn1.x509.Extension;
+import org.bouncycastle.operator.ContentVerifierProvider;
+
+public class DeltaCertAttributeUtils
+{
+    public static Extension makeDeltaCertificateExtension(DeltaCertificateRequestAttributeValue deltaReqAttr)
+        throws IOException
+    {
+         return null;
+    }
+
+    public static boolean isDeltaRequestSignatureValid(PKCS10CertificationRequest baseRequest, ContentVerifierProvider contentVerifierProvider)
+        throws PKCSException
+    {
+        Attribute[] attributes = baseRequest.getAttributes(new ASN1ObjectIdentifier("2.16.840.1.114027.80.6.2"));
+
+        DeltaCertificateRequestAttributeValue deltaReq = new DeltaCertificateRequestAttributeValue(attributes[0]);
+
+        attributes = baseRequest.getAttributes(new ASN1ObjectIdentifier("2.16.840.1.114027.80.6.3"));
+
+        CertificationRequest deltaPkcs10 = baseRequest.toASN1Structure();
+        CertificationRequestInfo deltaInfo = deltaPkcs10.getCertificationRequestInfo();
+
+        ASN1EncodableVector deltaPkcs10InfoV = new ASN1EncodableVector();
+        deltaPkcs10InfoV.add(deltaInfo.getVersion());
+        deltaPkcs10InfoV.add(deltaInfo.getSubject());
+        deltaPkcs10InfoV.add(deltaInfo.getSubjectPublicKeyInfo());
+
+        ASN1EncodableVector attrSetV = new ASN1EncodableVector();
+        for (Enumeration en = deltaInfo.getAttributes().getObjects(); en.hasMoreElements();)
+        {
+            Attribute attr = Attribute.getInstance(en.nextElement());
+
+            if (!attr.getAttrType().equals(new ASN1ObjectIdentifier("2.16.840.1.114027.80.6.3")))
+            {
+                attrSetV.add(attr);
+            }
+        }
+
+        deltaPkcs10InfoV.add(new DERTaggedObject(false, 0, new DERSet(attrSetV)));
+
+        ASN1EncodableVector deltaPkcs10V = new ASN1EncodableVector();
+
+        deltaPkcs10V.add(new DERSequence(deltaPkcs10InfoV));
+        deltaPkcs10V.add(deltaReq.getSignatureAlgorithm());
+        deltaPkcs10V.add(attributes[0].getAttributeValues()[0]);
+
+        PKCS10CertificationRequest deltaPkcs10Req = new PKCS10CertificationRequest(CertificationRequest.getInstance(new DERSequence(deltaPkcs10V)));
+
+        return deltaPkcs10Req.isSignatureValid(contentVerifierProvider);
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/pkcs/DeltaCertificateRequestAttributeValue.java b/bcpkix/src/main/java/org/bouncycastle/pkcs/DeltaCertificateRequestAttributeValue.java
new file mode 100644
index 0000000..58aec1d
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/pkcs/DeltaCertificateRequestAttributeValue.java
@@ -0,0 +1,102 @@
+package org.bouncycastle.pkcs;
+
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.pkcs.Attribute;
+import org.bouncycastle.asn1.x500.X500Name;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.asn1.x509.Extensions;
+import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+
+/**
+ * The delta certificate request attribute defined in: https://datatracker.ietf.org/doc/draft-bonnell-lamps-chameleon-certs/
+ */
+public class DeltaCertificateRequestAttributeValue
+    implements ASN1Encodable
+{
+    private final X500Name subject;
+    private final SubjectPublicKeyInfo subjectPKInfo;
+    private final Extensions extensions;
+    private final AlgorithmIdentifier signatureAlgorithm;
+    private final ASN1Sequence attrSeq;
+
+    public DeltaCertificateRequestAttributeValue(Attribute attribute)
+    {
+        this(ASN1Sequence.getInstance(attribute.getAttributeValues()[0]));
+    }
+
+    DeltaCertificateRequestAttributeValue(ASN1Sequence attrSeq)
+    {
+        this.attrSeq = attrSeq;
+        // TODO: validate attribute size
+
+        int idx = 0;
+        if (attrSeq.getObjectAt(0) instanceof ASN1TaggedObject)
+        {
+            subject = X500Name.getInstance(ASN1TaggedObject.getInstance(attrSeq.getObjectAt(0)), true);
+            idx++;
+        }
+        else
+        {
+            subject = null;
+        }
+
+        subjectPKInfo = SubjectPublicKeyInfo.getInstance(attrSeq.getObjectAt(idx));
+        idx++;
+
+        Extensions ext = null;
+        AlgorithmIdentifier sigAlg = null;
+
+        if (idx != attrSeq.size())
+        {
+            while (idx < attrSeq.size())
+            {
+                ASN1TaggedObject tagObj = ASN1TaggedObject.getInstance(attrSeq.getObjectAt(idx));
+                if (tagObj.getTagNo() == 1)
+                {
+                    ext = Extensions.getInstance(tagObj, false);
+                }
+                else if (tagObj.getTagNo() == 2)
+                {
+                    sigAlg = AlgorithmIdentifier.getInstance(tagObj, false);
+                }
+                else
+                {
+                    throw new IllegalArgumentException("unknown tag");
+                }
+                idx++;
+            }
+        }
+
+        this.extensions = ext;
+        this.signatureAlgorithm = sigAlg;
+    }
+
+    public X500Name getSubject()
+    {
+        return subject;
+    }
+
+    public SubjectPublicKeyInfo getSubjectPKInfo()
+    {
+        return subjectPKInfo;
+    }
+
+    public Extensions getExtensions()
+    {
+        return extensions;
+    }
+
+    public AlgorithmIdentifier getSignatureAlgorithm()
+    {
+        return signatureAlgorithm;
+    }
+
+    @Override
+    public ASN1Primitive toASN1Primitive()
+    {
+        return attrSeq;
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/pkcs/DeltaCertificateRequestAttributeValueBuilder.java b/bcpkix/src/main/java/org/bouncycastle/pkcs/DeltaCertificateRequestAttributeValueBuilder.java
new file mode 100644
index 0000000..1c844f2
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/pkcs/DeltaCertificateRequestAttributeValueBuilder.java
@@ -0,0 +1,57 @@
+package org.bouncycastle.pkcs;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.DERSet;
+import org.bouncycastle.asn1.DERTaggedObject;
+import org.bouncycastle.asn1.pkcs.Attribute;
+import org.bouncycastle.asn1.x500.X500Name;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+
+public class DeltaCertificateRequestAttributeValueBuilder
+{
+    private final SubjectPublicKeyInfo subjectPublicKey;
+
+    private AlgorithmIdentifier signatureAlgorithm;
+    private X500Name subject;
+
+    public DeltaCertificateRequestAttributeValueBuilder(SubjectPublicKeyInfo subjectPublicKey)
+    {
+        this.subjectPublicKey = subjectPublicKey;
+    }
+
+    public DeltaCertificateRequestAttributeValueBuilder setSignatureAlgorithm(AlgorithmIdentifier signatureAlgorithm)
+    {
+       this.signatureAlgorithm = signatureAlgorithm;
+
+       return this;
+    }
+
+    public DeltaCertificateRequestAttributeValueBuilder setSubject(X500Name subject)
+    {
+       this.subject = subject;
+
+       return this;
+    }
+
+    public DeltaCertificateRequestAttributeValue build()
+    {
+        ASN1EncodableVector v = new ASN1EncodableVector();
+
+        if (subject != null)
+        {
+            v.add(new DERTaggedObject(false, 0, subject));
+        }
+        v.add(subjectPublicKey);
+        if (signatureAlgorithm != null)
+        {
+            v.add(new DERTaggedObject(false, 2, signatureAlgorithm));
+        }
+
+        
+        return new DeltaCertificateRequestAttributeValue(new Attribute(new ASN1ObjectIdentifier("2.16.840.1.114027.80.6.2"),
+                        new DERSet(new DERSequence(v))));
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/pkcs/PKCS10CertificationRequest.java b/bcpkix/src/main/java/org/bouncycastle/pkcs/PKCS10CertificationRequest.java
new file mode 100644
index 0000000..67ebd62
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/pkcs/PKCS10CertificationRequest.java
@@ -0,0 +1,435 @@
+package org.bouncycastle.pkcs;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.List;
+
+import org.bouncycastle.asn1.ASN1BitString;
+import org.bouncycastle.asn1.ASN1Boolean;
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Encoding;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1Set;
+import org.bouncycastle.asn1.DERSet;
+import org.bouncycastle.asn1.pkcs.Attribute;
+import org.bouncycastle.asn1.pkcs.CertificationRequest;
+import org.bouncycastle.asn1.pkcs.CertificationRequestInfo;
+import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.bouncycastle.asn1.x500.X500Name;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.asn1.x509.Extension;
+import org.bouncycastle.asn1.x509.Extensions;
+import org.bouncycastle.asn1.x509.ExtensionsGenerator;
+import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.bouncycastle.operator.ContentVerifier;
+import org.bouncycastle.operator.ContentVerifierProvider;
+import org.bouncycastle.util.Exceptions;
+
+/**
+ * Holding class for a PKCS#10 certification request.
+ */
+public class PKCS10CertificationRequest
+{
+    private static Attribute[] EMPTY_ARRAY = new Attribute[0];
+
+    private final CertificationRequest certificationRequest;
+    private final boolean isAltRequest;
+    private final AlgorithmIdentifier altSignature;
+    private final SubjectPublicKeyInfo altPublicKey;
+    private final ASN1BitString altSignatureValue;
+
+    private static CertificationRequest parseBytes(byte[] encoding)
+        throws IOException
+    {
+        try
+        {
+            CertificationRequest rv = CertificationRequest.getInstance(ASN1Primitive.fromByteArray(encoding));
+
+            if (rv == null)
+            {
+                throw new PKCSIOException("empty data passed to constructor");
+            }
+
+            return rv;
+        }
+        catch (ClassCastException e)
+        {
+            throw new PKCSIOException("malformed data: " + e.getMessage(), e);
+        }
+        catch (IllegalArgumentException e)
+        {
+            throw new PKCSIOException("malformed data: " + e.getMessage(), e);
+        }
+    }
+
+    private static ASN1Encodable getSingleValue(Attribute at)
+    {
+        ASN1Encodable[] attrValues = at.getAttributeValues();
+        if (attrValues.length!= 1)
+        {
+            throw new IllegalArgumentException("single value attribute value not size of 1");
+        }
+
+        return attrValues[0];
+    }
+
+    /**
+     * Create a PKCS10CertificationRequestHolder from an underlying ASN.1 structure.
+     *
+     * @param certificationRequest the underlying ASN.1 structure representing a request.
+     */
+    public PKCS10CertificationRequest(CertificationRequest certificationRequest)
+    {
+        if (certificationRequest == null)
+        {
+            throw new NullPointerException("certificationRequest cannot be null");
+        }
+        this.certificationRequest = certificationRequest;
+
+        ASN1Set attributes = certificationRequest.getCertificationRequestInfo().getAttributes();
+
+        AlgorithmIdentifier altSig = null;
+        SubjectPublicKeyInfo altPub = null;
+        ASN1BitString altSigValue = null;
+
+        if (attributes != null)
+        {
+            for (Enumeration en = attributes.getObjects(); en.hasMoreElements();)
+            {
+                Attribute at = Attribute.getInstance(en.nextElement());
+
+                if (Extension.altSignatureAlgorithm.equals(at.getAttrType()))
+                {
+                    altSig = AlgorithmIdentifier.getInstance(getSingleValue(at));
+                }
+                if (Extension.subjectAltPublicKeyInfo.equals(at.getAttrType()))
+                {
+                    altPub = SubjectPublicKeyInfo.getInstance(getSingleValue(at));
+                }
+                if (Extension.altSignatureValue.equals(at.getAttrType()))
+                {
+                    altSigValue = ASN1BitString.getInstance(getSingleValue(at));
+                }
+            }
+        }
+
+        this.isAltRequest = (altSig != null) | (altPub != null) | (altSigValue != null);
+        if (isAltRequest)
+        {
+            if (!((altSig != null) & (altPub != null) & (altSigValue != null)))
+            {
+                throw new IllegalArgumentException("invalid alternate public key details found");
+            }
+        }
+
+        this.altSignature = altSig;
+        this.altPublicKey = altPub;
+        this.altSignatureValue = altSigValue;
+    }
+
+    /**
+     * Create a PKCS10CertificationRequestHolder from the passed in bytes.
+     *
+     * @param encoded BER/DER encoding of the CertificationRequest structure.
+     * @throws IOException in the event of corrupted data, or an incorrect structure.
+     */
+    public PKCS10CertificationRequest(byte[] encoded)
+        throws IOException
+    {
+        this(parseBytes(encoded));
+    }
+
+    /**
+     * Return the underlying ASN.1 structure for this request.
+     *
+     * @return a CertificateRequest object.
+     */
+    public CertificationRequest toASN1Structure()
+    {
+        return certificationRequest;
+    }
+
+    /**
+     * Return the subject on this request.
+     *
+     * @return the X500Name representing the request's subject.
+     */
+    public X500Name getSubject()
+    {
+        return X500Name.getInstance(certificationRequest.getCertificationRequestInfo().getSubject());
+    }
+
+    /**
+     * Return the details of the signature algorithm used to create this request.
+     *
+     * @return the AlgorithmIdentifier describing the signature algorithm used to create this request.
+     */
+    public AlgorithmIdentifier getSignatureAlgorithm()
+    {
+        return certificationRequest.getSignatureAlgorithm();
+    }
+
+    /**
+     * Return the bytes making up the signature associated with this request.
+     *
+     * @return the request signature bytes.
+     */
+    public byte[] getSignature()
+    {
+        return certificationRequest.getSignature().getOctets();
+    }
+
+    /**
+     * Return the SubjectPublicKeyInfo describing the public key this request is carrying.
+     *
+     * @return the public key ASN.1 structure contained in the request.
+     */
+    public SubjectPublicKeyInfo getSubjectPublicKeyInfo()
+    {
+        return certificationRequest.getCertificationRequestInfo().getSubjectPublicKeyInfo();
+    }
+
+    /**
+     * Return the attributes, if any associated with this request.
+     *
+     * @return an array of Attribute, zero length if none present.
+     */
+    public Attribute[] getAttributes()
+    {
+        ASN1Set attrSet = certificationRequest.getCertificationRequestInfo().getAttributes();
+
+        if (attrSet == null)
+        {
+            return EMPTY_ARRAY;
+        }
+
+        Attribute[] attrs = new Attribute[attrSet.size()];
+
+        for (int i = 0; i != attrSet.size(); i++)
+        {
+            attrs[i] = Attribute.getInstance(attrSet.getObjectAt(i));
+        }
+
+        return attrs;
+    }
+
+    /**
+     * Return an  array of attributes matching the passed in type OID.
+     *
+     * @param type the type of the attribute being looked for.
+     * @return an array of Attribute of the requested type, zero length if none present.
+     */
+    public Attribute[] getAttributes(ASN1ObjectIdentifier type)
+    {
+        ASN1Set attrSet = certificationRequest.getCertificationRequestInfo().getAttributes();
+
+        if (attrSet == null)
+        {
+            return EMPTY_ARRAY;
+        }
+
+        List list = new ArrayList();
+
+        for (int i = 0; i != attrSet.size(); i++)
+        {
+            Attribute attr = Attribute.getInstance(attrSet.getObjectAt(i));
+            if (attr.getAttrType().equals(type))
+            {
+                list.add(attr);
+            }
+        }
+
+        if (list.size() == 0)
+        {
+            return EMPTY_ARRAY;
+        }
+
+        return (Attribute[])list.toArray(new Attribute[list.size()]);
+    }
+
+    public byte[] getEncoded()
+        throws IOException
+    {
+        return certificationRequest.getEncoded();
+    }
+
+    /**
+     * Validate the signature on the PKCS10 certification request in this holder.
+     *
+     * @param verifierProvider a ContentVerifierProvider that can generate a verifier for the signature.
+     * @return true if the signature is valid, false otherwise.
+     * @throws PKCSException if the signature cannot be processed or is inappropriate.
+     */
+    public boolean isSignatureValid(ContentVerifierProvider verifierProvider)
+        throws PKCSException
+    {
+        CertificationRequestInfo requestInfo = certificationRequest.getCertificationRequestInfo();
+
+        ContentVerifier verifier;
+
+        try
+        {
+            verifier = verifierProvider.get(certificationRequest.getSignatureAlgorithm());
+
+            OutputStream sOut = verifier.getOutputStream();
+
+            sOut.write(requestInfo.getEncoded(ASN1Encoding.DER));
+
+            sOut.close();
+        }
+        catch (Exception e)
+        {
+            throw new PKCSException("unable to process signature: " + e.getMessage(), e);
+        }
+
+        return verifier.verify(this.getSignature());
+    }
+
+    /**
+     * Return true if the certification request has an alternate public key present.
+     *
+     * @return true if this is a dual key request, false otherwise.
+     */
+    public boolean hasAltPublicKey()
+    {
+        return isAltRequest;
+    }
+
+    /**
+     * Validate the alternate signature on the PKCS10 certification request in this holder.
+     *
+     * @param verifierProvider a ContentVerifierProvider that can generate a verifier for the signature.
+     * @return true if the alternate signature is valid, false otherwise.
+     * @throws PKCSException if the signature cannot be processed or is inappropriate.
+     */
+    public boolean isAltSignatureValid(ContentVerifierProvider verifierProvider)
+        throws PKCSException
+    {
+        if (!isAltRequest)
+        {
+            throw new IllegalStateException("no alternate public key present");
+        }
+
+        CertificationRequestInfo requestInfo = certificationRequest.getCertificationRequestInfo();
+        ASN1Set attributes = requestInfo.getAttributes();
+        ASN1EncodableVector atV = new ASN1EncodableVector();
+
+        for (Enumeration en = attributes.getObjects(); en.hasMoreElements();)
+        {
+            Attribute at = Attribute.getInstance(en.nextElement());
+
+            if (Extension.altSignatureValue.equals(at.getAttrType()))
+            {
+                continue;
+            }
+
+            atV.add(at);
+        }
+
+        requestInfo = new CertificationRequestInfo(requestInfo.getSubject(), requestInfo.getSubjectPublicKeyInfo(), new DERSet(atV));
+        ContentVerifier verifier;
+
+        try
+        {
+            verifier = verifierProvider.get(this.altSignature);
+
+            OutputStream sOut = verifier.getOutputStream();
+
+            sOut.write(requestInfo.getEncoded(ASN1Encoding.DER));
+
+            sOut.close();
+        }
+        catch (Exception e)
+        {
+            throw new PKCSException("unable to process signature: " + e.getMessage(), e);
+        }
+
+        return verifier.verify(this.altSignatureValue.getOctets());
+    }
+
+    /**
+     * Return any extensions requested in the PKCS#10 request. If none are present, the method
+     * will return null.
+     *
+     * @return the requested extensions, null if none are requested.
+     * @throws IllegalStateException if the extension request is and is somehow invalid.
+     */
+    public Extensions getRequestedExtensions()
+    {
+        Attribute[] attributes = getAttributes();
+        for (int i = 0; i != attributes.length; i++)
+        {
+            Attribute encodable = attributes[i];
+            if (PKCSObjectIdentifiers.pkcs_9_at_extensionRequest.equals(encodable.getAttrType()))
+            {
+                ExtensionsGenerator extensionsGenerator = new ExtensionsGenerator();
+
+                ASN1Set attrValues = encodable.getAttrValues();
+                if (attrValues == null || attrValues.size() == 0)
+                {
+                    throw new IllegalStateException("pkcs_9_at_extensionRequest present but has no value");
+                }
+
+                ASN1Sequence extensionSequence = ASN1Sequence.getInstance(attrValues.getObjectAt(0));
+
+                try
+                {
+                    for (Enumeration en = extensionSequence.getObjects(); en.hasMoreElements(); )
+                    {
+                        ASN1Sequence itemSeq = ASN1Sequence.getInstance(en.nextElement());
+
+                        boolean critical = itemSeq.size() == 3 && ASN1Boolean.getInstance(itemSeq.getObjectAt(1)).isTrue();
+                        if (itemSeq.size() == 2)
+                        {
+                            extensionsGenerator.addExtension(ASN1ObjectIdentifier.getInstance(itemSeq.getObjectAt(0)), false, ASN1OctetString.getInstance(itemSeq.getObjectAt(1)).getOctets());
+                        }
+                        else if (itemSeq.size() == 3)
+                        {
+                            extensionsGenerator.addExtension(ASN1ObjectIdentifier.getInstance(itemSeq.getObjectAt(0)), critical, ASN1OctetString.getInstance(itemSeq.getObjectAt(2)).getOctets());
+                        }
+                        else
+                        {
+                            throw new IllegalStateException("incorrect sequence size of Extension get " + itemSeq.size() + " expected 2 or three");
+                        }
+                    }
+                }
+                catch (IllegalArgumentException e)
+                {
+                    throw Exceptions.illegalStateException("asn1 processing issue: " + e.getMessage(), e);
+                }
+
+                return extensionsGenerator.generate();
+            }
+        }
+        return null;
+    }
+
+
+    public boolean equals(Object o)
+    {
+        if (o == this)
+        {
+            return true;
+        }
+
+        if (!(o instanceof PKCS10CertificationRequest))
+        {
+            return false;
+        }
+
+        PKCS10CertificationRequest other = (PKCS10CertificationRequest)o;
+
+        return this.toASN1Structure().equals(other.toASN1Structure());
+    }
+
+    public int hashCode()
+    {
+        return this.toASN1Structure().hashCode();
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/pkcs/PKCSException.java b/bcpkix/src/main/java/org/bouncycastle/pkcs/PKCSException.java
new file mode 100644
index 0000000..8ee6f6f
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/pkcs/PKCSException.java
@@ -0,0 +1,27 @@
+package org.bouncycastle.pkcs;
+
+/**
+ * General checked Exception thrown in the cert package and its sub-packages.
+ */
+public class PKCSException
+    extends Exception
+{
+    private Throwable cause;
+
+    public PKCSException(String msg, Throwable cause)
+    {
+        super(msg);
+
+        this.cause = cause;
+    }
+
+    public PKCSException(String msg)
+    {
+        super(msg);
+    }
+
+    public Throwable getCause()
+    {
+        return cause;
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/pkcs/PKCSIOException.java b/bcpkix/src/main/java/org/bouncycastle/pkcs/PKCSIOException.java
new file mode 100644
index 0000000..2776006
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/pkcs/PKCSIOException.java
@@ -0,0 +1,30 @@
+package org.bouncycastle.pkcs;
+
+import java.io.IOException;
+
+/**
+ * General IOException thrown in the cert package and its sub-packages.
+ */
+public class PKCSIOException
+    extends IOException
+{
+    private Throwable cause;
+
+    public PKCSIOException(String msg, Throwable cause)
+    {
+        super(msg);
+
+        this.cause = cause;
+    }
+
+    public PKCSIOException(String msg)
+    {
+        super(msg);
+    }
+
+    public Throwable getCause()
+    {
+        return cause;
+    }
+}
+
diff --git a/bcpkix/src/main/java/org/bouncycastle/pkix/jcajce/AnnotatedException.java b/bcpkix/src/main/java/org/bouncycastle/pkix/jcajce/AnnotatedException.java
deleted file mode 100644
index c7211dc..0000000
--- a/bcpkix/src/main/java/org/bouncycastle/pkix/jcajce/AnnotatedException.java
+++ /dev/null
@@ -1,24 +0,0 @@
-package org.bouncycastle.pkix.jcajce;
-
-class AnnotatedException
-    extends Exception
-{
-    private Throwable _underlyingException;
-
-    public AnnotatedException(String string, Throwable e)
-    {
-        super(string);
-
-        _underlyingException = e;
-    }
-
-    public AnnotatedException(String string)
-    {
-        this(string, null);
-    }
-
-    public Throwable getCause()
-    {
-        return _underlyingException;
-    }
-}
diff --git a/bcpkix/src/main/java/org/bouncycastle/pkix/jcajce/CRLNotFoundException.java b/bcpkix/src/main/java/org/bouncycastle/pkix/jcajce/CRLNotFoundException.java
deleted file mode 100644
index 06f58bf..0000000
--- a/bcpkix/src/main/java/org/bouncycastle/pkix/jcajce/CRLNotFoundException.java
+++ /dev/null
@@ -1,17 +0,0 @@
-package org.bouncycastle.pkix.jcajce;
-
-import java.security.cert.CertPathValidatorException;
-
-class CRLNotFoundException
-    extends CertPathValidatorException
-{
-    CRLNotFoundException(String message)
-    {
-        super(message);
-    }
-
-    public CRLNotFoundException(String message, Throwable cause)
-    {
-        super(message, cause);
-    }
-}
diff --git a/bcpkix/src/main/java/org/bouncycastle/pkix/jcajce/CertStatus.java b/bcpkix/src/main/java/org/bouncycastle/pkix/jcajce/CertStatus.java
deleted file mode 100644
index 8c334d4..0000000
--- a/bcpkix/src/main/java/org/bouncycastle/pkix/jcajce/CertStatus.java
+++ /dev/null
@@ -1,46 +0,0 @@
-package org.bouncycastle.pkix.jcajce;
-
-import java.util.Date;
-
-class CertStatus
-{
-    public static final int UNREVOKED = 11;
-
-    public static final int UNDETERMINED = 12;
-
-    int certStatus = UNREVOKED;
-
-    Date revocationDate = null;
-
-    /**
-     * @return Returns the revocationDate.
-     */
-    public Date getRevocationDate()
-    {
-        return revocationDate;
-    }
-
-    /**
-     * @param revocationDate The revocationDate to set.
-     */
-    public void setRevocationDate(Date revocationDate)
-    {
-        this.revocationDate = revocationDate;
-    }
-
-    /**
-     * @return Returns the certStatus.
-     */
-    public int getCertStatus()
-    {
-        return certStatus;
-    }
-
-    /**
-     * @param certStatus The certStatus to set.
-     */
-    public void setCertStatus(int certStatus)
-    {
-        this.certStatus = certStatus;
-    }
-}
diff --git a/bcpkix/src/main/java/org/bouncycastle/pkix/jcajce/PKIXCRLUtil.java b/bcpkix/src/main/java/org/bouncycastle/pkix/jcajce/PKIXCRLUtil.java
deleted file mode 100644
index bf3b7de..0000000
--- a/bcpkix/src/main/java/org/bouncycastle/pkix/jcajce/PKIXCRLUtil.java
+++ /dev/null
@@ -1,108 +0,0 @@
-package org.bouncycastle.pkix.jcajce;
-
-import java.security.cert.CertStore;
-import java.security.cert.CertStoreException;
-import java.security.cert.X509CRL;
-import java.security.cert.X509Certificate;
-import java.util.Date;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Set;
-
-import org.bouncycastle.jcajce.PKIXCRLStoreSelector;
-import org.bouncycastle.util.Store;
-import org.bouncycastle.util.StoreException;
-
-abstract class PKIXCRLUtil
-{
-    static Set findCRLs(PKIXCRLStoreSelector crlselect, Date validityDate, List certStores, List pkixCrlStores)
-        throws AnnotatedException
-    {
-        HashSet initialSet = new HashSet();
-
-        // get complete CRL(s)
-        try
-        {
-            findCRLs(initialSet, crlselect, pkixCrlStores);
-            findCRLs(initialSet, crlselect, certStores);
-        }
-        catch (AnnotatedException e)
-        {
-            throw new AnnotatedException("Exception obtaining complete CRLs.", e);
-        }
-
-        Set finalSet = new HashSet();
-
-        // based on RFC 5280 6.3.3
-        for (Iterator it = initialSet.iterator(); it.hasNext();)
-        {
-            X509CRL crl = (X509CRL)it.next();
-
-            if (crl.getNextUpdate().after(validityDate))
-            {
-                X509Certificate cert = crlselect.getCertificateChecking();
-
-                if (null == cert || crl.getThisUpdate().before(cert.getNotAfter()))
-                {
-                    finalSet.add(crl);
-                }
-            }
-        }
-
-        return finalSet;
-    }
-
-    /**
-     * Add to a HashSet any and all CRLs found in the X509Store's that are matching the crlSelect
-     * criteria.
-     *
-     * @param crls
-     *            the {@link HashSet} to add the CRLs to.
-     * @param crlSelect
-     *            a {@link PKIXCRLStoreSelector} object that will be used to select the CRLs
-     * @param crlStores
-     *            a List containing only {@link Store} objects. These are used to search for CRLs
-     */
-    private static void findCRLs(HashSet crls, PKIXCRLStoreSelector crlSelect, List crlStores) throws AnnotatedException
-    {
-        AnnotatedException lastException = null;
-        boolean foundValidStore = false;
-
-        Iterator iter = crlStores.iterator();
-        while (iter.hasNext())
-        {
-            Object obj = iter.next();
-            if (obj instanceof Store)
-            {
-                Store store = (Store)obj;
-                try
-                {
-                    crls.addAll(store.getMatches(crlSelect));
-                    foundValidStore = true;
-                }
-                catch (StoreException e)
-                {
-                    lastException = new AnnotatedException("Exception searching in X.509 CRL store.", e);
-                }
-            }
-            else
-            {
-                CertStore store = (CertStore)obj;
-                try
-                {
-                    crls.addAll(PKIXCRLStoreSelector.getCRLs(crlSelect, store));
-                    foundValidStore = true;
-                }
-                catch (CertStoreException e)
-                {
-                    lastException = new AnnotatedException("Exception searching in X.509 CRL store.", e);
-                }
-            }
-        }
-        if (!foundValidStore && lastException != null)
-        {
-            throw lastException;
-        }
-    }
-}
diff --git a/bcpkix/src/main/java/org/bouncycastle/pkix/jcajce/PKIXJcaJceHelper.java b/bcpkix/src/main/java/org/bouncycastle/pkix/jcajce/PKIXJcaJceHelper.java
deleted file mode 100644
index b3cd88e..0000000
--- a/bcpkix/src/main/java/org/bouncycastle/pkix/jcajce/PKIXJcaJceHelper.java
+++ /dev/null
@@ -1,14 +0,0 @@
-package org.bouncycastle.pkix.jcajce;
-
-import java.security.NoSuchAlgorithmException;
-import java.security.NoSuchProviderException;
-import java.security.cert.CertPathBuilder;
-
-import org.bouncycastle.jcajce.util.JcaJceHelper;
-
-interface PKIXJcaJceHelper
-    extends JcaJceHelper
-{
-    CertPathBuilder createCertPathBuilder(String type)
-        throws NoSuchAlgorithmException, NoSuchProviderException;
-}
diff --git a/bcpkix/src/main/java/org/bouncycastle/pkix/jcajce/RFC3280CertPathUtilities.java b/bcpkix/src/main/java/org/bouncycastle/pkix/jcajce/RFC3280CertPathUtilities.java
deleted file mode 100644
index 83b8ddc..0000000
--- a/bcpkix/src/main/java/org/bouncycastle/pkix/jcajce/RFC3280CertPathUtilities.java
+++ /dev/null
@@ -1,1002 +0,0 @@
-package org.bouncycastle.pkix.jcajce;
-
-import java.io.IOException;
-import java.security.PublicKey;
-import java.security.cert.CertPathBuilder;
-import java.security.cert.CertPathBuilderException;
-import java.security.cert.CertPathValidatorException;
-import java.security.cert.X509CRL;
-import java.security.cert.X509CRLSelector;
-import java.security.cert.X509CertSelector;
-import java.security.cert.X509Certificate;
-import java.security.cert.X509Extension;
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.Enumeration;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.LinkedHashSet;
-import java.util.List;
-import java.util.Set;
-
-import org.bouncycastle.asn1.ASN1Encodable;
-import org.bouncycastle.asn1.ASN1EncodableVector;
-import org.bouncycastle.asn1.ASN1Primitive;
-import org.bouncycastle.asn1.ASN1Sequence;
-import org.bouncycastle.asn1.DERSequence;
-import org.bouncycastle.asn1.x500.X500Name;
-import org.bouncycastle.asn1.x509.BasicConstraints;
-import org.bouncycastle.asn1.x509.CRLDistPoint;
-import org.bouncycastle.asn1.x509.CRLReason;
-import org.bouncycastle.asn1.x509.DistributionPoint;
-import org.bouncycastle.asn1.x509.DistributionPointName;
-import org.bouncycastle.asn1.x509.Extension;
-import org.bouncycastle.asn1.x509.GeneralName;
-import org.bouncycastle.asn1.x509.GeneralNames;
-import org.bouncycastle.asn1.x509.IssuingDistributionPoint;
-import org.bouncycastle.jcajce.PKIXCRLStoreSelector;
-import org.bouncycastle.jcajce.PKIXCertStoreSelector;
-import org.bouncycastle.jcajce.PKIXExtendedBuilderParameters;
-import org.bouncycastle.jcajce.PKIXExtendedParameters;
-import org.bouncycastle.jcajce.util.JcaJceHelper;
-import org.bouncycastle.util.Arrays;
-
-class RFC3280CertPathUtilities
-{
-    /**
-     * If the complete CRL includes an issuing distribution point (IDP) CRL
-     * extension check the following:
-     * <p>
-     * (i) If the distribution point name is present in the IDP CRL extension
-     * and the distribution field is present in the DP, then verify that one of
-     * the names in the IDP matches one of the names in the DP. If the
-     * distribution point name is present in the IDP CRL extension and the
-     * distribution field is omitted from the DP, then verify that one of the
-     * names in the IDP matches one of the names in the cRLIssuer field of the
-     * DP.
-     * </p>
-     * <p>
-     * (ii) If the onlyContainsUserCerts boolean is asserted in the IDP CRL
-     * extension, verify that the certificate does not include the basic
-     * constraints extension with the cA boolean asserted.
-     * </p>
-     * <p>
-     * (iii) If the onlyContainsCACerts boolean is asserted in the IDP CRL
-     * extension, verify that the certificate includes the basic constraints
-     * extension with the cA boolean asserted.
-     * </p>
-     * <p>
-     * (iv) Verify that the onlyContainsAttributeCerts boolean is not asserted.
-     * </p>
-     *
-     * @param dp   The distribution point.
-     * @param cert The certificate.
-     * @param crl  The CRL.
-     * @throws AnnotatedException if one of the conditions is not met or an error occurs.
-     */
-    protected static void processCRLB2(
-        DistributionPoint dp,
-        Object cert,
-        X509CRL crl)
-        throws AnnotatedException
-    {
-        IssuingDistributionPoint idp = null;
-        try
-        {
-            idp = IssuingDistributionPoint.getInstance(RevocationUtilities.getExtensionValue(crl,
-                Extension.issuingDistributionPoint));
-        }
-        catch (Exception e)
-        {
-            throw new AnnotatedException("Issuing distribution point extension could not be decoded.", e);
-        }
-        // (b) (2) (i)
-        // distribution point name is present
-        if (idp != null)
-        {
-            if (idp.getDistributionPoint() != null)
-            {
-                // make list of names
-                DistributionPointName dpName = IssuingDistributionPoint.getInstance(idp).getDistributionPoint();
-                List names = new ArrayList();
-
-                if (dpName.getType() == DistributionPointName.FULL_NAME)
-                {
-                    GeneralName[] genNames = GeneralNames.getInstance(dpName.getName()).getNames();
-                    for (int j = 0; j < genNames.length; j++)
-                    {
-                        names.add(genNames[j]);
-                    }
-                }
-                if (dpName.getType() == DistributionPointName.NAME_RELATIVE_TO_CRL_ISSUER)
-                {
-                    ASN1EncodableVector vec = new ASN1EncodableVector();
-                    try
-                    {
-                        Enumeration e = ASN1Sequence.getInstance(crl.getIssuerX500Principal().getEncoded()).getObjects();
-                        while (e.hasMoreElements())
-                        {
-                            vec.add((ASN1Encodable)e.nextElement());
-                        }
-                    }
-                    catch (Exception e)
-                    {
-                        throw new AnnotatedException("Could not read CRL issuer.", e);
-                    }
-                    vec.add(dpName.getName());
-                    names.add(new GeneralName(X500Name.getInstance(new DERSequence(vec))));
-                }
-                boolean matches = false;
-                // verify that one of the names in the IDP matches one
-                // of the names in the DP.
-                if (dp.getDistributionPoint() != null)
-                {
-                    dpName = dp.getDistributionPoint();
-                    GeneralName[] genNames = null;
-                    if (dpName.getType() == DistributionPointName.FULL_NAME)
-                    {
-                        genNames = GeneralNames.getInstance(dpName.getName()).getNames();
-                    }
-                    if (dpName.getType() == DistributionPointName.NAME_RELATIVE_TO_CRL_ISSUER)
-                    {
-                        if (dp.getCRLIssuer() != null)
-                        {
-                            genNames = dp.getCRLIssuer().getNames();
-                        }
-                        else
-                        {
-                            genNames = new GeneralName[1];
-                            try
-                            {
-                                genNames[0] = new GeneralName(X500Name.getInstance(((X509Certificate)cert).getIssuerX500Principal().getEncoded()));
-                            }
-                            catch (Exception e)
-                            {
-                                throw new AnnotatedException("Could not read certificate issuer.", e);
-                            }
-                        }
-                        for (int j = 0; j < genNames.length; j++)
-                        {
-                            Enumeration e = ASN1Sequence.getInstance(genNames[j].getName().toASN1Primitive()).getObjects();
-                            ASN1EncodableVector vec = new ASN1EncodableVector();
-                            while (e.hasMoreElements())
-                            {
-                                vec.add((ASN1Encodable)e.nextElement());
-                            }
-                            vec.add(dpName.getName());
-                            genNames[j] = new GeneralName(X500Name.getInstance(new DERSequence(vec)));
-                        }
-                    }
-                    if (genNames != null)
-                    {
-                        for (int j = 0; j < genNames.length; j++)
-                        {
-                            if (names.contains(genNames[j]))
-                            {
-                                matches = true;
-                                break;
-                            }
-                        }
-                    }
-                    if (!matches)
-                    {
-                        throw new AnnotatedException(
-                            "No match for certificate CRL issuing distribution point name to cRLIssuer CRL distribution point.");
-                    }
-                }
-                // verify that one of the names in
-                // the IDP matches one of the names in the cRLIssuer field of
-                // the DP
-                else
-                {
-                    if (dp.getCRLIssuer() == null)
-                    {
-                        throw new AnnotatedException("Either the cRLIssuer or the distributionPoint field must "
-                            + "be contained in DistributionPoint.");
-                    }
-                    GeneralName[] genNames = dp.getCRLIssuer().getNames();
-                    for (int j = 0; j < genNames.length; j++)
-                    {
-                        if (names.contains(genNames[j]))
-                        {
-                            matches = true;
-                            break;
-                        }
-                    }
-                    if (!matches)
-                    {
-                        throw new AnnotatedException(
-                            "No match for certificate CRL issuing distribution point name to cRLIssuer CRL distribution point.");
-                    }
-                }
-            }
-            BasicConstraints bc = null;
-            try
-            {
-                bc = BasicConstraints.getInstance(RevocationUtilities.getExtensionValue((X509Extension)cert,
-                    Extension.basicConstraints));
-            }
-            catch (Exception e)
-            {
-                throw new AnnotatedException("Basic constraints extension could not be decoded.", e);
-            }
-
-            if (cert instanceof X509Certificate)
-            {
-                // (b) (2) (ii)
-                if (idp.onlyContainsUserCerts() && (bc != null && bc.isCA()))
-                {
-                    throw new AnnotatedException("CA Cert CRL only contains user certificates.");
-                }
-
-                // (b) (2) (iii)
-                if (idp.onlyContainsCACerts() && (bc == null || !bc.isCA()))
-                {
-                    throw new AnnotatedException("End CRL only contains CA certificates.");
-                }
-            }
-
-            // (b) (2) (iv)
-            if (idp.onlyContainsAttributeCerts())
-            {
-                throw new AnnotatedException("onlyContainsAttributeCerts boolean is asserted.");
-            }
-        }
-    }
-
-    /**
-     * If the DP includes cRLIssuer, then verify that the issuer field in the
-     * complete CRL matches cRLIssuer in the DP and that the complete CRL
-     * contains an issuing distribution point extension with the indirectCRL
-     * boolean asserted. Otherwise, verify that the CRL issuer matches the
-     * certificate issuer.
-     *
-     * @param dp   The distribution point.
-     * @param cert The certificate ot attribute certificate.
-     * @param crl  The CRL for <code>cert</code>.
-     * @throws AnnotatedException if one of the above conditions does not apply or an error
-     *                            occurs.
-     */
-    protected static void processCRLB1(
-        DistributionPoint dp,
-        Object cert,
-        X509CRL crl)
-        throws AnnotatedException
-    {
-        ASN1Primitive idp = RevocationUtilities.getExtensionValue(crl, Extension.issuingDistributionPoint);
-        boolean isIndirect = false;
-        if (idp != null)
-        {
-            if (IssuingDistributionPoint.getInstance(idp).isIndirectCRL())
-            {
-                isIndirect = true;
-            }
-        }
-        byte[] issuerBytes;
-
-            issuerBytes = crl.getIssuerX500Principal().getEncoded();
-
-
-        boolean matchIssuer = false;
-        if (dp.getCRLIssuer() != null)
-        {
-            GeneralName genNames[] = dp.getCRLIssuer().getNames();
-            for (int j = 0; j < genNames.length; j++)
-            {
-                if (genNames[j].getTagNo() == GeneralName.directoryName)
-                {
-                    try
-                    {
-                        if (Arrays.areEqual(genNames[j].getName().toASN1Primitive().getEncoded(), issuerBytes))
-                        {
-                            matchIssuer = true;
-                        }
-                    }
-                    catch (IOException e)
-                    {
-                        throw new AnnotatedException(
-                            "CRL issuer information from distribution point cannot be decoded.", e);
-                    }
-                }
-            }
-            if (matchIssuer && !isIndirect)
-            {
-                throw new AnnotatedException("Distribution point contains cRLIssuer field but CRL is not indirect.");
-            }
-            if (!matchIssuer)
-            {
-                throw new AnnotatedException("CRL issuer of CRL does not match CRL issuer of distribution point.");
-            }
-        }
-        else
-        {
-            if (crl.getIssuerX500Principal().equals(((X509Certificate)cert).getIssuerX500Principal()))
-            {
-                matchIssuer = true;
-            }
-        }
-        if (!matchIssuer)
-        {
-            throw new AnnotatedException("Cannot find matching CRL issuer for certificate.");
-        }
-    }
-
-    protected static ReasonsMask processCRLD(
-        X509CRL crl,
-        DistributionPoint dp)
-        throws AnnotatedException
-    {
-        IssuingDistributionPoint idp = null;
-        try
-        {
-            idp = IssuingDistributionPoint.getInstance(RevocationUtilities.getExtensionValue(crl,
-                Extension.issuingDistributionPoint));
-        }
-        catch (Exception e)
-        {
-            throw new AnnotatedException("Issuing distribution point extension could not be decoded.", e);
-        }
-        // (d) (1)
-        if (idp != null && idp.getOnlySomeReasons() != null && dp.getReasons() != null)
-        {
-            return new ReasonsMask(dp.getReasons()).intersect(new ReasonsMask(idp.getOnlySomeReasons()));
-        }
-        // (d) (4)
-        if ((idp == null || idp.getOnlySomeReasons() == null) && dp.getReasons() == null)
-        {
-            return ReasonsMask.allReasons;
-        }
-        // (d) (2) and (d)(3)
-        return (dp.getReasons() == null
-            ? ReasonsMask.allReasons
-            : new ReasonsMask(dp.getReasons())).intersect(idp == null
-            ? ReasonsMask.allReasons
-            : new ReasonsMask(idp.getOnlySomeReasons()));
-
-    }
-
-
-    public static final String ISSUING_DISTRIBUTION_POINT = Extension.issuingDistributionPoint.getId();
-
-    public static final String FRESHEST_CRL = Extension.freshestCRL.getId();
-
-    public static final String DELTA_CRL_INDICATOR = Extension.deltaCRLIndicator.getId();
-
-    public static final String BASIC_CONSTRAINTS = Extension.basicConstraints.getId();
-
-    public static final String AUTHORITY_KEY_IDENTIFIER = Extension.authorityKeyIdentifier.getId();
-
-    /*
-     * key usage bits
-     */
-    protected static final int KEY_CERT_SIGN = 5;
-
-    protected static final int CRL_SIGN = 6;
-
-    /**
-     * Obtain and validate the certification path for the complete CRL issuer.
-     * If a key usage extension is present in the CRL issuer's certificate,
-     * verify that the cRLSign bit is set.
-     *
-     * @param crl                CRL which contains revocation information for the certificate
-     *                           <code>cert</code>.
-     * @param cert               The attribute certificate or certificate to check if it is
-     *                           revoked.
-     * @param defaultCRLSignCert The issuer certificate of the certificate <code>cert</code>.
-     * @param defaultCRLSignKey  The public key of the issuer certificate
-     *                           <code>defaultCRLSignCert</code>.
-     * @param paramsPKIX         PKIX parameters.
-     * @param certPathCerts      The certificates on the certification path.
-     * @return A <code>Set</code> with all keys of possible CRL issuer
-     *         certificates.
-     * @throws AnnotatedException if the CRL is not valid or the status cannot be checked or
-     *                            some error occurs.
-     */
-    protected static Set processCRLF(
-        X509CRL crl,
-        Object cert,
-        X509Certificate defaultCRLSignCert,
-        PublicKey defaultCRLSignKey,
-        PKIXExtendedParameters paramsPKIX,
-        List certPathCerts,
-        JcaJceHelper helper)
-        throws AnnotatedException
-    {
-        // (f)
-
-        // get issuer from CRL
-        X509CertSelector certSelector = new X509CertSelector();
-        try
-        {
-            byte[] issuerPrincipal = crl.getIssuerX500Principal().getEncoded();
-            certSelector.setSubject(issuerPrincipal);
-        }
-        catch (IOException e)
-        {
-            throw new AnnotatedException(
-                "subject criteria for certificate selector to find issuer certificate for CRL could not be set", e);
-        }
-
-        PKIXCertStoreSelector selector = new PKIXCertStoreSelector.Builder(certSelector).build();
-
-        // get CRL signing certs
-        LinkedHashSet coll = new LinkedHashSet();
-        try
-        {
-            RevocationUtilities.findCertificates(coll, selector, paramsPKIX.getCertificateStores());
-            RevocationUtilities.findCertificates(coll, selector, paramsPKIX.getCertStores());
-        }
-        catch (AnnotatedException e)
-        {
-            throw new AnnotatedException("Issuer certificate for CRL cannot be searched.", e);
-        }
-
-        coll.add(defaultCRLSignCert);
-
-        List validCerts = new ArrayList();
-        List validKeys = new ArrayList();
-
-        Iterator cert_it = coll.iterator();
-        while (cert_it.hasNext())
-        {
-            X509Certificate signingCert = (X509Certificate)cert_it.next();
-
-            /*
-             * CA of the certificate, for which this CRL is checked, has also
-             * signed CRL, so skip the path validation, because is already done
-             */
-            if (signingCert.equals(defaultCRLSignCert))
-            {
-                validCerts.add(signingCert);
-                validKeys.add(defaultCRLSignKey);
-                continue;
-            }
-            try
-            {
-                CertPathBuilder builder = helper.createCertPathBuilder("PKIX");
-                X509CertSelector tmpCertSelector = new X509CertSelector();
-                tmpCertSelector.setCertificate(signingCert);
-
-                PKIXExtendedParameters.Builder paramsBuilder = new PKIXExtendedParameters.Builder(paramsPKIX)
-                    .setTargetConstraints(new PKIXCertStoreSelector.Builder(tmpCertSelector).build());
-
-                /*
-                 * if signingCert is placed not higher on the cert path a
-                 * dependency loop results. CRL for cert is checked, but
-                 * signingCert is needed for checking the CRL which is dependent
-                 * on checking cert because it is higher in the cert path and so
-                 * signing signingCert transitively. so, revocation is disabled,
-                 * forgery attacks of the CRL are detected in this outer loop
-                 * for all other it must be enabled to prevent forgery attacks
-                 */
-                if (certPathCerts.contains(signingCert))
-                {
-                    paramsBuilder.setRevocationEnabled(false);
-                }
-                else
-                {
-                    paramsBuilder.setRevocationEnabled(true);
-                }
-
-                PKIXExtendedBuilderParameters extParams = new PKIXExtendedBuilderParameters.Builder(paramsBuilder.build()).build();
-
-                List certs = builder.build(extParams).getCertPath().getCertificates();
-                validCerts.add(signingCert);
-                validKeys.add(RevocationUtilities.getNextWorkingKey(certs, 0, helper));
-            }
-            catch (CertPathBuilderException e)
-            {
-                throw new AnnotatedException("CertPath for CRL signer failed to validate.", e);
-            }
-            catch (CertPathValidatorException e)
-            {
-                throw new AnnotatedException("Public key of issuer certificate of CRL could not be retrieved.", e);
-            }
-            catch (Exception e)
-            {
-                throw new AnnotatedException(e.getMessage());
-            }
-        }
-
-        Set checkKeys = new HashSet();
-
-        AnnotatedException lastException = null;
-        for (int i = 0; i < validCerts.size(); i++)
-        {
-            X509Certificate signCert = (X509Certificate)validCerts.get(i);
-            boolean[] keyUsage = signCert.getKeyUsage();
-
-            if (keyUsage != null && (keyUsage.length <= CRL_SIGN || !keyUsage[CRL_SIGN]))
-            {
-                lastException = new AnnotatedException(
-                    "Issuer certificate key usage extension does not permit CRL signing.");
-            }
-            else
-            {
-                checkKeys.add(validKeys.get(i));
-            }
-        }
-
-        if (checkKeys.isEmpty() && lastException == null)
-        {
-            throw new AnnotatedException("Cannot find a valid issuer certificate.");
-        }
-        if (checkKeys.isEmpty() && lastException != null)
-        {
-            throw lastException;
-        }
-
-        return checkKeys;
-    }
-
-    protected static PublicKey processCRLG(
-        X509CRL crl,
-        Set keys)
-        throws AnnotatedException
-    {
-        Exception lastException = null;
-        for (Iterator it = keys.iterator(); it.hasNext();)
-        {
-            PublicKey key = (PublicKey)it.next();
-            try
-            {
-                crl.verify(key);
-                return key;
-            }
-            catch (Exception e)
-            {
-                lastException = e;
-            }
-        }
-        throw new AnnotatedException("Cannot verify CRL.", lastException);
-    }
-
-    protected static X509CRL processCRLH(
-        Set deltacrls,
-        PublicKey key)
-        throws AnnotatedException
-    {
-        Exception lastException = null;
-
-        for (Iterator it = deltacrls.iterator(); it.hasNext();)
-        {
-            X509CRL crl = (X509CRL)it.next();
-            try
-            {
-                crl.verify(key);
-                return crl;
-            }
-            catch (Exception e)
-            {
-                lastException = e;
-            }
-        }
-
-        if (lastException != null)
-        {
-            throw new AnnotatedException("Cannot verify delta CRL.", lastException);
-        }
-        return null;
-    }
-
-    protected static Set processCRLA1i(
-        PKIXExtendedParameters paramsPKIX,
-        Date currentDate,
-        X509Certificate cert,
-        X509CRL crl)
-        throws AnnotatedException
-    {
-        Set set = new HashSet();
-        if (paramsPKIX.isUseDeltasEnabled())
-        {
-            CRLDistPoint freshestCRL = null;
-            try
-            {
-                freshestCRL = CRLDistPoint
-                    .getInstance(RevocationUtilities.getExtensionValue(cert, Extension.freshestCRL));
-            }
-            catch (AnnotatedException e)
-            {
-                throw new AnnotatedException("Freshest CRL extension could not be decoded from certificate.", e);
-            }
-            if (freshestCRL == null)
-            {
-                try
-                {
-                    freshestCRL = CRLDistPoint.getInstance(RevocationUtilities.getExtensionValue(crl,
-                        Extension.freshestCRL));
-                }
-                catch (AnnotatedException e)
-                {
-                    throw new AnnotatedException("Freshest CRL extension could not be decoded from CRL.", e);
-                }
-            }
-            if (freshestCRL != null)
-            {
-                List crlStores = new ArrayList();
-
-                crlStores.addAll(paramsPKIX.getCRLStores());
-
-                try
-                {
-                    crlStores.addAll(RevocationUtilities.getAdditionalStoresFromCRLDistributionPoint(freshestCRL, paramsPKIX.getNamedCRLStoreMap()));
-                }
-                catch (AnnotatedException e)
-                {
-                    throw new AnnotatedException(
-                        "No new delta CRL locations could be added from Freshest CRL extension.", e);
-                }
-
-                // get delta CRL(s)
-                try
-                {
-                    set.addAll(RevocationUtilities.getDeltaCRLs(currentDate, crl, paramsPKIX.getCertStores(), crlStores));
-                }
-                catch (AnnotatedException e)
-                {
-                    throw new AnnotatedException("Exception obtaining delta CRLs.", e);
-                }
-            }
-        }
-        return set;
-    }
-
-    protected static Set[] processCRLA1ii(
-        PKIXExtendedParameters paramsPKIX,
-        Date currentDate,
-        Date validityDate,
-        X509Certificate cert,
-        X509CRL crl)
-        throws AnnotatedException
-    {
-        X509CRLSelector crlselect = new X509CRLSelector();
-        crlselect.setCertificateChecking(cert);
-
-        try
-        {
-            crlselect.addIssuerName(crl.getIssuerX500Principal().getEncoded());
-        }
-        catch (IOException e)
-        {
-            throw new AnnotatedException("Cannot extract issuer from CRL." + e, e);
-        }
-
-        PKIXCRLStoreSelector extSelect = new PKIXCRLStoreSelector.Builder(crlselect).setCompleteCRLEnabled(true).build();
-
-        Set completeSet = PKIXCRLUtil.findCRLs(extSelect, validityDate, paramsPKIX.getCertStores(),
-            paramsPKIX.getCRLStores());
-        Set deltaSet = new HashSet();
-
-        if (paramsPKIX.isUseDeltasEnabled())
-        {
-            // get delta CRL(s)
-            try
-            {
-                deltaSet.addAll(RevocationUtilities.getDeltaCRLs(validityDate, crl, paramsPKIX.getCertStores(), paramsPKIX.getCRLStores()));
-            }
-            catch (AnnotatedException e)
-            {
-                throw new AnnotatedException("Exception obtaining delta CRLs.", e);
-            }
-        }
-        return new Set[]{ completeSet, deltaSet };
-    }
-
-    /**
-     * If use-deltas is set, verify the issuer and scope of the delta CRL.
-     *
-     * @param deltaCRL    The delta CRL.
-     * @param completeCRL The complete CRL.
-     * @param pkixParams  The PKIX paramaters.
-     * @throws AnnotatedException if an exception occurs.
-     */
-    protected static void processCRLC(
-        X509CRL deltaCRL,
-        X509CRL completeCRL,
-        PKIXExtendedParameters pkixParams)
-        throws AnnotatedException
-    {
-        if (deltaCRL == null)
-        {
-            return;
-        }
-        IssuingDistributionPoint completeidp = null;
-        try
-        {
-            completeidp = IssuingDistributionPoint.getInstance(RevocationUtilities.getExtensionValue(
-                completeCRL, Extension.issuingDistributionPoint));
-        }
-        catch (Exception e)
-        {
-            throw new AnnotatedException("issuing distribution point extension could not be decoded.", e);
-        }
-
-        if (pkixParams.isUseDeltasEnabled())
-        {
-            // (c) (1)
-            if (!deltaCRL.getIssuerX500Principal().equals(completeCRL.getIssuerX500Principal()))
-            {
-                throw new AnnotatedException("complete CRL issuer does not match delta CRL issuer");
-            }
-
-            // (c) (2)
-            IssuingDistributionPoint deltaidp = null;
-            try
-            {
-                deltaidp = IssuingDistributionPoint.getInstance(RevocationUtilities.getExtensionValue(
-                    deltaCRL, Extension.issuingDistributionPoint));
-            }
-            catch (Exception e)
-            {
-                throw new AnnotatedException(
-                    "Issuing distribution point extension from delta CRL could not be decoded.", e);
-            }
-
-            boolean match = false;
-            if (completeidp == null)
-            {
-                if (deltaidp == null)
-                {
-                    match = true;
-                }
-            }
-            else
-            {
-                if (completeidp.equals(deltaidp))
-                {
-                    match = true;
-                }
-            }
-            if (!match)
-            {
-                throw new AnnotatedException(
-                    "Issuing distribution point extension from delta CRL and complete CRL does not match.");
-            }
-
-            // (c) (3)
-            ASN1Primitive completeKeyIdentifier = null;
-            try
-            {
-                completeKeyIdentifier = RevocationUtilities.getExtensionValue(
-                    completeCRL, Extension.authorityKeyIdentifier);
-            }
-            catch (AnnotatedException e)
-            {
-                throw new AnnotatedException(
-                    "Authority key identifier extension could not be extracted from complete CRL.", e);
-            }
-
-            ASN1Primitive deltaKeyIdentifier = null;
-            try
-            {
-                deltaKeyIdentifier = RevocationUtilities.getExtensionValue(
-                    deltaCRL, Extension.authorityKeyIdentifier);
-            }
-            catch (AnnotatedException e)
-            {
-                throw new AnnotatedException(
-                    "Authority key identifier extension could not be extracted from delta CRL.", e);
-            }
-
-            if (completeKeyIdentifier == null)
-            {
-                throw new AnnotatedException("CRL authority key identifier is null.");
-            }
-
-            if (deltaKeyIdentifier == null)
-            {
-                throw new AnnotatedException("Delta CRL authority key identifier is null.");
-            }
-
-            if (!completeKeyIdentifier.equals(deltaKeyIdentifier))
-            {
-                throw new AnnotatedException(
-                    "Delta CRL authority key identifier does not match complete CRL authority key identifier.");
-            }
-        }
-    }
-
-    protected static void processCRLI(
-        Date validDate,
-        X509CRL deltacrl,
-        Object cert,
-        CertStatus certStatus,
-        PKIXExtendedParameters pkixParams)
-        throws AnnotatedException
-    {
-        if (pkixParams.isUseDeltasEnabled() && deltacrl != null)
-        {
-            RevocationUtilities.getCertStatus(validDate, deltacrl, cert, certStatus);
-        }
-    }
-
-    protected static void processCRLJ(
-        Date validDate,
-        X509CRL completecrl,
-        Object cert,
-        CertStatus certStatus)
-        throws AnnotatedException
-    {
-        if (certStatus.getCertStatus() == CertStatus.UNREVOKED)
-        {
-            RevocationUtilities.getCertStatus(validDate, completecrl, cert, certStatus);
-        }
-    }
-
-    /**
-     * Checks a distribution point for revocation information for the
-     * certificate <code>cert</code>.
-     *
-     * @param dp                 The distribution point to consider.
-     * @param paramsPKIX         PKIX parameters.
-     * @param cert               Certificate to check if it is revoked.
-     * @param validDate          The date when the certificate revocation status should be
-     *                           checked.
-     * @param defaultCRLSignCert The issuer certificate of the certificate <code>cert</code>.
-     * @param defaultCRLSignKey  The public key of the issuer certificate
-     *                           <code>defaultCRLSignCert</code>.
-     * @param certStatus         The current certificate revocation status.
-     * @param reasonMask         The reasons mask which is already checked.
-     * @param certPathCerts      The certificates of the certification path.
-     * @throws AnnotatedException if the certificate is revoked or the status cannot be checked
-     *                            or some error occurs.
-     */
-    static void checkCRL(
-        DistributionPoint dp,
-        PKIXExtendedParameters paramsPKIX,
-        Date currentDate,
-        Date validityDate,
-        X509Certificate cert,
-        X509Certificate defaultCRLSignCert,
-        PublicKey defaultCRLSignKey,
-        CertStatus certStatus,
-        ReasonsMask reasonMask,
-        List certPathCerts,
-        JcaJceHelper helper)
-        throws AnnotatedException, CRLNotFoundException
-    {
-        if (validityDate.getTime() > currentDate.getTime())
-        {
-            throw new AnnotatedException("Validation time is in future.");
-        }
-
-        // (a)
-        /*
-         * We always get timely valid CRLs, so there is no step (a) (1).
-         * "locally cached" CRLs are assumed to be in getStore(), additional
-         * CRLs must be enabled in the ExtendedPKIXParameters and are in
-         * getAdditionalStore()
-         */
-
-        Set crls = RevocationUtilities.getCompleteCRLs(dp, cert, validityDate, paramsPKIX.getCertStores(), paramsPKIX.getCRLStores());
-        boolean validCrlFound = false;
-        AnnotatedException lastException = null;
-        Iterator crl_iter = crls.iterator();
-
-        while (crl_iter.hasNext() && certStatus.getCertStatus() == CertStatus.UNREVOKED && !reasonMask.isAllReasons())
-        {
-            try
-            {
-                X509CRL crl = (X509CRL)crl_iter.next();
-
-                // (d)
-                ReasonsMask interimReasonsMask = RFC3280CertPathUtilities.processCRLD(crl, dp);
-
-                // (e)
-                /*
-                 * The reasons mask is updated at the end, so only valid CRLs
-                 * can update it. If this CRL does not contain new reasons it
-                 * must be ignored.
-                 */
-                if (!interimReasonsMask.hasNewReasons(reasonMask))
-                {
-                    continue;
-                }
-
-                // (f)
-                Set keys = RFC3280CertPathUtilities.processCRLF(crl, cert, defaultCRLSignCert, defaultCRLSignKey,
-                    paramsPKIX, certPathCerts, helper);
-                // (g)
-                PublicKey key = RFC3280CertPathUtilities.processCRLG(crl, keys);
-
-                X509CRL deltaCRL = null;
-
-                if (paramsPKIX.isUseDeltasEnabled())
-                {
-                    // get delta CRLs
-                    Set deltaCRLs = RevocationUtilities.getDeltaCRLs(validityDate, crl, paramsPKIX.getCertStores(), paramsPKIX.getCRLStores());
-                    // we only want one valid delta CRL
-                    // (h)
-                    deltaCRL = RFC3280CertPathUtilities.processCRLH(deltaCRLs, key);
-                }
-
-                /*
-                 * CRL must be be valid at the current time, not the validation
-                 * time. If a certificate is revoked with reason keyCompromise,
-                 * cACompromise, it can be used for forgery, also for the past.
-                 * This reason may not be contained in older CRLs.
-                 */
-
-                /*
-                 * in the chain model signatures stay valid also after the
-                 * certificate has been expired, so they do not have to be in
-                 * the CRL validity time
-                 */
-
-                if (paramsPKIX.getValidityModel() != PKIXExtendedParameters.CHAIN_VALIDITY_MODEL)
-                {
-                    /*
-                     * if a certificate has expired, but was revoked, it is not
-                     * more in the CRL, so it would be regarded as valid if the
-                     * first check is not done
-                     */
-                    if (cert.getNotAfter().getTime() < crl.getThisUpdate().getTime())
-                    {
-                        throw new AnnotatedException("No valid CRL for current time found.");
-                    }
-                }
-
-                RFC3280CertPathUtilities.processCRLB1(dp, cert, crl);
-
-                // (b) (2)
-                RFC3280CertPathUtilities.processCRLB2(dp, cert, crl);
-
-                // (c)
-                RFC3280CertPathUtilities.processCRLC(deltaCRL, crl, paramsPKIX);
-
-                // (i)
-                RFC3280CertPathUtilities.processCRLI(validityDate, deltaCRL, cert, certStatus, paramsPKIX);
-
-                // (j)
-                RFC3280CertPathUtilities.processCRLJ(validityDate, crl, cert, certStatus);
-
-                // (k)
-                if (certStatus.getCertStatus() == CRLReason.removeFromCRL)
-                {
-                    certStatus.setCertStatus(CertStatus.UNREVOKED);
-                }
-
-                // update reasons mask
-                reasonMask.addReasons(interimReasonsMask);
-
-                Set criticalExtensions = crl.getCriticalExtensionOIDs();
-                if (criticalExtensions != null)
-                {
-                    criticalExtensions = new HashSet(criticalExtensions);
-                    criticalExtensions.remove(Extension.issuingDistributionPoint.getId());
-                    criticalExtensions.remove(Extension.deltaCRLIndicator.getId());
-
-                    if (!criticalExtensions.isEmpty())
-                    {
-                        throw new AnnotatedException("CRL contains unsupported critical extensions.");
-                    }
-                }
-
-                if (deltaCRL != null)
-                {
-                    criticalExtensions = deltaCRL.getCriticalExtensionOIDs();
-                    if (criticalExtensions != null)
-                    {
-                        criticalExtensions = new HashSet(criticalExtensions);
-                        criticalExtensions.remove(Extension.issuingDistributionPoint.getId());
-                        criticalExtensions.remove(Extension.deltaCRLIndicator.getId());
-                        if (!criticalExtensions.isEmpty())
-                        {
-                            throw new AnnotatedException("Delta CRL contains unsupported critical extension.");
-                        }
-                    }
-                }
-
-                validCrlFound = true;
-            }
-            catch (AnnotatedException e)
-            {
-                lastException = e;
-            }
-        }
-        if (!validCrlFound)
-        {
-            throw lastException;
-        }
-    }
-}
diff --git a/bcpkix/src/main/java/org/bouncycastle/pkix/jcajce/ReasonsMask.java b/bcpkix/src/main/java/org/bouncycastle/pkix/jcajce/ReasonsMask.java
deleted file mode 100644
index d5b1fb0..0000000
--- a/bcpkix/src/main/java/org/bouncycastle/pkix/jcajce/ReasonsMask.java
+++ /dev/null
@@ -1,101 +0,0 @@
-package org.bouncycastle.pkix.jcajce;
-
-import org.bouncycastle.asn1.x509.ReasonFlags;
-
-/**
- * This class helps to handle CRL revocation reasons mask. Each CRL handles a
- * certain set of revocation reasons.
- */
-class ReasonsMask
-{
-    private int _reasons;
-
-    /**
-     * Constructs are reason mask with the reasons.
-     * 
-     * @param reasons The reasons.
-     */
-    ReasonsMask(ReasonFlags reasons)
-    {
-        _reasons = reasons.intValue();
-    }
-
-    private ReasonsMask(int reasons)
-    {
-        _reasons = reasons;
-    }
-
-    /**
-     * A reason mask with no reason.
-     * 
-     */
-    ReasonsMask()
-    {
-        this(0);
-    }
-
-    /**
-     * A mask with all revocation reasons.
-     */
-    static final ReasonsMask allReasons = new ReasonsMask(ReasonFlags.aACompromise
-            | ReasonFlags.affiliationChanged | ReasonFlags.cACompromise
-            | ReasonFlags.certificateHold | ReasonFlags.cessationOfOperation
-            | ReasonFlags.keyCompromise | ReasonFlags.privilegeWithdrawn
-            | ReasonFlags.unused | ReasonFlags.superseded);
-
-    /**
-     * Adds all reasons from the reasons mask to this mask.
-     * 
-     * @param mask The reasons mask to add.
-     */
-    void addReasons(ReasonsMask mask)
-    {
-        _reasons = _reasons | mask.getReasons();
-    }
-
-    /**
-     * Returns <code>true</code> if this reasons mask contains all possible
-     * reasons.
-     * 
-     * @return <code>true</code> if this reasons mask contains all possible
-     *         reasons.
-     */
-    boolean isAllReasons()
-    {
-        return _reasons == allReasons._reasons ? true : false;
-    }
-
-    /**
-     * Intersects this mask with the given reasons mask.
-     * 
-     * @param mask The mask to intersect with.
-     * @return The intersection of this and teh given mask.
-     */
-    ReasonsMask intersect(ReasonsMask mask)
-    {
-        ReasonsMask _mask = new ReasonsMask();
-        _mask.addReasons(new ReasonsMask(_reasons & mask.getReasons()));
-        return _mask;
-    }
-
-    /**
-     * Returns <code>true</code> if the passed reasons mask has new reasons.
-     * 
-     * @param mask The reasons mask which should be tested for new reasons.
-     * @return <code>true</code> if the passed reasons mask has new reasons.
-     */
-    boolean hasNewReasons(ReasonsMask mask)
-    {
-        return ((_reasons | mask.getReasons() ^ _reasons) != 0);
-    }
-
-    /**
-     * Returns the reasons in this mask.
-     * 
-     * @return Returns the reasons.
-     */
-    int getReasons()
-    {
-        return _reasons;
-    }
-}
diff --git a/bcpkix/src/main/java/org/bouncycastle/pkix/jcajce/RevocationUtilities.java b/bcpkix/src/main/java/org/bouncycastle/pkix/jcajce/RevocationUtilities.java
deleted file mode 100644
index 1354861..0000000
--- a/bcpkix/src/main/java/org/bouncycastle/pkix/jcajce/RevocationUtilities.java
+++ /dev/null
@@ -1,644 +0,0 @@
-package org.bouncycastle.pkix.jcajce;
-
-import java.io.IOException;
-import java.math.BigInteger;
-import java.security.KeyFactory;
-import java.security.PublicKey;
-import java.security.cert.CRLException;
-import java.security.cert.CertPathValidatorException;
-import java.security.cert.CertStore;
-import java.security.cert.CertStoreException;
-import java.security.cert.Certificate;
-import java.security.cert.X509CRL;
-import java.security.cert.X509CRLEntry;
-import java.security.cert.X509CRLSelector;
-import java.security.cert.X509Certificate;
-import java.security.interfaces.DSAParams;
-import java.security.interfaces.DSAPublicKey;
-import java.security.spec.DSAPublicKeySpec;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Date;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.LinkedHashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-import javax.security.auth.x500.X500Principal;
-
-import org.bouncycastle.asn1.ASN1Enumerated;
-import org.bouncycastle.asn1.ASN1Integer;
-import org.bouncycastle.asn1.ASN1ObjectIdentifier;
-import org.bouncycastle.asn1.ASN1OctetString;
-import org.bouncycastle.asn1.ASN1Primitive;
-import org.bouncycastle.asn1.x500.X500Name;
-import org.bouncycastle.asn1.x500.style.RFC4519Style;
-import org.bouncycastle.asn1.x509.CRLDistPoint;
-import org.bouncycastle.asn1.x509.CRLReason;
-import org.bouncycastle.asn1.x509.DistributionPoint;
-import org.bouncycastle.asn1.x509.DistributionPointName;
-import org.bouncycastle.asn1.x509.Extension;
-import org.bouncycastle.asn1.x509.GeneralName;
-import org.bouncycastle.asn1.x509.GeneralNames;
-import org.bouncycastle.asn1.x509.IssuingDistributionPoint;
-import org.bouncycastle.jcajce.PKIXCRLStore;
-import org.bouncycastle.jcajce.PKIXCRLStoreSelector;
-import org.bouncycastle.jcajce.PKIXCertStoreSelector;
-import org.bouncycastle.jcajce.PKIXExtendedParameters;
-import org.bouncycastle.jcajce.util.JcaJceHelper;
-import org.bouncycastle.util.Selector;
-import org.bouncycastle.util.Store;
-import org.bouncycastle.util.StoreException;
-
-class RevocationUtilities
-{
-    protected static final String ISSUING_DISTRIBUTION_POINT = Extension.issuingDistributionPoint.getId();
-
-    protected static Date getValidityDate(PKIXExtendedParameters paramsPKIX, Date currentDate)
-    {
-        Date validityDate = paramsPKIX.getValidityDate();
-
-        return null == validityDate ? currentDate : validityDate;
-    }
-
-    /**
-     * Extract the value of the given extension, if it exists.
-     *
-     * @param ext
-     *            The extension object.
-     * @param oid
-     *            The object identifier to obtain.
-     * @throws AnnotatedException
-     *             if the extension cannot be read.
-     */
-    protected static ASN1Primitive getExtensionValue(java.security.cert.X509Extension ext, ASN1ObjectIdentifier oid)
-        throws AnnotatedException
-    {
-        byte[] bytes = ext.getExtensionValue(oid.getId());
-
-        return null == bytes ? null : getObject(oid, bytes);
-    }
-
-    private static ASN1Primitive getObject(ASN1ObjectIdentifier oid, byte[] ext) throws AnnotatedException
-    {
-        try
-        {
-            return ASN1Primitive.fromByteArray(ASN1OctetString.getInstance(ext).getOctets());
-        }
-        catch (Exception e)
-        {
-            throw new AnnotatedException("exception processing extension " + oid, e);
-        }
-    }
-
-    /**
-     * Add to a LinkedHashSet all certificates or attribute certificates found in the X509Store's
-     * that are matching the certSelect criteria.
-     *
-     * @param certs
-     *            a {@link LinkedHashSet} to which the certificates will be added.
-     * @param certSelect
-     *            a {@link Selector} object that will be used to select the certificates
-     * @param certStores
-     *            a List containing only {@link Store} objects. These are used to search for
-     *            certificates.
-     * @return a Collection of all found {@link X509Certificate} May be empty but never
-     *         <code>null</code>.
-     */
-    protected static void findCertificates(LinkedHashSet certs, PKIXCertStoreSelector certSelect, List certStores)
-        throws AnnotatedException
-    {
-        Iterator iter = certStores.iterator();
-        while (iter.hasNext())
-        {
-            Object obj = iter.next();
-
-            if (obj instanceof Store)
-            {
-                Store certStore = (Store)obj;
-                try
-                {
-                    certs.addAll(certStore.getMatches(certSelect));
-                }
-                catch (StoreException e)
-                {
-                    throw new AnnotatedException("Problem while picking certificates from X.509 store.", e);
-                }
-            }
-            else
-            {
-                CertStore certStore = (CertStore)obj;
-                try
-                {
-                    certs.addAll(PKIXCertStoreSelector.getCertificates(certSelect, certStore));
-                }
-                catch (CertStoreException e)
-                {
-                    throw new AnnotatedException("Problem while picking certificates from certificate store.", e);
-                }
-            }
-        }
-    }
-
-    static List<PKIXCRLStore> getAdditionalStoresFromCRLDistributionPoint(CRLDistPoint crldp,
-        Map<GeneralName, PKIXCRLStore> namedCRLStoreMap) throws AnnotatedException
-    {
-        if (crldp == null)
-        {
-            return Collections.emptyList();
-        }
-
-        DistributionPoint dps[];
-        try
-        {
-            dps = crldp.getDistributionPoints();
-        }
-        catch (Exception e)
-        {
-            throw new AnnotatedException("Distribution points could not be read.", e);
-        }
-
-        List<PKIXCRLStore> stores = new ArrayList<PKIXCRLStore>();
-
-        for (int i = 0; i < dps.length; i++)
-        {
-            DistributionPointName dpn = dps[i].getDistributionPoint();
-            // look for URIs in fullName
-            if (dpn != null && dpn.getType() == DistributionPointName.FULL_NAME)
-            {
-                GeneralName[] genNames = GeneralNames.getInstance(dpn.getName()).getNames();
-
-                for (int j = 0; j < genNames.length; j++)
-                {
-                    PKIXCRLStore store = namedCRLStoreMap.get(genNames[j]);
-                    if (store != null)
-                    {
-                        stores.add(store);
-                    }
-                }
-            }
-        }
-
-        return stores;
-    }
-
-    /**
-     * Add the CRL issuers from the cRLIssuer field of the distribution point or
-     * from the certificate if not given to the issuer criterion of the
-     * <code>selector</code>.
-     * <p>
-     * The <code>issuerPrincipals</code> are a collection with a single
-     * <code>X500Name</code> for <code>X509Certificate</code>s.
-     * </p>
-     * @param dp               The distribution point.
-     * @param issuerPrincipals The issuers of the certificate or attribute
-     *                         certificate which contains the distribution point.
-     * @param selector         The CRL selector.
-     * @throws AnnotatedException if an exception occurs while processing.
-     * @throws ClassCastException if <code>issuerPrincipals</code> does not
-     * contain only <code>X500Name</code>s.
-     */
-    protected static void getCRLIssuersFromDistributionPoint(DistributionPoint dp, Collection issuerPrincipals,
-        X509CRLSelector selector) throws AnnotatedException
-    {
-        List issuers = new ArrayList();
-        // indirect CRL
-        if (dp.getCRLIssuer() != null)
-        {
-            GeneralName genNames[] = dp.getCRLIssuer().getNames();
-            // look for a DN
-            for (int j = 0; j < genNames.length; j++)
-            {
-                if (genNames[j].getTagNo() == GeneralName.directoryName)
-                {
-                    try
-                    {
-                        issuers.add(X500Name.getInstance(genNames[j].getName()));
-                    }
-                    catch (IllegalArgumentException e)
-                    {
-                        throw new AnnotatedException(
-                            "CRL issuer information from distribution point cannot be decoded.", e);
-                    }
-                }
-            }
-        }
-        else
-        {
-            /*
-             * certificate issuer is CRL issuer, distributionPoint field MUST be
-             * present.
-             */
-            if (dp.getDistributionPoint() == null)
-            {
-                throw new AnnotatedException(
-                    "CRL issuer is omitted from distribution point but no distributionPoint field present.");
-            }
-            // add and check issuer principals
-            for (Iterator it = issuerPrincipals.iterator(); it.hasNext(); )
-            {
-                issuers.add(it.next());
-            }
-        }
-        // TODO: is not found although this should correctly add the rel name. selector of Sun is buggy here or PKI test case is invalid
-        // distributionPoint
-//        if (dp.getDistributionPoint() != null)
-//        {
-//            // look for nameRelativeToCRLIssuer
-//            if (dp.getDistributionPoint().getType() == DistributionPointName.NAME_RELATIVE_TO_CRL_ISSUER)
-//            {
-//                // append fragment to issuer, only one
-//                // issuer can be there, if this is given
-//                if (issuers.size() != 1)
-//                {
-//                    throw new AnnotatedException(
-//                        "nameRelativeToCRLIssuer field is given but more than one CRL issuer is given.");
-//                }
-//                ASN1Encodable relName = dp.getDistributionPoint().getName();
-//                Iterator it = issuers.iterator();
-//                List issuersTemp = new ArrayList(issuers.size());
-//                while (it.hasNext())
-//                {
-//                    Enumeration e = null;
-//                    try
-//                    {
-//                        e = ASN1Sequence.getInstance(
-//                            new ASN1InputStream(((X500Principal) it.next())
-//                                .getEncoded()).readObject()).getObjects();
-//                    }
-//                    catch (IOException ex)
-//                    {
-//                        throw new AnnotatedException(
-//                            "Cannot decode CRL issuer information.", ex);
-//                    }
-//                    ASN1EncodableVector v = new ASN1EncodableVector();
-//                    while (e.hasMoreElements())
-//                    {
-//                        v.add((ASN1Encodable) e.nextElement());
-//                    }
-//                    v.add(relName);
-//                    issuersTemp.add(new X500Principal(new DERSequence(v)
-//                        .getDEREncoded()));
-//                }
-//                issuers.clear();
-//                issuers.addAll(issuersTemp);
-//            }
-//        }
-        Iterator it = issuers.iterator();
-        while (it.hasNext())
-        {
-            try
-            {
-                selector.addIssuerName(((X500Name)it.next()).getEncoded());
-            }
-            catch (IOException ex)
-            {
-                throw new AnnotatedException(
-                    "Cannot decode CRL issuer information.", ex);
-            }
-        }
-    }
-
-    protected static void getCertStatus(Date validDate, X509CRL crl, Object cert, CertStatus certStatus)
-        throws AnnotatedException
-    {
-        boolean isIndirect;
-        try
-        {
-            isIndirect = isIndirectCRL(crl);
-        }
-        catch (CRLException exception)
-        {
-            throw new AnnotatedException("Failed check for indirect CRL.", exception);
-        }
-
-        X509Certificate x509Cert = (X509Certificate)cert;
-        X500Name x509CertIssuer = getIssuer(x509Cert);
-
-        if (!isIndirect)
-        {
-            X500Name crlIssuer = getIssuer(crl);
-            if (!x509CertIssuer.equals(crlIssuer))
-            {
-                return;
-            }
-        }
-
-        X509CRLEntry crl_entry = crl.getRevokedCertificate(x509Cert.getSerialNumber());
-        if (null == crl_entry)
-        {
-            return;
-        }
-
-        if (isIndirect)
-        {
-            X500Principal certificateIssuer = crl_entry.getCertificateIssuer();
-
-            X500Name expectedCertIssuer;
-            if (null == certificateIssuer)
-            {
-                expectedCertIssuer = getIssuer(crl);
-            }
-            else
-            {
-                expectedCertIssuer = getX500Name(certificateIssuer);
-            }
-
-            if (!x509CertIssuer.equals(expectedCertIssuer))
-            {
-                return;
-            }
-        }
-
-        int reasonCodeValue = CRLReason.unspecified;
-
-        if (crl_entry.hasExtensions())
-        {
-            try
-            {
-                ASN1Primitive extValue = RevocationUtilities.getExtensionValue(crl_entry, Extension.reasonCode);
-                ASN1Enumerated reasonCode = ASN1Enumerated.getInstance(extValue);
-                if (null != reasonCode)
-                {
-                    reasonCodeValue = reasonCode.intValueExact();
-                }
-            }
-            catch (Exception e)
-            {
-                throw new AnnotatedException("Reason code CRL entry extension could not be decoded.", e);
-            }
-        }
-
-        Date revocationDate = crl_entry.getRevocationDate();
-
-        if (validDate.before(revocationDate))
-        {
-            switch (reasonCodeValue)
-            {
-            case CRLReason.unspecified:
-            case CRLReason.keyCompromise:
-            case CRLReason.cACompromise:
-            case CRLReason.aACompromise:
-                break;
-            default:
-                return;
-            }
-        }
-
-        // (i) or (j)
-        certStatus.setCertStatus(reasonCodeValue);
-        certStatus.setRevocationDate(revocationDate);
-    }
-
-    /**
-     * Fetches delta CRLs according to RFC 3280 section 5.2.4.
-     *
-     * @param validityDate
-     *            The date for which the delta CRLs must be valid.
-     * @param completeCRL
-     *            The complete CRL the delta CRL is for.
-     * @return A <code>Set</code> of <code>X509CRL</code>s with delta CRLs.
-     * @throws AnnotatedException
-     *             if an exception occurs while picking the delta CRLs.
-     */
-    protected static Set getDeltaCRLs(Date validityDate, X509CRL completeCRL, List<CertStore> certStores,
-        List<PKIXCRLStore> pkixCrlStores) throws AnnotatedException
-    {
-        X509CRLSelector baseDeltaSelect = new X509CRLSelector();
-        // 5.2.4 (a)
-        try
-        {
-            baseDeltaSelect.addIssuerName(completeCRL.getIssuerX500Principal().getEncoded());
-        }
-        catch (IOException e)
-        {
-            throw new AnnotatedException("cannot extract issuer from CRL.", e);
-        }
-
-        BigInteger completeCRLNumber = null;
-        try
-        {
-            ASN1Primitive derObject = RevocationUtilities.getExtensionValue(completeCRL, Extension.cRLNumber);
-            if (derObject != null)
-            {
-                completeCRLNumber = ASN1Integer.getInstance(derObject).getPositiveValue();
-            }
-        }
-        catch (Exception e)
-        {
-            throw new AnnotatedException(
-                "cannot extract CRL number extension from CRL", e);
-        }
-
-        // 5.2.4 (b)
-        byte[] idp;
-        try
-        {
-            idp = completeCRL.getExtensionValue(ISSUING_DISTRIBUTION_POINT);
-        }
-        catch (Exception e)
-        {
-            throw new AnnotatedException("issuing distribution point extension value could not be read", e);
-        }
-
-        // 5.2.4 (d)
-
-        baseDeltaSelect.setMinCRLNumber(completeCRLNumber == null ? null : completeCRLNumber
-            .add(BigInteger.valueOf(1)));
-
-        PKIXCRLStoreSelector.Builder selBuilder = new PKIXCRLStoreSelector.Builder(baseDeltaSelect);
-
-        selBuilder.setIssuingDistributionPoint(idp);
-        selBuilder.setIssuingDistributionPointEnabled(true);
-
-        // 5.2.4 (c)
-        selBuilder.setMaxBaseCRLNumber(completeCRLNumber);
-
-        PKIXCRLStoreSelector deltaSelect = selBuilder.build();
-
-        // find delta CRLs
-        Set temp = PKIXCRLUtil.findCRLs(deltaSelect, validityDate, certStores, pkixCrlStores);
-
-        Set result = new HashSet();
-
-        for (Iterator it = temp.iterator(); it.hasNext(); )
-        {
-            X509CRL crl = (X509CRL)it.next();
-
-            if (isDeltaCRL(crl))
-            {
-                result.add(crl);
-            }
-        }
-
-        return result;
-    }
-
-    private static boolean isDeltaCRL(X509CRL crl)
-    {
-        Set critical = crl.getCriticalExtensionOIDs();
-
-        return null == critical ? false : critical.contains(RFC3280CertPathUtilities.DELTA_CRL_INDICATOR);
-    }
-
-    /**
-     * Fetches complete CRLs according to RFC 3280.
-     *
-     * @param dp
-     *            The distribution point for which the complete CRL
-     * @param cert
-     *            The <code>X509Certificate</code> for which the CRL should be searched.
-     * @return A <code>Set</code> of <code>X509CRL</code>s with complete CRLs.
-     * @throws AnnotatedException
-     *             if an exception occurs while picking the CRLs or no CRLs are found.
-     */
-    protected static Set getCompleteCRLs(DistributionPoint dp, Object cert, Date validityDate, List certStores, List crlStores)
-        throws AnnotatedException, CRLNotFoundException
-    {
-        X509CRLSelector baseCrlSelect = new X509CRLSelector();
-
-        try
-        {
-            Set issuers = new HashSet();
-            issuers.add(getIssuer((X509Certificate)cert));
-
-            RevocationUtilities.getCRLIssuersFromDistributionPoint(dp, issuers, baseCrlSelect);
-        }
-        catch (AnnotatedException e)
-        {
-            throw new AnnotatedException(
-                "Could not get issuer information from distribution point.", e);
-        }
-
-        if (cert instanceof X509Certificate)
-        {
-            baseCrlSelect.setCertificateChecking((X509Certificate)cert);
-        }
-
-        PKIXCRLStoreSelector crlSelect = new PKIXCRLStoreSelector.Builder(baseCrlSelect).setCompleteCRLEnabled(true).build();
-
-        Set crls = PKIXCRLUtil.findCRLs(crlSelect, validityDate, certStores, crlStores);
-
-        checkCRLsNotEmpty(crls, cert);
-
-        return crls;
-    }
-
-    /**
-     * Return the next working key inheriting DSA parameters if necessary.
-     * <p>
-     * This methods inherits DSA parameters from the indexed certificate or
-     * previous certificates in the certificate chain to the returned
-     * <code>PublicKey</code>. The list is searched upwards, meaning the end
-     * certificate is at position 0 and previous certificates are following.
-     * </p>
-     * <p>
-     * If the indexed certificate does not contain a DSA key this method simply
-     * returns the public key. If the DSA key already contains DSA parameters
-     * the key is also only returned.
-     * </p>
-     *
-     * @param certs The certification path.
-     * @param index The index of the certificate which contains the public key
-     *              which should be extended with DSA parameters.
-     * @return The public key of the certificate in list position
-     *         <code>index</code> extended with DSA parameters if applicable.
-     * @throws AnnotatedException if DSA parameters cannot be inherited.
-     */
-    protected static PublicKey getNextWorkingKey(List certs, int index, JcaJceHelper helper)
-        throws CertPathValidatorException
-    {
-        Certificate cert = (Certificate)certs.get(index);
-        PublicKey pubKey = cert.getPublicKey();
-        if (!(pubKey instanceof DSAPublicKey))
-        {
-            return pubKey;
-        }
-        DSAPublicKey dsaPubKey = (DSAPublicKey)pubKey;
-        if (dsaPubKey.getParams() != null)
-        {
-            return dsaPubKey;
-        }
-        for (int i = index + 1; i < certs.size(); i++)
-        {
-            X509Certificate parentCert = (X509Certificate)certs.get(i);
-            pubKey = parentCert.getPublicKey();
-            if (!(pubKey instanceof DSAPublicKey))
-            {
-                throw new CertPathValidatorException(
-                    "DSA parameters cannot be inherited from previous certificate.");
-            }
-            DSAPublicKey prevDSAPubKey = (DSAPublicKey)pubKey;
-            if (prevDSAPubKey.getParams() == null)
-            {
-                continue;
-            }
-            DSAParams dsaParams = prevDSAPubKey.getParams();
-            DSAPublicKeySpec dsaPubKeySpec = new DSAPublicKeySpec(
-                dsaPubKey.getY(), dsaParams.getP(), dsaParams.getQ(), dsaParams.getG());
-            try
-            {
-                KeyFactory keyFactory = helper.createKeyFactory("DSA");
-                return keyFactory.generatePublic(dsaPubKeySpec);
-            }
-            catch (Exception exception)
-            {
-                throw new RuntimeException(exception.getMessage());
-            }
-        }
-        throw new CertPathValidatorException("DSA parameters cannot be inherited from previous certificate.");
-    }
-
-    static void checkCRLsNotEmpty(Set crls, Object cert)
-        throws CRLNotFoundException
-    {
-        if (crls.isEmpty())
-        {
-//            if (cert instanceof X509AttributeCertificate)
-//            {
-//                X509AttributeCertificate aCert = (X509AttributeCertificate)cert;
-//
-//                throw new NoCRLFoundException("No CRLs found for issuer \"" + aCert.getIssuer().getPrincipals()[0] + "\"");
-//            }
-//            else
-            {
-                X500Name certIssuer = getIssuer((X509Certificate)cert);
-
-                throw new CRLNotFoundException(
-                    "No CRLs found for issuer \"" + RFC4519Style.INSTANCE.toString(certIssuer) + "\"");
-            }
-        }
-    }
-
-    public static boolean isIndirectCRL(X509CRL crl) throws CRLException
-    {
-        try
-        {
-            byte[] idp = crl.getExtensionValue(Extension.issuingDistributionPoint.getId());
-            return idp != null
-                && IssuingDistributionPoint.getInstance(ASN1OctetString.getInstance(idp).getOctets()).isIndirectCRL();
-        }
-        catch (Exception e)
-        {
-            throw new CRLException("exception reading IssuingDistributionPoint", e);
-        }
-    }
-
-    private static X500Name getIssuer(X509Certificate cert)
-    {
-        return getX500Name(cert.getIssuerX500Principal());
-    }
-
-    private static X500Name getIssuer(X509CRL crl)
-    {
-        return getX500Name(crl.getIssuerX500Principal());
-    }
-
-    private static X500Name getX500Name(X500Principal principal)
-    {
-        return X500Name.getInstance(principal.getEncoded());
-    }
-}
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/ASN1Absent.java b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1Absent.java
new file mode 100644
index 0000000..0f74c77
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1Absent.java
@@ -0,0 +1,45 @@
+package org.bouncycastle.asn1;
+
+import java.io.IOException;
+
+/**
+ * An ASN1 class that encodes to nothing, used in the OER library to deal with the Optional type.
+ */
+public class ASN1Absent
+    extends ASN1Primitive
+{
+
+    public static final ASN1Absent INSTANCE = new ASN1Absent();
+
+    private ASN1Absent()
+    {
+
+    }
+
+    public int hashCode()
+    {
+        return 0;
+    }
+
+    boolean encodeConstructed()
+    {
+        return false;
+    }
+
+    int encodedLength(boolean withTag)
+        throws IOException
+    {
+        return 0;
+    }
+
+    void encode(ASN1OutputStream out, boolean withTag)
+        throws IOException
+    {
+
+    }
+
+    boolean asn1Equals(ASN1Primitive o)
+    {
+        return o == this;
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/ASN1ApplicationSpecific.java b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1ApplicationSpecific.java
deleted file mode 100644
index ac5db1c..0000000
--- a/bcprov/src/main/java/org/bouncycastle/asn1/ASN1ApplicationSpecific.java
+++ /dev/null
@@ -1,246 +0,0 @@
-package org.bouncycastle.asn1;
-
-import java.io.IOException;
-
-import org.bouncycastle.util.Arrays;
-import org.bouncycastle.util.encoders.Hex;
-
-/**
- * Base class for an ASN.1 ApplicationSpecific object
- */
-public abstract class ASN1ApplicationSpecific
-    extends ASN1Primitive
-{
-    protected final boolean   isConstructed;
-    protected final int       tag;
-    protected final byte[]    octets;
-
-    ASN1ApplicationSpecific(
-        boolean isConstructed,
-        int tag,
-        byte[] octets)
-    {
-        this.isConstructed = isConstructed;
-        this.tag = tag;
-        this.octets = Arrays.clone(octets);
-    }
-
-    /**
-     * Return an ASN1ApplicationSpecific from the passed in object, which may be a byte array, or null.
-     *
-     * @param obj the object to be converted.
-     * @return obj's representation as an ASN1ApplicationSpecific object.
-     */
-    public static ASN1ApplicationSpecific getInstance(Object obj)
-    {
-        if (obj == null || obj instanceof ASN1ApplicationSpecific)
-        {
-            return (ASN1ApplicationSpecific)obj;
-        }
-        else if (obj instanceof byte[])
-        {
-            try
-            {
-                return ASN1ApplicationSpecific.getInstance(ASN1Primitive.fromByteArray((byte[])obj));
-            }
-            catch (IOException e)
-            {
-                throw new IllegalArgumentException("Failed to construct object from byte[]: " + e.getMessage());
-            }
-        }
-
-        throw new IllegalArgumentException("unknown object in getInstance: " + obj.getClass().getName());
-    }
-
-    protected static int getLengthOfHeader(byte[] data)
-    {
-        int length = data[1] & 0xff; // TODO: assumes 1 byte tag
-
-        if (length == 0x80)
-        {
-            return 2;      // indefinite-length encoding
-        }
-
-        if (length > 127)
-        {
-            int size = length & 0x7f;
-
-            // Note: The invalid long form "0xff" (see X.690 8.1.3.5c) will be caught here
-            if (size > 4)
-            {
-                throw new IllegalStateException("DER length more than 4 bytes: " + size);
-            }
-
-            return size + 2;
-        }
-
-        return 2;
-    }
-
-    /**
-     * Return true if the object is marked as constructed, false otherwise.
-     *
-     * @return true if constructed, otherwise false.
-     */
-    public boolean isConstructed()
-    {
-        return isConstructed;
-    }
-
-    /**
-     * Return the contents of this object as a byte[]
-     *
-     * @return the encoded contents of the object.
-     */
-    public byte[] getContents()
-    {
-        return Arrays.clone(octets);
-    }
-
-    /**
-     * Return the tag number associated with this object,
-     *
-     * @return the application tag number.
-     */
-    public int getApplicationTag() 
-    {
-        return tag;
-    }
-
-    /**
-     * Return the enclosed object assuming explicit tagging.
-     *
-     * @return  the resulting object
-     * @throws IOException if reconstruction fails.
-     */
-    public ASN1Primitive getObject()
-        throws IOException 
-    {
-        return ASN1Primitive.fromByteArray(getContents());
-    }
-
-    /**
-     * Return the enclosed object assuming implicit tagging.
-     *
-     * @param derTagNo the type tag that should be applied to the object's contents.
-     * @return  the resulting object
-     * @throws IOException if reconstruction fails.
-     */
-    public ASN1Primitive getObject(int derTagNo)
-        throws IOException
-    {
-        if (derTagNo >= 0x1f)
-        {
-            throw new IOException("unsupported tag number");
-        }
-
-        byte[] orig = this.getEncoded();
-        byte[] tmp = replaceTagNumber(derTagNo, orig);
-
-        if ((orig[0] & BERTags.CONSTRUCTED) != 0)
-        {
-            tmp[0] |= BERTags.CONSTRUCTED;
-        }
-
-        return ASN1Primitive.fromByteArray(tmp);
-    }
-
-    int encodedLength()
-        throws IOException
-    {
-        return StreamUtil.calculateTagLength(tag) + StreamUtil.calculateBodyLength(octets.length) + octets.length;
-    }
-
-    /* (non-Javadoc)
-     * @see org.bouncycastle.asn1.ASN1Primitive#encode(org.bouncycastle.asn1.DEROutputStream)
-     */
-    void encode(ASN1OutputStream out, boolean withTag) throws IOException
-    {
-        int flags = BERTags.APPLICATION;
-        if (isConstructed)
-        {
-            flags |= BERTags.CONSTRUCTED;
-        }
-
-        out.writeEncoded(withTag, flags, tag, octets);
-    }
-
-    boolean asn1Equals(
-        ASN1Primitive o)
-    {
-        if (!(o instanceof ASN1ApplicationSpecific))
-        {
-            return false;
-        }
-
-        ASN1ApplicationSpecific other = (ASN1ApplicationSpecific)o;
-
-        return isConstructed == other.isConstructed
-            && tag == other.tag
-            && Arrays.areEqual(octets, other.octets);
-    }
-
-    public int hashCode()
-    {
-        return (isConstructed ? 1 : 0) ^ tag ^ Arrays.hashCode(octets);
-    }
-
-    private byte[] replaceTagNumber(int newTag, byte[] input)
-        throws IOException
-    {
-        int tagNo = input[0] & 0x1f;
-        int index = 1;
-        //
-        // with tagged object tag number is bottom 5 bits, or stored at the start of the content
-        //
-        if (tagNo == 0x1f)
-        {
-            int b = input[index++] & 0xff;
-
-            // X.690-0207 8.1.2.4.2
-            // "c) bits 7 to 1 of the first subsequent octet shall not all be zero."
-            if ((b & 0x7f) == 0) // Note: -1 will pass
-            {
-                throw new IOException("corrupted stream - invalid high tag number found");
-            }
-
-            while ((b & 0x80) != 0)
-            {
-                b = input[index++] & 0xff;
-            }
-        }
-
-        byte[] tmp = new byte[input.length - index + 1];
-
-        System.arraycopy(input, index, tmp, 1, tmp.length - 1);
-
-        tmp[0] = (byte)newTag;
-
-        return tmp;
-    }
-
-    public String toString()
-    {
-        StringBuffer sb = new StringBuffer();
-        sb.append("[");
-        if (isConstructed())
-        {
-            sb.append("CONSTRUCTED ");
-        }
-        sb.append("APPLICATION ");
-        sb.append(Integer.toString(getApplicationTag()));
-        sb.append("]");
-        // @todo content encoding somehow?
-        if (this.octets != null)
-        {
-            sb.append(" #");
-            sb.append(Hex.toHexString(this.octets));
-        }
-        else
-        {
-            sb.append(" #null");
-        }
-        sb.append(" ");
-        return sb.toString();
-    }
-}
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/ASN1BMPString.java b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1BMPString.java
new file mode 100644
index 0000000..6b1fb1c
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1BMPString.java
@@ -0,0 +1,213 @@
+package org.bouncycastle.asn1;
+
+import java.io.IOException;
+
+import org.bouncycastle.util.Arrays;
+
+/**
+ * ASN.1 BMPString object encodes BMP (<i>Basic Multilingual Plane</i>) subset
+ * (aka UCS-2) of UNICODE (ISO 10646) characters in codepoints 0 to 65535.
+ * <p>
+ * At ISO-10646:2011 the term "BMP" has been withdrawn, and replaced by
+ * term "UCS-2".
+ * </p>
+ */
+public abstract class ASN1BMPString
+    extends ASN1Primitive
+    implements ASN1String
+{
+    static final ASN1UniversalType TYPE = new ASN1UniversalType(ASN1BMPString.class, BERTags.BMP_STRING)
+    {
+        ASN1Primitive fromImplicitPrimitive(DEROctetString octetString)
+        {
+            return createPrimitive(octetString.getOctets());
+        }
+    };
+
+    /**
+     * Return a BMP String from the given object.
+     *
+     * @param obj the object we want converted.
+     * @exception IllegalArgumentException if the object cannot be converted.
+     * @return an ASN1BMPString instance, or null.
+     */
+    public static ASN1BMPString getInstance(Object obj)
+    {
+        if (obj == null || obj instanceof ASN1BMPString)
+        {
+            return (ASN1BMPString)obj;
+        }
+        if (obj instanceof ASN1Encodable)
+        {
+            ASN1Primitive primitive = ((ASN1Encodable)obj).toASN1Primitive();
+            if (primitive instanceof ASN1BMPString)
+            {
+                return (ASN1BMPString)primitive;
+            }
+        }
+        if (obj instanceof byte[])
+        {
+            try
+            {
+                return (ASN1BMPString)TYPE.fromByteArray((byte[])obj);
+            }
+            catch (Exception e)
+            {
+                throw new IllegalArgumentException("encoding error in getInstance: " + e.toString());
+            }
+        }
+
+        throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
+    }
+
+    /**
+     * Return a BMP String from a tagged object.
+     *
+     * @param taggedObject      the tagged object holding the object we want
+     * @param explicit true if the object is meant to be explicitly tagged false
+     *                 otherwise.
+     * @exception IllegalArgumentException if the tagged object cannot be converted.
+     * @return an ASN1BMPString instance.
+     */
+    public static ASN1BMPString getInstance(ASN1TaggedObject taggedObject, boolean explicit)
+    {
+        return (ASN1BMPString)TYPE.getContextInstance(taggedObject, explicit);
+    }
+
+    final char[] string;
+
+    ASN1BMPString(String string)
+    {
+        if (string == null)
+        {
+            throw new NullPointerException("'string' cannot be null");
+        }
+
+        this.string = string.toCharArray();
+    }
+
+    ASN1BMPString(byte[] string)
+    {
+        if (string == null)
+        {
+            throw new NullPointerException("'string' cannot be null");
+        }
+
+        int byteLen = string.length;
+        if (0 != (byteLen & 1))
+        {
+            throw new IllegalArgumentException("malformed BMPString encoding encountered");
+        }
+
+        int charLen = byteLen / 2;
+        char[] cs = new char[charLen];
+
+        for (int i = 0; i != charLen; i++)
+        {
+            cs[i] = (char)((string[2 * i] << 8) | (string[2 * i + 1] & 0xff));
+        }
+
+        this.string = cs;
+    }
+
+    ASN1BMPString(char[] string)
+    {
+        if (string == null)
+        {
+            throw new NullPointerException("'string' cannot be null");
+        }
+
+        this.string = string;
+    }
+
+    public final String getString()
+    {
+        return new String(string);
+    }
+
+    public String toString()
+    {
+        return getString();
+    }
+
+    final boolean asn1Equals(ASN1Primitive other)
+    {
+        if (!(other instanceof ASN1BMPString))
+        {
+            return false;
+        }
+
+        ASN1BMPString that = (ASN1BMPString)other;
+
+        return Arrays.areEqual(this.string, that.string);
+    }
+
+    public final int hashCode()
+    {
+        return Arrays.hashCode(string);
+    }
+
+    final boolean encodeConstructed()
+    {
+        return false;
+    }
+
+    final int encodedLength(boolean withTag)
+    {
+        return ASN1OutputStream.getLengthOfEncodingDL(withTag, string.length * 2);
+    }
+
+    final void encode(ASN1OutputStream out, boolean withTag) throws IOException
+    {
+        int count = string.length;
+
+        out.writeIdentifier(withTag, BERTags.BMP_STRING);
+        out.writeDL(count * 2);
+
+        byte[] buf = new byte[8];
+
+        int i = 0, limit = count & -4;
+        while (i < limit)
+        {
+            char c0 = string[i], c1 = string[i + 1], c2 = string[i + 2], c3 = string[i + 3];
+            i += 4;
+
+            buf[0] = (byte)(c0 >> 8);
+            buf[1] = (byte)c0;
+            buf[2] = (byte)(c1 >> 8);
+            buf[3] = (byte)c1;
+            buf[4] = (byte)(c2 >> 8);
+            buf[5] = (byte)c2;
+            buf[6] = (byte)(c3 >> 8);
+            buf[7] = (byte)c3;
+
+            out.write(buf, 0, 8);
+        }
+        if (i < count)
+        {
+            int bufPos = 0;
+            do
+            {
+                char c0 = string[i];
+                i += 1;
+
+                buf[bufPos++] = (byte)(c0 >> 8);
+                buf[bufPos++] = (byte)c0;
+            }
+            while (i < count);
+
+            out.write(buf, 0, bufPos);
+        }
+    }
+
+    static ASN1BMPString createPrimitive(byte[] contents)
+    {
+        return new DERBMPString(contents);
+    }
+
+    static ASN1BMPString createPrimitive(char[] string)
+    {
+        // TODO ASN1InputStream has a validator/converter that should be unified in this class somehow
+        return new DERBMPString(string);
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/ASN1BitString.java b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1BitString.java
index 95abee1..aff5209 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/ASN1BitString.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1BitString.java
@@ -1,23 +1,67 @@
 package org.bouncycastle.asn1;
 
-import java.io.EOFException;
+import java.io.ByteArrayInputStream;
 import java.io.IOException;
 import java.io.InputStream;
 
 import org.bouncycastle.util.Arrays;
-import org.bouncycastle.util.io.Streams;
 
 /**
  * Base class for BIT STRING objects
  */
 public abstract class ASN1BitString
     extends ASN1Primitive
-    implements ASN1String
+    implements ASN1String, ASN1BitStringParser
 {
-    private static final char[]  table = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
+    static final ASN1UniversalType TYPE = new ASN1UniversalType(ASN1BitString.class, BERTags.BIT_STRING)
+    {
+        ASN1Primitive fromImplicitPrimitive(DEROctetString octetString)
+        {
+            return createPrimitive(octetString.getOctets());
+        }
 
-    protected final byte[]      data;
-    protected final int         padBits;
+        ASN1Primitive fromImplicitConstructed(ASN1Sequence sequence)
+        {
+            return sequence.toASN1BitString();
+        }
+    };
+
+    public static ASN1BitString getInstance(Object obj)
+    {
+        if (obj == null || obj instanceof ASN1BitString)
+        {
+            return (ASN1BitString)obj;
+        }
+//      else if (obj instanceof ASN1BitStringParser)
+        else if (obj instanceof ASN1Encodable)
+        {
+            ASN1Primitive primitive = ((ASN1Encodable)obj).toASN1Primitive();
+            if (primitive instanceof ASN1BitString)
+            {
+                return (ASN1BitString)primitive;
+            }
+        }
+        else if (obj instanceof byte[])
+        {
+            try
+            {
+                return (ASN1BitString)TYPE.fromByteArray((byte[])obj);
+            }
+            catch (IOException e)
+            {
+                throw new IllegalArgumentException("failed to construct BIT STRING from byte[]: " + e.getMessage());
+            }
+        }
+
+        throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
+    }
+
+    public static ASN1BitString getInstance(ASN1TaggedObject taggedObject, boolean explicit)
+    {
+        return (ASN1BitString)TYPE.getContextInstance(taggedObject, explicit);
+    }
+
+    private static final char[]  table = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
 
     /**
      * @param bitString an int containing the BIT STRING
@@ -99,15 +143,16 @@
         return result;
     }
 
-    protected ASN1BitString(byte data, int padBits)
+    final byte[] contents;
+
+    ASN1BitString(byte data, int padBits)
     {
         if (padBits > 7 || padBits < 0)
         {
             throw new IllegalArgumentException("pad bits cannot be greater than 7 or less than 0");
         }
 
-        this.data = new byte[]{ data };
-        this.padBits = padBits;
+        this.contents = new byte[]{ (byte)padBits, data };
     }
 
     /**
@@ -116,9 +161,7 @@
      * @param data the octets making up the bit string.
      * @param padBits the number of extra bits at the end of the string.
      */
-    public ASN1BitString(
-        byte[]  data,
-        int     padBits)
+    ASN1BitString(byte[] data, int padBits)
     {
         if (data == null)
         {
@@ -133,8 +176,58 @@
             throw new IllegalArgumentException("pad bits cannot be greater than 7 or less than 0");
         }
 
-        this.data = Arrays.clone(data);
-        this.padBits = padBits;
+        this.contents = Arrays.prepend(data, (byte)padBits);
+    }
+
+    ASN1BitString(byte[] contents, boolean check)
+    {
+        if (check)
+        {
+            if (null == contents)
+            {
+                throw new NullPointerException("'contents' cannot be null");
+            }
+            if (contents.length < 1)
+            {
+                throw new IllegalArgumentException("'contents' cannot be empty");
+            }
+
+            int padBits = contents[0] & 0xFF;
+            if (padBits > 0)
+            {
+                if (contents.length < 2)
+                {
+                    throw new IllegalArgumentException("zero length data with non-zero pad bits");
+                }
+                if (padBits > 7)
+                {
+                    throw new IllegalArgumentException("pad bits cannot be greater than 7 or less than 0");
+                }
+            }
+        }
+
+        this.contents = contents;
+    }
+
+    public InputStream getBitStream() throws IOException
+    {
+        return new ByteArrayInputStream(contents, 1, contents.length - 1);
+    }
+
+    public InputStream getOctetStream() throws IOException
+    {
+        int padBits = contents[0] & 0xFF;
+        if (0 != padBits)
+        {
+            throw new IOException("expected octet-aligned bitstring, but found padBits: " + padBits);
+        }
+
+        return getBitStream();
+    }
+
+    public ASN1BitStringParser parser()
+    {
+        return this;
     }
 
     /**
@@ -144,8 +237,6 @@
      */
     public String getString()
     {
-        StringBuffer buf = new StringBuffer("#");
-
         byte[] string;
         try
         {
@@ -156,10 +247,14 @@
             throw new ASN1ParsingException("Internal error encoding BitString: " + e.getMessage(), e);
         }
 
+        StringBuffer buf = new StringBuffer(1 + string.length * 2);
+        buf.append('#');
+
         for (int i = 0; i != string.length; i++)
         {
-            buf.append(table[(string[i] >>> 4) & 0xf]);
-            buf.append(table[string[i] & 0xf]);
+            byte b = string[i];
+            buf.append(table[(b >>> 4) & 0xf]);
+            buf.append(table[b & 0xf]);
         }
 
         return buf.toString();
@@ -171,15 +266,16 @@
     public int intValue()
     {
         int value = 0;
-        int end = Math.min(4, data.length - 1);
-        for (int i = 0; i < end; ++i)
+        int end = Math.min(5, contents.length - 1);
+        for (int i = 1; i < end; ++i)
         {
-            value |= (data[i] & 0xFF) << (8 * i);
+            value |= (contents[i] & 0xFF) << (8 * (i - 1));
         }
-        if (0 <= end && end < 4)
+        if (1 <= end && end < 5)
         {
-            byte der = (byte)(data[end] & (0xFF << padBits));
-            value |= (der & 0xFF) << (8 * end);
+            int padBits = contents[0] & 0xFF;
+            byte der = (byte)(contents[end] & (0xFF << padBits));
+            value |= (der & 0xFF) << (8 * (end - 1));
         }
         return value;
     }
@@ -193,30 +289,31 @@
      */
     public byte[] getOctets()
     {
-        if (padBits != 0)
+        if (contents[0] != 0)
         {
             throw new IllegalStateException("attempt to get non-octet aligned data from BIT STRING");
         }
 
-        return Arrays.clone(data);
+        return Arrays.copyOfRange(contents, 1, contents.length);
     }
 
     public byte[] getBytes()
     {
-        if (0 == data.length)
+        if (contents.length == 1)
         {
-            return data;
+            return ASN1OctetString.EMPTY_OCTETS;
         }
 
-        byte[] rv = Arrays.clone(data);
+        int padBits = contents[0] & 0xFF;
+        byte[] rv = Arrays.copyOfRange(contents, 1, contents.length);
         // DER requires pad bits be zero
-        rv[data.length - 1] &= (0xFF << padBits);
+        rv[rv.length - 1] &= (byte)(0xFF << padBits);
         return rv;
     }
 
     public int getPadBits()
     {
-        return padBits;
+        return contents[0] & 0xFF;
     }
 
     public String toString()
@@ -226,85 +323,56 @@
 
     public int hashCode()
     {
-        int end = data.length;
-        if (--end < 0)
+        if (contents.length < 2)
         {
             return 1;
         }
 
-        byte der = (byte)(data[end] & (0xFF << padBits));
+        int padBits = contents[0] & 0xFF;
+        int last = contents.length - 1;
 
-        int hc = Arrays.hashCode(data, 0, end);
+        byte lastOctetDER = (byte)(contents[last] & (0xFF << padBits));
+
+        int hc = Arrays.hashCode(contents, 0, last);
         hc *= 257;
-        hc ^= der;
-        return hc ^ padBits;
+        hc ^= lastOctetDER;
+        return hc;
     }
 
-    boolean asn1Equals(
-        ASN1Primitive o)
+    boolean asn1Equals(ASN1Primitive other)
     {
-        if (!(o instanceof ASN1BitString))
+        if (!(other instanceof ASN1BitString))
         {
             return false;
         }
 
-        ASN1BitString other = (ASN1BitString)o;
-        if (padBits != other.padBits)
+        ASN1BitString that = (ASN1BitString)other;
+        byte[] thisContents = this.contents, thatContents = that.contents;
+
+        int length = thisContents.length;
+        if (thatContents.length != length)
         {
             return false;
         }
-        byte[] a = data, b = other.data;
-        int end = a.length;
-        if (end != b.length)
-        {
-            return false;
-        }
-        if (--end < 0)
+        if (length == 1)
         {
             return true;
         }
-        for (int i = 0; i < end; ++i)
+
+        int last = length - 1;
+        for (int i = 0; i < last; ++i)
         {
-            if (a[i] != b[i])
+            if (thisContents[i] != thatContents[i])
             {
                 return false;
             }
         }
 
-        byte derA = (byte)(a[end] & (0xFF << padBits));
-        byte derB = (byte)(b[end] & (0xFF << padBits));
+        int padBits = thisContents[0] & 0xFF;
+        byte thisLastOctetDER = (byte)(thisContents[last] & (0xFF << padBits));
+        byte thatLastOctetDER = (byte)(thatContents[last] & (0xFF << padBits));
 
-        return derA == derB;
-    }
-
-    static ASN1BitString fromInputStream(int length, InputStream stream)
-        throws IOException
-    {
-        if (length < 1)
-        {
-            throw new IllegalArgumentException("truncated BIT STRING detected");
-        }
-
-        int padBits = stream.read();
-        byte[] data = new byte[length - 1];
-
-        if (data.length != 0)
-        {
-            if (Streams.readFully(stream, data) != data.length)
-            {
-                throw new EOFException("EOF encountered in middle of BIT STRING");
-            }
-
-            if (padBits > 0 && padBits < 8)
-            {
-                if (data[data.length - 1] != (byte)(data[data.length - 1] & (0xFF << padBits)))
-                {
-                    return new DLBitString(data, padBits);
-                }
-            }
-        }
-
-        return new DERBitString(data, padBits);
+        return thisLastOctetDER == thatLastOctetDER;
     }
 
     public ASN1Primitive getLoadedObject()
@@ -314,13 +382,37 @@
 
     ASN1Primitive toDERObject()
     {
-        return new DERBitString(data, padBits);
+        return new DERBitString(contents, false);
     }
 
     ASN1Primitive toDLObject()
     {
-        return new DLBitString(data, padBits);
+        return new DLBitString(contents, false);
     }
 
-    abstract void encode(ASN1OutputStream out, boolean withTag) throws IOException;
+    static ASN1BitString createPrimitive(byte[] contents)
+    {
+        int length = contents.length;
+        if (length < 1)
+        {
+            throw new IllegalArgumentException("truncated BIT STRING detected");
+        }
+
+        int padBits = contents[0] & 0xFF;
+        if (padBits > 0)
+        {
+            if (padBits > 7 || length < 2)
+            {
+                throw new IllegalArgumentException("invalid pad bits detected");
+            }
+
+            byte finalOctet = contents[length - 1];
+            if (finalOctet != (byte)(finalOctet & (0xFF << padBits)))
+            {
+                return new DLBitString(contents, false);
+            }
+        }
+
+        return new DERBitString(contents, false);
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/ASN1BitStringParser.java b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1BitStringParser.java
new file mode 100644
index 0000000..29c73d2
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1BitStringParser.java
@@ -0,0 +1,40 @@
+package org.bouncycastle.asn1;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * A basic parser for a BIT STRING object
+ */
+public interface ASN1BitStringParser
+    extends ASN1Encodable, InMemoryRepresentable
+{
+    /**
+     * Return an InputStream representing the contents of the BIT STRING. The final
+     * byte, if any, may include pad bits. See {@link #getPadBits()}.
+     *
+     * @return an InputStream with its source as the BIT STRING content.
+     */
+    public InputStream getBitStream() throws IOException;
+
+    /**
+     * Return an InputStream representing the contents of the BIT STRING, where the
+     * content is expected to be octet-aligned (this will be automatically checked
+     * during parsing).
+     *
+     * @return an InputStream with its source as the BIT STRING content.
+     */
+    public InputStream getOctetStream() throws IOException;
+
+    /**
+     * Return the number of pad bits, if any, in the final byte, if any, read from
+     * {@link #getBitStream()}. This number is in the range zero to seven. That
+     * number of the least significant bits of the final byte, if any, are not part
+     * of the contents and should be ignored. NOTE: Must be called AFTER the stream
+     * has been fully processed. (Does not need to be called if
+     * {@link #getOctetStream()} was used instead of {@link #getBitStream()}).
+     *
+     * @return the number of pad bits. In the range zero to seven.
+     */
+    public int getPadBits();
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/ASN1Boolean.java b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1Boolean.java
index 93b40ec..752aaa6 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/ASN1Boolean.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1Boolean.java
@@ -16,6 +16,14 @@
 public class ASN1Boolean
     extends ASN1Primitive
 {
+    static final ASN1UniversalType TYPE = new ASN1UniversalType(ASN1Boolean.class, BERTags.BOOLEAN)
+    {
+        ASN1Primitive fromImplicitPrimitive(DEROctetString octetString)
+        {
+            return createPrimitive(octetString.getOctets());
+        }
+    };
+
     private static final byte FALSE_VALUE = 0x00;
     private static final byte TRUE_VALUE = (byte)0xFF;
 
@@ -44,7 +52,7 @@
             byte[] enc = (byte[])obj;
             try
             {
-                return (ASN1Boolean)fromByteArray(enc);
+                return (ASN1Boolean)TYPE.fromByteArray(enc);
             }
             catch (IOException e)
             {
@@ -89,25 +97,16 @@
     /**
      * Return a Boolean from a tagged object.
      *
-     * @param obj the tagged object holding the object we want
+     * @param taggedObject the tagged object holding the object we want
      * @param explicit true if the object is meant to be explicitly
      *              tagged false otherwise.
      * @exception IllegalArgumentException if the tagged object cannot
      *               be converted.
      * @return an ASN1Boolean instance.
      */
-    public static ASN1Boolean getInstance(ASN1TaggedObject obj, boolean explicit)
+    public static ASN1Boolean getInstance(ASN1TaggedObject taggedObject, boolean explicit)
     {
-        ASN1Primitive o = obj.getObject();
-
-        if (explicit || o instanceof ASN1Boolean)
-        {
-            return getInstance(o);
-        }
-        else
-        {
-            return ASN1Boolean.fromOctetString(ASN1OctetString.getInstance(o).getOctets());
-        }
+        return (ASN1Boolean)TYPE.getContextInstance(taggedObject, explicit);
     }
 
     private ASN1Boolean(byte value)
@@ -120,19 +119,19 @@
         return value != FALSE_VALUE;
     }
 
-    boolean isConstructed()
+    boolean encodeConstructed()
     {
         return false;
     }
 
-    int encodedLength()
+    int encodedLength(boolean withTag)
     {
-        return 3;
+        return ASN1OutputStream.getLengthOfEncodingDL(withTag, 1);
     }
 
     void encode(ASN1OutputStream out, boolean withTag) throws IOException
     {
-        out.writeEncoded(withTag, BERTags.BOOLEAN, value);
+        out.writeEncodingDL(withTag, BERTags.BOOLEAN, value);
     }
 
     boolean asn1Equals(ASN1Primitive other)
@@ -162,14 +161,14 @@
       return isTrue() ? "TRUE" : "FALSE";
     }
 
-    static ASN1Boolean fromOctetString(byte[] value)
+    static ASN1Boolean createPrimitive(byte[] contents)
     {
-        if (value.length != 1)
+        if (contents.length != 1)
         {
             throw new IllegalArgumentException("BOOLEAN value should have 1 byte in it");
         }
 
-        byte b = value[0];
+        byte b = contents[0];
         switch (b)
         {
         case FALSE_VALUE:   return FALSE;
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/ASN1EncodableVector.java b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1EncodableVector.java
index 9baff44..0b011c6 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/ASN1EncodableVector.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1EncodableVector.java
@@ -48,6 +48,16 @@
         this.elementCount = minCapacity;
     }
 
+    public void addAll(ASN1Encodable[] others)
+    {
+        if (null == others)
+        {
+            throw new NullPointerException("'others' cannot be null");
+        }
+
+        doAddAll(others, "'others' elements cannot be null");
+    }
+
     public void addAll(ASN1EncodableVector other)
     {
         if (null == other)
@@ -55,7 +65,12 @@
             throw new NullPointerException("'other' cannot be null");
         }
 
-        int otherElementCount = other.size();
+        doAddAll(other.elements, "'other' elements cannot be null");
+    }
+
+    private void doAddAll(ASN1Encodable[] others, String nullMsg)
+    {
+        int otherElementCount = others.length;
         if (otherElementCount < 1)
         {
             return;
@@ -71,10 +86,10 @@
         int i = 0;
         do
         {
-            ASN1Encodable otherElement = other.get(i);
+            ASN1Encodable otherElement = others[i];
             if (null == otherElement)
             {
-                throw new NullPointerException("'other' elements cannot be null");
+                throw new NullPointerException(nullMsg);
             }
 
             this.elements[elementCount + i] = otherElement;
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/ASN1Enumerated.java b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1Enumerated.java
index eff24a9..c2aa7dc 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/ASN1Enumerated.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1Enumerated.java
@@ -11,8 +11,13 @@
 public class ASN1Enumerated
     extends ASN1Primitive
 {
-    private final byte[] bytes;
-    private final int start;
+    static final ASN1UniversalType TYPE = new ASN1UniversalType(ASN1Enumerated.class, BERTags.ENUMERATED)
+    {
+        ASN1Primitive fromImplicitPrimitive(DEROctetString octetString)
+        {
+            return createPrimitive(octetString.getOctets(), false);
+        }
+    };
 
     /**
      * return an enumerated from the passed in object
@@ -33,7 +38,7 @@
         {
             try
             {
-                return (ASN1Enumerated)fromByteArray((byte[])obj);
+                return (ASN1Enumerated)TYPE.fromByteArray((byte[])obj);
             }
             catch (Exception e)
             {
@@ -47,29 +52,21 @@
     /**
      * return an Enumerated from a tagged object.
      *
-     * @param obj the tagged object holding the object we want
+     * @param taggedObject the tagged object holding the object we want
      * @param explicit true if the object is meant to be explicitly
      *              tagged false otherwise.
      * @exception IllegalArgumentException if the tagged object cannot
      *               be converted.
      * @return an ASN1Enumerated instance, or null.
      */
-    public static ASN1Enumerated getInstance(
-        ASN1TaggedObject obj,
-        boolean          explicit)
+    public static ASN1Enumerated getInstance(ASN1TaggedObject taggedObject, boolean explicit)
     {
-        ASN1Primitive o = obj.getObject();
-
-        if (explicit || o instanceof ASN1Enumerated)
-        {
-            return getInstance(o);
-        }
-        else
-        {
-            return fromOctetString(ASN1OctetString.getInstance(o).getOctets());
-        }
+        return (ASN1Enumerated)TYPE.getContextInstance(taggedObject, explicit);
     }
 
+    private final byte[] contents;
+    private final int start;
+
     /**
      * Constructor from int.
      *
@@ -82,7 +79,7 @@
             throw new IllegalArgumentException("enumerated must be non-negative");
         }
 
-        this.bytes = BigInteger.valueOf(value).toByteArray();
+        this.contents = BigInteger.valueOf(value).toByteArray();
         this.start = 0;
     }
 
@@ -98,67 +95,78 @@
             throw new IllegalArgumentException("enumerated must be non-negative");
         }
 
-        this.bytes = value.toByteArray();
+        this.contents = value.toByteArray();
         this.start = 0;
     }
 
     /**
      * Constructor from encoded BigInteger.
      *
-     * @param bytes the value of this enumerated as an encoded BigInteger (signed).
+     * @param contents the value of this enumerated as an encoded BigInteger (signed).
      */
-    public ASN1Enumerated(byte[] bytes)
+    public ASN1Enumerated(byte[] contents)
     {
-        if (ASN1Integer.isMalformed(bytes))
+        this(contents, true);
+    }
+
+    ASN1Enumerated(byte[] contents, boolean clone)
+    {
+        if (ASN1Integer.isMalformed(contents))
         {
             throw new IllegalArgumentException("malformed enumerated");
         }
-        if (0 != (bytes[0] & 0x80))
+        if (0 != (contents[0] & 0x80))
         {
             throw new IllegalArgumentException("enumerated must be non-negative");
         }
 
-        this.bytes = Arrays.clone(bytes);
-        this.start = ASN1Integer.signBytesToSkip(bytes); 
+        this.contents = clone ? Arrays.clone(contents) : contents;
+        this.start = ASN1Integer.signBytesToSkip(contents); 
     }
 
     public BigInteger getValue()
     {
-        return new BigInteger(bytes);
+        return new BigInteger(contents);
+    }
+
+    public boolean hasValue(int x)
+    {
+        return (contents.length - start) <= 4
+            && ASN1Integer.intValue(contents, start, ASN1Integer.SIGN_EXT_SIGNED) == x;
     }
 
     public boolean hasValue(BigInteger x)
     {
         return null != x
             // Fast check to avoid allocation
-            && ASN1Integer.intValue(bytes, start, ASN1Integer.SIGN_EXT_SIGNED) == x.intValue()
+            && ASN1Integer.intValue(contents, start, ASN1Integer.SIGN_EXT_SIGNED) == x.intValue()
             && getValue().equals(x);
     }
 
     public int intValueExact()
     {
-        int count = bytes.length - start;
+        int count = contents.length - start;
         if (count > 4)
         {
             throw new ArithmeticException("ASN.1 Enumerated out of int range");
         }
 
-        return ASN1Integer.intValue(bytes, start, ASN1Integer.SIGN_EXT_SIGNED); 
+        return ASN1Integer.intValue(contents, start, ASN1Integer.SIGN_EXT_SIGNED); 
     }
 
-    boolean isConstructed()
+    boolean encodeConstructed()
     {
         return false;
     }
 
-    int encodedLength()
+    int encodedLength(boolean withTag)
     {
-        return 1 + StreamUtil.calculateBodyLength(bytes.length) + bytes.length;
+        return ASN1OutputStream.getLengthOfEncodingDL(withTag, contents.length);
     }
 
     void encode(ASN1OutputStream out, boolean withTag) throws IOException
     {
-        out.writeEncoded(withTag, BERTags.ENUMERATED, bytes);
+        out.writeEncodingDL(withTag, BERTags.ENUMERATED, contents);
     }
 
     boolean asn1Equals(
@@ -171,39 +179,39 @@
 
         ASN1Enumerated other = (ASN1Enumerated)o;
 
-        return Arrays.areEqual(this.bytes, other.bytes);
+        return Arrays.areEqual(this.contents, other.contents);
     }
 
     public int hashCode()
     {
-        return Arrays.hashCode(bytes);
+        return Arrays.hashCode(contents);
     }
 
-    private static ASN1Enumerated[] cache = new ASN1Enumerated[12];
+    private static final ASN1Enumerated[] cache = new ASN1Enumerated[12];
 
-    static ASN1Enumerated fromOctetString(byte[] enc)
+    static ASN1Enumerated createPrimitive(byte[] contents, boolean clone)
     {
-        if (enc.length > 1)
+        if (contents.length > 1)
         {
-            return new ASN1Enumerated(enc);
+            return new ASN1Enumerated(contents, clone);
         }
 
-        if (enc.length == 0)
+        if (contents.length == 0)
         {
             throw new IllegalArgumentException("ENUMERATED has zero length");
         }
-        int value = enc[0] & 0xff;
+        int value = contents[0] & 0xff;
 
         if (value >= cache.length)
         {
-            return new ASN1Enumerated(enc);
+            return new ASN1Enumerated(contents, clone);
         }
 
         ASN1Enumerated possibleMatch = cache[value];
 
         if (possibleMatch == null)
         {
-            possibleMatch = cache[value] = new ASN1Enumerated(enc);
+            possibleMatch = cache[value] = new ASN1Enumerated(contents, clone);
         }
 
         return possibleMatch;
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/ASN1External.java b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1External.java
index 082605d..c246164 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/ASN1External.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1External.java
@@ -2,107 +2,132 @@
 
 import java.io.IOException;
 
+import org.bouncycastle.util.Objects;
+
 /**
  * Class representing the DER-type External
  */
 public abstract class ASN1External
     extends ASN1Primitive
 {
-    protected ASN1ObjectIdentifier directReference;
-    protected ASN1Integer indirectReference;
-    protected ASN1Primitive dataValueDescriptor;
-    protected int encoding;
-    protected ASN1Primitive externalContent;
+    static final ASN1UniversalType TYPE = new ASN1UniversalType(ASN1External.class, BERTags.EXTERNAL)
+    {
+        ASN1Primitive fromImplicitConstructed(ASN1Sequence sequence)
+        {
+            // TODO Ideally ASN1External would have no subclasses and just hold the sequence
+            return sequence.toASN1External();
+        }
+    };
 
-    /**
-     * Construct an EXTERNAL object, the input encoding vector must have exactly two elements on it.
-     * <p>
-     * Acceptable input formats are:
-     * <ul>
-     * <li> {@link ASN1ObjectIdentifier} + data {@link DERTaggedObject} (direct reference form)</li>
-     * <li> {@link ASN1Integer} + data {@link DERTaggedObject} (indirect reference form)</li>
-     * <li> Anything but {@link DERTaggedObject} + data {@link DERTaggedObject} (data value form)</li>
-     * </ul>
-     *
-     * @throws IllegalArgumentException if input size is wrong, or
-     */
-    public ASN1External(ASN1EncodableVector vector)
+    public static ASN1External getInstance(Object obj)
+    {
+        if (obj == null || obj instanceof ASN1External)
+        {
+            return (ASN1External)obj;
+        }
+        else if (obj instanceof ASN1Encodable)
+        {
+            ASN1Primitive primitive = ((ASN1Encodable)obj).toASN1Primitive();
+            if (primitive instanceof ASN1External)
+            {
+                return (ASN1External)primitive;
+            }
+        }
+        else if (obj instanceof byte[])
+        {
+            try
+            {
+                return (ASN1External)TYPE.fromByteArray((byte[])obj);
+            }
+            catch (IOException e)
+            {
+                throw new IllegalArgumentException("failed to construct external from byte[]: " + e.getMessage());
+            }
+        }
+
+        throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
+    }
+
+    public static ASN1External getInstance(ASN1TaggedObject taggedObject, boolean explicit)
+    {
+        return (ASN1External)TYPE.getContextInstance(taggedObject, explicit);
+    }
+
+    ASN1ObjectIdentifier directReference;
+    ASN1Integer indirectReference;
+    // TODO Actually use ASN1ObjectDescriptor for this
+    ASN1Primitive dataValueDescriptor;
+    int encoding;
+    ASN1Primitive externalContent;
+
+    ASN1External(ASN1Sequence sequence)
     {
         int offset = 0;
 
-        ASN1Primitive enc = getObjFromVector(vector, offset);
-        if (enc instanceof ASN1ObjectIdentifier)
+        ASN1Primitive asn1 = getObjFromSequence(sequence, offset);
+        if (asn1 instanceof ASN1ObjectIdentifier)
         {
-            directReference = (ASN1ObjectIdentifier)enc;
-            offset++;
-            enc = getObjFromVector(vector, offset);
+            directReference = (ASN1ObjectIdentifier)asn1;
+            asn1 = getObjFromSequence(sequence, ++offset);
         }
-        if (enc instanceof ASN1Integer)
+        if (asn1 instanceof ASN1Integer)
         {
-            indirectReference = (ASN1Integer) enc;
-            offset++;
-            enc = getObjFromVector(vector, offset);
+            indirectReference = (ASN1Integer)asn1;
+            asn1 = getObjFromSequence(sequence, ++offset);
         }
-        if (!(enc instanceof ASN1TaggedObject))
+        if (!(asn1 instanceof ASN1TaggedObject))
         {
-            dataValueDescriptor = (ASN1Primitive) enc;
-            offset++;
-            enc = getObjFromVector(vector, offset);
+            dataValueDescriptor = asn1;
+            asn1 = getObjFromSequence(sequence, ++offset);
         }
 
-        if (vector.size() != offset + 1)
+        if (sequence.size() != offset + 1)
         {
-            throw new IllegalArgumentException("input vector too large");
+            throw new IllegalArgumentException("input sequence too large");
         }
 
-        if (!(enc instanceof ASN1TaggedObject))
+        if (!(asn1 instanceof ASN1TaggedObject))
         {
-            throw new IllegalArgumentException("No tagged object found in vector. Structure doesn't seem to be of type External");
+            throw new IllegalArgumentException(
+                "No tagged object found in sequence. Structure doesn't seem to be of type External");
         }
-        ASN1TaggedObject obj = (ASN1TaggedObject)enc;
-        setEncoding(obj.getTagNo());
-        externalContent = obj.getObject();
+
+        ASN1TaggedObject obj = (ASN1TaggedObject)asn1;
+        this.encoding = checkEncoding(obj.getTagNo());
+        this.externalContent = getExternalContent(obj);
     }
 
-    private ASN1Primitive getObjFromVector(ASN1EncodableVector v, int index)
+    ASN1External(ASN1ObjectIdentifier directReference, ASN1Integer indirectReference, ASN1Primitive dataValueDescriptor,
+        DERTaggedObject externalData)
     {
-        if (v.size() <= index)
-        {
-            throw new IllegalArgumentException("too few objects in input vector");
-        }
-
-        return v.get(index).toASN1Primitive();
+        this.directReference = directReference;
+        this.indirectReference = indirectReference;
+        this.dataValueDescriptor = dataValueDescriptor;
+        this.encoding = checkEncoding(externalData.getTagNo());
+        this.externalContent = getExternalContent(externalData);
     }
 
-    /**
-     * Creates a new instance of External
-     * See X.690 for more informations about the meaning of these parameters
-     * @param directReference The direct reference or <code>null</code> if not set.
-     * @param indirectReference The indirect reference or <code>null</code> if not set.
-     * @param dataValueDescriptor The data value descriptor or <code>null</code> if not set.
-     * @param externalData The external data in its encoded form.
-     */
-    public ASN1External(ASN1ObjectIdentifier directReference, ASN1Integer indirectReference, ASN1Primitive dataValueDescriptor, DERTaggedObject externalData)
+    ASN1External(ASN1ObjectIdentifier directReference, ASN1Integer indirectReference, ASN1Primitive dataValueDescriptor,
+        int encoding, ASN1Primitive externalData)
     {
-        this(directReference, indirectReference, dataValueDescriptor, externalData.getTagNo(), externalData.toASN1Primitive());
+        this.directReference = directReference;
+        this.indirectReference = indirectReference;
+        this.dataValueDescriptor = dataValueDescriptor;
+        this.encoding = checkEncoding(encoding);
+        this.externalContent = checkExternalContent(encoding, externalData);
     }
 
-    /**
-     * Creates a new instance of External.
-     * See X.690 for more informations about the meaning of these parameters
-     * @param directReference The direct reference or <code>null</code> if not set.
-     * @param indirectReference The indirect reference or <code>null</code> if not set.
-     * @param dataValueDescriptor The data value descriptor or <code>null</code> if not set.
-     * @param encoding The encoding to be used for the external data
-     * @param externalData The external data
-     */
-    public ASN1External(ASN1ObjectIdentifier directReference, ASN1Integer indirectReference, ASN1Primitive dataValueDescriptor, int encoding, ASN1Primitive externalData)
+    abstract ASN1Sequence buildSequence();
+
+    int encodedLength(boolean withTag) throws IOException
     {
-        setDirectReference(directReference);
-        setIndirectReference(indirectReference);
-        setDataValueDescriptor(dataValueDescriptor);
-        setEncoding(encoding);
-        setExternalContent(externalData.toASN1Primitive());
+        return buildSequence().encodedLength(withTag);
+    }
+
+    void encode(ASN1OutputStream out, boolean withTag) throws IOException
+    {
+        out.writeIdentifier(withTag, BERTags.CONSTRUCTED | BERTags.EXTERNAL);
+        buildSequence().encode(out, false);
     }
 
     ASN1Primitive toDERObject()
@@ -115,75 +140,38 @@
         return new DLExternal(directReference, indirectReference, dataValueDescriptor, encoding, externalContent);
     }
 
-    /* (non-Javadoc)
-     * @see java.lang.Object#hashCode()
-     */
     public int hashCode()
     {
-        int ret = 0;
-        if (directReference != null)
-        {
-            ret = directReference.hashCode();
-        }
-        if (indirectReference != null)
-        {
-            ret ^= indirectReference.hashCode();
-        }
-        if (dataValueDescriptor != null)
-        {
-            ret ^= dataValueDescriptor.hashCode();
-        }
-        ret ^= externalContent.hashCode();
-        return ret;
+        return Objects.hashCode(this.directReference)
+            ^  Objects.hashCode(this.indirectReference)
+            ^  Objects.hashCode(this.dataValueDescriptor)
+            ^  this.encoding
+            ^  this.externalContent.hashCode();
     }
 
-    boolean isConstructed()
+    boolean encodeConstructed()
     {
         return true;
     }
 
-    int encodedLength()
-        throws IOException
+    boolean asn1Equals(ASN1Primitive primitive)
     {
-        return this.getEncoded().length;
-    }
-
-    /* (non-Javadoc)
-     * @see org.bouncycastle.asn1.ASN1Primitive#asn1Equals(org.bouncycastle.asn1.ASN1Primitive)
-     */
-    boolean asn1Equals(ASN1Primitive o)
-    {
-        if (!(o instanceof ASN1External))
-        {
-            return false;
-        }
-        if (this == o)
+        if (this == primitive)
         {
             return true;
         }
-        ASN1External other = (ASN1External)o;
-        if (directReference != null)
+        if (!(primitive instanceof ASN1External))
         {
-            if (other.directReference == null || !other.directReference.equals(directReference))  
-            {
-                return false;
-            }
+            return false;
         }
-        if (indirectReference != null)
-        {
-            if (other.indirectReference == null || !other.indirectReference.equals(indirectReference))
-            {
-                return false;
-            }
-        }
-        if (dataValueDescriptor != null)
-        {
-            if (other.dataValueDescriptor == null || !other.dataValueDescriptor.equals(dataValueDescriptor))
-            {
-                return false;
-            }
-        }
-        return externalContent.equals(other.externalContent);
+
+        ASN1External that = (ASN1External)primitive;
+
+        return Objects.areEqual(this.directReference, that.directReference)
+            && Objects.areEqual(this.indirectReference, that.indirectReference)
+            && Objects.areEqual(this.dataValueDescriptor, that.dataValueDescriptor)
+            && this.encoding == that.encoding
+            && this.externalContent.equals(that.externalContent);
     }
 
     /**
@@ -217,7 +205,7 @@
     {
         return encoding;
     }
-    
+
     /**
      * Returns the content of this element
      * @return The content
@@ -226,7 +214,7 @@
     {
         return externalContent;
     }
-    
+
     /**
      * Returns the indirect reference of this element
      * @return The reference
@@ -235,27 +223,9 @@
     {
         return indirectReference;
     }
-    
-    /**
-     * Sets the data value descriptor
-     * @param dataValueDescriptor The descriptor
-     */
-    private void setDataValueDescriptor(ASN1Primitive dataValueDescriptor)
-    {
-        this.dataValueDescriptor = dataValueDescriptor;
-    }
 
     /**
-     * Sets the direct reference of the external element
-     * @param directReferemce The reference
-     */
-    private void setDirectReference(ASN1ObjectIdentifier directReferemce)
-    {
-        this.directReference = directReferemce;
-    }
-    
-    /**
-     * Sets the encoding of the content. Valid values are
+     * Checks the encoding of the content. Valid values are
      * <ul>
      * <li><code>0</code> single-ASN1-type</li>
      * <li><code>1</code> OCTET STRING</li>
@@ -263,30 +233,57 @@
      * </ul>
      * @param encoding The encoding
      */
-    private void setEncoding(int encoding)
+    private static int checkEncoding(int encoding)
     {
         if (encoding < 0 || encoding > 2)
         {
             throw new IllegalArgumentException("invalid encoding value: " + encoding);
         }
-        this.encoding = encoding;
+
+        return encoding;
     }
-    
-    /**
-     * Sets the content of this element
-     * @param externalContent The content
-     */
-    private void setExternalContent(ASN1Primitive externalContent)
+
+    private static ASN1Primitive checkExternalContent(int tagNo, ASN1Primitive externalContent)
     {
-        this.externalContent = externalContent;
+        switch (tagNo)
+        {
+        case 1:
+            return ASN1OctetString.TYPE.checkedCast(externalContent);
+        case 2:
+            return ASN1BitString.TYPE.checkedCast(externalContent);
+        default:
+            return externalContent;
+        }
     }
-    
-    /**
-     * Sets the indirect reference of this element
-     * @param indirectReference The reference
-     */
-    private void setIndirectReference(ASN1Integer indirectReference)
+
+    private static ASN1Primitive getExternalContent(ASN1TaggedObject encoding)
     {
-        this.indirectReference = indirectReference;
+        int tagClass = encoding.getTagClass(), tagNo = encoding.getTagNo();
+        if (BERTags.CONTEXT_SPECIFIC != tagClass)
+        {
+            throw new IllegalArgumentException("invalid tag: " + ASN1Util.getTagText(tagClass, tagNo));
+        }
+
+        switch (tagNo)
+        {
+        case 0:
+            return encoding.getExplicitBaseObject().toASN1Primitive();
+        case 1:
+            return ASN1OctetString.getInstance(encoding, false);
+        case 2:
+            return ASN1BitString.getInstance(encoding, false);
+        default:
+            throw new IllegalArgumentException("invalid tag: " + ASN1Util.getTagText(tagClass, tagNo));
+        }
+    }
+
+    private static ASN1Primitive getObjFromSequence(ASN1Sequence sequence, int index)
+    {
+        if (sequence.size() <= index)
+        {
+            throw new IllegalArgumentException("too few objects in input sequence");
+        }
+
+        return sequence.getObjectAt(index).toASN1Primitive();
     }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/ASN1ApplicationSpecificParser.java b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1ExternalParser.java
similarity index 76%
rename from bcprov/src/main/java/org/bouncycastle/asn1/ASN1ApplicationSpecificParser.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/ASN1ExternalParser.java
index 422bccf..e335e54 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/ASN1ApplicationSpecificParser.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1ExternalParser.java
@@ -3,9 +3,9 @@
 import java.io.IOException;
 
 /**
- * Interface to parse ASN.1 ApplicationSpecific objects.
+ * Parser DER EXTERNAL tagged objects.
  */
-public interface ASN1ApplicationSpecificParser
+public interface ASN1ExternalParser
     extends ASN1Encodable, InMemoryRepresentable
 {
     /**
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/ASN1GeneralString.java b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1GeneralString.java
new file mode 100644
index 0000000..c527f4d
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1GeneralString.java
@@ -0,0 +1,151 @@
+package org.bouncycastle.asn1;
+
+import java.io.IOException;
+
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.Strings;
+
+/**
+ * ASN.1 GENERAL-STRING data type.
+ * <p>
+ * This is an 8-bit encoded ISO 646 (ASCII) character set
+ * with optional escapes to other character sets.
+ * </p>
+ */
+public abstract class ASN1GeneralString 
+    extends ASN1Primitive
+    implements ASN1String
+{
+    static final ASN1UniversalType TYPE = new ASN1UniversalType(ASN1GeneralString.class, BERTags.GENERAL_STRING)
+    {
+        ASN1Primitive fromImplicitPrimitive(DEROctetString octetString)
+        {
+            return createPrimitive(octetString.getOctets());
+        }
+    };
+
+    /**
+     * Return a GeneralString from the given object.
+     *
+     * @param obj the object we want converted.
+     * @exception IllegalArgumentException if the object cannot be converted.
+     * @return an ASN1GeneralString instance, or null.
+     */
+    public static ASN1GeneralString getInstance(Object obj)
+    {
+        if (obj == null || obj instanceof ASN1GeneralString) 
+        {
+            return (ASN1GeneralString) obj;
+        }
+        if (obj instanceof ASN1Encodable)
+        {
+            ASN1Primitive primitive = ((ASN1Encodable)obj).toASN1Primitive();
+            if (primitive instanceof ASN1GeneralString)
+            {
+                return (ASN1GeneralString)primitive;
+            }
+        }
+        if (obj instanceof byte[])
+        {
+            try
+            {
+                return (ASN1GeneralString)TYPE.fromByteArray((byte[])obj);
+            }
+            catch (Exception e)
+            {
+                throw new IllegalArgumentException("encoding error in getInstance: " + e.toString());
+            }
+        }
+
+        throw new IllegalArgumentException("illegal object in getInstance: "
+                + obj.getClass().getName());
+    }
+
+    /**
+     * Return a GeneralString from a tagged object.
+     *
+     * @param taggedObject      the tagged object holding the object we want
+     * @param explicit true if the object is meant to be explicitly tagged false
+     *                 otherwise.
+     * @exception IllegalArgumentException if the tagged object cannot be converted.
+     * @return an ASN1GeneralString instance.
+     */
+    public static ASN1GeneralString getInstance(ASN1TaggedObject taggedObject, boolean explicit)
+    {
+        return (ASN1GeneralString)TYPE.getContextInstance(taggedObject, explicit);
+    }
+
+    final byte[] contents;
+
+    ASN1GeneralString(String string) 
+    {
+        this.contents = Strings.toByteArray(string);
+    }
+
+    ASN1GeneralString(byte[] contents, boolean clone)
+    {
+        this.contents = clone ? Arrays.clone(contents) : contents;
+    }
+
+    /**
+     * Return a Java String representation of our contained String.
+     *
+     * @return a Java String representing our contents.
+     */
+    public final String getString() 
+    {
+        return Strings.fromByteArray(contents);
+    }
+
+    public String toString()
+    {
+        return getString();
+    }
+
+    /**
+     * Return a byte array representation of our contained String.
+     *
+     * @return a byte array representing our contents.
+     */
+    public final byte[] getOctets() 
+    {
+        return Arrays.clone(contents);
+    }
+
+    final boolean encodeConstructed()
+    {
+        return false;
+    }
+
+    final int encodedLength(boolean withTag)
+    {
+        return ASN1OutputStream.getLengthOfEncodingDL(withTag, contents.length);
+    }
+
+    final void encode(ASN1OutputStream out, boolean withTag) throws IOException
+    {
+        out.writeEncodingDL(withTag, BERTags.GENERAL_STRING, contents);
+    }
+
+    final boolean asn1Equals(ASN1Primitive other)
+    {
+        if (!(other instanceof ASN1GeneralString))
+        {
+            return false;
+        }
+
+        ASN1GeneralString that = (ASN1GeneralString)other;
+
+        return Arrays.areEqual(this.contents, that.contents);
+    }
+
+    public final int hashCode() 
+    {
+        return Arrays.hashCode(contents);
+    }
+
+    static ASN1GeneralString createPrimitive(byte[] contents)
+    {
+        return new DERGeneralString(contents, false);
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/ASN1GeneralizedTime.java b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1GeneralizedTime.java
index 3d076cc..620e1ad 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/ASN1GeneralizedTime.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1GeneralizedTime.java
@@ -46,7 +46,13 @@
 public class ASN1GeneralizedTime
     extends ASN1Primitive
 {
-    protected byte[] time;
+    static final ASN1UniversalType TYPE = new ASN1UniversalType(ASN1GeneralizedTime.class, BERTags.GENERALIZED_TIME)
+    {
+        ASN1Primitive fromImplicitPrimitive(DEROctetString octetString)
+        {
+            return createPrimitive(octetString.getOctets());
+        }
+    };
 
     /**
      * return a generalized time from the passed in object
@@ -62,12 +68,19 @@
         {
             return (ASN1GeneralizedTime)obj;
         }
-
+        if (obj instanceof ASN1Encodable)
+        {
+            ASN1Primitive primitive = ((ASN1Encodable)obj).toASN1Primitive();
+            if (primitive instanceof ASN1GeneralizedTime)
+            {
+                return (ASN1GeneralizedTime)primitive;
+            }
+        }
         if (obj instanceof byte[])
         {
             try
             {
-                return (ASN1GeneralizedTime)fromByteArray((byte[])obj);
+                return (ASN1GeneralizedTime)TYPE.fromByteArray((byte[])obj);
             }
             catch (Exception e)
             {
@@ -81,29 +94,19 @@
     /**
      * return a Generalized Time object from a tagged object.
      *
-     * @param obj      the tagged object holding the object we want
-     * @param explicit true if the object is meant to be explicitly
-     *                 tagged false otherwise.
+     * @param taggedObject the tagged object holding the object we want
+     * @param explicit     true if the object is meant to be explicitly tagged false
+     *                     otherwise.
      * @return an ASN1GeneralizedTime instance.
-     * @throws IllegalArgumentException if the tagged object cannot
-     * be converted.
+     * @throws IllegalArgumentException if the tagged object cannot be converted.
      */
-    public static ASN1GeneralizedTime getInstance(
-        ASN1TaggedObject obj,
-        boolean explicit)
+    public static ASN1GeneralizedTime getInstance(ASN1TaggedObject taggedObject, boolean explicit)
     {
-        ASN1Primitive o = obj.getObject();
-
-        if (explicit || o instanceof ASN1GeneralizedTime)
-        {
-            return getInstance(o);
-        }
-        else
-        {
-            return new ASN1GeneralizedTime(ASN1OctetString.getInstance(o).getOctets());
-        }
+        return (ASN1GeneralizedTime)TYPE.getContextInstance(taggedObject, explicit);
     }
 
+    final byte[] contents;
+
     /**
      * The correct format for this is YYYYMMDDHHMMSS[.f]Z, or without the Z
      * for local time, or Z+-HHMM on the end, for difference between local
@@ -116,7 +119,7 @@
     public ASN1GeneralizedTime(
         String time)
     {
-        this.time = Strings.toByteArray(time);
+        this.contents = Strings.toByteArray(time);
         try
         {
             this.getDate();
@@ -141,7 +144,7 @@
 
         dateF.setTimeZone(new SimpleTimeZone(0, "Z"));
 
-        this.time = Strings.toByteArray(dateF.format(time));
+        this.contents = Strings.toByteArray(dateF.format(time));
     }
 
     /**
@@ -163,7 +166,7 @@
 
         dateF.setTimeZone(new SimpleTimeZone(0, "Z"));
 
-        this.time = Strings.toByteArray(dateF.format(time));
+        this.contents = Strings.toByteArray(dateF.format(time));
     }
 
     ASN1GeneralizedTime(
@@ -173,7 +176,7 @@
         {
             throw new IllegalArgumentException("GeneralizedTime string too short");
         }
-        this.time = bytes;
+        this.contents = bytes;
 
         if (!(isDigit(0) && isDigit(1) && isDigit(2) && isDigit(3)))
         {
@@ -188,7 +191,7 @@
      */
     public String getTimeString()
     {
-        return Strings.fromByteArray(time);
+        return Strings.fromByteArray(contents);
     }
 
     /**
@@ -206,7 +209,7 @@
      */
     public String getTime()
     {
-        String stime = Strings.fromByteArray(time);
+        String stime = Strings.fromByteArray(contents);
 
         //
         // standardise the format.
@@ -358,34 +361,26 @@
         throws ParseException
     {
         SimpleDateFormat dateF;
-        String stime = Strings.fromByteArray(time);
+        String stime = Strings.fromByteArray(contents);
         String d = stime;
 
         if (stime.endsWith("Z"))
         {
             if (hasFractionalSeconds())
             {
-                // Android-changed: Use localized version
-                // dateF = new SimpleDateFormat("yyyyMMddHHmmss.SSS'Z'");
-                dateF = new SimpleDateFormat("yyyyMMddHHmmss.SSS'Z'", Locale.US);
+                dateF = new SimpleDateFormat("yyyyMMddHHmmss.SSS'Z'", LocaleUtil.EN_Locale);
             }
             else if (hasSeconds())
             {
-                // Android-changed: Use localized version
-                // dateF = new SimpleDateFormat("yyyyMMddHHmmss'Z'");
-                dateF = new SimpleDateFormat("yyyyMMddHHmmss'Z'", Locale.US);
+                dateF = new SimpleDateFormat("yyyyMMddHHmmss'Z'", LocaleUtil.EN_Locale);
             }
             else if (hasMinutes())
             {
-                // Android-changed: Use localized version
-                // dateF = new SimpleDateFormat("yyyyMMddHHmm'Z'");
-                dateF = new SimpleDateFormat("yyyyMMddHHmm'Z'", Locale.US);
+                dateF = new SimpleDateFormat("yyyyMMddHHmm'Z'", LocaleUtil.EN_Locale);
             }
             else
             {
-                // Android-changed: Use localized version
-                // dateF = new SimpleDateFormat("yyyyMMddHH'Z'");
-                dateF = new SimpleDateFormat("yyyyMMddHH'Z'", Locale.US);
+                dateF = new SimpleDateFormat("yyyyMMddHH'Z'", LocaleUtil.EN_Locale);
             }
 
             dateF.setTimeZone(new SimpleTimeZone(0, "Z"));
@@ -430,14 +425,14 @@
             d = pruneFractionalSeconds(d);
         }
         
-        return DateUtil.epochAdjust(dateF.parse(d));
+        return dateF.parse(d);
     }
 
     protected boolean hasFractionalSeconds()
     {
-        for (int i = 0; i != time.length; i++)
+        for (int i = 0; i != contents.length; i++)
         {
-            if (time[i] == '.')
+            if (contents[i] == '.')
             {
                 if (i == 14)
                 {
@@ -460,49 +455,46 @@
 
     private boolean isDigit(int pos)
     {
-        return time.length > pos && time[pos] >= '0' && time[pos] <= '9';
+        return contents.length > pos && contents[pos] >= '0' && contents[pos] <= '9';
     }
 
-    boolean isConstructed()
+    final boolean encodeConstructed()
     {
         return false;
     }
 
-    int encodedLength()
+    int encodedLength(boolean withTag)
     {
-        int length = time.length;
-
-        return 1 + StreamUtil.calculateBodyLength(length) + length;
+        return ASN1OutputStream.getLengthOfEncodingDL(withTag, contents.length);
     }
 
     void encode(ASN1OutputStream out, boolean withTag) throws IOException
     {
-        out.writeEncoded(withTag, BERTags.GENERALIZED_TIME, time);
+        out.writeEncodingDL(withTag, BERTags.GENERALIZED_TIME, contents);
     }
 
     ASN1Primitive toDERObject()
     {
-        return new DERGeneralizedTime(time);
+        return new DERGeneralizedTime(contents);
     }
 
-    ASN1Primitive toDLObject()
-    {
-        return new DERGeneralizedTime(time);
-    }
-
-    boolean asn1Equals(
-        ASN1Primitive o)
+    boolean asn1Equals(ASN1Primitive o)
     {
         if (!(o instanceof ASN1GeneralizedTime))
         {
             return false;
         }
 
-        return Arrays.areEqual(time, ((ASN1GeneralizedTime)o).time);
+        return Arrays.areEqual(contents, ((ASN1GeneralizedTime)o).contents);
     }
 
     public int hashCode()
     {
-        return Arrays.hashCode(time);
+        return Arrays.hashCode(contents);
+    }
+
+    static ASN1GeneralizedTime createPrimitive(byte[] contents)
+    {
+        return new ASN1GeneralizedTime(contents);
     }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/ASN1GraphicString.java b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1GraphicString.java
new file mode 100644
index 0000000..b21142b
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1GraphicString.java
@@ -0,0 +1,128 @@
+package org.bouncycastle.asn1;
+
+import java.io.IOException;
+
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.Strings;
+
+public abstract class ASN1GraphicString
+    extends ASN1Primitive
+    implements ASN1String
+{
+    static final ASN1UniversalType TYPE = new ASN1UniversalType(ASN1GraphicString.class, BERTags.GRAPHIC_STRING)
+    {
+        ASN1Primitive fromImplicitPrimitive(DEROctetString octetString)
+        {
+            return createPrimitive(octetString.getOctets());
+        }
+    };
+
+    /**
+     * Return a GraphicString from the passed in object.
+     *
+     * @param obj an ASN1GraphicString or an object that can be converted into one.
+     * @exception IllegalArgumentException if the object cannot be converted.
+     * @return an ASN1GraphicString instance, or null.
+     */
+    public static ASN1GraphicString getInstance(Object obj)
+    {
+        if (obj == null || obj instanceof ASN1GraphicString)
+        {
+            return (ASN1GraphicString)obj;
+        }
+        if (obj instanceof ASN1Encodable)
+        {
+            ASN1Primitive primitive = ((ASN1Encodable)obj).toASN1Primitive();
+            if (primitive instanceof ASN1GraphicString)
+            {
+                return (ASN1GraphicString)primitive;
+            }
+        }
+        if (obj instanceof byte[])
+        {
+            try
+            {
+                return (ASN1GraphicString)TYPE.fromByteArray((byte[])obj);
+            }
+            catch (Exception e)
+            {
+                throw new IllegalArgumentException("encoding error in getInstance: " + e.toString());
+            }
+        }
+
+        throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
+    }
+
+    /**
+     * Return a GraphicString from a tagged object.
+     *
+     * @param taggedObject the tagged object holding the object we want.
+     * @param explicit     true if the object is meant to be explicitly tagged,
+     *                     false otherwise.
+     * @exception IllegalArgumentException if the tagged object cannot be converted.
+     * @return an ASN1GraphicString instance, or null.
+     */
+    public static ASN1GraphicString getInstance(ASN1TaggedObject taggedObject, boolean explicit)
+    {
+        return (ASN1GraphicString)TYPE.getContextInstance(taggedObject, explicit);
+    }
+
+    final byte[] contents;
+
+    ASN1GraphicString(byte[] contents, boolean clone)
+    {
+        if (null == contents)
+        {
+            throw new NullPointerException("'contents' cannot be null");
+        }
+
+        this.contents = clone ? Arrays.clone(contents) : contents;
+    }
+
+    public final byte[] getOctets()
+    {
+        return Arrays.clone(contents);
+    }
+
+    final boolean encodeConstructed()
+    {
+        return false;
+    }
+
+    final int encodedLength(boolean withTag)
+    {
+        return ASN1OutputStream.getLengthOfEncodingDL(withTag, contents.length);
+    }
+
+    final void encode(ASN1OutputStream out, boolean withTag) throws IOException
+    {
+        out.writeEncodingDL(withTag, BERTags.GRAPHIC_STRING, contents);
+    }
+
+    final boolean asn1Equals(ASN1Primitive other)
+    {
+        if (!(other instanceof ASN1GraphicString))
+        {
+            return false;
+        }
+
+        ASN1GraphicString that = (ASN1GraphicString)other;
+
+        return Arrays.areEqual(this.contents, that.contents);
+    }
+
+    public final int hashCode()
+    {
+        return Arrays.hashCode(contents);
+    }
+
+    public final String getString()
+    {
+        return Strings.fromByteArray(contents);
+    }
+
+    static ASN1GraphicString createPrimitive(byte[] contents)
+    {
+        return new DERGraphicString(contents, false);
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/ASN1IA5String.java b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1IA5String.java
new file mode 100644
index 0000000..ecf848f
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1IA5String.java
@@ -0,0 +1,170 @@
+package org.bouncycastle.asn1;
+
+import java.io.IOException;
+
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.Strings;
+
+/**
+ * ASN.1 IA5String object - this is a ISO 646 (ASCII) string encoding code points 0 to 127.
+ * <p>
+ * Explicit character set escape sequences are not allowed.
+ * </p>
+ */
+public abstract class ASN1IA5String
+    extends ASN1Primitive
+    implements ASN1String
+{
+    static final ASN1UniversalType TYPE = new ASN1UniversalType(ASN1IA5String.class, BERTags.IA5_STRING)
+    {
+        ASN1Primitive fromImplicitPrimitive(DEROctetString octetString)
+        {
+            return createPrimitive(octetString.getOctets());
+        }
+    };
+
+    /**
+     * Return an IA5 string from the passed in object
+     *
+     * @param obj an ASN1IA5String or an object that can be converted into one.
+     * @exception IllegalArgumentException if the object cannot be converted.
+     * @return a ASN1IA5String instance, or null.
+     */
+    public static ASN1IA5String getInstance(Object obj)
+    {
+        if (obj == null || obj instanceof ASN1IA5String)
+        {
+            return (ASN1IA5String)obj;
+        }
+        if (obj instanceof ASN1Encodable)
+        {
+            ASN1Primitive primitive = ((ASN1Encodable)obj).toASN1Primitive();
+            if (primitive instanceof ASN1IA5String)
+            {
+                return (ASN1IA5String)primitive;
+            }
+        }
+        if (obj instanceof byte[])
+        {
+            try
+            {
+                return (ASN1IA5String)TYPE.fromByteArray((byte[])obj);
+            }
+            catch (Exception e)
+            {
+                throw new IllegalArgumentException("encoding error in getInstance: " + e.toString());
+            }
+        }
+
+        throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
+    }
+
+    /**
+     * Return an IA5 String from a tagged object.
+     *
+     * @param taggedObject the tagged object holding the object we want
+     * @param explicit true if the object is meant to be explicitly
+     *              tagged false otherwise.
+     * @exception IllegalArgumentException if the tagged object cannot
+     *               be converted.
+     * @return an ASN1IA5String instance, or null.
+     */
+    public static ASN1IA5String getInstance(ASN1TaggedObject taggedObject, boolean explicit)
+    {
+        return (ASN1IA5String)TYPE.getContextInstance(taggedObject, explicit);
+    }
+
+    final byte[] contents;
+
+    ASN1IA5String(String string, boolean validate)
+    {
+        if (string == null)
+        {
+            throw new NullPointerException("'string' cannot be null");
+        }
+        if (validate && !isIA5String(string))
+        {
+            throw new IllegalArgumentException("'string' contains illegal characters");
+        }
+
+        this.contents = Strings.toByteArray(string);
+    }
+
+    ASN1IA5String(byte[] contents, boolean clone)
+    {
+        this.contents = clone ? Arrays.clone(contents) : contents;
+    }
+
+    public final String getString()
+    {
+        return Strings.fromByteArray(contents);
+    }
+
+    public String toString()
+    {
+        return getString();
+    }
+
+    public final byte[] getOctets()
+    {
+        return Arrays.clone(contents);
+    }
+
+    final boolean encodeConstructed()
+    {
+        return false;
+    }
+
+    final int encodedLength(boolean withTag)
+    {
+        return ASN1OutputStream.getLengthOfEncodingDL(withTag, contents.length);
+    }
+
+    final void encode(ASN1OutputStream out, boolean withTag) throws IOException
+    {
+        out.writeEncodingDL(withTag, BERTags.IA5_STRING, contents);
+    }
+
+    final boolean asn1Equals(ASN1Primitive other)
+    {
+        if (!(other instanceof ASN1IA5String))
+        {
+            return false;
+        }
+
+        ASN1IA5String that = (ASN1IA5String)other;
+
+        return Arrays.areEqual(this.contents, that.contents);
+    }
+
+    public final int hashCode()
+    {
+        return Arrays.hashCode(contents);
+    }
+
+    /**
+     * return true if the passed in String can be represented without
+     * loss as an IA5String, false otherwise.
+     *
+     * @param str the string to check.
+     * @return true if character set in IA5String set, false otherwise.
+     */
+    public static boolean isIA5String(String str)
+    {
+        for (int i = str.length() - 1; i >= 0; i--)
+        {
+            char ch = str.charAt(i);
+            if (ch > 0x007f)
+            {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    static ASN1IA5String createPrimitive(byte[] contents)
+    {
+        return new DERIA5String(contents, false);
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/ASN1InputStream.java b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1InputStream.java
index ca8257b..ff7e3e0 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/ASN1InputStream.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1InputStream.java
@@ -20,11 +20,9 @@
 {
     private final int limit;
     private final boolean lazyEvaluate;
-
     private final byte[][] tmpBuffers;
 
-    public ASN1InputStream(
-        InputStream is)
+    public ASN1InputStream(InputStream is)
     {
         this(is, StreamUtil.findLimit(is));
     }
@@ -35,8 +33,7 @@
      * 
      * @param input array containing ASN.1 encoded data.
      */
-    public ASN1InputStream(
-        byte[] input)
+    public ASN1InputStream(byte[] input)
     {
         this(new ByteArrayInputStream(input), input.length);
     }
@@ -48,22 +45,18 @@
      * @param input array containing ASN.1 encoded data.
      * @param lazyEvaluate true if parsing inside constructed objects can be delayed.
      */
-    public ASN1InputStream(
-        byte[] input,
-        boolean lazyEvaluate)
+    public ASN1InputStream(byte[] input, boolean lazyEvaluate)
     {
         this(new ByteArrayInputStream(input), input.length, lazyEvaluate);
     }
-    
+
     /**
      * Create an ASN1InputStream where no DER object will be longer than limit.
      * 
      * @param input stream containing ASN.1 encoded data.
      * @param limit maximum size of a DER encoded object.
      */
-    public ASN1InputStream(
-        InputStream input,
-        int         limit)
+    public ASN1InputStream(InputStream input, int limit)
     {
         this(input, limit, false);
     }
@@ -75,9 +68,7 @@
      * @param input stream containing ASN.1 encoded data.
      * @param lazyEvaluate true if parsing inside constructed objects can be delayed.
      */
-    public ASN1InputStream(
-        InputStream input,
-        boolean     lazyEvaluate)
+    public ASN1InputStream(InputStream input, boolean lazyEvaluate)
     {
         this(input, StreamUtil.findLimit(input), lazyEvaluate);
     }
@@ -90,15 +81,17 @@
      * @param limit maximum size of a DER encoded object.
      * @param lazyEvaluate true if parsing inside constructed objects can be delayed.
      */
-    public ASN1InputStream(
-        InputStream input,
-        int         limit,
-        boolean     lazyEvaluate)
+    public ASN1InputStream(InputStream input, int limit, boolean lazyEvaluate)
+    {
+        this(input, limit, lazyEvaluate, new byte[11][]);
+    }
+
+    private ASN1InputStream(InputStream input, int limit, boolean lazyEvaluate, byte[][] tmpBuffers)
     {
         super(input);
         this.limit = limit;
         this.lazyEvaluate = lazyEvaluate;
-        this.tmpBuffers = new byte[11][];
+        this.tmpBuffers = tmpBuffers;
     }
 
     int getLimit()
@@ -116,7 +109,7 @@
         byte[]  bytes)
         throws IOException
     {
-        if (Streams.readFully(this, bytes) != bytes.length)
+        if (Streams.readFully(this, bytes, 0, bytes.length) != bytes.length)
         {
             throw new EOFException("EOF encountered in middle of object");
         }
@@ -137,82 +130,57 @@
         int       length)
         throws IOException
     {
-        boolean isConstructed = (tag & CONSTRUCTED) != 0;
+        // TODO[asn1] Special-case zero length first?
 
         DefiniteLengthInputStream defIn = new DefiniteLengthInputStream(this, length, limit);
 
-        if ((tag & APPLICATION) != 0)
+        if (0 == (tag & FLAGS))
         {
-            return new DLApplicationSpecific(isConstructed, tagNo, defIn.toByteArray());
+            return createPrimitiveDERObject(tagNo, defIn, tmpBuffers);
         }
 
-        if ((tag & TAGGED) != 0)
+        int tagClass = tag & PRIVATE;
+        if (0 != tagClass)
         {
-            return new ASN1StreamParser(defIn).readTaggedObject(isConstructed, tagNo);
+            boolean isConstructed = (tag & CONSTRUCTED) != 0;
+            return readTaggedObjectDL(tagClass, tagNo, isConstructed, defIn);
         }
 
-        if (isConstructed)
+        switch (tagNo)
         {
-            // TODO There are other tags that may be constructed (e.g. BIT_STRING)
-            switch (tagNo)
+        case BIT_STRING:
+        {
+            return buildConstructedBitString(readVector(defIn));
+        }
+        case OCTET_STRING:
+        {
+            //
+            // yes, people actually do this...
+            //
+            return buildConstructedOctetString(readVector(defIn));
+        }
+        case SEQUENCE:
+        {
+            if (defIn.getRemaining() < 1)
             {
-                case OCTET_STRING:
-                    //
-                    // yes, people actually do this...
-                    //
-                    ASN1EncodableVector v = readVector(defIn);
-                    ASN1OctetString[] strings = new ASN1OctetString[v.size()];
-
-                    for (int i = 0; i != strings.length; i++)
-                    {
-                        ASN1Encodable asn1Obj = v.get(i);
-                        if (asn1Obj instanceof ASN1OctetString)
-                        {
-                            strings[i] = (ASN1OctetString)asn1Obj;
-                        }
-                        else
-                        {
-                            throw new ASN1Exception("unknown object encountered in constructed OCTET STRING: " + asn1Obj.getClass());
-                        }
-                    }
-
-                    return new BEROctetString(strings);
-                case SEQUENCE:
-                    if (lazyEvaluate)
-                    {
-                        return new LazyEncodedSequence(defIn.toByteArray());
-                    }
-                    else
-                    {
-                        return DLFactory.createSequence(readVector(defIn));   
-                    }
-                case SET:
-                    return DLFactory.createSet(readVector(defIn));
-                case EXTERNAL:
-                    return new DLExternal(readVector(defIn));
-                default:
-                    throw new IOException("unknown tag " + tagNo + " encountered");
+                return DLFactory.EMPTY_SEQUENCE;
+            }
+            else if (lazyEvaluate)
+            {
+                return new LazyEncodedSequence(defIn.toByteArray());
+            }
+            else
+            {
+                return DLFactory.createSequence(readVector(defIn));
             }
         }
-
-        return createPrimitiveDERObject(tagNo, defIn, tmpBuffers);
-    }
-
-    ASN1EncodableVector readVector(DefiniteLengthInputStream dIn) throws IOException
-    {
-        if (dIn.getRemaining() < 1)
-        {
-            return new ASN1EncodableVector(0);
+        case SET:
+            return DLFactory.createSet(readVector(defIn));
+        case EXTERNAL:
+            return DLFactory.createSequence(readVector(defIn)).toASN1External();
+        default:
+            throw new IOException("unknown tag " + tagNo + " encountered");
         }
-
-        ASN1InputStream subStream = new ASN1InputStream(dIn);
-        ASN1EncodableVector v = new ASN1EncodableVector();
-        ASN1Primitive p;
-        while ((p = subStream.readObject()) != null)
-        {
-            v.add(p);
-        }
-        return v;
     }
 
     public ASN1Primitive readObject()
@@ -229,55 +197,12 @@
             return null;
         }
 
-        //
-        // calculate tag number
-        //
         int tagNo = readTagNumber(this, tag);
-
-        boolean isConstructed = (tag & CONSTRUCTED) != 0;
-
-        //
-        // calculate length
-        //
         int length = readLength();
 
-        if (length < 0) // indefinite-length method
+        if (length >= 0)
         {
-            if (!isConstructed)
-            {
-                throw new IOException("indefinite-length primitive encoding encountered");
-            }
-
-            IndefiniteLengthInputStream indIn = new IndefiniteLengthInputStream(this, limit);
-            ASN1StreamParser sp = new ASN1StreamParser(indIn, limit);
-
-            if ((tag & APPLICATION) != 0)
-            {
-                return new BERApplicationSpecificParser(tagNo, sp).getLoadedObject();
-            }
-
-            if ((tag & TAGGED) != 0)
-            {
-                return new BERTaggedObjectParser(true, tagNo, sp).getLoadedObject();
-            }
-
-            // TODO There are other tags that may be constructed (e.g. BIT_STRING)
-            switch (tagNo)
-            {
-                case OCTET_STRING:
-                    return new BEROctetStringParser(sp).getLoadedObject();
-                case SEQUENCE:
-                    return new BERSequenceParser(sp).getLoadedObject();
-                case SET:
-                    return new BERSetParser(sp).getLoadedObject();
-                case EXTERNAL:
-                    return new DERExternalParser(sp).getLoadedObject();
-                default:
-                    throw new IOException("unknown BER object encountered");
-            }
-        }
-        else
-        {
+            // definite-length
             try
             {
                 return buildObject(tag, tagNo, length);
@@ -287,6 +212,124 @@
                 throw new ASN1Exception("corrupted stream detected", e);
             }
         }
+
+        // indefinite-length
+
+        if (0 == (tag & CONSTRUCTED))
+        {
+            throw new IOException("indefinite-length primitive encoding encountered");
+        }
+
+        IndefiniteLengthInputStream indIn = new IndefiniteLengthInputStream(this, limit);
+        ASN1StreamParser sp = new ASN1StreamParser(indIn, limit, tmpBuffers);
+
+        int tagClass = tag & PRIVATE;
+        if (0 != tagClass)
+        {
+            return sp.loadTaggedIL(tagClass, tagNo);
+        }
+
+        switch (tagNo)
+        {
+        case BIT_STRING:
+            return BERBitStringParser.parse(sp);
+        case OCTET_STRING:
+            return BEROctetStringParser.parse(sp);
+        case EXTERNAL:
+            // TODO[asn1] BERExternalParser
+            return DERExternalParser.parse(sp);
+        case SEQUENCE:
+            return BERSequenceParser.parse(sp);
+        case SET:
+            return BERSetParser.parse(sp);
+        default:
+            throw new IOException("unknown BER object encountered");
+        }
+    }
+
+    ASN1BitString buildConstructedBitString(ASN1EncodableVector contentsElements) throws IOException
+    {
+        ASN1BitString[] strings = new ASN1BitString[contentsElements.size()];
+
+        for (int i = 0; i != strings.length; i++)
+        {
+            ASN1Encodable asn1Obj = contentsElements.get(i);
+            if (asn1Obj instanceof ASN1BitString)
+            {
+                strings[i] = (ASN1BitString)asn1Obj;
+            }
+            else
+            {
+                throw new ASN1Exception(
+                    "unknown object encountered in constructed BIT STRING: " + asn1Obj.getClass());
+            }
+        }
+
+        // TODO Probably ought to be DLBitString
+        return new BERBitString(strings);
+    }
+
+    ASN1OctetString buildConstructedOctetString(ASN1EncodableVector contentsElements) throws IOException
+    {
+        ASN1OctetString[] strings = new ASN1OctetString[contentsElements.size()];
+
+        for (int i = 0; i != strings.length; i++)
+        {
+            ASN1Encodable asn1Obj = contentsElements.get(i);
+            if (asn1Obj instanceof ASN1OctetString)
+            {
+                strings[i] = (ASN1OctetString)asn1Obj;
+            }
+            else
+            {
+                throw new ASN1Exception(
+                    "unknown object encountered in constructed OCTET STRING: " + asn1Obj.getClass());
+            }
+        }
+
+        // TODO Probably ought to be DEROctetString (no DLOctetString available)
+        return new BEROctetString(strings);
+    }
+
+    ASN1Primitive readTaggedObjectDL(int tagClass, int tagNo, boolean constructed, DefiniteLengthInputStream defIn)
+        throws IOException
+    {
+        if (!constructed)
+        {
+            byte[] contentsOctets = defIn.toByteArray();
+            return ASN1TaggedObject.createPrimitive(tagClass, tagNo, contentsOctets);
+        }
+
+        ASN1EncodableVector contentsElements = readVector(defIn);
+        return ASN1TaggedObject.createConstructedDL(tagClass, tagNo, contentsElements);
+    }
+
+    ASN1EncodableVector readVector() throws IOException
+    {
+        ASN1Primitive p = readObject();
+        if (null == p)
+        {
+            return new ASN1EncodableVector(0);
+        }
+
+        ASN1EncodableVector v = new ASN1EncodableVector();
+        do
+        {
+            v.add(p);
+        }
+        while ((p = readObject()) != null);
+        return v;
+    }
+
+    ASN1EncodableVector readVector(DefiniteLengthInputStream defIn) throws IOException
+    {
+        int remaining = defIn.getRemaining();
+        if (remaining < 1)
+        {
+            return new ASN1EncodableVector(0);
+        }
+
+        return new ASN1InputStream(defIn, remaining, lazyEvaluate, tmpBuffers).readVector();
     }
 
     static int readTagNumber(InputStream s, int tag) 
@@ -299,32 +342,44 @@
         //
         if (tagNo == 0x1f)
         {
-            tagNo = 0;
-
             int b = s.read();
+            if (b < 31)
+            {
+                if (b < 0)
+                {
+                    throw new EOFException("EOF found inside tag value.");
+                }
+                throw new IOException("corrupted stream - high tag number < 31 found");
+            }
+
+            tagNo = b & 0x7f;
 
             // X.690-0207 8.1.2.4.2
             // "c) bits 7 to 1 of the first subsequent octet shall not all be zero."
-            if ((b & 0x7f) == 0) // Note: -1 will pass
+            if (0 == tagNo)
             {
                 throw new IOException("corrupted stream - invalid high tag number found");
             }
 
-            while ((b >= 0) && ((b & 0x80) != 0))
+            while ((b & 0x80) != 0)
             {
-                tagNo |= (b & 0x7f);
-                tagNo <<= 7;
-                b = s.read();
-            }
+                if ((tagNo >>> 24) != 0)
+                {
+                    throw new IOException("Tag number more than 31 bits");
+                }
 
-            if (b < 0)
-            {
-                throw new EOFException("EOF found inside tag value.");
+                tagNo <<= 7;
+
+                b = s.read();
+                if (b < 0)
+                {
+                    throw new EOFException("EOF found inside tag value.");
+                }
+
+                tagNo |= (b & 0x7f);
             }
-            
-            tagNo |= (b & 0x7f);
         }
-        
+
         return tagNo;
     }
 
@@ -332,48 +387,48 @@
         throws IOException
     {
         int length = s.read();
+        if (0 == (length >>> 7))
+        {
+            // definite-length short form 
+            return length;
+        }
+        if (0x80 == length)
+        {
+            // indefinite-length
+            return -1;
+        }
         if (length < 0)
         {
             throw new EOFException("EOF found when length expected");
         }
-
-        if (length == 0x80)
+        if (0xFF == length)
         {
-            return -1;      // indefinite-length encoding
+            throw new IOException("invalid long form definite-length 0xFF");
         }
 
-        if (length > 127)
+        int octetsCount = length & 0x7F, octetsPos = 0;
+
+        length = 0;
+        do
         {
-            int size = length & 0x7f;
-
-            // Note: The invalid long form "0xff" (see X.690 8.1.3.5c) will be caught here
-            if (size > 4)
+            int octet = s.read();
+            if (octet < 0)
             {
-                throw new IOException("DER length more than 4 bytes: " + size);
+                throw new EOFException("EOF found reading length");
             }
 
-            length = 0;
-            for (int i = 0; i < size; i++)
+            if ((length >>> 23) != 0)
             {
-                int next = s.read();
-
-                if (next < 0)
-                {
-                    throw new EOFException("EOF found reading length");
-                }
-
-                length = (length << 8) + next;
+                throw new IOException("long form definite-length more than 31 bits");
             }
 
-            if (length < 0)
-            {
-                throw new IOException("corrupted stream - negative length found");
-            }
+            length = (length << 8) + octet;
+        }
+        while (++octetsPos < octetsCount);
 
-            if (length >= limit && !isParsing)   // after all we must have read at least 1 byte
-            {
-                throw new IOException("corrupted stream - out of bounds length found: " + length + " >= " + limit);
-            }
+        if (length >= limit && !isParsing)   // after all we must have read at least 1 byte
+        {
+            throw new IOException("corrupted stream - out of bounds length found: " + length + " >= " + limit);
         }
 
         return length;
@@ -457,50 +512,79 @@
         byte[][] tmpBuffers)
         throws IOException
     {
-        switch (tagNo)
+        /*
+         * TODO[asn1] Lookup the universal type object and get it to parse the stream directly (possibly with
+         * access to a single temporary buffer replacing tmpBuffers).
+         */
+        try
         {
+            switch (tagNo)
+            {
             case BIT_STRING:
-                return ASN1BitString.fromInputStream(defIn.getRemaining(), defIn);
+                return ASN1BitString.createPrimitive(defIn.toByteArray());
             case BMP_STRING:
-                return new DERBMPString(getBMPCharBuffer(defIn));
+                return ASN1BMPString.createPrimitive(getBMPCharBuffer(defIn));
             case BOOLEAN:
-                return ASN1Boolean.fromOctetString(getBuffer(defIn, tmpBuffers));
+                return ASN1Boolean.createPrimitive(getBuffer(defIn, tmpBuffers));
             case ENUMERATED:
-                return ASN1Enumerated.fromOctetString(getBuffer(defIn, tmpBuffers));
-            case GENERALIZED_TIME:
-                return new ASN1GeneralizedTime(defIn.toByteArray());
+                // TODO Ideally only clone if we used a buffer
+                return ASN1Enumerated.createPrimitive(getBuffer(defIn, tmpBuffers), true);
             case GENERAL_STRING:
-                return new DERGeneralString(defIn.toByteArray());
-            case IA5_STRING:
-                return new DERIA5String(defIn.toByteArray());
-            case INTEGER:
-                return new ASN1Integer(defIn.toByteArray(), false);
-            case NULL:
-                return DERNull.INSTANCE;   // actual content is ignored (enforce 0 length?)
-            case NUMERIC_STRING:
-                return new DERNumericString(defIn.toByteArray());
-            case OBJECT_IDENTIFIER:
-                return ASN1ObjectIdentifier.fromOctetString(getBuffer(defIn, tmpBuffers));
-            case OCTET_STRING:
-                return new DEROctetString(defIn.toByteArray());
-            case PRINTABLE_STRING:
-                return new DERPrintableString(defIn.toByteArray());
-            case T61_STRING:
-                return new DERT61String(defIn.toByteArray());
-            case UNIVERSAL_STRING:
-                return new DERUniversalString(defIn.toByteArray());
-            case UTC_TIME:
-                return new ASN1UTCTime(defIn.toByteArray());
-            case UTF8_STRING:
-                return new DERUTF8String(defIn.toByteArray());
-            case VISIBLE_STRING:
-                return new DERVisibleString(defIn.toByteArray());
+                return ASN1GeneralString.createPrimitive(defIn.toByteArray());
+            case GENERALIZED_TIME:
+                return ASN1GeneralizedTime.createPrimitive(defIn.toByteArray());
             case GRAPHIC_STRING:
-                return new DERGraphicString(defIn.toByteArray());
+                return ASN1GraphicString.createPrimitive(defIn.toByteArray());
+            case IA5_STRING:
+                return ASN1IA5String.createPrimitive(defIn.toByteArray());
+            case INTEGER:
+                return ASN1Integer.createPrimitive(defIn.toByteArray());
+            case NULL:
+                return ASN1Null.createPrimitive(defIn.toByteArray());
+            case NUMERIC_STRING:
+                return ASN1NumericString.createPrimitive(defIn.toByteArray());
+            case OBJECT_DESCRIPTOR:
+                return ASN1ObjectDescriptor.createPrimitive(defIn.toByteArray());
+            case OBJECT_IDENTIFIER:
+                // TODO Ideally only clone if we used a buffer
+                return ASN1ObjectIdentifier.createPrimitive(getBuffer(defIn, tmpBuffers), true);
+            case OCTET_STRING:
+                return ASN1OctetString.createPrimitive(defIn.toByteArray());
+            case PRINTABLE_STRING:
+                return ASN1PrintableString.createPrimitive(defIn.toByteArray());
+            case RELATIVE_OID:
+                return ASN1RelativeOID.createPrimitive(defIn.toByteArray(), false);
+            case T61_STRING:
+                return ASN1T61String.createPrimitive(defIn.toByteArray());
+            case UNIVERSAL_STRING:
+                return ASN1UniversalString.createPrimitive(defIn.toByteArray());
+            case UTC_TIME:
+                return ASN1UTCTime.createPrimitive(defIn.toByteArray());
+            case UTF8_STRING:
+                return ASN1UTF8String.createPrimitive(defIn.toByteArray());
             case VIDEOTEX_STRING:
-                return new DERVideotexString(defIn.toByteArray());
+                return ASN1VideotexString.createPrimitive(defIn.toByteArray());
+            case VISIBLE_STRING:
+                return ASN1VisibleString.createPrimitive(defIn.toByteArray());
+            case TIME:
+            case DATE:
+            case TIME_OF_DAY:
+            case DATE_TIME:
+            case DURATION:
+            case OBJECT_IDENTIFIER_IRI:
+            case RELATIVE_OID_IRI:
+                throw new IOException("unsupported tag " + tagNo + " encountered");
             default:
                 throw new IOException("unknown tag " + tagNo + " encountered");
+            }
+        }
+        catch (IllegalArgumentException e)
+        {
+            throw new ASN1Exception(e.getMessage(), e);
+        }
+        catch (IllegalStateException e)
+        {
+            throw new ASN1Exception(e.getMessage(), e);
         }
     }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/ASN1Integer.java b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1Integer.java
index baad01c..cfc4049 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/ASN1Integer.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1Integer.java
@@ -12,6 +12,14 @@
 public class ASN1Integer
     extends ASN1Primitive
 {
+    static final ASN1UniversalType TYPE = new ASN1UniversalType(ASN1Integer.class, BERTags.INTEGER)
+    {
+        ASN1Primitive fromImplicitPrimitive(DEROctetString octetString)
+        {
+            return createPrimitive(octetString.getOctets());
+        }
+    };
+
     static final int SIGN_EXT_SIGNED = 0xFFFFFFFF;
     static final int SIGN_EXT_UNSIGNED = 0xFF;
 
@@ -37,7 +45,7 @@
         {
             try
             {
-                return (ASN1Integer)fromByteArray((byte[])obj);
+                return (ASN1Integer)TYPE.fromByteArray((byte[])obj);
             }
             catch (Exception e)
             {
@@ -51,27 +59,16 @@
     /**
      * Return an Integer from a tagged object.
      *
-     * @param obj      the tagged object holding the object we want
+     * @param taggedObject the tagged object holding the object we want
      * @param explicit true if the object is meant to be explicitly
      *                 tagged false otherwise.
      * @return an ASN1Integer instance.
      * @throws IllegalArgumentException if the tagged object cannot
      * be converted.
      */
-    public static ASN1Integer getInstance(
-        ASN1TaggedObject obj,
-        boolean explicit)
+    public static ASN1Integer getInstance(ASN1TaggedObject taggedObject, boolean explicit)
     {
-        ASN1Primitive o = obj.getObject();
-
-        if (explicit || o instanceof ASN1Integer)
-        {
-            return getInstance(o);
-        }
-        else
-        {
-            return new ASN1Integer(ASN1OctetString.getInstance(o).getOctets());
-        }
+        return (ASN1Integer)TYPE.getContextInstance(taggedObject, explicit);
     }
 
     /**
@@ -150,6 +147,18 @@
         return new BigInteger(bytes);
     }
 
+    public boolean hasValue(int x)
+    {
+        return (bytes.length - start) <= 4
+            && intValue(bytes, start, SIGN_EXT_SIGNED) == x;
+    }
+
+    public boolean hasValue(long x)
+    {
+        return (bytes.length - start) <= 8
+            && longValue(bytes, start, SIGN_EXT_SIGNED) == x;
+    }
+
     public boolean hasValue(BigInteger x)
     {
         return null != x
@@ -191,19 +200,19 @@
         return longValue(bytes, start, SIGN_EXT_SIGNED);
     }
 
-    boolean isConstructed()
+    boolean encodeConstructed()
     {
         return false;
     }
 
-    int encodedLength()
+    int encodedLength(boolean withTag)
     {
-        return 1 + StreamUtil.calculateBodyLength(bytes.length) + bytes.length;
+        return ASN1OutputStream.getLengthOfEncodingDL(withTag, bytes.length);
     }
 
     void encode(ASN1OutputStream out, boolean withTag) throws IOException
     {
-        out.writeEncoded(withTag, BERTags.INTEGER, bytes);
+        out.writeEncodingDL(withTag, BERTags.INTEGER, bytes);
     }
 
     public int hashCode()
@@ -228,6 +237,11 @@
         return getValue().toString();
     }
 
+    static ASN1Integer createPrimitive(byte[] contents)
+    {
+        return new ASN1Integer(contents, false);
+    }
+
     static int intValue(byte[] bytes, int start, int signExt)
     {
         int length = bytes.length;
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/ASN1Null.java b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1Null.java
index c181527..bad2347 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/ASN1Null.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1Null.java
@@ -1,6 +1,3 @@
-/***************************************************************/
-/******    DO NOT EDIT THIS CLASS bc-java SOURCE FILE     ******/
-/***************************************************************/
 package org.bouncycastle.asn1;
 
 import java.io.IOException;
@@ -11,10 +8,13 @@
 public abstract class ASN1Null
     extends ASN1Primitive
 {
-    ASN1Null()
+    static final ASN1UniversalType TYPE = new ASN1UniversalType(ASN1Null.class, BERTags.NULL)
     {
-
-    }
+        ASN1Primitive fromImplicitPrimitive(DEROctetString octetString)
+        {
+            return createPrimitive(octetString.getOctets());
+        }
+    };
 
     /**
      * Return an instance of ASN.1 NULL from the passed in object.
@@ -42,21 +42,26 @@
         {
             try
             {
-                return ASN1Null.getInstance(ASN1Primitive.fromByteArray((byte[])o));
+                return (ASN1Null)TYPE.fromByteArray((byte[])o);
             }
             catch (IOException e)
             {
                 throw new IllegalArgumentException("failed to construct NULL from byte[]: " + e.getMessage());
             }
-            catch (ClassCastException e)
-            {
-                throw new IllegalArgumentException("unknown object in getInstance(): " + o.getClass().getName());
-            }
         }
 
         return null;
     }
 
+    public static ASN1Null getInstance(ASN1TaggedObject taggedObject, boolean explicit)
+    {
+        return (ASN1Null)TYPE.getContextInstance(taggedObject, explicit);
+    }
+
+    ASN1Null()
+    {
+    }
+
     public int hashCode()
     {
         return -1;
@@ -73,10 +78,17 @@
         return true;
     }
 
-    abstract void encode(ASN1OutputStream out, boolean withTag) throws IOException;
-
     public String toString()
     {
          return "NULL";
     }
+
+    static ASN1Null createPrimitive(byte[] contents)
+    {
+        if (0 != contents.length)
+        {
+            throw new IllegalStateException("malformed NULL encoding encountered");
+        }
+        return DERNull.INSTANCE;
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/ASN1NumericString.java b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1NumericString.java
new file mode 100644
index 0000000..3609aca
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1NumericString.java
@@ -0,0 +1,213 @@
+package org.bouncycastle.asn1;
+
+import java.io.IOException;
+
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.Strings;
+
+/**
+ * NumericString object - this is an ascii string of characters {0,1,2,3,4,5,6,7,8,9, }.
+ * ASN.1 NUMERIC-STRING object.
+ * <p>
+ * This is an ASCII string of characters {0,1,2,3,4,5,6,7,8,9} + space.
+ * <p>
+ * See X.680 section 37.2.
+ * <p>
+ * Explicit character set escape sequences are not allowed.
+ */
+public abstract class ASN1NumericString
+    extends ASN1Primitive
+    implements ASN1String
+{
+    static final ASN1UniversalType TYPE = new ASN1UniversalType(ASN1NumericString.class, BERTags.NUMERIC_STRING)
+    {
+        ASN1Primitive fromImplicitPrimitive(DEROctetString octetString)
+        {
+            return createPrimitive(octetString.getOctets());
+        }
+    };
+
+    /**
+     * Return a Numeric string from the passed in object
+     *
+     * @param obj an ASN1NumericString or an object that can be converted into one.
+     * @exception IllegalArgumentException if the object cannot be converted.
+     * @return an ASN1NumericString instance, or null
+     */
+    public static ASN1NumericString getInstance(Object obj)
+    {
+        if (obj == null || obj instanceof ASN1NumericString)
+        {
+            return (ASN1NumericString)obj;
+        }
+        if (obj instanceof ASN1Encodable)
+        {
+            ASN1Primitive primitive = ((ASN1Encodable)obj).toASN1Primitive();
+            if (primitive instanceof ASN1NumericString)
+            {
+                return (ASN1NumericString)primitive;
+            }
+        }
+        if (obj instanceof byte[])
+        {
+            try
+            {
+                return (ASN1NumericString)TYPE.fromByteArray((byte[])obj);
+            }
+            catch (Exception e)
+            {
+                throw new IllegalArgumentException("encoding error in getInstance: " + e.toString());
+            }
+        }
+
+        throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
+    }
+
+    /**
+     * Return an Numeric String from a tagged object.
+     *
+     * @param taggedObject the tagged object holding the object we want
+     * @param explicit     true if the object is meant to be explicitly tagged false
+     *                     otherwise.
+     * @exception IllegalArgumentException if the tagged object cannot be converted.
+     * @return an ASN1NumericString instance, or null.
+     */
+    public static ASN1NumericString getInstance(ASN1TaggedObject taggedObject, boolean explicit)
+    {
+        return (ASN1NumericString)TYPE.getContextInstance(taggedObject, explicit);
+    }
+
+    final byte[] contents;
+
+    /**
+     * Constructor with optional validation.
+     *
+     * @param string the base string to wrap.
+     * @param validate whether or not to check the string.
+     * @throws IllegalArgumentException if validate is true and the string
+     * contains characters that should not be in a NumericString.
+     */
+    ASN1NumericString(String string, boolean validate)
+    {
+        if (validate && !isNumericString(string))
+        {
+            throw new IllegalArgumentException("string contains illegal characters");
+        }
+
+        this.contents = Strings.toByteArray(string);
+    }
+
+    ASN1NumericString(byte[] contents, boolean clone)
+    {
+        this.contents = clone ? Arrays.clone(contents) : contents;
+    }
+
+    public final String getString()
+    {
+        return Strings.fromByteArray(contents);
+    }
+
+    public String toString()
+    {
+        return getString();
+    }
+
+    public final byte[] getOctets()
+    {
+        return Arrays.clone(contents);
+    }
+
+    final boolean encodeConstructed()
+    {
+        return false;
+    }
+
+    final int encodedLength(boolean withTag)
+    {
+        return ASN1OutputStream.getLengthOfEncodingDL(withTag, contents.length);
+    }
+
+    final void encode(ASN1OutputStream out, boolean withTag) throws IOException
+    {
+        out.writeEncodingDL(withTag, BERTags.NUMERIC_STRING, contents);
+    }
+
+    public final int hashCode()
+    {
+        return Arrays.hashCode(contents);
+    }
+
+    final boolean asn1Equals(ASN1Primitive other)
+    {
+        if (!(other instanceof ASN1NumericString))
+        {
+            return false;
+        }
+
+        ASN1NumericString that = (ASN1NumericString)other;
+
+        return Arrays.areEqual(this.contents, that.contents);
+    }
+
+    /**
+     * Return true if the string can be represented as a NumericString ('0'..'9', ' ')
+     *
+     * @param str string to validate.
+     * @return true if numeric, false otherwise.
+     */
+    public static boolean isNumericString(String str)
+    {
+        for (int i = str.length() - 1; i >= 0; i--)
+        {
+            char ch = str.charAt(i);
+
+            if (ch > 0x007f)
+            {
+                return false;
+            }
+
+            if (('0' <= ch && ch <= '9') || ch == ' ')
+            {
+                continue;
+            }
+
+            return false;
+        }
+
+        return true;
+    }
+
+    static boolean isNumericString(byte[] contents)
+    {
+        for (int i = 0; i < contents.length; ++i)
+        {
+            switch (contents[i])
+            {
+            case 0x20:
+            case 0x30:
+            case 0x31:
+            case 0x32:
+            case 0x33:
+            case 0x34:
+            case 0x35:
+            case 0x36:
+            case 0x37:
+            case 0x38:
+            case 0x39:
+                break;
+            default:
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    static ASN1NumericString createPrimitive(byte[] contents)
+    {
+        // TODO Validation - sort out exception types
+//        if (!isNumericString(contents))
+
+        return new DERNumericString(contents, false);
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/ASN1Object.java b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1Object.java
index 0af2cdd..d099008 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/ASN1Object.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1Object.java
@@ -14,12 +14,12 @@
 {
     public void encodeTo(OutputStream output) throws IOException
     {
-        ASN1OutputStream.create(output).writeObject(this);
+        toASN1Primitive().encodeTo(output);
     }
 
     public void encodeTo(OutputStream output, String encoding) throws IOException
     {
-        ASN1OutputStream.create(output, encoding).writeObject(this);
+        toASN1Primitive().encodeTo(output, encoding);
     }
 
     /**
@@ -31,7 +31,7 @@
     public byte[] getEncoded() throws IOException
     {
         ByteArrayOutputStream bOut = new ByteArrayOutputStream();
-        encodeTo(bOut);
+        toASN1Primitive().encodeTo(bOut);
         return bOut.toByteArray();
     }
 
@@ -45,7 +45,7 @@
     public byte[] getEncoded(String encoding) throws IOException
     {
         ByteArrayOutputStream bOut = new ByteArrayOutputStream();
-        encodeTo(bOut, encoding);
+        toASN1Primitive().encodeTo(bOut, encoding);
         return bOut.toByteArray();
     }
 
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/ASN1ObjectDescriptor.java b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1ObjectDescriptor.java
new file mode 100644
index 0000000..5323d5c
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1ObjectDescriptor.java
@@ -0,0 +1,141 @@
+package org.bouncycastle.asn1;
+
+import java.io.IOException;
+
+public final class ASN1ObjectDescriptor
+    extends ASN1Primitive
+{
+    static final ASN1UniversalType TYPE = new ASN1UniversalType(ASN1ObjectDescriptor.class, BERTags.OBJECT_DESCRIPTOR)
+    {
+        ASN1Primitive fromImplicitPrimitive(DEROctetString octetString)
+        {
+            return new ASN1ObjectDescriptor(
+                (ASN1GraphicString)ASN1GraphicString.TYPE.fromImplicitPrimitive(octetString));
+        }
+
+        ASN1Primitive fromImplicitConstructed(ASN1Sequence sequence)
+        {
+            return new ASN1ObjectDescriptor(
+                (ASN1GraphicString)ASN1GraphicString.TYPE.fromImplicitConstructed(sequence));
+        }
+    };
+
+    /**
+     * Return an ObjectDescriptor from the passed in object.
+     *
+     * @param obj an ASN1ObjectDescriptor or an object that can be converted into one.
+     * @exception IllegalArgumentException if the object cannot be converted.
+     * @return an ASN1ObjectDescriptor instance, or null.
+     */
+    public static ASN1ObjectDescriptor getInstance(Object obj)
+    {
+        if (obj == null || obj instanceof ASN1ObjectDescriptor)
+        {
+            return (ASN1ObjectDescriptor)obj;
+        }
+        else if (obj instanceof ASN1Encodable)
+        {
+            ASN1Primitive primitive = ((ASN1Encodable)obj).toASN1Primitive();
+            if (primitive instanceof ASN1ObjectDescriptor)
+            {
+                return (ASN1ObjectDescriptor)primitive;
+            }
+        }
+        else if (obj instanceof byte[])
+        {
+            try
+            {
+                return (ASN1ObjectDescriptor)TYPE.fromByteArray((byte[])obj);
+            }
+            catch (IOException e)
+            {
+                throw new IllegalArgumentException("failed to construct object descriptor from byte[]: " + e.getMessage());
+            }
+        }
+
+        throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
+    }
+
+    /**
+     * Return an ObjectDescriptor from a tagged object.
+     *
+     * @param taggedObject the tagged object holding the object we want.
+     * @param explicit     true if the object is meant to be explicitly tagged,
+     *                     false otherwise.
+     * @exception IllegalArgumentException if the tagged object cannot be converted.
+     * @return an ASN1ObjectDescriptor instance, or null.
+     */
+    public static ASN1ObjectDescriptor getInstance(ASN1TaggedObject taggedObject, boolean explicit)
+    {
+        return (ASN1ObjectDescriptor)TYPE.getContextInstance(taggedObject, explicit);
+    }
+
+    private final ASN1GraphicString baseGraphicString;
+
+    public ASN1ObjectDescriptor(ASN1GraphicString baseGraphicString)
+    {
+        if (null == baseGraphicString)
+        {
+            throw new NullPointerException("'baseGraphicString' cannot be null");
+        }
+
+        this.baseGraphicString = baseGraphicString;
+    }
+
+    public ASN1GraphicString getBaseGraphicString()
+    {
+        return baseGraphicString;
+    }
+
+    boolean encodeConstructed()
+    {
+        return false;
+    }
+
+    int encodedLength(boolean withTag)
+    {
+        return baseGraphicString.encodedLength(withTag);
+    }
+
+    void encode(ASN1OutputStream out, boolean withTag) throws IOException
+    {
+        out.writeIdentifier(withTag, BERTags.OBJECT_DESCRIPTOR);
+        baseGraphicString.encode(out, false);
+    }
+
+    ASN1Primitive toDERObject()
+    {
+        ASN1GraphicString der = (ASN1GraphicString)baseGraphicString.toDERObject();
+
+        return der == baseGraphicString ? this : new ASN1ObjectDescriptor(der);
+    }
+
+    ASN1Primitive toDLObject()
+    {
+        ASN1GraphicString dl = (ASN1GraphicString)baseGraphicString.toDLObject();
+
+        return dl == baseGraphicString ? this : new ASN1ObjectDescriptor(dl);
+    }
+
+    boolean asn1Equals(ASN1Primitive other)
+    {
+        if (!(other instanceof ASN1ObjectDescriptor))
+        {
+            return false;
+        }
+
+        ASN1ObjectDescriptor that = (ASN1ObjectDescriptor)other;
+
+        return this.baseGraphicString.asn1Equals(that.baseGraphicString);
+    }
+
+    public int hashCode()
+    {
+        return ~baseGraphicString.hashCode();
+    }
+
+    static ASN1ObjectDescriptor createPrimitive(byte[] contents)
+    {
+        return new ASN1ObjectDescriptor(ASN1GraphicString.createPrimitive(contents));
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/ASN1ObjectIdentifier.java b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1ObjectIdentifier.java
index 8373dca..b4bb0d2 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/ASN1ObjectIdentifier.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1ObjectIdentifier.java
@@ -14,9 +14,18 @@
 public class ASN1ObjectIdentifier
     extends ASN1Primitive
 {
-    private final String identifier;
+    static final ASN1UniversalType TYPE = new ASN1UniversalType(ASN1ObjectIdentifier.class, BERTags.OBJECT_IDENTIFIER)
+    {
+        ASN1Primitive fromImplicitPrimitive(DEROctetString octetString)
+        {
+            return createPrimitive(octetString.getOctets(), false);
+        }
+    };
 
-    private byte[] body;
+    public static ASN1ObjectIdentifier fromContents(byte[] contents)
+    {
+        return createPrimitive(contents, true);
+    }
 
     /**
      * Return an OID from the passed in object
@@ -25,30 +34,25 @@
      * @return an ASN1ObjectIdentifier instance, or null.
      * @throws IllegalArgumentException if the object cannot be converted.
      */
-    public static ASN1ObjectIdentifier getInstance(
-        Object obj)
+    public static ASN1ObjectIdentifier getInstance(Object obj)
     {
         if (obj == null || obj instanceof ASN1ObjectIdentifier)
         {
             return (ASN1ObjectIdentifier)obj;
         }
-
-        if (obj instanceof ASN1Encodable)
+        else if (obj instanceof ASN1Encodable)
         {
             ASN1Primitive primitive = ((ASN1Encodable)obj).toASN1Primitive();
-
             if (primitive instanceof ASN1ObjectIdentifier)
             {
                 return (ASN1ObjectIdentifier)primitive;
             }
         }
-
-        if (obj instanceof byte[])
+        else if (obj instanceof byte[])
         {
-            byte[] enc = (byte[])obj;
             try
             {
-                return (ASN1ObjectIdentifier)fromByteArray(enc);
+                return (ASN1ObjectIdentifier)TYPE.fromByteArray((byte[])obj);
             }
             catch (IOException e)
             {
@@ -62,47 +66,60 @@
     /**
      * Return an OBJECT IDENTIFIER from a tagged object.
      *
-     * @param obj      the tagged object holding the object we want
+     * @param taggedObject      the tagged object holding the object we want
      * @param explicit true if the object is meant to be explicitly
      *                 tagged false otherwise.
      * @return an ASN1ObjectIdentifier instance, or null.
      * @throws IllegalArgumentException if the tagged object cannot
      * be converted.
      */
-    public static ASN1ObjectIdentifier getInstance(
-        ASN1TaggedObject obj,
-        boolean explicit)
+    public static ASN1ObjectIdentifier getInstance(ASN1TaggedObject taggedObject, boolean explicit)
     {
-        ASN1Primitive o = obj.getObject();
+        /*
+         * TODO[asn1] This block here is for backward compatibility, but should eventually be removed.
+         * 
+         * - see https://github.com/bcgit/bc-java/issues/1015
+         */
+        if (!explicit && !taggedObject.isParsed() && BERTags.CONTEXT_SPECIFIC == taggedObject.getTagClass())
+        {
+            ASN1Primitive base = taggedObject.getBaseObject().toASN1Primitive();
+            if (!(base instanceof ASN1ObjectIdentifier))
+            {
+                return fromContents(ASN1OctetString.getInstance(base).getOctets());
+            }
+        }
 
-        if (explicit || o instanceof ASN1ObjectIdentifier)
-        {
-            return getInstance(o);
-        }
-        else
-        {
-            return ASN1ObjectIdentifier.fromOctetString(ASN1OctetString.getInstance(o).getOctets());
-        }
+        return (ASN1ObjectIdentifier)TYPE.getContextInstance(taggedObject, explicit);
     }
 
-    private static final long LONG_LIMIT = (Long.MAX_VALUE >> 7) - 0x7f;
+    private static final long LONG_LIMIT = (Long.MAX_VALUE >> 7) - 0x7F;
 
-    ASN1ObjectIdentifier(
-        byte[] bytes)
+    private static final ConcurrentMap<OidHandle, ASN1ObjectIdentifier> pool =
+        new ConcurrentHashMap<OidHandle, ASN1ObjectIdentifier>();
+
+    private final String identifier;
+    private byte[] contents;
+
+    ASN1ObjectIdentifier(byte[] contents, boolean clone)
     {
-        StringBuffer objId = new StringBuffer();
+        if (contents.length == 0)
+        {
+            throw new IllegalArgumentException("empty OBJECT IDENTIFIER with no sub-identifiers");
+        }
+
+        StringBuilder objId = new StringBuilder();
         long value = 0;
         BigInteger bigValue = null;
         boolean first = true;
 
-        for (int i = 0; i != bytes.length; i++)
+        for (int i = 0; i != contents.length; i++)
         {
-            int b = bytes[i] & 0xff;
+            int b = contents[i] & 0xff;
 
             if (value <= LONG_LIMIT)
             {
-                value += (b & 0x7f);
-                if ((b & 0x80) == 0)             // end of number reached
+                value += b & 0x7F;
+                if ((b & 0x80) == 0)
                 {
                     if (first)
                     {
@@ -138,7 +155,7 @@
                 {
                     bigValue = BigInteger.valueOf(value);
                 }
-                bigValue = bigValue.or(BigInteger.valueOf(b & 0x7f));
+                bigValue = bigValue.or(BigInteger.valueOf(b & 0x7F));
                 if ((b & 0x80) == 0)
                 {
                     if (first)
@@ -162,7 +179,7 @@
 
         // Android-changed: Intern the identifier so there aren't hundreds of duplicates in practice.
         this.identifier = objId.toString().intern();
-        this.body = Arrays.clone(bytes);
+        this.contents = clone ? Arrays.clone(contents) : contents;
     }
 
     /**
@@ -194,7 +211,7 @@
      */
     ASN1ObjectIdentifier(ASN1ObjectIdentifier oid, String branchID)
     {
-        if (!isValidBranchID(branchID, 0))
+        if (!ASN1RelativeOID.isValidIdentifier(branchID, 0))
         {
             throw new IllegalArgumentException("string " + branchID + " not a valid OID branch");
         }
@@ -235,44 +252,6 @@
         return id.length() > stemId.length() && id.charAt(stemId.length()) == '.' && id.startsWith(stemId);
     }
 
-    private void writeField(
-        ByteArrayOutputStream out,
-        long fieldValue)
-    {
-        byte[] result = new byte[9];
-        int pos = 8;
-        result[pos] = (byte)((int)fieldValue & 0x7f);
-        while (fieldValue >= (1L << 7))
-        {
-            fieldValue >>= 7;
-            result[--pos] = (byte)((int)fieldValue & 0x7f | 0x80);
-        }
-        out.write(result, pos, 9 - pos);
-    }
-
-    private void writeField(
-        ByteArrayOutputStream out,
-        BigInteger fieldValue)
-    {
-        int byteCount = (fieldValue.bitLength() + 6) / 7;
-        if (byteCount == 0)
-        {
-            out.write(0);
-        }
-        else
-        {
-            BigInteger tmpValue = fieldValue;
-            byte[] tmp = new byte[byteCount];
-            for (int i = byteCount - 1; i >= 0; i--)
-            {
-                tmp[i] = (byte)((tmpValue.intValue() & 0x7f) | 0x80);
-                tmpValue = tmpValue.shiftRight(7);
-            }
-            tmp[byteCount - 1] &= 0x7f;
-            out.write(tmp, 0, tmp.length);
-        }
-    }
-
     private void doOutput(ByteArrayOutputStream aOut)
     {
         OIDTokenizer tok = new OIDTokenizer(identifier);
@@ -281,11 +260,11 @@
         String secondToken = tok.nextToken();
         if (secondToken.length() <= 18)
         {
-            writeField(aOut, first + Long.parseLong(secondToken));
+            ASN1RelativeOID.writeField(aOut, first + Long.parseLong(secondToken));
         }
         else
         {
-            writeField(aOut, new BigInteger(secondToken).add(BigInteger.valueOf(first)));
+            ASN1RelativeOID.writeField(aOut, new BigInteger(secondToken).add(BigInteger.valueOf(first)));
         }
 
         while (tok.hasMoreTokens())
@@ -293,45 +272,42 @@
             String token = tok.nextToken();
             if (token.length() <= 18)
             {
-                writeField(aOut, Long.parseLong(token));
+                ASN1RelativeOID.writeField(aOut, Long.parseLong(token));
             }
             else
             {
-                writeField(aOut, new BigInteger(token));
+                ASN1RelativeOID.writeField(aOut, new BigInteger(token));
             }
         }
     }
 
-    private synchronized byte[] getBody()
+    private synchronized byte[] getContents()
     {
-        if (body == null)
+        if (contents == null)
         {
             ByteArrayOutputStream bOut = new ByteArrayOutputStream();
 
             doOutput(bOut);
 
-            body = bOut.toByteArray();
+            contents = bOut.toByteArray();
         }
 
-        return body;
+        return contents;
     }
 
-    boolean isConstructed()
+    boolean encodeConstructed()
     {
         return false;
     }
 
-    int encodedLength()
-        throws IOException
+    int encodedLength(boolean withTag)
     {
-        int length = getBody().length;
-
-        return 1 + StreamUtil.calculateBodyLength(length) + length;
+        return ASN1OutputStream.getLengthOfEncodingDL(withTag, getContents().length);
     }
 
     void encode(ASN1OutputStream out, boolean withTag) throws IOException
     {
-        out.writeEncoded(withTag, BERTags.OBJECT_IDENTIFIER, getBody());
+        out.writeEncodingDL(withTag, BERTags.OBJECT_IDENTIFIER, getContents());
     }
 
     public int hashCode()
@@ -360,45 +336,6 @@
         return getId();
     }
 
-    private static boolean isValidBranchID(
-        String branchID, int start)
-    {
-        int digitCount = 0;
-
-        int pos = branchID.length();
-        while (--pos >= start)
-        {
-            char ch = branchID.charAt(pos);
-
-            if (ch == '.')
-            {
-                if (0 == digitCount
-                    || (digitCount > 1 && branchID.charAt(pos + 1) == '0'))
-                {
-                    return false;
-                }
-
-                digitCount = 0;
-            }
-            else if ('0' <= ch && ch <= '9')
-            {
-                ++digitCount;
-            }
-            else
-            {
-                return false;
-            }
-        }
-
-        if (0 == digitCount
-            || (digitCount > 1 && branchID.charAt(pos + 1) == '0'))
-        {
-            return false;
-        }
-
-        return true;
-    }
-
     private static boolean isValidIdentifier(
         String identifier)
     {
@@ -413,7 +350,7 @@
             return false;
         }
 
-        return isValidBranchID(identifier, 2);
+        return ASN1RelativeOID.isValidIdentifier(identifier, 2);
     }
 
     /**
@@ -428,30 +365,35 @@
      */
     public ASN1ObjectIdentifier intern()
     {
-        final OidHandle hdl = new OidHandle(getBody());
+        final OidHandle hdl = new OidHandle(getContents());
         ASN1ObjectIdentifier oid = pool.get(hdl);
         if (oid == null)
         {
-            oid = pool.putIfAbsent(hdl, this);
-            if (oid == null)
+            synchronized (pool)
             {
-                oid = this;
+                if (!pool.containsKey(hdl))
+                {
+                    pool.put(hdl, this);
+                    return this;
+                }
+                else
+                {
+                    return pool.get(hdl);
+                }
             }
         }
         return oid;
     }
 
-    private static final ConcurrentMap<OidHandle, ASN1ObjectIdentifier> pool = new ConcurrentHashMap<OidHandle, ASN1ObjectIdentifier>();
-
     private static class OidHandle
     {
         private final int key;
-        private final byte[] enc;
+        private final byte[] contents;
 
-        OidHandle(byte[] enc)
+        OidHandle(byte[] contents)
         {
-            this.key = Arrays.hashCode(enc);
-            this.enc = enc;
+            this.key = Arrays.hashCode(contents);
+            this.contents = contents;
         }
 
         public int hashCode()
@@ -463,20 +405,20 @@
         {
             if (o instanceof OidHandle)
             {
-                return Arrays.areEqual(enc, ((OidHandle)o).enc);
+                return Arrays.areEqual(contents, ((OidHandle)o).contents);
             }
 
             return false;
         }
     }
 
-    static ASN1ObjectIdentifier fromOctetString(byte[] enc)
+    static ASN1ObjectIdentifier createPrimitive(byte[] contents, boolean clone)
     {
-        final OidHandle hdl = new OidHandle(enc);
+        final OidHandle hdl = new OidHandle(contents);
         ASN1ObjectIdentifier oid = pool.get(hdl);
         if (oid == null)
         {
-            return new ASN1ObjectIdentifier(enc);
+            return new ASN1ObjectIdentifier(contents, clone);
         }
         return oid;
     }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/ASN1OctetString.java b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1OctetString.java
index 59c6f34..896f56f 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/ASN1OctetString.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1OctetString.java
@@ -100,7 +100,18 @@
     extends ASN1Primitive
     implements ASN1OctetStringParser
 {
-    byte[]  string;
+    static final ASN1UniversalType TYPE = new ASN1UniversalType(ASN1OctetString.class, BERTags.OCTET_STRING)
+    {
+        ASN1Primitive fromImplicitPrimitive(DEROctetString octetString)
+        {
+            return octetString;
+        }
+
+        ASN1Primitive fromImplicitConstructed(ASN1Sequence sequence)
+        {
+            return sequence.toASN1OctetString();
+        }
+    };
 
     /**
      * return an Octet String from a tagged object.
@@ -111,70 +122,9 @@
      * @exception IllegalArgumentException if the tagged object cannot
      *              be converted.
      */
-    public static ASN1OctetString getInstance(
-        ASN1TaggedObject    taggedObject,
-        boolean             explicit)
+    public static ASN1OctetString getInstance(ASN1TaggedObject taggedObject, boolean explicit)
     {
-        if (explicit)
-        {
-            if (!taggedObject.isExplicit())
-            {
-                throw new IllegalArgumentException("object implicit - explicit expected.");
-            }
-
-            return getInstance(taggedObject.getObject());
-        }
-
-        ASN1Primitive o = taggedObject.getObject();
-
-        /*
-         * constructed object which appears to be explicitly tagged and it's really implicit means
-         * we have to add the surrounding octet string.
-         */
-        if (taggedObject.isExplicit())
-        {
-            ASN1OctetString singleSegment = getInstance(o);
-
-            if (taggedObject instanceof BERTaggedObject)
-            {
-                return new BEROctetString(new ASN1OctetString[]{ singleSegment });
-            }
-
-            // TODO Should really be similar to the BERTaggedObject case above:
-//            return new DLOctetString(new ASN1OctetString[]{ singleSegment });
-            return (ASN1OctetString)new BEROctetString(new ASN1OctetString[]{ singleSegment }).toDLObject();
-        }
-
-        if (o instanceof ASN1OctetString)
-        {
-            ASN1OctetString s = (ASN1OctetString)o;
-
-            if (taggedObject instanceof BERTaggedObject)
-            {
-                return s;
-            }
-
-            return (ASN1OctetString)s.toDLObject();
-        }
-
-        /*
-         * in this case the parser returns a sequence, convert it into an octet string.
-         */
-        if (o instanceof ASN1Sequence)
-        {
-            ASN1Sequence s = (ASN1Sequence)o;
-
-            if (taggedObject instanceof BERTaggedObject)
-            {
-                return BEROctetString.fromSequence(s);
-            }
-
-            // TODO Should really be similar to the BERTaggedObject case above:
-//            return DLOctetString.fromSequence(s);
-            return (ASN1OctetString)BEROctetString.fromSequence(s).toDLObject();
-        }
-
-        throw new IllegalArgumentException("unknown object in getInstance: " + taggedObject.getClass().getName());
+        return (ASN1OctetString)TYPE.getContextInstance(taggedObject, explicit);
     }
 
     /**
@@ -183,37 +133,40 @@
      * @param obj the object we want converted.
      * @exception IllegalArgumentException if the object cannot be converted.
      */
-    public static ASN1OctetString getInstance(
-        Object  obj)
+    public static ASN1OctetString getInstance(Object obj)
     {
         if (obj == null || obj instanceof ASN1OctetString)
         {
             return (ASN1OctetString)obj;
         }
+//      else if (obj instanceof ASN1OctetStringParser)
+        else if (obj instanceof ASN1Encodable)
+        {
+            ASN1Primitive primitive = ((ASN1Encodable)obj).toASN1Primitive();
+            if (primitive instanceof ASN1OctetString)
+            {
+                return (ASN1OctetString)primitive;
+            }
+        }
         else if (obj instanceof byte[])
         {
             try
             {
-                return getInstance(fromByteArray((byte[])obj));
+                return (ASN1OctetString)TYPE.fromByteArray((byte[])obj);
             }
             catch (IOException e)
             {
                 throw new IllegalArgumentException("failed to construct OCTET STRING from byte[]: " + e.getMessage());
             }
         }
-        else if (obj instanceof ASN1Encodable)
-        {
-            ASN1Primitive primitive = ((ASN1Encodable)obj).toASN1Primitive();
-
-            if (primitive instanceof ASN1OctetString)
-            {
-                return (ASN1OctetString)primitive;
-            }
-        }
 
         throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
     }
 
+    static final byte[] EMPTY_OCTETS = new byte[0];
+
+    byte[] string;
+
     /**
      * Base constructor.
      *
@@ -259,6 +212,11 @@
         return string;
     }
 
+    public int getOctetsLength()
+    {
+        return getOctets().length;
+    }
+
     public int hashCode()
     {
         return Arrays.hashCode(this.getOctets());
@@ -292,10 +250,13 @@
         return new DEROctetString(string);
     }
 
-    abstract void encode(ASN1OutputStream out, boolean withTag) throws IOException;
-
     public String toString()
     {
       return "#" + Strings.fromByteArray(Hex.encode(string));
     }
+
+    static ASN1OctetString createPrimitive(byte[] contents)
+    {
+        return new DEROctetString(contents);
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/ASN1OutputStream.java b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1OutputStream.java
index e6ae93f..73bab8a 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/ASN1OutputStream.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1OutputStream.java
@@ -32,250 +32,33 @@
 
     private OutputStream os;
 
-    /**
-     * @deprecated Use {@link ASN1OutputStream#create(OutputStream)} instead.
-     */
-    public ASN1OutputStream(OutputStream os)
+    ASN1OutputStream(OutputStream os)
     {
         this.os = os;
     }
 
-    final void writeLength(
-        int length)
-        throws IOException
+    public void close() throws IOException
     {
-        if (length > 127)
-        {
-            int size = 1;
-            int val = length;
-
-            while ((val >>>= 8) != 0)
-            {
-                size++;
-            }
-
-            write((byte)(size | 0x80));
-
-            for (int i = (size - 1) * 8; i >= 0; i -= 8)
-            {
-                write((byte)(length >> i));
-            }
-        }
-        else
-        {
-            write((byte)length);
-        }
+        os.close();
     }
 
-    final void write(int b)
-        throws IOException
+    public void flush() throws IOException
     {
-        os.write(b);
+        os.flush();
     }
 
-    final void write(byte[] bytes, int off, int len)
-        throws IOException
+    public final void writeObject(ASN1Encodable encodable) throws IOException
     {
-        os.write(bytes, off, len);
-    }
-
-    final void writeElements(ASN1Encodable[] elements)
-        throws IOException
-    {
-        int count = elements.length;
-        for (int i = 0; i < count; ++i)
-        {
-            ASN1Primitive primitive = elements[i].toASN1Primitive();
-
-            writePrimitive(primitive, true);
-        }
-    }
-
-    final void writeElements(Enumeration elements)
-        throws IOException
-    {
-        while (elements.hasMoreElements())
-        {
-            ASN1Primitive primitive = ((ASN1Encodable)elements.nextElement()).toASN1Primitive();
-
-            writePrimitive(primitive, true);
-        }
-    }
-
-    final void writeEncoded(
-        boolean withTag,
-        int     tag,
-        byte    contents)
-        throws IOException
-    {
-        if (withTag)
-        {
-            write(tag);
-        }
-        writeLength(1);
-        write(contents);
-    }
-
-    final void writeEncoded(
-        boolean withTag,
-        int     tag,
-        byte[]  contents)
-        throws IOException
-    {
-        if (withTag)
-        {
-            write(tag);
-        }
-        writeLength(contents.length);
-        write(contents, 0, contents.length);
-    }
-
-    final void writeEncoded(
-        boolean withTag,
-        int     tag,
-        byte[]  contents,
-        int     contentsOff,
-        int     contentsLen)
-        throws IOException
-    {
-        if (withTag)
-        {
-            write(tag);
-        }
-        writeLength(contentsLen);
-        write(contents, contentsOff, contentsLen);
-    }
-
-    final void writeEncoded(
-        boolean withTag,
-        int     tag,
-        byte    headByte,
-        byte[]  tailBytes)
-        throws IOException
-    {
-        if (withTag)
-        {
-            write(tag);
-        }
-        writeLength(1 + tailBytes.length);
-        write(headByte);
-        write(tailBytes, 0, tailBytes.length);
-    }
-
-    final void writeEncoded(
-        boolean withTag,
-        int     tag,
-        byte    headByte,
-        byte[]  body,
-        int     bodyOff,
-        int     bodyLen,
-        byte    tailByte)
-        throws IOException
-    {
-        if (withTag)
-        {
-            write(tag);
-        }
-        writeLength(2 + bodyLen);
-        write(headByte);
-        write(body, bodyOff, bodyLen);
-        write(tailByte);
-    }
-
-    final void writeEncoded(boolean withTag, int flags, int tagNo, byte[] contents)
-        throws IOException
-    {
-        writeTag(withTag, flags, tagNo);
-        writeLength(contents.length);
-        write(contents, 0, contents.length);
-    }
-
-    final void writeEncodedIndef(boolean withTag, int flags, int tagNo, byte[] contents)
-        throws IOException
-    {
-        writeTag(withTag, flags, tagNo);
-        write(0x80);
-        write(contents, 0, contents.length);
-        write(0x00);
-        write(0x00);
-    }
-
-    final void writeEncodedIndef(boolean withTag, int tag, ASN1Encodable[] elements)
-        throws IOException
-    {
-        if (withTag)
-        {
-            write(tag);
-        }
-        write(0x80);
-        writeElements(elements);
-        write(0x00);
-        write(0x00);
-    }
-
-    final void writeEncodedIndef(boolean withTag, int tag, Enumeration elements)
-        throws IOException
-    {
-        if (withTag)
-        {
-            write(tag);
-        }
-        write(0x80);
-        writeElements(elements);
-        write(0x00);
-        write(0x00);
-    }
-
-    final void writeTag(boolean withTag, int flags, int tagNo)
-        throws IOException
-    {
-        if (!withTag)
-        {
-            return;
-        }
-
-        if (tagNo < 31)
-        {
-            write(flags | tagNo);
-        }
-        else
-        {
-            write(flags | 0x1f);
-            if (tagNo < 128)
-            {
-                write(tagNo);
-            }
-            else
-            {
-                byte[] stack = new byte[5];
-                int pos = stack.length;
-
-                stack[--pos] = (byte)(tagNo & 0x7F);
-
-                do
-                {
-                    tagNo >>= 7;
-                    stack[--pos] = (byte)(tagNo & 0x7F | 0x80);
-                }
-                while (tagNo > 127);
-
-                write(stack, pos, stack.length - pos);
-            }
-        }
-    }
-
-    public void writeObject(ASN1Encodable obj) throws IOException
-    {
-        if (null == obj)
+        if (null == encodable)
         {
             throw new IOException("null object detected");
         }
 
-        writePrimitive(obj.toASN1Primitive(), true);
+        writePrimitive(encodable.toASN1Primitive(), true);
         flushInternal();
     }
 
-    public void writeObject(ASN1Primitive primitive) throws IOException
+    public final void writeObject(ASN1Primitive primitive) throws IOException
     {
         if (null == primitive)
         {
@@ -286,25 +69,7 @@
         flushInternal();
     }
 
-    void writePrimitive(ASN1Primitive primitive, boolean withTag) throws IOException
-    {
-        primitive.encode(this, withTag);
-    }
-
-    public void close()
-        throws IOException
-    {
-        os.close();
-    }
-
-    public void flush()
-        throws IOException
-    {
-        os.flush();
-    }
-
-    void flushInternal()
-        throws IOException
+    void flushInternal() throws IOException
     {
         // Placeholder to support future internal buffering
     }
@@ -314,8 +79,192 @@
         return new DEROutputStream(os);
     }
 
-    ASN1OutputStream getDLSubStream()
+    DLOutputStream getDLSubStream()
     {
         return new DLOutputStream(os);
     }
+
+    final void writeDL(int length) throws IOException
+    {
+        if (length < 128)
+        {
+            write(length);
+        }
+        else
+        {
+            byte[] stack = new byte[5];
+            int pos = stack.length;
+
+            do
+            {
+                stack[--pos] = (byte)length;
+                length >>>= 8;
+            }
+            while (length != 0);
+
+            int count = stack.length - pos;
+            stack[--pos] = (byte)(0x80 | count);
+
+            write(stack, pos, count + 1);
+        }
+    }
+
+    final void write(int b) throws IOException
+    {
+        os.write(b);
+    }
+
+    final void write(byte[] bytes, int off, int len) throws IOException
+    {
+        os.write(bytes, off, len);
+    }
+
+    void writeElements(ASN1Encodable[] elements)
+        throws IOException
+    {
+        for (int i = 0, count = elements.length; i < count; ++i)
+        {
+            elements[i].toASN1Primitive().encode(this, true);
+        }
+    }
+
+    final void writeEncodingDL(boolean withID, int identifier, byte contents) throws IOException
+    {
+        writeIdentifier(withID, identifier);
+        writeDL(1);
+        write(contents);
+    }
+
+    final void writeEncodingDL(boolean withID, int identifier, byte[] contents) throws IOException
+    {
+        writeIdentifier(withID, identifier);
+        writeDL(contents.length);
+        write(contents, 0, contents.length);
+    }
+
+    final void writeEncodingDL(boolean withID, int identifier, byte[] contents, int contentsOff, int contentsLen)
+        throws IOException
+    {
+        writeIdentifier(withID, identifier);
+        writeDL(contentsLen);
+        write(contents, contentsOff, contentsLen);
+    }
+
+    final void writeEncodingDL(boolean withID, int identifier, byte contentsPrefix, byte[] contents, int contentsOff,
+        int contentsLen) throws IOException
+    {
+        writeIdentifier(withID, identifier);
+        writeDL(1 + contentsLen);
+        write(contentsPrefix);
+        write(contents, contentsOff, contentsLen);
+    }
+
+    final void writeEncodingDL(boolean withID, int identifier, byte[] contents, int contentsOff, int contentsLen,
+        byte contentsSuffix) throws IOException
+    {
+        writeIdentifier(withID, identifier);
+        writeDL(contentsLen + 1);
+        write(contents, contentsOff, contentsLen);
+        write(contentsSuffix);
+    }
+
+    final void writeEncodingDL(boolean withID, int flags, int tag, byte[] contents) throws IOException
+    {
+        writeIdentifier(withID, flags, tag);
+        writeDL(contents.length);
+        write(contents, 0, contents.length);
+    }
+
+    final void writeEncodingIL(boolean withID, int identifier, ASN1Encodable[] elements) throws IOException
+    {
+        writeIdentifier(withID, identifier);
+        write(0x80);
+        writeElements(elements);
+        write(0x00);
+        write(0x00);
+    }
+
+    final void writeIdentifier(boolean withID, int identifier) throws IOException
+    {
+        if (withID)
+        {
+            write(identifier);
+        }
+    }
+
+    final void writeIdentifier(boolean withID, int flags, int tag) throws IOException
+    {
+        if (!withID)
+        {
+            // Don't write the identifier
+        }
+        else if (tag < 31)
+        {
+            write(flags | tag);
+        }
+        else
+        {
+            byte[] stack = new byte[6];
+            int pos = stack.length;
+
+            stack[--pos] = (byte)(tag & 0x7F);
+            while (tag > 127)
+            {
+                tag >>>= 7;
+                stack[--pos] = (byte)(tag & 0x7F | 0x80);
+            }
+
+            stack[--pos] = (byte)(flags | 0x1F);
+
+            write(stack, pos, stack.length - pos);
+        }
+    }
+
+    void writePrimitive(ASN1Primitive primitive, boolean withID) throws IOException
+    {
+        primitive.encode(this, withID);
+    }
+
+    void writePrimitives(ASN1Primitive[] primitives) throws IOException
+    {
+        for (int i = 0, count = primitives.length; i < count; ++i)
+        {
+            primitives[i].encode(this, true);
+        }
+    }
+
+    static int getLengthOfDL(int dl)
+    {
+        if (dl < 128)
+        {
+            return 1;
+        }
+
+        int length = 2;
+        while ((dl >>>= 8) != 0)
+        {
+            ++length;
+        }
+        return length;
+    }
+
+    static int getLengthOfEncodingDL(boolean withID, int contentsLength)
+    {
+        return (withID ? 1 : 0) + getLengthOfDL(contentsLength) + contentsLength;
+    }
+
+    static int getLengthOfIdentifier(int tag)
+    {
+        if (tag < 31)
+        {
+            return 1;
+        }
+
+        int length = 2;
+        while ((tag >>>= 7) != 0)
+        {
+            ++length;
+        }
+        return length;
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/ASN1Primitive.java b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1Primitive.java
index b562b06..6e5d3d6 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/ASN1Primitive.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1Primitive.java
@@ -15,12 +15,16 @@
 
     public void encodeTo(OutputStream output) throws IOException
     {
-        ASN1OutputStream.create(output).writeObject(this);
+        ASN1OutputStream asn1Out = ASN1OutputStream.create(output); 
+        asn1Out.writePrimitive(this, true);
+        asn1Out.flushInternal();
     }
 
     public void encodeTo(OutputStream output, String encoding) throws IOException
     {
-        ASN1OutputStream.create(output, encoding).writeObject(this);
+        ASN1OutputStream asn1Out = ASN1OutputStream.create(output, encoding); 
+        asn1Out.writePrimitive(this, true);
+        asn1Out.flushInternal();
     }
 
     /**
@@ -103,14 +107,9 @@
      * Return true if this objected is a CONSTRUCTED one, false otherwise.
      * @return true if CONSTRUCTED bit set on object's tag, false otherwise.
      */
-    abstract boolean isConstructed();
+    abstract boolean encodeConstructed();
 
-    /**
-     * Return the length of the encoding this object will produce.
-     * @return the length of the object's encoding.
-     * @throws IOException if the encoding length cannot be calculated.
-     */
-    abstract int encodedLength() throws IOException;
+    abstract int encodedLength(boolean withTag) throws IOException;
 
     abstract void encode(ASN1OutputStream out, boolean withTag) throws IOException;
 
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/ASN1PrintableString.java b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1PrintableString.java
new file mode 100644
index 0000000..a859b7d
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1PrintableString.java
@@ -0,0 +1,229 @@
+package org.bouncycastle.asn1;
+
+import java.io.IOException;
+
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.Strings;
+
+/**
+ * ASN.1 PrintableString object.
+ * <p>
+ * X.680 section 37.4 defines PrintableString character codes as ASCII subset of following characters:
+ * </p>
+ * <ul>
+ * <li>Latin capital letters: 'A' .. 'Z'</li>
+ * <li>Latin small letters: 'a' .. 'z'</li>
+ * <li>Digits: '0'..'9'</li>
+ * <li>Space</li>
+ * <li>Apostrophe: '\''</li>
+ * <li>Left parenthesis: '('</li>
+ * <li>Right parenthesis: ')'</li>
+ * <li>Plus sign: '+'</li>
+ * <li>Comma: ','</li>
+ * <li>Hyphen-minus: '-'</li>
+ * <li>Full stop: '.'</li>
+ * <li>Solidus: '/'</li>
+ * <li>Colon: ':'</li>
+ * <li>Equals sign: '='</li>
+ * <li>Question mark: '?'</li>
+ * </ul>
+ * <p>
+ * Explicit character set escape sequences are not allowed.
+ * </p>
+ */
+public abstract class ASN1PrintableString
+    extends ASN1Primitive
+    implements ASN1String
+{
+    static final ASN1UniversalType TYPE = new ASN1UniversalType(ASN1PrintableString.class, BERTags.PRINTABLE_STRING)
+    {
+        ASN1Primitive fromImplicitPrimitive(DEROctetString octetString)
+        {
+            return createPrimitive(octetString.getOctets());
+        }
+    };
+
+    /**
+     * Return a printable string from the passed in object.
+     *
+     * @param obj an ASN1PrintableString or an object that can be converted into one.
+     * @exception IllegalArgumentException if the object cannot be converted.
+     * @return an ASN1PrintableString instance, or null.
+     */
+    public static ASN1PrintableString getInstance(Object obj)
+    {
+        if (obj == null || obj instanceof ASN1PrintableString)
+        {
+            return (ASN1PrintableString)obj;
+        }
+        if (obj instanceof ASN1Encodable)
+        {
+            ASN1Primitive primitive = ((ASN1Encodable)obj).toASN1Primitive();
+            if (primitive instanceof ASN1PrintableString)
+            {
+                return (ASN1PrintableString)primitive;
+            }
+        }
+        if (obj instanceof byte[])
+        {
+            try
+            {
+                return (ASN1PrintableString)TYPE.fromByteArray((byte[])obj);
+            }
+            catch (Exception e)
+            {
+                throw new IllegalArgumentException("encoding error in getInstance: " + e.toString());
+            }
+        }
+
+        throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
+    }
+
+    /**
+     * Return a Printable String from a tagged object.
+     *
+     * @param taggedObject the tagged object holding the object we want
+     * @param explicit true if the object is meant to be explicitly
+     *              tagged false otherwise.
+     * @exception IllegalArgumentException if the tagged object cannot
+     *               be converted.
+     * @return an ASN1PrintableString instance, or null.
+     */
+    public static ASN1PrintableString getInstance(ASN1TaggedObject taggedObject, boolean explicit)
+    {
+        return (ASN1PrintableString)TYPE.getContextInstance(taggedObject, explicit);
+    }
+
+    final byte[] contents;
+
+    /**
+     * Constructor with optional validation.
+     *
+     * @param string the base string to wrap.
+     * @param validate whether or not to check the string.
+     * @throws IllegalArgumentException if validate is true and the string
+     * contains characters that should not be in a PrintableString.
+     */
+    ASN1PrintableString(String string, boolean validate)
+    {
+        if (validate && !isPrintableString(string))
+        {
+            throw new IllegalArgumentException("string contains illegal characters");
+        }
+
+        this.contents = Strings.toByteArray(string);
+    }
+
+    ASN1PrintableString(byte[] contents, boolean clone)
+    {
+        this.contents = clone ? Arrays.clone(contents) : contents;
+    }
+
+    public final String getString()
+    {
+        return Strings.fromByteArray(contents);
+    }
+
+    public final byte[] getOctets()
+    {
+        return Arrays.clone(contents);
+    }
+
+    final boolean encodeConstructed()
+    {
+        return false;
+    }
+
+    final int encodedLength(boolean withTag)
+    {
+        return ASN1OutputStream.getLengthOfEncodingDL(withTag, contents.length);
+    }
+
+    final void encode(ASN1OutputStream out, boolean withTag) throws IOException
+    {
+        out.writeEncodingDL(withTag, BERTags.PRINTABLE_STRING, contents);
+    }
+
+    final boolean asn1Equals(ASN1Primitive other)
+    {
+        if (!(other instanceof ASN1PrintableString))
+        {
+            return false;
+        }
+
+        ASN1PrintableString that = (ASN1PrintableString)other;
+
+        return Arrays.areEqual(this.contents, that.contents);
+    }
+
+    public final int hashCode()
+    {
+        return Arrays.hashCode(contents);
+    }
+
+    public String toString()
+    {
+        return getString();
+    }
+
+    /**
+     * return true if the passed in String can be represented without
+     * loss as a PrintableString, false otherwise.
+     *
+     * @return true if in printable set, false otherwise.
+     */
+    public static boolean isPrintableString(
+        String  str)
+    {
+        for (int i = str.length() - 1; i >= 0; i--)
+        {
+            char    ch = str.charAt(i);
+
+            if (ch > 0x007f)
+            {
+                return false;
+            }
+
+            if ('a' <= ch && ch <= 'z')
+            {
+                continue;
+            }
+
+            if ('A' <= ch && ch <= 'Z')
+            {
+                continue;
+            }
+
+            if ('0' <= ch && ch <= '9')
+            {
+                continue;
+            }
+
+            switch (ch)
+            {
+            case ' ':
+            case '\'':
+            case '(':
+            case ')':
+            case '+':
+            case '-':
+            case '.':
+            case ':':
+            case '=':
+            case '?':
+            case '/':
+            case ',':
+                continue;
+            }
+
+            return false;
+        }
+
+        return true;
+    }
+
+    static ASN1PrintableString createPrimitive(byte[] contents)
+    {
+        return new DERPrintableString(contents, false);
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/ASN1RelativeOID.java b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1RelativeOID.java
new file mode 100644
index 0000000..b107cb2
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1RelativeOID.java
@@ -0,0 +1,313 @@
+package org.bouncycastle.asn1;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.math.BigInteger;
+
+import org.bouncycastle.util.Arrays;
+
+public class ASN1RelativeOID
+    extends ASN1Primitive
+{
+    static final ASN1UniversalType TYPE = new ASN1UniversalType(ASN1RelativeOID.class, BERTags.RELATIVE_OID)
+    {
+        ASN1Primitive fromImplicitPrimitive(DEROctetString octetString)
+        {
+            return createPrimitive(octetString.getOctets(), false);
+        }
+    };
+
+    public static ASN1RelativeOID fromContents(byte[] contents)
+    {
+        return createPrimitive(contents, true);
+    }
+
+    public static ASN1RelativeOID getInstance(Object obj)
+    {
+        if (obj == null || obj instanceof ASN1RelativeOID)
+        {
+            return (ASN1RelativeOID)obj;
+        }
+        if (obj instanceof ASN1Encodable)
+        {
+            ASN1Primitive primitive = ((ASN1Encodable)obj).toASN1Primitive();
+            if (primitive instanceof ASN1RelativeOID)
+            {
+                return (ASN1RelativeOID)primitive;
+            }
+        }
+        else if (obj instanceof byte[])
+        {
+            byte[] enc = (byte[])obj;
+            try
+            {
+                return (ASN1RelativeOID)TYPE.fromByteArray(enc);
+            }
+            catch (IOException e)
+            {
+                throw new IllegalArgumentException("failed to construct relative OID from byte[]: " + e.getMessage());
+            }
+        }
+
+        throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
+    }
+
+    public static ASN1RelativeOID getInstance(ASN1TaggedObject taggedObject, boolean explicit)
+    {
+        return (ASN1RelativeOID)TYPE.getContextInstance(taggedObject, explicit);
+    }
+
+    private static final long LONG_LIMIT = (Long.MAX_VALUE >> 7) - 0x7F;
+
+    private final String identifier;
+    private byte[] contents;
+
+    public ASN1RelativeOID(String identifier)
+    {
+        if (identifier == null)
+        {
+            throw new NullPointerException("'identifier' cannot be null");
+        }
+        if (!isValidIdentifier(identifier, 0))
+        {
+            throw new IllegalArgumentException("string " + identifier + " not a relative OID");
+        }
+
+        this.identifier = identifier;
+    }
+
+    ASN1RelativeOID(ASN1RelativeOID oid, String branchID)
+    {
+        if (!isValidIdentifier(branchID, 0))
+        {
+            throw new IllegalArgumentException("string " + branchID + " not a valid OID branch");
+        }
+
+        this.identifier = oid.getId() + "." + branchID;
+    }
+
+    private ASN1RelativeOID(byte[] contents, boolean clone)
+    {
+        StringBuffer objId = new StringBuffer();
+        long value = 0;
+        BigInteger bigValue = null;
+        boolean first = true;
+
+        for (int i = 0; i != contents.length; i++)
+        {
+            int b = contents[i] & 0xff;
+
+            if (value <= LONG_LIMIT)
+            {
+                value += b & 0x7F;
+                if ((b & 0x80) == 0)
+                {
+                    if (first)
+                    {
+                        first = false;
+                    }
+                    else
+                    {
+                        objId.append('.');
+                    }
+
+                    objId.append(value);
+                    value = 0;
+                }
+                else
+                {
+                    value <<= 7;
+                }
+            }
+            else
+            {
+                if (bigValue == null)
+                {
+                    bigValue = BigInteger.valueOf(value);
+                }
+                bigValue = bigValue.or(BigInteger.valueOf(b & 0x7F));
+                if ((b & 0x80) == 0)
+                {
+                    if (first)
+                    {
+                        first = false;
+                    }
+                    else
+                    {
+                        objId.append('.');
+                    }
+
+                    objId.append(bigValue);
+                    bigValue = null;
+                    value = 0;
+                }
+                else
+                {
+                    bigValue = bigValue.shiftLeft(7);
+                }
+            }
+        }
+
+        this.identifier = objId.toString();
+        this.contents = clone ? Arrays.clone(contents) : contents;
+    }
+
+    public ASN1RelativeOID branch(String branchID)
+    {
+        return new ASN1RelativeOID(this, branchID);
+    }
+
+    public String getId()
+    {
+        return identifier;
+    }
+
+    public int hashCode()
+    {
+        return identifier.hashCode();
+    }
+
+    public String toString()
+    {
+        return getId();
+    }
+
+    boolean asn1Equals(ASN1Primitive other)
+    {
+        if (this == other)
+        {
+            return true;
+        }
+        if (!(other instanceof ASN1RelativeOID))
+        {
+            return false;
+        }
+
+        ASN1RelativeOID that = (ASN1RelativeOID)other;
+
+        return this.identifier.equals(that.identifier);
+    }
+
+    int encodedLength(boolean withTag)
+    {
+        return ASN1OutputStream.getLengthOfEncodingDL(withTag, getContents().length);
+    }
+
+    void encode(ASN1OutputStream out, boolean withTag) throws IOException
+    {
+        out.writeEncodingDL(withTag, BERTags.RELATIVE_OID, getContents());
+    }
+
+    boolean encodeConstructed()
+    {
+        return false;
+    }
+
+    private void doOutput(ByteArrayOutputStream aOut)
+    {
+        OIDTokenizer tok = new OIDTokenizer(identifier);
+        while (tok.hasMoreTokens())
+        {
+            String token = tok.nextToken();
+            if (token.length() <= 18)
+            {
+                writeField(aOut, Long.parseLong(token));
+            }
+            else
+            {
+                writeField(aOut, new BigInteger(token));
+            }
+        }
+    }
+
+    private synchronized byte[] getContents()
+    {
+        if (contents == null)
+        {
+            ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+
+            doOutput(bOut);
+
+            contents = bOut.toByteArray();
+        }
+
+        return contents;
+    }
+
+    static ASN1RelativeOID createPrimitive(byte[] contents, boolean clone)
+    {
+        return new ASN1RelativeOID(contents, clone);
+    }
+
+    static boolean isValidIdentifier(String identifier, int from)
+    {
+        int digitCount = 0;
+
+        int pos = identifier.length();
+        while (--pos >= from)
+        {
+            char ch = identifier.charAt(pos);
+
+            if (ch == '.')
+            {
+                if (0 == digitCount
+                    || (digitCount > 1 && identifier.charAt(pos + 1) == '0'))
+                {
+                    return false;
+                }
+
+                digitCount = 0;
+            }
+            else if ('0' <= ch && ch <= '9')
+            {
+                ++digitCount;
+            }
+            else
+            {
+                return false;
+            }
+        }
+
+        if (0 == digitCount
+            || (digitCount > 1 && identifier.charAt(pos + 1) == '0'))
+        {
+            return false;
+        }
+
+        return true;
+    }
+
+    static void writeField(ByteArrayOutputStream out, long fieldValue)
+    {
+        byte[] result = new byte[9];
+        int pos = 8;
+        result[pos] = (byte)((int)fieldValue & 0x7F);
+        while (fieldValue >= (1L << 7))
+        {
+            fieldValue >>= 7;
+            result[--pos] = (byte)((int)fieldValue | 0x80);
+        }
+        out.write(result, pos, 9 - pos);
+    }
+
+    static void writeField(ByteArrayOutputStream out, BigInteger fieldValue)
+    {
+        int byteCount = (fieldValue.bitLength() + 6) / 7;
+        if (byteCount == 0)
+        {
+            out.write(0);
+        }
+        else
+        {
+            BigInteger tmpValue = fieldValue;
+            byte[] tmp = new byte[byteCount];
+            for (int i = byteCount - 1; i >= 0; i--)
+            {
+                tmp[i] = (byte)(tmpValue.intValue() | 0x80);
+                tmpValue = tmpValue.shiftRight(7);
+            }
+            tmp[byteCount - 1] &= 0x7F;
+            out.write(tmp, 0, tmp.length);
+        }
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/ASN1Sequence.java b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1Sequence.java
index b64127a..99d680f 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/ASN1Sequence.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1Sequence.java
@@ -60,8 +60,13 @@
     extends ASN1Primitive
     implements org.bouncycastle.util.Iterable<ASN1Encodable>
 {
-    // NOTE: Only non-final to support LazyEncodedSequence
-    ASN1Encodable[] elements;
+    static final ASN1UniversalType TYPE = new ASN1UniversalType(ASN1Sequence.class, BERTags.SEQUENCE)
+    {
+        ASN1Primitive fromImplicitConstructed(ASN1Sequence sequence)
+        {
+            return sequence;
+        }
+    };
 
     /**
      * Return an ASN1Sequence from the given object.
@@ -70,37 +75,32 @@
      * @exception IllegalArgumentException if the object cannot be converted.
      * @return an ASN1Sequence instance, or null.
      */
-    public static ASN1Sequence getInstance(
-        Object  obj)
+    public static ASN1Sequence getInstance(Object obj)
     {
         if (obj == null || obj instanceof ASN1Sequence)
         {
             return (ASN1Sequence)obj;
         }
-        else if (obj instanceof ASN1SequenceParser)
+//      else if (obj instanceof ASN1SequenceParser)
+        else if (obj instanceof ASN1Encodable)
         {
-            return ASN1Sequence.getInstance(((ASN1SequenceParser)obj).toASN1Primitive());
+            ASN1Primitive primitive = ((ASN1Encodable)obj).toASN1Primitive();
+            if (primitive instanceof ASN1Sequence)
+            {
+                return (ASN1Sequence)primitive;
+            }
         }
         else if (obj instanceof byte[])
         {
             try
             {
-                return ASN1Sequence.getInstance(fromByteArray((byte[])obj));
+                return (ASN1Sequence)TYPE.fromByteArray((byte[])obj);
             }
             catch (IOException e)
             {
                 throw new IllegalArgumentException("failed to construct sequence from byte[]: " + e.getMessage());
             }
         }
-        else if (obj instanceof ASN1Encodable)
-        {
-            ASN1Primitive primitive = ((ASN1Encodable)obj).toASN1Primitive();
-
-            if (primitive instanceof ASN1Sequence)
-            {
-                return (ASN1Sequence)primitive;
-            }
-        }
 
         throw new IllegalArgumentException("unknown object in getInstance: " + obj.getClass().getName());
     }
@@ -122,51 +122,14 @@
      *          be converted.
      * @return an ASN1Sequence instance.
      */
-    public static ASN1Sequence getInstance(
-        ASN1TaggedObject    taggedObject,
-        boolean             explicit)
+    public static ASN1Sequence getInstance(ASN1TaggedObject taggedObject, boolean explicit)
     {
-        if (explicit)
-        {
-            if (!taggedObject.isExplicit())
-            {
-                throw new IllegalArgumentException("object implicit - explicit expected.");
-            }
-
-            return getInstance(taggedObject.getObject());
-        }
-
-        ASN1Primitive o = taggedObject.getObject();
-
-        /*
-         * constructed object which appears to be explicitly tagged when it should be implicit means
-         * we have to add the surrounding sequence.
-         */
-        if (taggedObject.isExplicit())
-        {
-            if (taggedObject instanceof BERTaggedObject)
-            {
-                return new BERSequence(o);
-            }
-
-            return new DLSequence(o);
-        }
-
-        if (o instanceof ASN1Sequence)
-        {
-            ASN1Sequence s = (ASN1Sequence)o;
-
-            if (taggedObject instanceof BERTaggedObject)
-            {
-                return s;
-            }
-
-            return (ASN1Sequence)s.toDLObject();
-        }
-
-        throw new IllegalArgumentException("unknown object in getInstance: " + taggedObject.getClass().getName());
+        return (ASN1Sequence)TYPE.getContextInstance(taggedObject, explicit);
     }
 
+    // NOTE: Only non-final to support LazyEncodedSequence
+    ASN1Encodable[] elements;
+
     /**
      * Create an empty SEQUENCE
      */
@@ -340,6 +303,7 @@
 
         ASN1Sequence that = (ASN1Sequence)other;
 
+        // NOTE: Call size() here (on both) to 'force' a LazyEncodedSequence
         int count = this.size();
         if (that.size() != count)
         {
@@ -378,13 +342,19 @@
         return new DLSequence(elements, false);
     }
 
-    boolean isConstructed()
+    abstract ASN1BitString toASN1BitString();
+
+    abstract ASN1External toASN1External();
+
+    abstract ASN1OctetString toASN1OctetString();
+
+    abstract ASN1Set toASN1Set();
+
+    boolean encodeConstructed()
     {
         return true;
     }
 
-    abstract void encode(ASN1OutputStream out, boolean withTag) throws IOException;
-
     public String toString() 
     {
         // NOTE: Call size() here to 'force' a LazyEncodedSequence
@@ -413,4 +383,28 @@
     {
         return new Arrays.Iterator<ASN1Encodable>(elements);
     }
+
+    ASN1BitString[] getConstructedBitStrings()
+    {
+        // NOTE: Call size() here to 'force' a LazyEncodedSequence
+        int count = size();
+        ASN1BitString[] bitStrings = new ASN1BitString[count];
+        for (int i = 0; i < count; ++i)
+        {
+            bitStrings[i] = ASN1BitString.getInstance(elements[i]);
+        }
+        return bitStrings;
+    }
+
+    ASN1OctetString[] getConstructedOctetStrings()
+    {
+        // NOTE: Call size() here to 'force' a LazyEncodedSequence
+        int count = size();
+        ASN1OctetString[] octetStrings = new ASN1OctetString[count];
+        for (int i = 0; i < count; ++i)
+        {
+            octetStrings[i] = ASN1OctetString.getInstance(elements[i]);
+        }
+        return octetStrings;
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/ASN1Set.java b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1Set.java
index fd676d7..8cd3ad5 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/ASN1Set.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1Set.java
@@ -98,8 +98,13 @@
     extends ASN1Primitive
     implements org.bouncycastle.util.Iterable<ASN1Encodable>
 {
-    protected final ASN1Encodable[] elements;
-    protected final boolean isSorted;
+    static final ASN1UniversalType TYPE = new ASN1UniversalType(ASN1Set.class, BERTags.SET)
+    {
+        ASN1Primitive fromImplicitConstructed(ASN1Sequence sequence)
+        {
+            return sequence.toASN1Set();
+        }
+    };
 
     /**
      * return an ASN1Set from the given object.
@@ -108,37 +113,32 @@
      * @exception IllegalArgumentException if the object cannot be converted.
      * @return an ASN1Set instance, or null.
      */
-    public static ASN1Set getInstance(
-        Object  obj)
+    public static ASN1Set getInstance(Object obj)
     {
         if (obj == null || obj instanceof ASN1Set)
         {
             return (ASN1Set)obj;
         }
-        else if (obj instanceof ASN1SetParser)
+//      else if (obj instanceof ASN1SetParser)
+        else if (obj instanceof ASN1Encodable)
         {
-            return ASN1Set.getInstance(((ASN1SetParser)obj).toASN1Primitive());
+            ASN1Primitive primitive = ((ASN1Encodable)obj).toASN1Primitive();
+            if (primitive instanceof ASN1Set)
+            {
+                return (ASN1Set)primitive;
+            }
         }
         else if (obj instanceof byte[])
         {
             try
             {
-                return ASN1Set.getInstance(ASN1Primitive.fromByteArray((byte[])obj));
+                return (ASN1Set)TYPE.fromByteArray((byte[])obj);
             }
             catch (IOException e)
             {
                 throw new IllegalArgumentException("failed to construct set from byte[]: " + e.getMessage());
             }
         }
-        else if (obj instanceof ASN1Encodable)
-        {
-            ASN1Primitive primitive = ((ASN1Encodable)obj).toASN1Primitive();
-
-            if (primitive instanceof ASN1Set)
-            {
-                return (ASN1Set)primitive;
-            }
-        }
 
         throw new IllegalArgumentException("unknown object in getInstance: " + obj.getClass().getName());
     }
@@ -160,73 +160,19 @@
      *          be converted.
      * @return an ASN1Set instance.
      */
-    public static ASN1Set getInstance(
-        ASN1TaggedObject    taggedObject,
-        boolean             explicit)
+    public static ASN1Set getInstance(ASN1TaggedObject taggedObject, boolean explicit)
     {
-        if (explicit)
-        {
-            if (!taggedObject.isExplicit())
-            {
-                throw new IllegalArgumentException("object implicit - explicit expected.");
-            }
-
-            return getInstance(taggedObject.getObject());
-        }
-
-        ASN1Primitive o = taggedObject.getObject();
-
-        /*
-         * constructed object which appears to be explicitly tagged and it's really implicit means
-         * we have to add the surrounding set.
-         */
-        if (taggedObject.isExplicit())
-        {
-            if (taggedObject instanceof BERTaggedObject)
-            {
-                return new BERSet(o);
-            }
-
-            return new DLSet(o);
-        }
-
-        if (o instanceof ASN1Set)
-        {
-            ASN1Set s = (ASN1Set)o;
-
-            if (taggedObject instanceof BERTaggedObject)
-            {
-                return s;
-            }
-
-            return (ASN1Set)s.toDLObject();
-        }
-
-        /*
-         * in this case the parser returns a sequence, convert it into a set.
-         */
-        if (o instanceof ASN1Sequence)
-        {
-            ASN1Sequence s = (ASN1Sequence)o;
-
-            // NOTE: Will force() a LazyEncodedSequence
-            ASN1Encodable[] elements = s.toArrayInternal();
-
-            if (taggedObject instanceof BERTaggedObject)
-            {
-                return new BERSet(false, elements);
-            }
-
-            return new DLSet(false, elements);
-        }
-
-        throw new IllegalArgumentException("unknown object in getInstance: " + taggedObject.getClass().getName());
+        return (ASN1Set)TYPE.getContextInstance(taggedObject, explicit);
     }
 
+    protected final ASN1Encodable[] elements;
+
+    protected ASN1Encodable[] sortedElements;
+
     protected ASN1Set()
     {
         this.elements = ASN1EncodableVector.EMPTY_ELEMENTS;
-        this.isSorted = true;
+        this.sortedElements = elements;
     }
 
     /**
@@ -241,7 +187,7 @@
         }
 
         this.elements = new ASN1Encodable[]{ element };
-        this.isSorted = true;
+        this.sortedElements = elements;
     }
 
     /**
@@ -268,7 +214,7 @@
         }
 
         this.elements = tmp;
-        this.isSorted = doSort || tmp.length < 2;
+        this.sortedElements = (doSort || tmp.length < 2) ? elements : null;
     }
 
     /**
@@ -290,13 +236,19 @@
         }
 
         this.elements = tmp;
-        this.isSorted = doSort || tmp.length < 2;
+        this.sortedElements = (doSort || tmp.length < 2) ? elements : null;
     }
 
     ASN1Set(boolean isSorted, ASN1Encodable[] elements)
     {
         this.elements = elements;
-        this.isSorted = isSorted || elements.length < 2;
+        this.sortedElements = (isSorted || elements.length < 2) ? elements : null;
+    }
+
+    ASN1Set(ASN1Encodable[] elements, ASN1Encodable[] sortedElements)
+    {
+        this.elements = elements;
+        this.sortedElements = sortedElements;
     }
 
     public Enumeration getObjects()
@@ -408,18 +360,13 @@
      */
     ASN1Primitive toDERObject()
     {
-        ASN1Encodable[] tmp;
-        if (isSorted)
+        if (sortedElements == null)
         {
-            tmp = elements;
-        }
-        else
-        {
-            tmp = (ASN1Encodable[])elements.clone();
-            sort(tmp);
+            sortedElements = (ASN1Encodable[])elements.clone();
+            sort(sortedElements);
         }
 
-        return new DERSet(true, tmp);
+        return new DERSet(true, sortedElements);
     }
 
     /**
@@ -428,7 +375,7 @@
      */
     ASN1Primitive toDLObject()
     {
-        return new DLSet(isSorted, elements);
+        return new DLSet(elements, sortedElements);
     }
 
     boolean asn1Equals(ASN1Primitive other)
@@ -463,13 +410,11 @@
         return true;
     }
 
-    boolean isConstructed()
+    boolean encodeConstructed()
     {
         return true;
     }
 
-    abstract void encode(ASN1OutputStream out, boolean withTag) throws IOException;
-
     public String toString() 
     {
         int count = size();
@@ -529,8 +474,8 @@
          * primitive form accordingly. Failing to ignore the CONSTRUCTED bit could therefore lead to
          * ordering inversions.
          */
-        int a0 = a[0] & ~BERTags.CONSTRUCTED;
-        int b0 = b[0] & ~BERTags.CONSTRUCTED;
+        int a0 = a[0] & (~BERTags.CONSTRUCTED & 0xff);
+        int b0 = b[0] & (~BERTags.CONSTRUCTED & 0xff);
         if (a0 != b0)
         {
             return a0 < b0;
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/ASN1StreamParser.java b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1StreamParser.java
index 95bc611..bfcebc8 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/ASN1StreamParser.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1StreamParser.java
@@ -10,214 +10,257 @@
 public class ASN1StreamParser
 {
     private final InputStream _in;
-    private final int         _limit;
+    private final int _limit;
     private final byte[][] tmpBuffers;
 
-    public ASN1StreamParser(
-        InputStream in)
+    public ASN1StreamParser(InputStream in)
     {
         this(in, StreamUtil.findLimit(in));
     }
 
-    public ASN1StreamParser(
-        InputStream in,
-        int         limit)
-    {
-        this._in = in;
-        this._limit = limit;
-
-        this.tmpBuffers = new byte[11][];
-    }
-
-    public ASN1StreamParser(
-        byte[] encoding)
+    public ASN1StreamParser(byte[] encoding)
     {
         this(new ByteArrayInputStream(encoding), encoding.length);
     }
 
-    ASN1Encodable readIndef(int tagValue) throws IOException
+    public ASN1StreamParser(InputStream in, int limit)
     {
-        // Note: INDEF => CONSTRUCTED
-
-        // TODO There are other tags that may be constructed (e.g. BIT_STRING)
-        switch (tagValue)
-        {
-            case BERTags.EXTERNAL:
-                return new DERExternalParser(this);
-            case BERTags.OCTET_STRING:
-                return new BEROctetStringParser(this);
-            case BERTags.SEQUENCE:
-                return new BERSequenceParser(this);
-            case BERTags.SET:
-                return new BERSetParser(this);
-            default:
-                throw new ASN1Exception("unknown BER object encountered: 0x" + Integer.toHexString(tagValue));
-        }
+        this(in, limit, new byte[11][]);
     }
 
-    ASN1Encodable readImplicit(boolean constructed, int tag) throws IOException
+    ASN1StreamParser(InputStream in, int limit, byte[][] tmpBuffers)
     {
-        if (_in instanceof IndefiniteLengthInputStream)
-        {
-            if (!constructed)
-            {
-                throw new IOException("indefinite-length primitive encoding encountered");
-            }
-            
-            return readIndef(tag);
-        }
-
-        if (constructed)
-        {
-            switch (tag)
-            {
-                case BERTags.SET:
-                    return new DLSetParser(this);
-                case BERTags.SEQUENCE:
-                    return new DLSequenceParser(this);
-                case BERTags.OCTET_STRING:
-                    return new BEROctetStringParser(this);
-            }
-        }
-        else
-        {
-            switch (tag)
-            {
-                case BERTags.SET:
-                    throw new ASN1Exception("sequences must use constructed encoding (see X.690 8.9.1/8.10.1)");
-                case BERTags.SEQUENCE:
-                    throw new ASN1Exception("sets must use constructed encoding (see X.690 8.11.1/8.12.1)");
-                case BERTags.OCTET_STRING:
-                    return new DEROctetStringParser((DefiniteLengthInputStream)_in);
-            }
-        }
-
-        throw new ASN1Exception("implicit tagging not implemented");
+        this._in = in;
+        this._limit = limit;
+        this.tmpBuffers = tmpBuffers;
     }
 
-    ASN1Primitive readTaggedObject(boolean constructed, int tag) throws IOException
+    public ASN1Encodable readObject() throws IOException
     {
-        if (!constructed)
-        {
-            // Note: !CONSTRUCTED => IMPLICIT
-            DefiniteLengthInputStream defIn = (DefiniteLengthInputStream)_in;
-            return new DLTaggedObject(false, tag, new DEROctetString(defIn.toByteArray()));
-        }
-
-        ASN1EncodableVector v = readVector();
-
-        if (_in instanceof IndefiniteLengthInputStream)
-        {
-            return v.size() == 1
-                ?   new BERTaggedObject(true, tag, v.get(0))
-                :   new BERTaggedObject(false, tag, BERFactory.createSequence(v));
-        }
-
-        return v.size() == 1
-            ?   new DLTaggedObject(true, tag, v.get(0))
-            :   new DLTaggedObject(false, tag, DLFactory.createSequence(v));
-    }
-
-    public ASN1Encodable readObject()
-        throws IOException
-    {
-        int tag = _in.read();
-        if (tag == -1)
+        int tagHdr = _in.read();
+        if (tagHdr < 0)
         {
             return null;
         }
 
+        return implParseObject(tagHdr);
+    }
+
+    ASN1Encodable implParseObject(int tagHdr) throws IOException
+    {
         //
-        // turn of looking for "00" while we resolve the tag
+        // turn off looking for "00" while we resolve the tag
         //
         set00Check(false);
 
         //
         // calculate tag number
         //
-        int tagNo = ASN1InputStream.readTagNumber(_in, tag);
-
-        boolean isConstructed = (tag & BERTags.CONSTRUCTED) != 0;
+        int tagNo = ASN1InputStream.readTagNumber(_in, tagHdr);
 
         //
         // calculate length
         //
         int length = ASN1InputStream.readLength(_in, _limit,
-            tagNo == BERTags.OCTET_STRING || tagNo == BERTags.SEQUENCE || tagNo == BERTags.SET || tagNo == BERTags.EXTERNAL);
+            tagNo == BERTags.BIT_STRING || tagNo == BERTags.OCTET_STRING || tagNo == BERTags.SEQUENCE
+                || tagNo == BERTags.SET || tagNo == BERTags.EXTERNAL);
 
         if (length < 0) // indefinite-length method
         {
-            if (!isConstructed)
+            if (0 == (tagHdr & BERTags.CONSTRUCTED))
             {
                 throw new IOException("indefinite-length primitive encoding encountered");
             }
 
             IndefiniteLengthInputStream indIn = new IndefiniteLengthInputStream(_in, _limit);
-            ASN1StreamParser sp = new ASN1StreamParser(indIn, _limit);
+            ASN1StreamParser sp = new ASN1StreamParser(indIn, _limit, tmpBuffers);
 
-            if ((tag & BERTags.APPLICATION) != 0)
+            int tagClass = tagHdr & BERTags.PRIVATE;
+            if (0 != tagClass)
             {
-                return new BERApplicationSpecificParser(tagNo, sp);
+                return new BERTaggedObjectParser(tagClass, tagNo, sp);
             }
 
-            if ((tag & BERTags.TAGGED) != 0)
-            {
-                return new BERTaggedObjectParser(true, tagNo, sp);
-            }
-
-            return sp.readIndef(tagNo);
+            return sp.parseImplicitConstructedIL(tagNo);
         }
         else
         {
             DefiniteLengthInputStream defIn = new DefiniteLengthInputStream(_in, length, _limit);
 
-            if ((tag & BERTags.APPLICATION) != 0)
+            if (0 == (tagHdr & BERTags.FLAGS))
             {
-                return new DLApplicationSpecific(isConstructed, tagNo, defIn.toByteArray());
+                return parseImplicitPrimitive(tagNo, defIn);
             }
 
-            if ((tag & BERTags.TAGGED) != 0)
+            ASN1StreamParser sp = new ASN1StreamParser(defIn, defIn.getLimit(), tmpBuffers);
+
+            int tagClass = tagHdr & BERTags.PRIVATE;
+            if (0 != tagClass)
             {
-                return new BERTaggedObjectParser(isConstructed, tagNo, new ASN1StreamParser(defIn));
+                boolean isConstructed = (tagHdr & BERTags.CONSTRUCTED) != 0;
+
+                return new DLTaggedObjectParser(tagClass, tagNo, isConstructed, sp);
             }
 
-            if (isConstructed)
-            {
-                // TODO There are other tags that may be constructed (e.g. BIT_STRING)
-                switch (tagNo)
-                {
-                    case BERTags.OCTET_STRING:
-                        //
-                        // yes, people actually do this...
-                        //
-                        return new BEROctetStringParser(new ASN1StreamParser(defIn));
-                    case BERTags.SEQUENCE:
-                        return new DLSequenceParser(new ASN1StreamParser(defIn));
-                    case BERTags.SET:
-                        return new DLSetParser(new ASN1StreamParser(defIn));
-                    case BERTags.EXTERNAL:
-                        return new DERExternalParser(new ASN1StreamParser(defIn));
-                    default:
-                        throw new IOException("unknown tag " + tagNo + " encountered");
-                }
-            }
+            return sp.parseImplicitConstructedDL(tagNo);
+        }
+    }
 
-            // Some primitive encodings can be handled by parsers too...
-            switch (tagNo)
-            {
-                case BERTags.OCTET_STRING:
-                    return new DEROctetStringParser(defIn);
-            }
+    ASN1Primitive loadTaggedDL(int tagClass, int tagNo, boolean constructed) throws IOException
+    {
+        if (!constructed)
+        {
+            byte[] contentsOctets = ((DefiniteLengthInputStream) _in).toByteArray();
+            return ASN1TaggedObject.createPrimitive(tagClass, tagNo, contentsOctets);
+        }
 
-            try
+        ASN1EncodableVector contentsElements = readVector();
+        return ASN1TaggedObject.createConstructedDL(tagClass, tagNo, contentsElements);
+    }
+
+    ASN1Primitive loadTaggedIL(int tagClass, int tagNo) throws IOException
+    {
+        ASN1EncodableVector contentsElements = readVector();
+        return ASN1TaggedObject.createConstructedIL(tagClass, tagNo, contentsElements);
+    }
+
+    ASN1Encodable parseImplicitConstructedDL(int univTagNo) throws IOException
+    {
+        switch (univTagNo)
+        {
+        case BERTags.BIT_STRING:
+            // TODO[asn1] DLConstructedBitStringParser
+            return new BERBitStringParser(this);
+        case BERTags.EXTERNAL:
+            return new DERExternalParser(this);
+        case BERTags.OCTET_STRING:
+            // TODO[asn1] DLConstructedOctetStringParser
+            return new BEROctetStringParser(this);
+        case BERTags.SET:
+            return new DLSetParser(this);
+        case BERTags.SEQUENCE:
+            return new DLSequenceParser(this);
+        default:
+            // -DM toHexString
+            throw new ASN1Exception("unknown DL object encountered: 0x" + Integer.toHexString(univTagNo));
+        }
+    }
+
+    ASN1Encodable parseImplicitConstructedIL(int univTagNo) throws IOException
+    {
+        switch (univTagNo)
+        {
+        case BERTags.BIT_STRING:
+            return new BERBitStringParser(this);
+        case BERTags.OCTET_STRING:
+            return new BEROctetStringParser(this);
+        case BERTags.EXTERNAL:
+            // TODO[asn1] BERExternalParser
+            return new DERExternalParser(this);
+        case BERTags.SEQUENCE:
+            return new BERSequenceParser(this);
+        case BERTags.SET:
+            return new BERSetParser(this);
+        default:
+            throw new ASN1Exception("unknown BER object encountered: 0x" + Integer.toHexString(univTagNo));
+        }
+    }
+
+    ASN1Encodable parseImplicitPrimitive(int univTagNo) throws IOException
+    {
+        return parseImplicitPrimitive(univTagNo, (DefiniteLengthInputStream)_in);
+    }
+
+    ASN1Encodable parseImplicitPrimitive(int univTagNo, DefiniteLengthInputStream defIn) throws IOException
+    {
+        // Some primitive encodings can be handled by parsers too...
+        switch (univTagNo)
+        {
+        case BERTags.BIT_STRING:
+            return new DLBitStringParser(defIn);
+        case BERTags.EXTERNAL:
+            throw new ASN1Exception("externals must use constructed encoding (see X.690 8.18)");
+        case BERTags.OCTET_STRING:
+            return new DEROctetStringParser(defIn);
+        case BERTags.SET:
+            throw new ASN1Exception("sequences must use constructed encoding (see X.690 8.9.1/8.10.1)");
+        case BERTags.SEQUENCE:
+            throw new ASN1Exception("sets must use constructed encoding (see X.690 8.11.1/8.12.1)");
+        }
+
+        try
+        {
+            return ASN1InputStream.createPrimitiveDERObject(univTagNo, defIn, tmpBuffers);
+        }
+        catch (IllegalArgumentException e)
+        {
+            throw new ASN1Exception("corrupted stream detected", e);
+        }
+    }
+
+    ASN1Encodable parseObject(int univTagNo) throws IOException
+    {
+        if (univTagNo < 0 || univTagNo > 30)
+        {
+            throw new IllegalArgumentException("invalid universal tag number: " + univTagNo);
+        }
+
+        int tagHdr = _in.read();
+        if (tagHdr < 0)
+        {
+            return null;
+        }
+
+        if ((tagHdr & ~BERTags.CONSTRUCTED) != univTagNo)
+        {
+            throw new IOException("unexpected identifier encountered: " + tagHdr);
+        }
+
+        return implParseObject(tagHdr);
+    }
+
+    ASN1TaggedObjectParser parseTaggedObject() throws IOException
+    {
+        int tagHdr = _in.read();
+        if (tagHdr < 0)
+        {
+            return null;
+        }
+
+        int tagClass = tagHdr & BERTags.PRIVATE;
+        if (0 == tagClass)
+        {
+            throw new ASN1Exception("no tagged object found");
+        }
+
+        return (ASN1TaggedObjectParser)implParseObject(tagHdr);
+    }
+
+    // TODO[asn1] Prefer 'loadVector'
+    ASN1EncodableVector readVector() throws IOException
+    {
+        int tagHdr = _in.read();
+        if (tagHdr < 0)
+        {
+            return new ASN1EncodableVector(0);
+        }
+
+        ASN1EncodableVector v = new ASN1EncodableVector();
+        do
+        {
+            ASN1Encodable obj = implParseObject(tagHdr);
+
+            if (obj instanceof InMemoryRepresentable)
             {
-                return ASN1InputStream.createPrimitiveDERObject(tagNo, defIn, tmpBuffers);
+                v.add(((InMemoryRepresentable) obj).getLoadedObject());
             }
-            catch (IllegalArgumentException e)
+            else
             {
-                throw new ASN1Exception("corrupted stream detected", e);
+                v.add(obj.toASN1Primitive());
             }
         }
+        while ((tagHdr = _in.read()) >= 0);
+        return v;
     }
 
     private void set00Check(boolean enabled)
@@ -227,28 +270,4 @@
             ((IndefiniteLengthInputStream)_in).setEofOn00(enabled);
         }
     }
-
-    ASN1EncodableVector readVector() throws IOException
-    {
-        ASN1Encodable obj = readObject();
-        if (null == obj)
-        {
-            return new ASN1EncodableVector(0);
-        }
-
-        ASN1EncodableVector v = new ASN1EncodableVector();
-        do
-        {
-            if (obj instanceof InMemoryRepresentable)
-            {
-                v.add(((InMemoryRepresentable)obj).getLoadedObject());
-            }
-            else
-            {
-                v.add(obj.toASN1Primitive());
-            }
-        }
-        while ((obj = readObject()) != null);
-        return v;
-    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/ASN1T61String.java b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1T61String.java
new file mode 100644
index 0000000..2592005
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1T61String.java
@@ -0,0 +1,145 @@
+package org.bouncycastle.asn1;
+
+import java.io.IOException;
+
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.Strings;
+
+/**
+ * ASN.1 T61String (also the teletex string), try not to use this if you don't need to. The standard support the encoding for
+ * this has been withdrawn.
+ */
+public abstract class ASN1T61String
+    extends ASN1Primitive
+    implements ASN1String
+{
+    static final ASN1UniversalType TYPE = new ASN1UniversalType(ASN1T61String.class, BERTags.T61_STRING)
+    {
+        ASN1Primitive fromImplicitPrimitive(DEROctetString octetString)
+        {
+            return createPrimitive(octetString.getOctets());
+        }
+    };
+
+    /**
+     * Return a T61 string from the passed in object.
+     *
+     * @param obj an ASN1T61String or an object that can be converted into one.
+     * @exception IllegalArgumentException if the object cannot be converted.
+     * @return an ASN1T61String instance, or null
+     */
+    public static ASN1T61String getInstance(Object obj)
+    {
+        if (obj == null || obj instanceof ASN1T61String)
+        {
+            return (ASN1T61String)obj;
+        }
+        if (obj instanceof ASN1Encodable)
+        {
+            ASN1Primitive primitive = ((ASN1Encodable)obj).toASN1Primitive();
+            if (primitive instanceof ASN1T61String)
+            {
+                return (ASN1T61String)primitive;
+            }
+        }
+        if (obj instanceof byte[])
+        {
+            try
+            {
+                return (ASN1T61String)TYPE.fromByteArray((byte[])obj);
+            }
+            catch (Exception e)
+            {
+                throw new IllegalArgumentException("encoding error in getInstance: " + e.toString());
+            }
+        }
+
+        throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
+    }
+
+    /**
+     * Return an T61 String from a tagged object.
+     *
+     * @param taggedObject      the tagged object holding the object we want
+     * @param explicit true if the object is meant to be explicitly tagged false
+     *                 otherwise.
+     * @exception IllegalArgumentException if the tagged object cannot be converted.
+     * @return an ASN1T61String instance, or null
+     */
+    public static ASN1T61String getInstance(ASN1TaggedObject taggedObject, boolean explicit)
+    {
+        return (ASN1T61String)TYPE.getContextInstance(taggedObject, explicit);
+    }
+
+    final byte[] contents;
+
+    ASN1T61String(String string)
+    {
+        this.contents = Strings.toByteArray(string);
+    }
+
+    ASN1T61String(byte[] contents, boolean clone)
+    {
+        this.contents = clone ? Arrays.clone(contents) : contents;
+    }
+
+    /**
+     * Decode the encoded string and return it, 8 bit encoding assumed.
+     * @return the decoded String
+     */
+    public final String getString()
+    {
+        return Strings.fromByteArray(contents);
+    }
+
+    public String toString()
+    {
+        return getString();
+    }
+
+    final boolean encodeConstructed()
+    {
+        return false;
+    }
+
+    final int encodedLength(boolean withTag)
+    {
+        return ASN1OutputStream.getLengthOfEncodingDL(withTag, contents.length);
+    }
+
+    final void encode(ASN1OutputStream out, boolean withTag) throws IOException
+    {
+        out.writeEncodingDL(withTag, BERTags.T61_STRING, contents);
+    }
+
+    /**
+     * Return the encoded string as a byte array.
+     * @return the actual bytes making up the encoded body of the T61 string.
+     */
+    public final byte[] getOctets()
+    {
+        return Arrays.clone(contents);
+    }
+
+    final boolean asn1Equals(ASN1Primitive other)
+    {
+        if (!(other instanceof ASN1T61String))
+        {
+            return false;
+        }
+
+        ASN1T61String that = (ASN1T61String)other;
+
+        return Arrays.areEqual(this.contents, that.contents);
+    }
+
+    public final int hashCode()
+    {
+        return Arrays.hashCode(contents);
+    }
+
+    static ASN1T61String createPrimitive(byte[] contents)
+    {
+        return new DERT61String(contents, false);
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/ASN1Tag.java b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1Tag.java
new file mode 100644
index 0000000..280e8c6
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1Tag.java
@@ -0,0 +1,28 @@
+package org.bouncycastle.asn1;
+
+final class ASN1Tag
+{
+    static ASN1Tag create(int tagClass, int tagNumber)
+    {
+        return new ASN1Tag(tagClass, tagNumber);
+    }
+
+    private final int tagClass;
+    private final int tagNumber;
+
+    private ASN1Tag(int tagClass, int tagNumber)
+    {
+        this.tagClass = tagClass;
+        this.tagNumber = tagNumber;
+    }
+
+    int getTagClass()
+    {
+        return tagClass;
+    }
+
+    int getTagNumber()
+    {
+        return tagNumber;
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/ASN1TaggedObject.java b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1TaggedObject.java
index 00aac91..78c59b9 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/ASN1TaggedObject.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1TaggedObject.java
@@ -2,6 +2,8 @@
 
 import java.io.IOException;
 
+import org.bouncycastle.util.Arrays;
+
 /**
  * ASN.1 TaggedObject - in ASN.1 notation this is any object preceded by
  * a [n] where n is some number - these are assumed to follow the construction
@@ -11,34 +13,32 @@
     extends ASN1Primitive
     implements ASN1TaggedObjectParser
 {
-    final int           tagNo;
-    final boolean       explicit;
-    final ASN1Encodable obj;
+    private static final int DECLARED_EXPLICIT = 1;
+    private static final int DECLARED_IMPLICIT = 2;
+    // TODO It will probably be better to track parsing constructed vs primitive instead
+    private static final int PARSED_EXPLICIT = 3;
+    private static final int PARSED_IMPLICIT = 4;
 
-    static public ASN1TaggedObject getInstance(
-        ASN1TaggedObject    obj,
-        boolean             explicit)
-    {
-        if (explicit)
-        {
-            return getInstance(obj.getObject());
-        }
-
-        throw new IllegalArgumentException("implicitly tagged tagged object");
-    }
-
-    static public ASN1TaggedObject getInstance(
-        Object obj) 
+    public static ASN1TaggedObject getInstance(Object obj)
     {
         if (obj == null || obj instanceof ASN1TaggedObject) 
         {
             return (ASN1TaggedObject)obj;
         }
+//      else if (obj instanceof ASN1TaggedObjectParser)
+        else if (obj instanceof ASN1Encodable)
+        {
+            ASN1Primitive primitive = ((ASN1Encodable)obj).toASN1Primitive();
+            if (primitive instanceof ASN1TaggedObject)
+            {
+                return (ASN1TaggedObject)primitive;
+            }
+        }
         else if (obj instanceof byte[])
         {
             try
             {
-                return ASN1TaggedObject.getInstance(fromByteArray((byte[])obj));
+                return checkedCast(fromByteArray((byte[])obj));
             }
             catch (IOException e)
             {
@@ -49,6 +49,58 @@
         throw new IllegalArgumentException("unknown object in getInstance: " + obj.getClass().getName());
     }
 
+    public static ASN1TaggedObject getInstance(Object obj, int tagClass)
+    {
+        if (obj == null)
+        {
+            throw new NullPointerException("'obj' cannot be null");
+        }
+
+        ASN1TaggedObject taggedObject = getInstance(obj);
+        if (tagClass != taggedObject.getTagClass())
+        {
+            throw new IllegalArgumentException("unexpected tag in getInstance: " + ASN1Util.getTagText(taggedObject));
+        }
+
+        return taggedObject;
+    }
+
+    public static ASN1TaggedObject getInstance(Object obj, int tagClass, int tagNo)
+    {
+        if (obj == null)
+        {
+            throw new NullPointerException("'obj' cannot be null");
+        }
+
+        ASN1TaggedObject taggedObject = getInstance(obj);
+        if (!taggedObject.hasTag(tagClass, tagNo))
+        {
+            throw new IllegalArgumentException("unexpected tag in getInstance: " + ASN1Util.getTagText(taggedObject));
+        }
+
+        return taggedObject;
+    }
+
+    public static ASN1TaggedObject getInstance(ASN1TaggedObject taggedObject, boolean declaredExplicit)
+    {
+        if (BERTags.CONTEXT_SPECIFIC != taggedObject.getTagClass())
+        {
+            throw new IllegalStateException("this method only valid for CONTEXT_SPECIFIC tags");
+        }
+
+        if (declaredExplicit)
+        {
+            return taggedObject.getExplicitBaseTagged();
+        }
+
+        throw new IllegalArgumentException("this method not valid for implicitly tagged tagged objects");
+    }
+
+    final int           explicitness;
+    final int           tagClass;
+    final int           tagNo;
+    final ASN1Encodable obj;
+
     /**
      * Create a tagged object with the style given by the value of explicit.
      * <p>
@@ -59,22 +111,34 @@
      * @param tagNo the tag number for this object.
      * @param obj the tagged object.
      */
-    public ASN1TaggedObject(
-        boolean         explicit,
-        int             tagNo,
-        ASN1Encodable   obj)
+    protected ASN1TaggedObject(boolean explicit, int tagNo, ASN1Encodable obj)
+    {
+        this(explicit, BERTags.CONTEXT_SPECIFIC, tagNo, obj);
+    }
+
+    protected ASN1TaggedObject(boolean explicit, int tagClass, int tagNo, ASN1Encodable obj)
+    {
+        this(explicit ? DECLARED_EXPLICIT : DECLARED_IMPLICIT, tagClass, tagNo, obj);
+    }
+
+    ASN1TaggedObject(int explicitness, int tagClass, int tagNo, ASN1Encodable obj)
     {
         if (null == obj)
         {
             throw new NullPointerException("'obj' cannot be null");
         }
+        if (tagClass == BERTags.UNIVERSAL || (tagClass & BERTags.PRIVATE) != tagClass)
+        {
+            throw new IllegalArgumentException("invalid tag class: " + tagClass);
+        }
 
+        this.explicitness = (obj instanceof ASN1Choice) ? DECLARED_EXPLICIT : explicitness;
+        this.tagClass = tagClass;
         this.tagNo = tagNo;
-        this.explicit = explicit || (obj instanceof ASN1Choice);
         this.obj = obj;
     }
 
-    boolean asn1Equals(ASN1Primitive other)
+    final boolean asn1Equals(ASN1Primitive other)
     {
         if (!(other instanceof ASN1TaggedObject))
         {
@@ -83,20 +147,58 @@
 
         ASN1TaggedObject that = (ASN1TaggedObject)other;
 
-        if (this.tagNo != that.tagNo || this.explicit != that.explicit)
+        if (this.tagNo != that.tagNo ||
+            this.tagClass != that.tagClass)
         {
             return false;
         }
 
+        if (this.explicitness != that.explicitness)
+        {
+            /*
+             * TODO This seems incorrect for some cases of implicit tags e.g. if one is a
+             * declared-implicit SET and the other a parsed object.
+             */
+            if (this.isExplicit() != that.isExplicit())
+            {
+                return false;
+            }
+        }
+
         ASN1Primitive p1 = this.obj.toASN1Primitive();
         ASN1Primitive p2 = that.obj.toASN1Primitive();
 
-        return p1 == p2 || p1.asn1Equals(p2);
+        if (p1 == p2)
+        {
+            return true;
+        }
+
+        if (!this.isExplicit())
+        {
+            try
+            {
+                byte[] d1 = this.getEncoded();
+                byte[] d2 = that.getEncoded();
+                
+                return Arrays.areEqual(d1, d2);
+            }
+            catch (IOException e)
+            {
+                return false;
+            }
+        }
+
+        return p1.asn1Equals(p2);
     }
 
     public int hashCode()
     {
-        return tagNo ^ (explicit ? 0x0F : 0xF0) ^ obj.toASN1Primitive().hashCode();
+        return (tagClass * 7919) ^ tagNo ^ (isExplicit() ? 0x0F : 0xF0) ^ obj.toASN1Primitive().hashCode();
+    }
+
+    public int getTagClass()
+    {
+        return tagClass;
     }
 
     /**
@@ -109,18 +211,24 @@
         return tagNo;
     }
 
-    /**
-     * return whether or not the object may be explicitly tagged. 
-     * <p>
-     * Note: if the object has been read from an input stream, the only
-     * time you can be sure if isExplicit is returning the true state of
-     * affairs is if it returns false. An implicitly tagged object may appear
-     * to be explicitly tagged, so you need to understand the context under
-     * which the reading was done as well, see getObject below.
-     */
-    public boolean isExplicit()
+    public boolean hasContextTag()
     {
-        return explicit;
+        return this.tagClass == BERTags.CONTEXT_SPECIFIC;
+    }
+
+    public boolean hasContextTag(int tagNo)
+    {
+        return this.tagClass == BERTags.CONTEXT_SPECIFIC && this.tagNo == tagNo;
+    }
+
+    public boolean hasTag(int tagClass, int tagNo)
+    {
+        return this.tagClass == tagClass && this.tagNo == tagNo;
+    }
+
+    public boolean hasTagClass(int tagClass)
+    {
+        return this.tagClass == tagClass;
     }
 
     /**
@@ -136,52 +244,246 @@
     }
 
     /**
-     * Return the object held in this tagged object as a parser assuming it has
-     * the type of the passed in tag. If the object doesn't have a parser
-     * associated with it, the base object is returned.
+     * return whether or not the object may be explicitly tagged. 
+     * <p>
+     * Note: if the object has been read from an input stream, the only
+     * time you can be sure if isExplicit is returning the true state of
+     * affairs is if it returns false. An implicitly tagged object may appear
+     * to be explicitly tagged, so you need to understand the context under
+     * which the reading was done as well, see getObject below.
      */
-    public ASN1Encodable getObjectParser(
-        int     tag,
-        boolean isExplicit)
-        throws IOException
+    public boolean isExplicit()
     {
-        switch (tag)
+        // TODO New methods like 'isKnownExplicit' etc. to distinguish uncertain cases?
+        switch (explicitness)
         {
-        case BERTags.SET:
-            return ASN1Set.getInstance(this, isExplicit).parser();
-        case BERTags.SEQUENCE:
-            return ASN1Sequence.getInstance(this, isExplicit).parser();
+        case DECLARED_EXPLICIT:
+        case PARSED_EXPLICIT:
+            return true;
+        default:
+            return false;
+        }
+    }
+
+    boolean isParsed()
+    {
+        switch (explicitness)
+        {
+        case PARSED_EXPLICIT:
+        case PARSED_IMPLICIT:
+            return true;
+        default:
+            return false;
+        }
+    }
+
+    /**
+     * Needed for open types, until we have better type-guided parsing support. Use sparingly for other
+     * purposes, and prefer {@link #getExplicitBaseTagged()}, {@link #getImplicitBaseTagged(int, int)} or
+     * {@link #getBaseUniversal(boolean, int)} where possible. Before using, check for matching tag
+     * {@link #getTagClass() class} and {@link #getTagNo() number}.
+     */
+    public ASN1Object getBaseObject()
+    {
+        return obj instanceof ASN1Object ? (ASN1Object)obj : obj.toASN1Primitive();
+    }
+
+    /**
+     * Needed for open types, until we have better type-guided parsing support. Use
+     * sparingly for other purposes, and prefer {@link #getExplicitBaseTagged()} or
+     * {@link #getBaseUniversal(boolean, int)} where possible. Before using, check
+     * for matching tag {@link #getTagClass() class} and {@link #getTagNo() number}.
+     */
+    public ASN1Object getExplicitBaseObject()
+    {
+        if (!isExplicit())
+        {
+            throw new IllegalStateException("object implicit - explicit expected.");
+        }
+
+        return obj instanceof ASN1Object ? (ASN1Object)obj : obj.toASN1Primitive();
+    }
+
+    public ASN1TaggedObject getExplicitBaseTagged()
+    {
+        if (!isExplicit())
+        {
+            throw new IllegalStateException("object implicit - explicit expected.");
+        }
+
+        return checkedCast(obj.toASN1Primitive());
+    }
+
+    public ASN1TaggedObject getImplicitBaseTagged(int baseTagClass, int baseTagNo)
+    {
+        if (baseTagClass == BERTags.UNIVERSAL || (baseTagClass & BERTags.PRIVATE) != baseTagClass)
+        {
+            throw new IllegalArgumentException("invalid base tag class: " + baseTagClass);
+        }
+
+        switch (explicitness)
+        {
+        case DECLARED_EXPLICIT:
+            throw new IllegalStateException("object explicit - implicit expected.");
+
+        case DECLARED_IMPLICIT:
+        {
+            ASN1TaggedObject declared = checkedCast(obj.toASN1Primitive());
+            return ASN1Util.checkTag(declared, baseTagClass, baseTagNo);
+        }
+
+        // Parsed; return a virtual tag (i.e. that couldn't have been present in the encoding)
+        default:
+            return replaceTag(baseTagClass, baseTagNo);
+        }
+    }
+
+    /**
+     * Note: tagged objects are generally context dependent. Before trying to
+     * extract a tagged object this way, make sure you have checked that both the
+     * {@link #getTagClass() tag class} and {@link #getTagNo() tag number} match
+     * what you are looking for.
+     * 
+     * @param declaredExplicit Whether the tagged type for this object was declared
+     *                         EXPLICIT.
+     * @param tagNo            The universal {@link BERTags tag number} of the
+     *                         expected base object.
+     */
+    public ASN1Primitive getBaseUniversal(boolean declaredExplicit, int tagNo)
+    {
+        ASN1UniversalType universalType = ASN1UniversalTypes.get(tagNo);
+        if (null == universalType)
+        {
+            throw new IllegalArgumentException("unsupported UNIVERSAL tag number: " + tagNo);
+        }
+
+        return getBaseUniversal(declaredExplicit, universalType);
+    }
+
+    ASN1Primitive getBaseUniversal(boolean declaredExplicit, ASN1UniversalType universalType)
+    {
+        if (declaredExplicit)
+        {
+            if (!isExplicit())
+            {
+                throw new IllegalStateException("object explicit - implicit expected.");
+            }
+
+            return universalType.checkedCast(obj.toASN1Primitive());
+        }
+
+        if (DECLARED_EXPLICIT == explicitness)
+        {
+            throw new IllegalStateException("object explicit - implicit expected.");
+        }
+
+        ASN1Primitive primitive = obj.toASN1Primitive();
+        switch (explicitness)
+        {
+        case PARSED_EXPLICIT:
+            return universalType.fromImplicitConstructed(rebuildConstructed(primitive));
+        case PARSED_IMPLICIT:
+        {
+            if (primitive instanceof ASN1Sequence)
+            {
+                return universalType.fromImplicitConstructed((ASN1Sequence)primitive);
+            }
+            return universalType.fromImplicitPrimitive((DEROctetString)primitive);
+        }
+        default:
+            return universalType.checkedCast(primitive);
+        }
+    }
+
+    public ASN1Encodable parseBaseUniversal(boolean declaredExplicit, int baseTagNo) throws IOException
+    {
+        ASN1Primitive primitive = getBaseUniversal(declaredExplicit, baseTagNo);
+
+        switch (baseTagNo)
+        {
+        case BERTags.BIT_STRING:
+            return ((ASN1BitString)primitive).parser();
         case BERTags.OCTET_STRING:
-            return ASN1OctetString.getInstance(this, isExplicit).parser();
+            return ((ASN1OctetString)primitive).parser();
+        case BERTags.SEQUENCE:
+            return ((ASN1Sequence)primitive).parser();
+        case BERTags.SET:
+            return ((ASN1Set)primitive).parser();
         }
 
-        if (isExplicit)
-        {
-            return getObject();
-        }
-
-        throw new ASN1Exception("implicit tagging not implemented for tag: " + tag);
+        return primitive;
     }
 
-    public ASN1Primitive getLoadedObject()
+    public ASN1Encodable parseExplicitBaseObject() throws IOException
     {
-        return this.toASN1Primitive();
+        return getExplicitBaseObject();
     }
 
+    public ASN1TaggedObjectParser parseExplicitBaseTagged() throws IOException
+    {
+        return getExplicitBaseTagged();
+    }
+
+    public ASN1TaggedObjectParser parseImplicitBaseTagged(int baseTagClass, int baseTagNo) throws IOException
+    {
+        return getImplicitBaseTagged(baseTagClass, baseTagNo);
+    }
+
+    public final ASN1Primitive getLoadedObject()
+    {
+        return this;
+    }
+
+    abstract ASN1Sequence rebuildConstructed(ASN1Primitive primitive);
+
+    abstract ASN1TaggedObject replaceTag(int tagClass, int tagNo);
+
     ASN1Primitive toDERObject()
     {
-        return new DERTaggedObject(explicit, tagNo, obj);
+        return new DERTaggedObject(explicitness, tagClass, tagNo, obj);
     }
 
     ASN1Primitive toDLObject()
     {
-        return new DLTaggedObject(explicit, tagNo, obj);
+        return new DLTaggedObject(explicitness, tagClass, tagNo, obj);
     }
 
-    abstract void encode(ASN1OutputStream out, boolean withTag) throws IOException;
-
     public String toString()
     {
-        return "[" + tagNo + "]" + obj;
+        return ASN1Util.getTagText(tagClass, tagNo) + obj;
+    }
+
+    static ASN1Primitive createConstructedDL(int tagClass, int tagNo, ASN1EncodableVector contentsElements)
+    {
+        boolean maybeExplicit = (contentsElements.size() == 1);
+
+        return maybeExplicit
+            ?   new DLTaggedObject(PARSED_EXPLICIT, tagClass, tagNo, contentsElements.get(0))
+            :   new DLTaggedObject(PARSED_IMPLICIT, tagClass, tagNo, DLFactory.createSequence(contentsElements));
+    }
+
+    static ASN1Primitive createConstructedIL(int tagClass, int tagNo, ASN1EncodableVector contentsElements)
+    {
+        boolean maybeExplicit = (contentsElements.size() == 1);
+
+        return maybeExplicit
+            ?   new BERTaggedObject(PARSED_EXPLICIT, tagClass, tagNo, contentsElements.get(0))
+            :   new BERTaggedObject(PARSED_IMPLICIT, tagClass, tagNo, BERFactory.createSequence(contentsElements));
+    }
+
+    static ASN1Primitive createPrimitive(int tagClass, int tagNo, byte[] contentsOctets)
+    {
+        // Note: !CONSTRUCTED => IMPLICIT
+        return new DLTaggedObject(PARSED_IMPLICIT, tagClass, tagNo, new DEROctetString(contentsOctets));
+    }
+
+    private static ASN1TaggedObject checkedCast(ASN1Primitive primitive)
+    {
+        if (primitive instanceof ASN1TaggedObject)
+        {
+            return (ASN1TaggedObject)primitive;
+        }
+
+        throw new IllegalStateException("unexpected object: " + primitive.getClass().getName());
     }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/ASN1TaggedObjectParser.java b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1TaggedObjectParser.java
index 660d9a2..f92bc1c 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/ASN1TaggedObjectParser.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1TaggedObjectParser.java
@@ -9,19 +9,38 @@
     extends ASN1Encodable, InMemoryRepresentable
 {
     /**
-     * Return the tag number associated with the underlying tagged object.
-     * @return the object's tag number.
+     * Return the tag class associated with this object.
+     *
+     * @return the tag class.
+     */
+    int getTagClass();
+
+    /**
+     * Return the tag number associated with this object.
+     *
+     * @return the tag number.
      */
     int getTagNo();
 
+    boolean hasContextTag();
+
+    boolean hasContextTag(int tagNo);
+
+    boolean hasTag(int tagClass, int tagNo);
+
+    boolean hasTagClass(int tagClass);
+
+    ASN1Encodable parseBaseUniversal(boolean declaredExplicit, int baseTagNo) throws IOException;
+
     /**
-     * Return a parser for the actual object tagged.
-     *
-     * @param tag the primitive tag value for the object tagged originally.
-     * @param isExplicit true if the tagging was done explicitly.
-     * @return a parser for the tagged object.
-     * @throws IOException if a parser cannot be constructed.
+     * Needed for open types, until we have better type-guided parsing support. Use sparingly for other
+     * purposes, and prefer {@link #parseExplicitBaseTagged()} or {@link #parseBaseUniversal(boolean, int)}
+     * where possible. Before using, check for matching tag {@link #getTagClass() class} and
+     * {@link #getTagNo() number}.
      */
-    ASN1Encodable getObjectParser(int tag, boolean isExplicit)
-        throws IOException;
+    ASN1Encodable parseExplicitBaseObject() throws IOException;
+
+    ASN1TaggedObjectParser parseExplicitBaseTagged() throws IOException;
+
+    ASN1TaggedObjectParser parseImplicitBaseTagged(int baseTagClass, int baseTagNo) throws IOException;
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/ASN1Type.java b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1Type.java
new file mode 100644
index 0000000..5ab85d7
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1Type.java
@@ -0,0 +1,26 @@
+package org.bouncycastle.asn1;
+
+abstract class ASN1Type
+{
+    final Class javaClass;
+
+    ASN1Type(Class javaClass)
+    {
+        this.javaClass = javaClass;
+    }
+
+    final Class getJavaClass()
+    {
+        return javaClass;
+    }
+
+    public final boolean equals(Object that)
+    {
+        return this == that;
+    }
+
+    public final int hashCode()
+    {
+        return super.hashCode();
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/ASN1UTCTime.java b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1UTCTime.java
index d9a2629..34ba2fe 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/ASN1UTCTime.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1UTCTime.java
@@ -36,7 +36,13 @@
 public class ASN1UTCTime
     extends ASN1Primitive
 {
-    private byte[]      time;
+    static final ASN1UniversalType TYPE = new ASN1UniversalType(ASN1UTCTime.class, BERTags.UTC_TIME)
+    {
+        ASN1Primitive fromImplicitPrimitive(DEROctetString octetString)
+        {
+            return createPrimitive(octetString.getOctets());
+        }
+    };
 
     /**
      * Return an UTC Time from the passed in object.
@@ -52,12 +58,19 @@
         {
             return (ASN1UTCTime)obj;
         }
-
+        if (obj instanceof ASN1Encodable)
+        {
+            ASN1Primitive primitive = ((ASN1Encodable)obj).toASN1Primitive();
+            if (primitive instanceof ASN1UTCTime)
+            {
+                return (ASN1UTCTime)primitive;
+            }
+        }
         if (obj instanceof byte[])
         {
             try
             {
-                return (ASN1UTCTime)fromByteArray((byte[])obj);
+                return (ASN1UTCTime)TYPE.fromByteArray((byte[])obj);
             }
             catch (Exception e)
             {
@@ -71,29 +84,19 @@
     /**
      * Return an UTC Time from a tagged object.
      *
-     * @param obj the tagged object holding the object we want
-     * @param explicit true if the object is meant to be explicitly
-     *              tagged false otherwise.
-     * @exception IllegalArgumentException if the tagged object cannot
-     *               be converted.
+     * @param taggedObject the tagged object holding the object we want
+     * @param explicit     true if the object is meant to be explicitly tagged false
+     *                     otherwise.
+     * @exception IllegalArgumentException if the tagged object cannot be converted.
      * @return an ASN1UTCTime instance, or null.
      */
-    public static ASN1UTCTime getInstance(
-        ASN1TaggedObject obj,
-        boolean          explicit)
+    public static ASN1UTCTime getInstance(ASN1TaggedObject taggedObject, boolean explicit)
     {
-        ASN1Object o = obj.getObject();
-
-        if (explicit || o instanceof ASN1UTCTime)
-        {
-            return getInstance(o);
-        }
-        else
-        {
-            return new ASN1UTCTime(ASN1OctetString.getInstance(o).getOctets());
-        }
+        return (ASN1UTCTime)TYPE.getContextInstance(taggedObject, explicit);
     }
 
+    final byte[] contents;
+
     /**
      * The correct format for this is YYMMDDHHMMSSZ (it used to be that seconds were
      * never encoded. When you're creating one of these objects from scratch, that's
@@ -107,7 +110,7 @@
     public ASN1UTCTime(
         String time)
     {
-        this.time = Strings.toByteArray(time);
+        this.contents = Strings.toByteArray(time);
         try
         {
             this.getDate();
@@ -131,7 +134,7 @@
 
         dateF.setTimeZone(new SimpleTimeZone(0,"Z"));
 
-        this.time = Strings.toByteArray(dateF.format(time));
+        this.contents = Strings.toByteArray(dateF.format(time));
     }
 
     /**
@@ -153,17 +156,16 @@
 
         dateF.setTimeZone(new SimpleTimeZone(0,"Z"));
 
-        this.time = Strings.toByteArray(dateF.format(time));
+        this.contents = Strings.toByteArray(dateF.format(time));
     }
 
-    ASN1UTCTime(
-        byte[] time)
+    ASN1UTCTime(byte[] contents)
     {
-        if (time.length < 2)
+        if (contents.length < 2)
         {
             throw new IllegalArgumentException("UTCTime string too short");
         }
-        this.time = time;
+        this.contents = contents;
         if (!(isDigit(0) && isDigit(1)))
         {
             throw new IllegalArgumentException("illegal characters in UTCTime string");
@@ -184,7 +186,7 @@
         // SimpleDateFormat dateF = new SimpleDateFormat("yyMMddHHmmssz");
         SimpleDateFormat dateF = new SimpleDateFormat("yyMMddHHmmssz", Locale.US);
 
-        return DateUtil.epochAdjust(dateF.parse(getTime()));
+        return dateF.parse(getTime());
     }
 
     /**
@@ -202,8 +204,8 @@
         SimpleDateFormat dateF = new SimpleDateFormat("yyyyMMddHHmmssz", Locale.US);
 
         dateF.setTimeZone(new SimpleTimeZone(0,"Z"));
-        
-        return DateUtil.epochAdjust(dateF.parse(getAdjustedTime()));
+
+        return dateF.parse(getAdjustedTime());
     }
 
     /**
@@ -224,7 +226,7 @@
      */
     public String getTime()
     {
-        String stime = Strings.fromByteArray(time);
+        String stime = Strings.fromByteArray(contents);
 
         //
         // standardise the format.
@@ -285,24 +287,22 @@
 
     private boolean isDigit(int pos)
     {
-        return time.length > pos && time[pos] >= '0' && time[pos] <= '9';
+        return contents.length > pos && contents[pos] >= '0' && contents[pos] <= '9';
     }
 
-    boolean isConstructed()
+    final boolean encodeConstructed()
     {
         return false;
     }
 
-    int encodedLength()
+    int encodedLength(boolean withTag)
     {
-        int length = time.length;
-
-        return 1 + StreamUtil.calculateBodyLength(length) + length;
+        return ASN1OutputStream.getLengthOfEncodingDL(withTag, contents.length);
     }
 
     void encode(ASN1OutputStream out, boolean withTag) throws IOException
     {
-        out.writeEncoded(withTag, BERTags.UTC_TIME, time);
+        out.writeEncodingDL(withTag, BERTags.UTC_TIME, contents);
     }
 
     boolean asn1Equals(
@@ -313,16 +313,21 @@
             return false;
         }
 
-        return Arrays.areEqual(time, ((ASN1UTCTime)o).time);
+        return Arrays.areEqual(contents, ((ASN1UTCTime)o).contents);
     }
 
     public int hashCode()
     {
-        return Arrays.hashCode(time);
+        return Arrays.hashCode(contents);
     }
 
     public String toString()
     {
-      return Strings.fromByteArray(time);
+      return Strings.fromByteArray(contents);
+    }
+
+    static ASN1UTCTime createPrimitive(byte[] contents)
+    {
+        return new ASN1UTCTime(contents);
     }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/ASN1UTF8String.java b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1UTF8String.java
new file mode 100644
index 0000000..8f8d00c
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1UTF8String.java
@@ -0,0 +1,129 @@
+package org.bouncycastle.asn1;
+
+import java.io.IOException;
+
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.Strings;
+
+public abstract class ASN1UTF8String
+    extends ASN1Primitive
+    implements ASN1String
+{
+    static final ASN1UniversalType TYPE = new ASN1UniversalType(ASN1UTF8String.class, BERTags.UTF8_STRING)
+    {
+        ASN1Primitive fromImplicitPrimitive(DEROctetString octetString)
+        {
+            return createPrimitive(octetString.getOctets());
+        }
+    };
+
+    /**
+     * Return a UTF8 string from the passed in object.
+     *
+     * @param obj an ASN1UTF8String or an object that can be converted into one.
+     * @exception IllegalArgumentException if the object cannot be converted.
+     * @return an ASN1UTF8String instance, or null
+     */
+    public static ASN1UTF8String getInstance(Object obj)
+    {
+        if (obj == null || obj instanceof ASN1UTF8String)
+        {
+            return (ASN1UTF8String)obj;
+        }
+        if (obj instanceof ASN1Encodable)
+        {
+            ASN1Primitive primitive = ((ASN1Encodable)obj).toASN1Primitive();
+            if (primitive instanceof ASN1UTF8String)
+            {
+                return (ASN1UTF8String)primitive;
+            }
+        }
+        if (obj instanceof byte[])
+        {
+            try
+            {
+                return (ASN1UTF8String)TYPE.fromByteArray((byte[])obj);
+            }
+            catch (Exception e)
+            {
+                throw new IllegalArgumentException("encoding error in getInstance: " + e.toString());
+            }
+        }
+
+        throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
+    }
+
+    /**
+     * Return an UTF8 String from a tagged object.
+     * 
+     * @param taggedObject the tagged object holding the object we want
+     * @param explicit     true if the object is meant to be explicitly tagged false
+     *                     otherwise.
+     * @exception IllegalArgumentException if the tagged object cannot be converted.
+     * @return a DERUTF8String instance, or null
+     */
+    public static ASN1UTF8String getInstance(ASN1TaggedObject taggedObject, boolean explicit)
+    {
+        return (ASN1UTF8String)TYPE.getContextInstance(taggedObject, explicit);
+    }
+
+    final byte[] contents;
+
+    ASN1UTF8String(String string)
+    {
+        this(Strings.toUTF8ByteArray(string), false);
+    }
+
+    ASN1UTF8String(byte[] contents, boolean clone)
+    {
+        this.contents = clone ? Arrays.clone(contents) : contents;
+    }
+
+    public final String getString()
+    {
+        return Strings.fromUTF8ByteArray(contents);
+    }
+
+    // TODO Not sure this is useful unless all ASN.1 types have a meaningful one
+    public String toString()
+    {
+        return getString();
+    }
+
+    public final int hashCode()
+    {
+        return Arrays.hashCode(contents);
+    }
+
+    final boolean asn1Equals(ASN1Primitive other)
+    {
+        if (!(other instanceof ASN1UTF8String))
+        {
+            return false;
+        }
+
+        ASN1UTF8String that = (ASN1UTF8String)other;
+
+        return Arrays.areEqual(this.contents, that.contents);
+    }
+
+    final boolean encodeConstructed()
+    {
+        return false;
+    }
+
+    final int encodedLength(boolean withTag)
+    {
+        return ASN1OutputStream.getLengthOfEncodingDL(withTag, contents.length);
+    }
+
+    final void encode(ASN1OutputStream out, boolean withTag) throws IOException
+    {
+        out.writeEncodingDL(withTag, BERTags.UTF8_STRING, contents);
+    }
+
+    static ASN1UTF8String createPrimitive(byte[] contents)
+    {
+        return new DERUTF8String(contents, false);
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/ASN1UniversalString.java b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1UniversalString.java
new file mode 100644
index 0000000..fe8addf
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1UniversalString.java
@@ -0,0 +1,178 @@
+package org.bouncycastle.asn1;
+
+import java.io.IOException;
+
+import org.bouncycastle.util.Arrays;
+
+/**
+ * ASN.1 UniversalString object - encodes UNICODE (ISO 10646) characters using 32-bit format. In Java we
+ * have no way of representing this directly so we rely on byte arrays to carry these.
+ */
+public abstract class ASN1UniversalString
+    extends ASN1Primitive
+    implements ASN1String
+{
+    static final ASN1UniversalType TYPE = new ASN1UniversalType(ASN1UniversalString.class, BERTags.UNIVERSAL_STRING)
+    {
+        ASN1Primitive fromImplicitPrimitive(DEROctetString octetString)
+        {
+            return createPrimitive(octetString.getOctets());
+        }
+    };
+
+    private static final char[]  table = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
+
+    /**
+     * Return a Universal String from the passed in object.
+     *
+     * @param obj an ASN1UniversalString or an object that can be converted into
+     *            one.
+     * @exception IllegalArgumentException if the object cannot be converted.
+     * @return an ASN1UniversalString instance, or null
+     */
+    public static ASN1UniversalString getInstance(Object obj)
+    {
+        if (obj == null || obj instanceof ASN1UniversalString)
+        {
+            return (ASN1UniversalString)obj;
+        }
+        if (obj instanceof ASN1Encodable)
+        {
+            ASN1Primitive primitive = ((ASN1Encodable)obj).toASN1Primitive();
+            if (primitive instanceof ASN1UniversalString)
+            {
+                return (ASN1UniversalString)primitive;
+            }
+        }
+        if (obj instanceof byte[])
+        {
+            try
+            {
+                return (ASN1UniversalString)TYPE.fromByteArray((byte[])obj);
+            }
+            catch (Exception e)
+            {
+                throw new IllegalArgumentException("encoding error getInstance: " + e.toString());
+            }
+        }
+
+        throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
+    }
+
+    /**
+     * Return a Universal String from a tagged object.
+     *
+     * @param taggedObject      the tagged object holding the object we want
+     * @param explicit true if the object is meant to be explicitly tagged false
+     *                 otherwise.
+     * @exception IllegalArgumentException if the tagged object cannot be converted.
+     * @return a ASN1UniversalString instance, or null
+     */
+    public static ASN1UniversalString getInstance(ASN1TaggedObject taggedObject, boolean explicit)
+    {
+        return (ASN1UniversalString)TYPE.getContextInstance(taggedObject, explicit);
+    }
+
+    final byte[] contents;
+
+    ASN1UniversalString(byte[] contents, boolean clone)
+    {
+        this.contents = clone ? Arrays.clone(contents) : contents;
+    }
+
+    public final String getString()
+    {
+        int dl = contents.length;
+        StringBuffer buf = new StringBuffer(3 + 2 * (ASN1OutputStream.getLengthOfDL(dl) + dl));
+        buf.append("#1C");
+        encodeHexDL(buf, dl);
+
+        for (int i = 0; i < dl; ++i)
+        {
+            encodeHexByte(buf, contents[i]);
+        }
+
+        return buf.toString();
+    }
+
+    public String toString()
+    {
+        return getString();
+    }
+
+    public final byte[] getOctets()
+    {
+        return Arrays.clone(contents);
+    }
+
+    final boolean encodeConstructed()
+    {
+        return false;
+    }
+
+    final int encodedLength(boolean withTag)
+    {
+        return ASN1OutputStream.getLengthOfEncodingDL(withTag, contents.length);
+    }
+
+    final void encode(ASN1OutputStream out, boolean withTag) throws IOException
+    {
+        out.writeEncodingDL(withTag, BERTags.UNIVERSAL_STRING, contents);
+    }
+
+    final boolean asn1Equals(ASN1Primitive other)
+    {
+        if (!(other instanceof ASN1UniversalString))
+        {
+            return false;
+        }
+
+        ASN1UniversalString that = (ASN1UniversalString)other;
+
+        return Arrays.areEqual(this.contents, that.contents);
+    }
+
+    public final int hashCode()
+    {
+        return Arrays.hashCode(contents);
+    }
+
+    static ASN1UniversalString createPrimitive(byte[] contents)
+    {
+        return new DERUniversalString(contents, false);
+    }
+
+    private static void encodeHexByte(StringBuffer buf, int i)
+    {
+        buf.append(table[(i >>> 4) & 0xF]);
+        buf.append(table[i & 0xF]);
+    }
+
+    private static void encodeHexDL(StringBuffer buf, int dl)
+    {
+        if (dl < 128)
+        {
+            encodeHexByte(buf, dl);
+            return;
+        }
+
+        byte[] stack = new byte[5];
+        int pos = 5;
+
+        do
+        {
+            stack[--pos] = (byte)dl;
+            dl >>>= 8;
+        }
+        while (dl != 0);
+
+        int count = stack.length - pos;
+        stack[--pos] = (byte)(0x80 | count);
+
+        do
+        {
+            encodeHexByte(buf, stack[pos++]);
+        }
+        while (pos < stack.length);
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/ASN1UniversalType.java b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1UniversalType.java
new file mode 100644
index 0000000..f0bde9d
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1UniversalType.java
@@ -0,0 +1,56 @@
+package org.bouncycastle.asn1;
+
+import java.io.IOException;
+
+abstract class ASN1UniversalType
+    extends ASN1Type
+{
+    final ASN1Tag tag;
+
+    ASN1UniversalType(Class javaClass, int tagNumber)
+    {
+        super(javaClass);
+
+        this.tag = ASN1Tag.create(BERTags.UNIVERSAL, tagNumber);
+    }
+
+    final ASN1Primitive checkedCast(ASN1Primitive primitive)
+    {
+        if (javaClass.isInstance(primitive))
+        {
+            return primitive;
+        }
+
+        throw new IllegalStateException("unexpected object: " + primitive.getClass().getName());
+    }
+
+    ASN1Primitive fromImplicitPrimitive(DEROctetString octetString)
+    {
+        throw new IllegalStateException("unexpected implicit primitive encoding");
+    }
+
+    ASN1Primitive fromImplicitConstructed(ASN1Sequence sequence)
+    {
+        throw new IllegalStateException("unexpected implicit constructed encoding");
+    }
+
+    final ASN1Primitive fromByteArray(byte[] bytes) throws IOException
+    {
+        return checkedCast(ASN1Primitive.fromByteArray(bytes));
+    }
+
+    final ASN1Primitive getContextInstance(ASN1TaggedObject taggedObject, boolean declaredExplicit)
+    {
+        if (BERTags.CONTEXT_SPECIFIC != taggedObject.getTagClass())
+        {
+            throw new IllegalStateException("this method only valid for CONTEXT_SPECIFIC tags");
+        }
+
+        return checkedCast(taggedObject.getBaseUniversal(declaredExplicit, this));
+    }
+
+    final ASN1Tag getTag()
+    {
+        return tag;
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/ASN1UniversalTypes.java b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1UniversalTypes.java
new file mode 100644
index 0000000..da217fa
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1UniversalTypes.java
@@ -0,0 +1,71 @@
+package org.bouncycastle.asn1;
+
+final class ASN1UniversalTypes
+{
+    private ASN1UniversalTypes()
+    {
+    }
+
+    static ASN1UniversalType get(int tagNumber)
+    {
+        switch (tagNumber)
+        {
+        case BERTags.BOOLEAN:
+            return ASN1Boolean.TYPE;
+        case BERTags.INTEGER:
+            return ASN1Integer.TYPE;
+        case BERTags.BIT_STRING:
+            return ASN1BitString.TYPE;
+        case BERTags.OCTET_STRING:
+            return ASN1OctetString.TYPE;
+        case BERTags.NULL:
+            return ASN1Null.TYPE;
+        case BERTags.OBJECT_IDENTIFIER:
+            return ASN1ObjectIdentifier.TYPE;
+        case BERTags.OBJECT_DESCRIPTOR:         // [UNIVERSAL 7] IMPLICIT GraphicString
+            return ASN1ObjectDescriptor.TYPE;
+        case BERTags.EXTERNAL:
+            return ASN1External.TYPE;
+        case BERTags.ENUMERATED:
+            return ASN1Enumerated.TYPE;
+        case BERTags.UTF8_STRING:               // [UNIVERSAL 12] IMPLICIT OCTET STRING (encode as if)
+            return ASN1UTF8String.TYPE;
+        case BERTags.RELATIVE_OID:
+            return ASN1RelativeOID.TYPE;
+        case BERTags.SEQUENCE:
+            return ASN1Sequence.TYPE;
+        case BERTags.SET:
+            return ASN1Set.TYPE;
+        case BERTags.NUMERIC_STRING:            // [UNIVERSAL 18] IMPLICIT OCTET STRING (encode as if)
+            return ASN1NumericString.TYPE;
+        case BERTags.PRINTABLE_STRING:          // [UNIVERSAL 19] IMPLICIT OCTET STRING (encode as if)
+            return ASN1PrintableString.TYPE;
+        case BERTags.T61_STRING:                // [UNIVERSAL 20] IMPLICIT OCTET STRING (encode as if)
+            return ASN1T61String.TYPE;
+        case BERTags.VIDEOTEX_STRING:           // [UNIVERSAL 21] IMPLICIT OCTET STRING (encode as if)
+            return ASN1VideotexString.TYPE;
+        case BERTags.IA5_STRING:                // [UNIVERSAL 22] IMPLICIT OCTET STRING (encode as if)
+            return ASN1IA5String.TYPE;
+        case BERTags.UTC_TIME:                  // [UNIVERSAL 23] IMPLICIT VisibleString (restricted values)
+            return ASN1UTCTime.TYPE;
+        case BERTags.GENERALIZED_TIME:          // [UNIVERSAL 24] IMPLICIT VisibleString (restricted values)
+            return ASN1GeneralizedTime.TYPE;
+        case BERTags.GRAPHIC_STRING:            // [UNIVERSAL 25] IMPLICIT OCTET STRING (encode as if)
+            return ASN1GraphicString.TYPE;
+        case BERTags.VISIBLE_STRING:            // [UNIVERSAL 26] IMPLICIT OCTET STRING (encode as if)
+            return ASN1VisibleString.TYPE;
+        case BERTags.GENERAL_STRING:            // [UNIVERSAL 27] IMPLICIT OCTET STRING (encode as if)
+            return ASN1GeneralString.TYPE;
+        case BERTags.UNIVERSAL_STRING:          // [UNIVERSAL 28] IMPLICIT OCTET STRING (encode as if)
+            return ASN1UniversalString.TYPE;
+        case BERTags.BMP_STRING:                // [UNIVERSAL 30] IMPLICIT OCTET STRING (encode as if)
+            return ASN1BMPString.TYPE;
+
+        case BERTags.REAL:
+        case BERTags.EMBEDDED_PDV:
+        case BERTags.UNRESTRICTED_STRING:
+        default:
+            return null;
+        }
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/ASN1Util.java b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1Util.java
new file mode 100644
index 0000000..eced42d
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1Util.java
@@ -0,0 +1,327 @@
+package org.bouncycastle.asn1;
+
+import java.io.IOException;
+
+public abstract class ASN1Util
+{
+    static ASN1TaggedObject checkTag(ASN1TaggedObject taggedObject, int tagClass, int tagNo)
+    {
+        if (!taggedObject.hasTag(tagClass, tagNo))
+        {
+            String expected = getTagText(tagClass, tagNo);
+            String found = getTagText(taggedObject);
+            throw new IllegalStateException("Expected " + expected + " tag but found " + found);
+        }
+        return taggedObject;
+    }
+
+    static ASN1TaggedObjectParser checkTag(ASN1TaggedObjectParser taggedObjectParser, int tagClass, int tagNo)
+    {
+        if (!taggedObjectParser.hasTag(tagClass, tagNo))
+        {
+            String expected = getTagText(tagClass, tagNo);
+            String found = getTagText(taggedObjectParser);
+            throw new IllegalStateException("Expected " + expected + " tag but found " + found);
+        }
+        return taggedObjectParser;
+    }
+
+
+    /*
+     * Tag text methods
+     */
+
+    static String getTagText(ASN1Tag tag)
+    {
+        return getTagText(tag.getTagClass(), tag.getTagNumber());
+    }
+
+    public static String getTagText(ASN1TaggedObject taggedObject)
+    {
+        return getTagText(taggedObject.getTagClass(), taggedObject.getTagNo());
+    }
+
+    public static String getTagText(ASN1TaggedObjectParser taggedObjectParser)
+    {
+        return getTagText(taggedObjectParser.getTagClass(), taggedObjectParser.getTagNo());
+    }
+
+    public static String getTagText(int tagClass, int tagNo)
+    {
+        switch (tagClass)
+        {
+        case BERTags.APPLICATION:
+            return "[APPLICATION " + tagNo + "]";
+        case BERTags.CONTEXT_SPECIFIC:
+            return "[CONTEXT " + tagNo + "]";
+        case BERTags.PRIVATE:
+            return "[PRIVATE " + tagNo + "]";
+        default:
+            return "[UNIVERSAL " + tagNo + "]";
+        }
+    }
+
+
+    /*
+     * Wrappers for ASN1TaggedObject#getExplicitBaseObject
+     */
+
+    public static ASN1Object getExplicitBaseObject(ASN1TaggedObject taggedObject, int tagClass, int tagNo)
+    {
+        return checkTag(taggedObject, tagClass, tagNo).getExplicitBaseObject();
+    }
+
+    public static ASN1Object getExplicitContextBaseObject(ASN1TaggedObject taggedObject, int tagNo)
+    {
+        return getExplicitBaseObject(taggedObject, BERTags.CONTEXT_SPECIFIC, tagNo);
+    }
+
+    public static ASN1Object tryGetExplicitBaseObject(ASN1TaggedObject taggedObject, int tagClass, int tagNo)
+    {
+        if (!taggedObject.hasTag(tagClass, tagNo))
+        {
+            return null;
+        }
+
+        return taggedObject.getExplicitBaseObject();
+    }
+
+    public static ASN1Object tryGetExplicitContextBaseObject(ASN1TaggedObject taggedObject, int tagNo)
+    {
+        return tryGetExplicitBaseObject(taggedObject, BERTags.CONTEXT_SPECIFIC, tagNo);
+    }
+
+
+    /*
+     * Wrappers for ASN1TaggedObject#getExplicitBaseTagged
+     */
+
+    public static ASN1TaggedObject getExplicitBaseTagged(ASN1TaggedObject taggedObject, int tagClass, int tagNo)
+    {
+        return checkTag(taggedObject, tagClass, tagNo).getExplicitBaseTagged();
+    }
+
+    public static ASN1TaggedObject getExplicitContextBaseTagged(ASN1TaggedObject taggedObject, int tagNo)
+    {
+        return getExplicitBaseTagged(taggedObject, BERTags.CONTEXT_SPECIFIC, tagNo);
+    }
+
+    public static ASN1TaggedObject tryGetExplicitBaseTagged(ASN1TaggedObject taggedObject, int tagClass, int tagNo)
+    {
+        if (!taggedObject.hasTag(tagClass, tagNo))
+        {
+            return null;
+        }
+
+        return taggedObject.getExplicitBaseTagged();
+    }
+
+    public static ASN1TaggedObject tryGetExplicitContextBaseTagged(ASN1TaggedObject taggedObject, int tagNo)
+    {
+        return tryGetExplicitBaseTagged(taggedObject, BERTags.CONTEXT_SPECIFIC, tagNo);
+    }
+
+
+    /*
+     * Wrappers for ASN1TaggedObject#getImplicitBaseTagged
+     */
+
+    public static ASN1TaggedObject getImplicitBaseTagged(ASN1TaggedObject taggedObject, int tagClass, int tagNo,
+        int baseTagClass, int baseTagNo)
+    {
+        return checkTag(taggedObject, tagClass, tagNo).getImplicitBaseTagged(baseTagClass, baseTagNo);
+    }
+
+    public static ASN1TaggedObject getImplicitContextBaseTagged(ASN1TaggedObject taggedObject, int tagNo,
+        int baseTagClass, int baseTagNo)
+    {
+        return getImplicitBaseTagged(taggedObject, BERTags.CONTEXT_SPECIFIC, tagNo, baseTagClass, baseTagNo);
+    }
+
+    public static ASN1TaggedObject tryGetImplicitBaseTagged(ASN1TaggedObject taggedObject, int tagClass, int tagNo,
+        int baseTagClass, int baseTagNo)
+    {
+        if (!taggedObject.hasTag(tagClass, tagNo))
+        {
+            return null;
+        }
+
+        return taggedObject.getImplicitBaseTagged(baseTagClass, baseTagNo);
+    }
+
+    public static ASN1TaggedObject tryGetImplicitContextBaseTagged(ASN1TaggedObject taggedObject, int tagNo,
+        int baseTagClass, int baseTagNo)
+    {
+        return tryGetImplicitBaseTagged(taggedObject, BERTags.CONTEXT_SPECIFIC, tagNo, baseTagClass, baseTagNo);
+    }
+
+
+    /*
+     * Wrappers for ASN1TaggedObject#getBaseUniversal
+     */
+
+    public static ASN1Primitive getBaseUniversal(ASN1TaggedObject taggedObject, int tagClass, int tagNo,
+        boolean declaredExplicit, int baseTagNo)
+    {
+        return checkTag(taggedObject, tagClass, tagNo).getBaseUniversal(declaredExplicit, baseTagNo);  
+    }
+
+    public static ASN1Primitive getContextBaseUniversal(ASN1TaggedObject taggedObject, int tagNo,
+        boolean declaredExplicit, int baseTagNo)
+    {
+        return getBaseUniversal(taggedObject, BERTags.CONTEXT_SPECIFIC, tagNo, declaredExplicit, baseTagNo);
+    }
+
+    public static ASN1Primitive tryGetBaseUniversal(ASN1TaggedObject taggedObject, int tagClass, int tagNo,
+        boolean declaredExplicit, int baseTagNo)
+    {
+        if (!taggedObject.hasTag(tagClass, tagNo))
+        {
+            return null;
+        }
+
+        return taggedObject.getBaseUniversal(declaredExplicit, baseTagNo);  
+    }
+
+    public static ASN1Primitive tryGetContextBaseUniversal(ASN1TaggedObject taggedObject, int tagNo,
+        boolean declaredExplicit, int baseTagNo)
+    {
+        return tryGetBaseUniversal(taggedObject, BERTags.CONTEXT_SPECIFIC, tagNo, declaredExplicit, baseTagNo);
+    }
+
+
+    /*
+     * Wrappers for ASN1TaggedObjectParser#parseExplicitBaseTagged
+     */
+
+    public static ASN1TaggedObjectParser parseExplicitBaseTagged(ASN1TaggedObjectParser taggedObjectParser,
+        int tagClass, int tagNo) throws IOException
+    {
+        return checkTag(taggedObjectParser, tagClass, tagNo).parseExplicitBaseTagged();
+    }
+
+    public static ASN1TaggedObjectParser parseExplicitContextBaseTagged(ASN1TaggedObjectParser taggedObjectParser,
+        int tagNo) throws IOException
+    {
+        return parseExplicitBaseTagged(taggedObjectParser, BERTags.CONTEXT_SPECIFIC, tagNo);
+    }
+
+    public static ASN1TaggedObjectParser tryParseExplicitBaseTagged(ASN1TaggedObjectParser taggedObjectParser,
+        int tagClass, int tagNo) throws IOException
+    {
+        if (!taggedObjectParser.hasTag(tagClass, tagNo))
+        {
+            return null;
+        }
+
+        return taggedObjectParser.parseExplicitBaseTagged();
+    }
+
+    public static ASN1TaggedObjectParser tryParseExplicitContextBaseTagged(ASN1TaggedObjectParser taggedObjectParser,
+        int tagNo) throws IOException
+    {
+        return tryParseExplicitBaseTagged(taggedObjectParser, BERTags.CONTEXT_SPECIFIC, tagNo);
+    }
+
+
+    /*
+     * Wrappers for ASN1TaggedObjectParser#parseImplicitBaseTagged
+     */
+
+    public static ASN1TaggedObjectParser parseImplicitBaseTagged(ASN1TaggedObjectParser taggedObjectParser,
+        int tagClass, int tagNo, int baseTagClass, int baseTagNo) throws IOException
+    {
+        return checkTag(taggedObjectParser, tagClass, tagNo).parseImplicitBaseTagged(baseTagClass, baseTagNo);
+    }
+
+    public static ASN1TaggedObjectParser parseImplicitContextBaseTagged(ASN1TaggedObjectParser taggedObjectParser,
+        int tagNo, int baseTagClass, int baseTagNo) throws IOException
+    {
+        return parseImplicitBaseTagged(taggedObjectParser, BERTags.CONTEXT_SPECIFIC, tagNo, baseTagClass, baseTagNo);
+    }
+
+    public static ASN1TaggedObjectParser tryParseImplicitBaseTagged(ASN1TaggedObjectParser taggedObjectParser,
+        int tagClass, int tagNo, int baseTagClass, int baseTagNo) throws IOException
+    {
+        if (!taggedObjectParser.hasTag(tagClass, tagNo))
+        {
+            return null;
+        }
+
+        return taggedObjectParser.parseImplicitBaseTagged(baseTagClass, baseTagNo);
+    }
+
+    public static ASN1TaggedObjectParser tryParseImplicitContextBaseTagged(ASN1TaggedObjectParser taggedObjectParser,
+        int tagNo, int baseTagClass, int baseTagNo) throws IOException
+    {
+        return tryParseImplicitBaseTagged(taggedObjectParser, BERTags.CONTEXT_SPECIFIC, tagNo, baseTagClass, baseTagNo);
+    }
+
+
+    /*
+     * Wrappers for ASN1TaggedObjectParser#parseBaseUniversal
+     */
+
+    public static ASN1Encodable parseBaseUniversal(ASN1TaggedObjectParser taggedObjectParser, int tagClass,
+        int tagNo, boolean declaredExplicit, int baseTagNo) throws IOException
+    {
+        return checkTag(taggedObjectParser, tagClass, tagNo).parseBaseUniversal(declaredExplicit, baseTagNo);
+    }
+
+    public static ASN1Encodable parseContextBaseUniversal(ASN1TaggedObjectParser taggedObjectParser, int tagNo,
+        boolean declaredExplicit, int baseTagNo) throws IOException
+    {
+        return parseBaseUniversal(taggedObjectParser, BERTags.CONTEXT_SPECIFIC, tagNo, declaredExplicit, baseTagNo);
+    }
+
+    public static ASN1Encodable tryParseBaseUniversal(ASN1TaggedObjectParser taggedObjectParser, int tagClass,
+        int tagNo, boolean declaredExplicit, int baseTagNo) throws IOException
+    {
+        if (!taggedObjectParser.hasTag(tagClass, tagNo))
+        {
+            return null;
+        }
+
+        return taggedObjectParser.parseBaseUniversal(declaredExplicit, baseTagNo);
+    }
+
+    public static ASN1Encodable tryParseContextBaseUniversal(ASN1TaggedObjectParser taggedObjectParser, int tagNo,
+        boolean declaredExplicit, int baseTagNo) throws IOException
+    {
+        return tryParseBaseUniversal(taggedObjectParser, BERTags.CONTEXT_SPECIFIC, tagNo, declaredExplicit, baseTagNo);
+    }
+
+
+    /*
+     * Wrappers for ASN1TaggedObjectParser#parseExplicitBaseObject
+     */
+
+    public static ASN1Encodable parseExplicitBaseObject(ASN1TaggedObjectParser taggedObjectParser, int tagClass,
+        int tagNo) throws IOException
+    {
+        return checkTag(taggedObjectParser, tagClass, tagNo).parseExplicitBaseObject();
+    }
+
+    public static ASN1Encodable parseExplicitContextBaseObject(ASN1TaggedObjectParser taggedObjectParser, int tagNo)
+        throws IOException
+    {
+        return parseExplicitBaseObject(taggedObjectParser, BERTags.CONTEXT_SPECIFIC, tagNo);
+    }
+
+    public static ASN1Encodable tryParseExplicitBaseObject(ASN1TaggedObjectParser taggedObjectParser, int tagClass,
+        int tagNo) throws IOException
+    {
+        if (!taggedObjectParser.hasTag(tagClass, tagNo))
+        {
+            return null;
+        }
+
+        return taggedObjectParser.parseExplicitBaseObject();
+    }
+
+    public static ASN1Encodable tryParseExplicitContextBaseObject(ASN1TaggedObjectParser taggedObjectParser, int tagNo)
+        throws IOException
+    {
+        return tryParseExplicitBaseObject(taggedObjectParser, BERTags.CONTEXT_SPECIFIC, tagNo);
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/ASN1VideotexString.java b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1VideotexString.java
new file mode 100644
index 0000000..ae9c422
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1VideotexString.java
@@ -0,0 +1,127 @@
+package org.bouncycastle.asn1;
+
+import java.io.IOException;
+
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.Strings;
+
+public abstract class ASN1VideotexString
+    extends ASN1Primitive
+    implements ASN1String
+{
+    static final ASN1UniversalType TYPE = new ASN1UniversalType(ASN1VideotexString.class, BERTags.VIDEOTEX_STRING)
+    {
+        ASN1Primitive fromImplicitPrimitive(DEROctetString octetString)
+        {
+            return createPrimitive(octetString.getOctets());
+        }
+    };
+
+    /**
+     * return a Videotex String from the passed in object
+     *
+     * @param obj an ASN1VideotexString or an object that can be converted into one.
+     * @exception IllegalArgumentException if the object cannot be converted.
+     * @return an ASN1VideotexString instance, or null.
+     */
+    public static ASN1VideotexString getInstance(Object obj)
+    {
+        if (obj == null || obj instanceof ASN1VideotexString)
+        {
+            return (ASN1VideotexString)obj;
+        }
+        if (obj instanceof ASN1Encodable)
+        {
+            ASN1Primitive primitive = ((ASN1Encodable)obj).toASN1Primitive();
+            if (primitive instanceof ASN1VideotexString)
+            {
+                return (ASN1VideotexString)primitive;
+            }
+        }
+        if (obj instanceof byte[])
+        {
+            try
+            {
+                return (ASN1VideotexString)TYPE.fromByteArray((byte[])obj);
+            }
+            catch (Exception e)
+            {
+                throw new IllegalArgumentException("encoding error in getInstance: " + e.toString());
+            }
+        }
+
+        throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
+    }
+
+    /**
+     * return a Videotex String from a tagged object.
+     *
+     * @param taggedObject the tagged object holding the object we want
+     * @param explicit     true if the object is meant to be explicitly tagged false
+     *                     otherwise.
+     * @exception IllegalArgumentException if the tagged object cannot be converted.
+     * @return an ASN1VideotexString instance, or null.
+     */
+    public static ASN1VideotexString getInstance(ASN1TaggedObject taggedObject, boolean explicit)
+    {
+        return (ASN1VideotexString)TYPE.getContextInstance(taggedObject, explicit);
+    }
+
+    final byte[] contents;
+
+    /**
+     * basic constructor - with bytes.
+     * @param string the byte encoding of the characters making up the string.
+     */
+    ASN1VideotexString(byte[] contents, boolean clone)
+    {
+        this.contents = clone ? Arrays.clone(contents) : contents;
+    }
+
+    public final byte[] getOctets()
+    {
+        return Arrays.clone(contents);
+    }
+
+    final boolean encodeConstructed()
+    {
+        return false;
+    }
+
+    final int encodedLength(boolean withTag)
+    {
+        return ASN1OutputStream.getLengthOfEncodingDL(withTag, contents.length);
+    }
+
+    final void encode(ASN1OutputStream out, boolean withTag) throws IOException
+    {
+        out.writeEncodingDL(withTag, BERTags.VIDEOTEX_STRING, contents);
+    }
+
+    final boolean asn1Equals(ASN1Primitive other)
+    {
+        if (!(other instanceof ASN1VideotexString))
+        {
+            return false;
+        }
+
+        ASN1VideotexString that = (ASN1VideotexString)other;
+
+        return Arrays.areEqual(this.contents, that.contents);
+    }
+
+    public final int hashCode()
+    {
+        return Arrays.hashCode(contents);
+    }
+
+    public final String getString()
+    {
+        return Strings.fromByteArray(contents);
+    }
+
+    static ASN1VideotexString createPrimitive(byte[] contents)
+    {
+        return new DERVideotexString(contents, false);
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/ASN1VisibleString.java b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1VisibleString.java
new file mode 100644
index 0000000..6424cfc
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1VisibleString.java
@@ -0,0 +1,141 @@
+package org.bouncycastle.asn1;
+
+import java.io.IOException;
+
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.Strings;
+
+/**
+ * ASN.1 VisibleString object encoding ISO 646 (ASCII) character code points 32 to 126.
+ * <p>
+ * Explicit character set escape sequences are not allowed.
+ * </p>
+ */
+public abstract class ASN1VisibleString
+    extends ASN1Primitive
+    implements ASN1String
+{
+    static final ASN1UniversalType TYPE = new ASN1UniversalType(ASN1VisibleString.class, BERTags.VISIBLE_STRING)
+    {
+        ASN1Primitive fromImplicitPrimitive(DEROctetString octetString)
+        {
+            return createPrimitive(octetString.getOctets());
+        }
+    };
+
+    /**
+     * Return a Visible String from the passed in object.
+     *
+     * @param obj an ASN1VisibleString or an object that can be converted into one.
+     * @exception IllegalArgumentException if the object cannot be converted.
+     * @return an ASN1VisibleString instance, or null
+     */
+    public static ASN1VisibleString getInstance(
+        Object  obj)
+    {
+        if (obj == null || obj instanceof ASN1VisibleString)
+        {
+            return (ASN1VisibleString)obj;
+        }
+        if (obj instanceof ASN1Encodable)
+        {
+            ASN1Primitive primitive = ((ASN1Encodable)obj).toASN1Primitive();
+            if (primitive instanceof ASN1VisibleString)
+            {
+                return (ASN1VisibleString)primitive;
+            }
+        }
+        if (obj instanceof byte[])
+        {
+            try
+            {
+                return (ASN1VisibleString)TYPE.fromByteArray((byte[])obj);
+            }
+            catch (Exception e)
+            {
+                throw new IllegalArgumentException("encoding error in getInstance: " + e.toString());
+            }
+        }
+
+        throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
+    }
+
+    /**
+     * Return a Visible String from a tagged object.
+     *
+     * @param taggedObject the tagged object holding the object we want
+     * @param explicit true if the object is meant to be explicitly
+     *              tagged false otherwise.
+     * @exception IllegalArgumentException if the tagged object cannot
+     *               be converted.
+     * @return an ASN1VisibleString instance, or null
+     */
+    public static ASN1VisibleString getInstance(ASN1TaggedObject taggedObject, boolean explicit)
+    {
+        return (ASN1VisibleString)TYPE.getContextInstance(taggedObject, explicit);
+    }
+
+    final byte[] contents;
+
+    ASN1VisibleString(String string)
+    {
+        this.contents = Strings.toByteArray(string);
+    }
+
+    ASN1VisibleString(byte[] contents, boolean clone)
+    {
+        this.contents = clone ? Arrays.clone(contents) : contents;
+    }
+
+    public final String getString()
+    {
+        return Strings.fromByteArray(contents);
+    }
+
+    public String toString()
+    {
+        return getString();
+    }
+
+    public final byte[] getOctets()
+    {
+        return Arrays.clone(contents);
+    }
+
+    final boolean encodeConstructed()
+    {
+        return false;
+    }
+
+    final int encodedLength(boolean withTag)
+    {
+        return ASN1OutputStream.getLengthOfEncodingDL(withTag, contents.length);
+    }
+
+    final void encode(ASN1OutputStream out, boolean withTag) throws IOException
+    {
+        out.writeEncodingDL(withTag, BERTags.VISIBLE_STRING, contents);
+    }
+
+    final boolean asn1Equals(ASN1Primitive other)
+    {
+        if (!(other instanceof ASN1VisibleString))
+        {
+            return false;
+        }
+
+        ASN1VisibleString that = (ASN1VisibleString)other;
+
+        return Arrays.areEqual(this.contents, that.contents);
+    }
+
+    public final int hashCode()
+    {
+        return Arrays.hashCode(contents);
+    }
+
+    static ASN1VisibleString createPrimitive(byte[] contents)
+    {
+        return new DERVisibleString(contents, false);
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/BERApplicationSpecific.java b/bcprov/src/main/java/org/bouncycastle/asn1/BERApplicationSpecific.java
deleted file mode 100644
index a0e3600..0000000
--- a/bcprov/src/main/java/org/bouncycastle/asn1/BERApplicationSpecific.java
+++ /dev/null
@@ -1,110 +0,0 @@
-package org.bouncycastle.asn1;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-
-/**
- * An indefinite-length encoding version of an ASN.1 ApplicationSpecific object.
- */
-public class BERApplicationSpecific
-    extends ASN1ApplicationSpecific
-{
-    BERApplicationSpecific(
-        boolean isConstructed,
-        int tag,
-        byte[] octets)
-    {
-        super(isConstructed, tag, octets);
-    }
-
-    /**
-     * Create an application specific object with a tagging of explicit/constructed.
-     *
-     * @param tag the tag number for this object.
-     * @param object the object to be contained.
-     */
-    public BERApplicationSpecific(
-        int tag,
-        ASN1Encodable object)
-        throws IOException
-    {
-        this(true, tag, object);
-    }
-
-    /**
-     * Create an application specific object with the tagging style given by the value of constructed.
-     *
-     * @param constructed true if the object is constructed.
-     * @param tag the tag number for this object.
-     * @param object the object to be contained.
-     */
-    public BERApplicationSpecific(
-        boolean constructed,
-        int tag,
-        ASN1Encodable object)
-        throws IOException
-    {
-        super(constructed || object.toASN1Primitive().isConstructed(), tag, getEncoding(constructed, object));
-    }
-
-    private static byte[] getEncoding(boolean explicit, ASN1Encodable object)
-        throws IOException
-    {
-        byte[] data = object.toASN1Primitive().getEncoded(ASN1Encoding.BER);
-
-        if (explicit)
-        {
-            return data;
-        }
-        else
-        {
-            int lenBytes = getLengthOfHeader(data);
-            byte[] tmp = new byte[data.length - lenBytes];
-            System.arraycopy(data, lenBytes, tmp, 0, tmp.length);
-            return tmp;
-        }
-    }
-
-    /**
-     * Create an application specific object which is marked as constructed
-     *
-     * @param tagNo the tag number for this object.
-     * @param vec the objects making up the application specific object.
-     */
-    public BERApplicationSpecific(int tagNo, ASN1EncodableVector vec)
-    {
-        super(true, tagNo, getEncodedVector(vec));
-    }
-
-    private static byte[] getEncodedVector(ASN1EncodableVector vec)
-    {
-        ByteArrayOutputStream bOut = new ByteArrayOutputStream();
-
-        for (int i = 0; i != vec.size(); i++)
-        {
-            try
-            {
-                bOut.write(((ASN1Object)vec.get(i)).getEncoded(ASN1Encoding.BER));
-            }
-            catch (IOException e)
-            {
-                throw new ASN1ParsingException("malformed object: " + e, e);
-            }
-        }
-        return bOut.toByteArray();
-    }
-
-    /* (non-Javadoc)
-     * @see org.bouncycastle.asn1.ASN1Primitive#encode(org.bouncycastle.asn1.DEROutputStream)
-     */
-    void encode(ASN1OutputStream out, boolean withTag) throws IOException
-    {
-        int flags = BERTags.APPLICATION;
-        if (isConstructed)
-        {
-            flags |= BERTags.CONSTRUCTED;
-        }
-
-        out.writeEncodedIndef(withTag, flags, tag, octets);
-    }
-}
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/BERApplicationSpecificParser.java b/bcprov/src/main/java/org/bouncycastle/asn1/BERApplicationSpecificParser.java
deleted file mode 100644
index c915415..0000000
--- a/bcprov/src/main/java/org/bouncycastle/asn1/BERApplicationSpecificParser.java
+++ /dev/null
@@ -1,59 +0,0 @@
-package org.bouncycastle.asn1;
-
-import java.io.IOException;
-
-/**
- * A parser for indefinite-length ASN.1 ApplicationSpecific objects.
- */
-public class BERApplicationSpecificParser
-    implements ASN1ApplicationSpecificParser
-{
-    private final int tag;
-    private final ASN1StreamParser parser;
-
-    BERApplicationSpecificParser(int tag, ASN1StreamParser parser)
-    {
-        this.tag = tag;
-        this.parser = parser;
-    }
-
-    /**
-     * Return the object contained in this application specific object,
-     * @return the contained object.
-     * @throws IOException if the underlying stream cannot be read, or does not contain an ASN.1 encoding.
-     */
-    public ASN1Encodable readObject()
-        throws IOException
-    {
-        return parser.readObject();
-    }
-
-    /**
-     * Return an in-memory, encodable, representation of the application specific object.
-     *
-     * @return a BERApplicationSpecific.
-     * @throws IOException if there is an issue loading the data.
-     */
-    public ASN1Primitive getLoadedObject()
-        throws IOException
-    {
-         return new BERApplicationSpecific(tag, parser.readVector());
-    }
-
-    /**
-     * Return a BERApplicationSpecific representing this parser and its contents.
-     *
-     * @return a BERApplicationSpecific
-     */
-    public ASN1Primitive toASN1Primitive()
-    {
-        try
-        {
-            return getLoadedObject();
-        }
-        catch (IOException e)
-        {
-            throw new ASN1ParsingException(e.getMessage(), e);
-        }
-    }
-}
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/BERBitString.java b/bcprov/src/main/java/org/bouncycastle/asn1/BERBitString.java
new file mode 100644
index 0000000..45ebaa4
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/BERBitString.java
@@ -0,0 +1,188 @@
+package org.bouncycastle.asn1;
+
+import java.io.IOException;
+
+public class BERBitString
+    extends ASN1BitString
+{
+    private static final int DEFAULT_SEGMENT_LIMIT = 1000;
+
+    private final int segmentLimit;
+    private final ASN1BitString[] elements;
+
+    /**
+     * Convert a vector of bit strings into a single bit string
+     */
+    static byte[] flattenBitStrings(ASN1BitString[] bitStrings)
+    {
+        int count = bitStrings.length;
+        switch (count)
+        {
+        case 0:
+            // No bits
+            return new byte[]{ 0 };
+        case 1:
+            return bitStrings[0].contents;
+        default:
+        {
+            int last = count - 1, totalLength = 0;
+            for (int i = 0; i < last; ++i)
+            {
+                byte[] elementContents = bitStrings[i].contents;
+                if (elementContents[0] != 0)
+                {
+                    throw new IllegalArgumentException("only the last nested bitstring can have padding");
+                }
+
+                totalLength += elementContents.length - 1;
+            }
+
+            // Last one can have padding
+            byte[] lastElementContents = bitStrings[last].contents;
+            byte padBits = lastElementContents[0];
+            totalLength += lastElementContents.length;
+
+            byte[] contents = new byte[totalLength];
+            contents[0] = padBits;
+
+            int pos = 1;
+            for (int i = 0; i < count; ++i)
+            {
+                byte[] elementContents = bitStrings[i].contents;
+                int length = elementContents.length - 1;
+                System.arraycopy(elementContents, 1, contents, pos, length);
+                pos += length;
+            }
+
+//            assert pos == totalLength;
+            return contents;
+        }
+        }
+    }
+    
+    public BERBitString(byte[] data)
+    {
+        this(data, 0);
+    }
+
+    public BERBitString(byte data, int padBits)
+    {
+        super(data, padBits);
+        this.elements = null;
+        this.segmentLimit = DEFAULT_SEGMENT_LIMIT;
+    }
+
+    public BERBitString(byte[] data, int padBits)
+    {
+        this(data, padBits, DEFAULT_SEGMENT_LIMIT);
+    }
+
+    public BERBitString(byte[] data, int padBits, int segmentLimit)
+    {
+        super(data, padBits);
+        this.elements = null;
+        this.segmentLimit = segmentLimit;
+    }
+
+    public BERBitString(ASN1Encodable obj) throws IOException
+    {
+        this(obj.toASN1Primitive().getEncoded(ASN1Encoding.DER), 0);
+    }
+
+    public BERBitString(ASN1BitString[] elements)
+    {
+        this(elements, DEFAULT_SEGMENT_LIMIT);
+    }
+
+    public BERBitString(ASN1BitString[] elements, int segmentLimit)
+    {
+        super(flattenBitStrings(elements), false);
+        this.elements = elements;
+        this.segmentLimit = segmentLimit;
+    }    
+
+    BERBitString(byte[] contents, boolean check)
+    {
+        super(contents, check);
+        this.elements = null;
+        this.segmentLimit = DEFAULT_SEGMENT_LIMIT;
+    }
+
+    boolean encodeConstructed()
+    {
+        return null != elements || contents.length > segmentLimit;
+    }
+
+    int encodedLength(boolean withTag)
+        throws IOException
+    {
+        if (!encodeConstructed())
+        {
+            return DLBitString.encodedLength(withTag, contents.length);
+        }
+
+        int totalLength = withTag ? 4 : 3;
+
+        if (null != elements)
+        {
+            for (int i = 0; i < elements.length; ++i)
+            {
+                totalLength += elements[i].encodedLength(true);
+            }
+        }
+        else if (contents.length < 2)
+        {
+            // No bits
+        }
+        else
+        {
+            int extraSegments = (contents.length - 2) / (segmentLimit - 1);
+            totalLength += extraSegments * DLBitString.encodedLength(true, segmentLimit);
+
+            int lastSegmentLength = contents.length - (extraSegments * (segmentLimit - 1));
+            totalLength += DLBitString.encodedLength(true, lastSegmentLength);
+        }
+
+        return totalLength;
+    }
+
+    void encode(ASN1OutputStream out, boolean withTag) throws IOException
+    {
+        if (!encodeConstructed())
+        {
+            DLBitString.encode(out, withTag, contents, 0, contents.length);
+            return;
+        }
+
+        out.writeIdentifier(withTag, BERTags.CONSTRUCTED | BERTags.BIT_STRING);
+        out.write(0x80);
+
+        if (null != elements)
+        {
+            out.writePrimitives(elements);
+        }
+        else if (contents.length < 2)
+        {
+            // No bits
+        }
+        else
+        {
+            byte pad = contents[0];
+            int length = contents.length;
+            int remaining = length - 1;
+            int segmentLength = segmentLimit - 1;
+
+            while (remaining > segmentLength)
+            {
+                DLBitString.encode(out, true, (byte)0, contents, length - remaining, segmentLength);
+                remaining -= segmentLength;
+            }
+
+            DLBitString.encode(out, true, pad, contents, length - remaining, remaining);
+        }
+
+        out.write(0x00);
+        out.write(0x00);
+    }
+}
+
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/BERBitStringParser.java b/bcprov/src/main/java/org/bouncycastle/asn1/BERBitStringParser.java
new file mode 100644
index 0000000..9b2cfaf
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/BERBitStringParser.java
@@ -0,0 +1,66 @@
+package org.bouncycastle.asn1;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.bouncycastle.util.io.Streams;
+
+/**
+ * A parser for indefinite-length BIT STRINGs.
+ * 
+ * @deprecated Check for 'ASN1BitStringParser' instead 
+ */
+public class BERBitStringParser
+    implements ASN1BitStringParser
+{
+    private ASN1StreamParser _parser;
+
+    private ConstructedBitStream _bitStream;
+
+    BERBitStringParser(
+        ASN1StreamParser parser)
+    {
+        _parser = parser;
+    }
+
+    public InputStream getOctetStream() throws IOException
+    {
+        return _bitStream = new ConstructedBitStream(_parser, true);
+    }
+
+    public InputStream getBitStream() throws IOException
+    {
+        return _bitStream = new ConstructedBitStream(_parser, false);
+    }
+
+    public int getPadBits()
+    {
+        return _bitStream.getPadBits();
+    }
+
+    public ASN1Primitive getLoadedObject()
+        throws IOException
+    {
+        return parse(_parser);
+    }
+
+    public ASN1Primitive toASN1Primitive()
+    {
+        try
+        {
+            return getLoadedObject();
+        }
+        catch (IOException e)
+        {
+            throw new ASN1ParsingException("IOException converting stream to byte array: " + e.getMessage(), e);
+        }
+    }
+
+    static BERBitString parse(ASN1StreamParser sp) throws IOException
+    {
+        ConstructedBitStream bitStream = new ConstructedBitStream(sp, false);
+        byte[] data = Streams.readAll(bitStream);
+        int padBits = bitStream.getPadBits();
+        return new BERBitString(data, padBits);
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/BERConstructedOctetString.java b/bcprov/src/main/java/org/bouncycastle/asn1/BERConstructedOctetString.java
deleted file mode 100644
index cad6e42..0000000
--- a/bcprov/src/main/java/org/bouncycastle/asn1/BERConstructedOctetString.java
+++ /dev/null
@@ -1,144 +0,0 @@
-package org.bouncycastle.asn1;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.util.Enumeration;
-import java.util.Vector;
-
-/**
- * @deprecated use BEROctetString
- */
-public class BERConstructedOctetString
-    extends BEROctetString
-{
-    private static final int MAX_LENGTH = 1000;
-
-    /**
-     * convert a vector of octet strings into a single byte string
-     */
-    static private byte[] toBytes(
-        Vector  octs)
-    {
-        ByteArrayOutputStream   bOut = new ByteArrayOutputStream();
-
-        for (int i = 0; i != octs.size(); i++)
-        {
-            try
-            {
-                DEROctetString  o = (DEROctetString)octs.elementAt(i);
-
-                bOut.write(o.getOctets());
-            }
-            catch (ClassCastException e)
-            {
-                throw new IllegalArgumentException(octs.elementAt(i).getClass().getName() + " found in input should only contain DEROctetString");
-            }
-            catch (IOException e)
-            {
-                throw new IllegalArgumentException("exception converting octets " + e.toString());
-            }
-        }
-
-        return bOut.toByteArray();
-    }
-
-    private Vector  octs;
-
-    /**
-     * @param string the octets making up the octet string.
-     */
-    public BERConstructedOctetString(
-        byte[]  string)
-    {
-        super(string);
-    }
-
-    public BERConstructedOctetString(
-        Vector  octs)
-    {
-        super(toBytes(octs));
-
-        this.octs = octs;
-    }
-
-    public BERConstructedOctetString(
-        ASN1Primitive  obj)
-    {
-        super(toByteArray(obj));
-    }
-
-    private static byte[] toByteArray(ASN1Primitive obj)
-    {
-        try
-        {
-            return obj.getEncoded();
-        }
-        catch (IOException e)
-        {
-            throw new IllegalArgumentException("Unable to encode object");
-        }
-    }
-
-    public BERConstructedOctetString(
-        ASN1Encodable  obj)
-    {
-        this(obj.toASN1Primitive());
-    }
-
-    public byte[] getOctets()
-    {
-        return string;
-    }
-
-    /**
-     * return the DER octets that make up this string.
-     */
-    public Enumeration getObjects()
-    {
-        if (octs == null)
-        {
-            return generateOcts().elements();
-        }
-
-        return octs.elements();
-    }
-
-    private Vector generateOcts() 
-    { 
-        Vector vec = new Vector(); 
-        for (int i = 0; i < string.length; i += MAX_LENGTH) 
-        { 
-            int end; 
-
-            if (i + MAX_LENGTH > string.length) 
-            { 
-                end = string.length; 
-            } 
-            else 
-            { 
-                end = i + MAX_LENGTH; 
-            } 
-
-            byte[] nStr = new byte[end - i]; 
-
-            System.arraycopy(string, i, nStr, 0, nStr.length); 
-
-            vec.addElement(new DEROctetString(nStr)); 
-         } 
-        
-         return vec; 
-    }
-
-    public static BEROctetString fromSequence(ASN1Sequence seq)
-    {
-        Vector      v = new Vector();
-        Enumeration e = seq.getObjects();
-
-        while (e.hasMoreElements())
-        {
-            v.addElement(e.nextElement());
-        }
-
-        return new BERConstructedOctetString(v);
-    }
-}
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/BERGenerator.java b/bcprov/src/main/java/org/bouncycastle/asn1/BERGenerator.java
index b492157..ad2b37f 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/BERGenerator.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/BERGenerator.java
@@ -6,7 +6,7 @@
 /**
  * Base class for generators for indefinite-length structures.
  */
-public class BERGenerator
+public abstract class BERGenerator
     extends ASN1Generator
 {
     private boolean _tagged = false;
@@ -43,7 +43,7 @@
     {
         if (_tagged)
         {
-            int tagNum = _tagNo | BERTags.TAGGED;
+            int tagNum = _tagNo | BERTags.CONTEXT_SPECIFIC;
 
             if (_isExplicit)
             {
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/BEROctetString.java b/bcprov/src/main/java/org/bouncycastle/asn1/BEROctetString.java
index 3544c56..dbc8e4b 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/BEROctetString.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/BEROctetString.java
@@ -1,9 +1,6 @@
 package org.bouncycastle.asn1;
 
-import java.io.ByteArrayOutputStream;
 import java.io.IOException;
-import java.util.Enumeration;
-import java.util.NoSuchElementException;
 
 /**
  * ASN.1 OctetStrings, with indefinite length rules, and <i>constructed form</i> support.
@@ -22,172 +19,148 @@
 public class BEROctetString
     extends ASN1OctetString
 {
-    private static final int DEFAULT_CHUNK_SIZE = 1000;
+    private static final int DEFAULT_SEGMENT_LIMIT = 1000;
 
-    private final int chunkSize;
-    private final ASN1OctetString[] octs;
+    private final int segmentLimit;
+    private final ASN1OctetString[] elements;
 
     /**
      * Convert a vector of octet strings into a single byte string
      */
-    static private byte[] toBytes(
-        ASN1OctetString[]  octs)
+    static byte[] flattenOctetStrings(ASN1OctetString[] octetStrings)
     {
-        ByteArrayOutputStream bOut = new ByteArrayOutputStream();
-
-        for (int i = 0; i != octs.length; i++)
+        int count = octetStrings.length;
+        switch (count)
         {
-            try
+        case 0:
+            return EMPTY_OCTETS;
+        case 1:
+            return octetStrings[0].string;
+        default:
+        {
+            int totalOctets = 0;
+            for (int i = 0; i < count; ++i)
             {
-                bOut.write(octs[i].getOctets());
+                totalOctets += octetStrings[i].string.length;
             }
-            catch (IOException e)
+
+            byte[] string = new byte[totalOctets];
+            for (int i = 0, pos = 0; i < count; ++i)
             {
-                throw new IllegalArgumentException("exception converting octets " + e.toString());
+                byte[] octets = octetStrings[i].string;
+                System.arraycopy(octets, 0, string, pos, octets.length);
+                pos += octets.length;
             }
+
+//            assert pos == totalOctets;
+            return string;
         }
-
-        return bOut.toByteArray();
+        }
     }
 
     /**
      * Create an OCTET-STRING object from a byte[]
      * @param string the octets making up the octet string.
      */
-    public BEROctetString(
-        byte[] string)
+    public BEROctetString(byte[] string)
     {
-        this(string, DEFAULT_CHUNK_SIZE);
+        this(string, DEFAULT_SEGMENT_LIMIT);
     }
 
     /**
      * Multiple {@link ASN1OctetString} data blocks are input,
      * the result is <i>constructed form</i>.
      *
-     * @param octs an array of OCTET STRING to construct the BER OCTET STRING from.
+     * @param elements an array of OCTET STRING to construct the BER OCTET STRING from.
      */
-    public BEROctetString(
-        ASN1OctetString[] octs)
+    public BEROctetString(ASN1OctetString[] elements)
     {
-        this(octs, DEFAULT_CHUNK_SIZE);
+        this(elements, DEFAULT_SEGMENT_LIMIT);
     }
 
     /**
      * Create an OCTET-STRING object from a byte[]
      * @param string the octets making up the octet string.
-     * @param chunkSize the number of octets stored in each DER encoded component OCTET STRING.
+     * @param segmentLimit the number of octets stored in each DER encoded component OCTET STRING.
      */
-    public BEROctetString(
-        byte[] string,
-        int    chunkSize)
+    public BEROctetString(byte[] string, int segmentLimit)
     {
-        this(string, null, chunkSize);
+        this(string, null, segmentLimit);
     }
 
     /**
      * Multiple {@link ASN1OctetString} data blocks are input,
      * the result is <i>constructed form</i>.
      *
-     * @param octs an array of OCTET STRING to construct the BER OCTET STRING from.
-     * @param chunkSize the number of octets stored in each DER encoded component OCTET STRING.
+     * @param elements an array of OCTET STRING to construct the BER OCTET STRING from.
+     * @param segmentLimit the number of octets stored in each DER encoded component OCTET STRING.
      */
-    public BEROctetString(
-        ASN1OctetString[] octs,
-        int chunkSize)
+    public BEROctetString(ASN1OctetString[] elements, int segmentLimit)
     {
-        this(toBytes(octs), octs, chunkSize);
+        this(flattenOctetStrings(elements), elements, segmentLimit);
     }
 
-    private BEROctetString(byte[] string, ASN1OctetString[] octs, int chunkSize)
+    private BEROctetString(byte[] string, ASN1OctetString[] elements, int segmentLimit)
     {
         super(string);
-        this.octs = octs;
-        this.chunkSize = chunkSize;
+        this.elements = elements;
+        this.segmentLimit = segmentLimit;
     }
 
-    /**
-     * Return the OCTET STRINGs that make up this string.
-     *
-     * @return an Enumeration of the component OCTET STRINGs.
-     */
-    public Enumeration getObjects()
-    {
-        if (octs == null)
-        {
-            return new Enumeration()
-            {
-                int pos = 0;
-
-                public boolean hasMoreElements()
-                {
-                    return pos < string.length;
-                }
-
-                public Object nextElement()
-                {
-                    if (pos < string.length)
-                    {
-                        int length = Math.min(string.length - pos, chunkSize);
-                        byte[] chunk = new byte[length];
-                        System.arraycopy(string, pos, chunk, 0, length);
-                        pos += length;
-                        return new DEROctetString(chunk);
-                    }
-                    throw new NoSuchElementException();
-                }
-            };
-        }
-
-        return new Enumeration()
-        {
-            int counter = 0;
-
-            public boolean hasMoreElements()
-            {
-                return counter < octs.length;
-            }
-
-            public Object nextElement()
-            {
-                if (counter < octs.length)
-                {
-                    return octs[counter++];
-                }
-                throw new NoSuchElementException();
-            }
-        };
-    }
-
-    boolean isConstructed()
+    boolean encodeConstructed()
     {
         return true;
     }
 
-    int encodedLength()
+    int encodedLength(boolean withTag)
         throws IOException
     {
-        int length = 0;
-        for (Enumeration e = getObjects(); e.hasMoreElements();)
+        int totalLength = withTag ? 4 : 3;
+
+        if (null != elements)
         {
-            length += ((ASN1Encodable)e.nextElement()).toASN1Primitive().encodedLength();
+            for (int i = 0; i < elements.length; ++i)
+            {
+                totalLength += elements[i].encodedLength(true);
+            }
+        }
+        else
+        {
+            int fullSegments = string.length / segmentLimit;
+            totalLength += fullSegments * DEROctetString.encodedLength(true, segmentLimit);
+
+            int lastSegmentLength = string.length - (fullSegments * segmentLimit);
+            if (lastSegmentLength > 0)
+            {
+                totalLength += DEROctetString.encodedLength(true, lastSegmentLength);
+            }
         }
 
-        return 2 + length + 2;
+        return totalLength;
     }
 
     void encode(ASN1OutputStream out, boolean withTag) throws IOException
     {
-        out.writeEncodedIndef(withTag, BERTags.CONSTRUCTED | BERTags.OCTET_STRING,  getObjects());
-    }
+        out.writeIdentifier(withTag, BERTags.CONSTRUCTED | BERTags.OCTET_STRING);
+        out.write(0x80);
 
-    static BEROctetString fromSequence(ASN1Sequence seq)
-    {
-        int count = seq.size();
-        ASN1OctetString[] v = new ASN1OctetString[count];
-        for (int i = 0; i < count; ++i)
+        if (null != elements)
         {
-            v[i] = ASN1OctetString.getInstance(seq.getObjectAt(i));
+            out.writePrimitives(elements);
         }
-        return new BEROctetString(v);
+        else
+        {
+            int pos = 0;
+            while (pos < string.length)
+            {
+                int segmentLength = Math.min(string.length - pos, segmentLimit);
+                DEROctetString.encode(out, true, string, pos, segmentLength);
+                pos += segmentLength;
+            }
+        }
+
+        out.write(0x00);
+        out.write(0x00);
     }
 }
+
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/BEROctetStringGenerator.java b/bcprov/src/main/java/org/bouncycastle/asn1/BEROctetStringGenerator.java
index 68bf801..8be481f 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/BEROctetStringGenerator.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/BEROctetStringGenerator.java
@@ -96,23 +96,32 @@
 
         public void write(byte[] b, int off, int len) throws IOException
         {
-            while (len > 0)
+            int bufLen = _buf.length;
+            int available = bufLen - _off;
+            if (len < available)
             {
-                int numToCopy = Math.min(len, _buf.length - _off);
-                System.arraycopy(b, off, _buf, _off, numToCopy);
-
-                _off += numToCopy;
-                if (_off < _buf.length)
-                {
-                    break;
-                }
-
-                DEROctetString.encode(_derOut, true, _buf, 0, _buf.length);
-                _off = 0;
-
-                off += numToCopy;
-                len -= numToCopy;
+                System.arraycopy(b, off, _buf, _off, len);
+                _off += len;
+                return;
             }
+
+            int count = 0;
+            if (_off > 0)
+            {
+                System.arraycopy(b, off, _buf, _off, available);
+                count += available;
+                DEROctetString.encode(_derOut, true, _buf, 0, bufLen);
+            }
+
+            int remaining;
+            while ((remaining = (len - count)) >= bufLen)
+            {
+                DEROctetString.encode(_derOut, true, b, off + count, bufLen);
+                count += bufLen;
+            }
+
+            System.arraycopy(b, off + count, _buf, 0, remaining);
+            this._off = remaining;
         }
 
         public void close()
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/BEROctetStringParser.java b/bcprov/src/main/java/org/bouncycastle/asn1/BEROctetStringParser.java
index 77a3049..bce7f01 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/BEROctetStringParser.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/BEROctetStringParser.java
@@ -7,6 +7,8 @@
 
 /**
  * A parser for indefinite-length OCTET STRINGs.
+ * 
+ * @deprecated Check for 'ASN1OctetStringParser' instead 
  */
 public class BEROctetStringParser
     implements ASN1OctetStringParser
@@ -38,7 +40,7 @@
     public ASN1Primitive getLoadedObject()
         throws IOException
     {
-        return new BEROctetString(Streams.readAll(getOctetStream()));
+        return parse(_parser);
     }
 
     /**
@@ -57,4 +59,9 @@
             throw new ASN1ParsingException("IOException converting stream to byte array: " + e.getMessage(), e);
         }
     }
+
+    static BEROctetString parse(ASN1StreamParser sp) throws IOException
+    {
+        return new BEROctetString(Streams.readAll(new ConstructedOctetStream(sp)));
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/BERSequence.java b/bcprov/src/main/java/org/bouncycastle/asn1/BERSequence.java
index 269c6d4..b50f493 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/BERSequence.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/BERSequence.java
@@ -44,22 +44,42 @@
         super(elements);
     }
 
-    int encodedLength() throws IOException
+    int encodedLength(boolean withTag) throws IOException
     {
-        int count = elements.length;
-        int totalLength = 0;
+        int totalLength = withTag ? 4 : 3;
 
-        for (int i = 0; i < count; ++i)
+        for (int i = 0, count = elements.length; i < count; ++i)
         {
             ASN1Primitive p = elements[i].toASN1Primitive();
-            totalLength += p.encodedLength();
+            totalLength += p.encodedLength(true);
         }
 
-        return 2 + totalLength + 2;
+        return totalLength;
     }
 
     void encode(ASN1OutputStream out, boolean withTag) throws IOException
     {
-        out.writeEncodedIndef(withTag, BERTags.SEQUENCE | BERTags.CONSTRUCTED, elements);
+        out.writeEncodingIL(withTag, BERTags.CONSTRUCTED | BERTags.SEQUENCE, elements);
+    }
+
+    ASN1BitString toASN1BitString()
+    {
+        return new BERBitString(getConstructedBitStrings());
+    }
+
+    ASN1External toASN1External()
+    {
+        // TODO There is currently no BERExternal class
+        return ((ASN1Sequence)toDLObject()).toASN1External();
+    }
+
+    ASN1OctetString toASN1OctetString()
+    {
+        return new BEROctetString(getConstructedOctetStrings());
+    }
+
+    ASN1Set toASN1Set()
+    {
+        return new BERSet(false, toArrayInternal());
     }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/BERSequenceParser.java b/bcprov/src/main/java/org/bouncycastle/asn1/BERSequenceParser.java
index 543a182..c53b001 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/BERSequenceParser.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/BERSequenceParser.java
@@ -4,6 +4,8 @@
 
 /**
  * Parser for indefinite-length SEQUENCEs.
+ * 
+ * @deprecated Check for 'ASN1SequenceParser' instead 
  */
 public class BERSequenceParser
     implements ASN1SequenceParser
@@ -36,7 +38,7 @@
     public ASN1Primitive getLoadedObject()
         throws IOException
     {
-        return new BERSequence(_parser.readVector());
+        return parse(_parser);
     }
 
     /**
@@ -55,4 +57,9 @@
             throw new IllegalStateException(e.getMessage());
         }
     }
+
+    static BERSequence parse(ASN1StreamParser sp) throws IOException
+    {
+        return new BERSequence(sp.readVector());
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/BERSet.java b/bcprov/src/main/java/org/bouncycastle/asn1/BERSet.java
index 905b4e7..f900258 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/BERSet.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/BERSet.java
@@ -59,22 +59,21 @@
         super(isSorted, elements);
     }
 
-    int encodedLength() throws IOException
+    int encodedLength(boolean withTag) throws IOException
     {
-        int count = elements.length;
-        int totalLength = 0;
+        int totalLength = withTag ? 4 : 3;
 
-        for (int i = 0; i < count; ++i)
+        for (int i = 0, count = elements.length; i < count; ++i)
         {
             ASN1Primitive p = elements[i].toASN1Primitive();
-            totalLength += p.encodedLength();
+            totalLength += p.encodedLength(true);
         }
 
-        return 2 + totalLength + 2;
+        return totalLength;
     }
 
     void encode(ASN1OutputStream out, boolean withTag) throws IOException
     {
-        out.writeEncodedIndef(withTag, BERTags.SET | BERTags.CONSTRUCTED, elements);
+        out.writeEncodingIL(withTag, BERTags.CONSTRUCTED | BERTags.SET, elements);
     }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/BERSetParser.java b/bcprov/src/main/java/org/bouncycastle/asn1/BERSetParser.java
index c6e1809..cc369df 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/BERSetParser.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/BERSetParser.java
@@ -4,6 +4,8 @@
 
 /**
  * Parser for indefinite-length SETs.
+ * 
+ * @deprecated Check for 'ASN1SetParser' instead 
  */
 public class BERSetParser
     implements ASN1SetParser
@@ -36,7 +38,7 @@
     public ASN1Primitive getLoadedObject()
         throws IOException
     {
-        return new BERSet(_parser.readVector());
+        return parse(_parser);
     }
 
     /**
@@ -55,4 +57,9 @@
             throw new ASN1ParsingException(e.getMessage(), e);
         }
     }
-}
\ No newline at end of file
+
+    static BERSet parse(ASN1StreamParser sp) throws IOException
+    {
+        return new BERSet(sp.readVector());
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/BERTaggedObject.java b/bcprov/src/main/java/org/bouncycastle/asn1/BERTaggedObject.java
index 51036f6..1ef1ccb 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/BERTaggedObject.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/BERTaggedObject.java
@@ -1,7 +1,6 @@
 package org.bouncycastle.asn1;
 
 import java.io.IOException;
-import java.util.Enumeration;
 
 /**
  * BER TaggedObject - in ASN.1 notation this is any object preceded by
@@ -15,124 +14,96 @@
      * @param tagNo the tag number for this object.
      * @param obj the tagged object.
      */
-    public BERTaggedObject(
-        int             tagNo,
-        ASN1Encodable    obj)
+    public BERTaggedObject(int tagNo, ASN1Encodable obj)
     {
         super(true, tagNo, obj);
     }
 
+    public BERTaggedObject(int tagClass, int tagNo, ASN1Encodable obj)
+    {
+        super(true, tagClass, tagNo, obj);
+    }
+
     /**
      * @param explicit true if an explicitly tagged object.
      * @param tagNo the tag number for this object.
      * @param obj the tagged object.
      */
-    public BERTaggedObject(
-        boolean         explicit,
-        int             tagNo,
-        ASN1Encodable    obj)
+    public BERTaggedObject(boolean explicit, int tagNo, ASN1Encodable obj)
     {
         super(explicit, tagNo, obj);
     }
 
-    /**
-     * create an implicitly tagged object that contains a zero
-     * length sequence.
-     */
-    public BERTaggedObject(
-        int             tagNo)
+    public BERTaggedObject(boolean explicit, int tagClass, int tagNo, ASN1Encodable obj)
     {
-        super(false, tagNo, new BERSequence());
+        super(explicit, tagClass, tagNo, obj);
     }
 
-    boolean isConstructed()
+    BERTaggedObject(int explicitness, int tagClass, int tagNo, ASN1Encodable obj)
     {
-        return explicit || obj.toASN1Primitive().isConstructed();
+        super(explicitness, tagClass, tagNo, obj);
     }
 
-    int encodedLength()
-        throws IOException
+    boolean encodeConstructed()
+    {
+        return isExplicit() || obj.toASN1Primitive().encodeConstructed();
+    }
+
+    int encodedLength(boolean withTag) throws IOException
     {
         ASN1Primitive primitive = obj.toASN1Primitive();
-        int length = primitive.encodedLength();
+        boolean explicit = isExplicit();
+
+        int length = primitive.encodedLength(explicit);
 
         if (explicit)
         {
-            return StreamUtil.calculateTagLength(tagNo) + StreamUtil.calculateBodyLength(length) + length;
+            length += 3;
         }
-        else
-        {
-            // header length already in calculation
-            length = length - 1;
 
-            return StreamUtil.calculateTagLength(tagNo) + length;
-        }
+        length += withTag ? ASN1OutputStream.getLengthOfIdentifier(tagNo) : 0;
+
+        return length;
     }
 
     void encode(ASN1OutputStream out, boolean withTag) throws IOException
     {
-        out.writeTag(withTag, BERTags.CONSTRUCTED | BERTags.TAGGED, tagNo);
-        out.write(0x80);
+//        assert out.getClass().isAssignableFrom(ASN1OutputStream.class);
 
-        if (!explicit)
+        ASN1Primitive primitive = obj.toASN1Primitive();
+        boolean explicit = isExplicit();
+
+        if (withTag)
         {
-            Enumeration e;
-            if (obj instanceof ASN1OctetString)
+            int flags = tagClass;
+            if (explicit || primitive.encodeConstructed())
             {
-                if (obj instanceof BEROctetString)
-                {
-                    e = ((BEROctetString)obj).getObjects();
-                }
-                else
-                {
-                    ASN1OctetString octs = (ASN1OctetString)obj;
-                    BEROctetString berO = new BEROctetString(octs.getOctets());
-                    e = berO.getObjects();
-                }
-            }
-            else if (obj instanceof ASN1Sequence)
-            {
-                e = ((ASN1Sequence)obj).getObjects();
-            }
-            else if (obj instanceof ASN1Set)
-            {
-                e = ((ASN1Set)obj).getObjects();
-            }
-            else
-            {
-                throw new ASN1Exception("not implemented: " + obj.getClass().getName());
+                flags |= BERTags.CONSTRUCTED;
             }
 
-            out.writeElements(e);
+            out.writeIdentifier(true, flags, tagNo);
+        }
+
+        if (explicit)
+        {
+            out.write(0x80);
+            primitive.encode(out, true);
+            out.write(0x00);
+            out.write(0x00);
         }
         else
         {
-            out.writePrimitive(obj.toASN1Primitive(), true);
+            primitive.encode(out, false);
         }
+    }
 
-        out.write(0x00);
-        out.write(0x00);
+    ASN1Sequence rebuildConstructed(ASN1Primitive primitive)
+    {
+        return new BERSequence(primitive);
+    }
 
-//        ASN1Primitive primitive = obj.toASN1Primitive();
-//
-//        int flags = BERTags.TAGGED;
-//        if (explicit || primitive.isConstructed())
-//        {
-//            flags |= BERTags.CONSTRUCTED;
-//        }
-//
-//        out.writeTag(withTag, flags, tagNo);
-//
-//        if (explicit)
-//        {
-//            out.write(0x80);
-//            out.writePrimitive(obj.toASN1Primitive(), true);
-//            out.write(0x00);
-//            out.write(0x00);
-//        }
-//        else
-//        {
-//            out.writePrimitive(obj.toASN1Primitive(), false);
-//        }
+    ASN1TaggedObject replaceTag(int tagClass, int tagNo)
+    {
+        return new BERTaggedObject(explicitness, tagClass, tagNo, obj);
     }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/BERTaggedObjectParser.java b/bcprov/src/main/java/org/bouncycastle/asn1/BERTaggedObjectParser.java
index 02f3f26..838e884 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/BERTaggedObjectParser.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/BERTaggedObjectParser.java
@@ -5,66 +5,48 @@
 /**
  * Parser for indefinite-length tagged objects.
  */
-public class BERTaggedObjectParser
+class BERTaggedObjectParser
     implements ASN1TaggedObjectParser
 {
-    private boolean _constructed;
-    private int _tagNumber;
-    private ASN1StreamParser _parser;
+    final int _tagClass;
+    final int _tagNo;
+    final ASN1StreamParser _parser;
 
-    BERTaggedObjectParser(
-        boolean             constructed,
-        int                 tagNumber,
-        ASN1StreamParser    parser)
+    BERTaggedObjectParser(int tagClass, int tagNo, ASN1StreamParser parser)
     {
-        _constructed = constructed;
-        _tagNumber = tagNumber;
+        _tagClass = tagClass;
+        _tagNo = tagNo;
         _parser = parser;
     }
 
-    /**
-     * Return true if this tagged object is marked as constructed.
-     *
-     * @return true if constructed, false otherwise.
-     */
-    public boolean isConstructed()
+    public int getTagClass()
     {
-        return _constructed;
+        return _tagClass;
     }
 
-    /**
-     * Return the tag number associated with this object.
-     *
-     * @return the tag number.
-     */
     public int getTagNo()
     {
-        return _tagNumber;
+        return _tagNo;
     }
 
-    /**
-     * Return an object parser for the contents of this tagged object.
-     *
-     * @param tag the actual tag number of the object (needed if implicit).
-     * @param isExplicit true if the contained object was explicitly tagged, false if implicit.
-     * @return an ASN.1 encodable object parser.
-     * @throws IOException if there is an issue building the object parser from the stream.
-     */
-    public ASN1Encodable getObjectParser(
-        int     tag,
-        boolean isExplicit)
-        throws IOException
+    public boolean hasContextTag()
     {
-        if (isExplicit)
-        {
-            if (!_constructed)
-            {
-                throw new IOException("Explicit tags must be constructed (see X.690 8.14.2)");
-            }
-            return _parser.readObject();
-        }
+        return this._tagClass == BERTags.CONTEXT_SPECIFIC;
+    }
 
-        return _parser.readImplicit(_constructed, tag);
+    public boolean hasContextTag(int tagNo)
+    {
+        return this._tagClass == BERTags.CONTEXT_SPECIFIC && this._tagNo == tagNo;
+    }
+
+    public boolean hasTag(int tagClass, int tagNo)
+    {
+        return this._tagClass == tagClass && this._tagNo == tagNo;
+    }
+
+    public boolean hasTagClass(int tagClass)
+    {
+        return this._tagClass == tagClass;
     }
 
     /**
@@ -76,7 +58,32 @@
     public ASN1Primitive getLoadedObject()
         throws IOException
     {
-        return _parser.readTaggedObject(_constructed, _tagNumber);
+        return _parser.loadTaggedIL(_tagClass, _tagNo);
+    }
+
+    public ASN1Encodable parseBaseUniversal(boolean declaredExplicit, int baseTagNo) throws IOException
+    {
+        if (declaredExplicit)
+        {
+            return _parser.parseObject(baseTagNo);
+        }
+
+        return _parser.parseImplicitConstructedIL(baseTagNo);
+    }
+
+    public ASN1Encodable parseExplicitBaseObject() throws IOException
+    {
+        return _parser.readObject();
+    }
+
+    public ASN1TaggedObjectParser parseExplicitBaseTagged() throws IOException
+    {
+        return _parser.parseTaggedObject();
+    }
+
+    public ASN1TaggedObjectParser parseImplicitBaseTagged(int baseTagClass, int baseTagNo) throws IOException
+    {
+        return new BERTaggedObjectParser(baseTagClass, baseTagNo, _parser);
     }
 
     /**
@@ -88,7 +95,7 @@
     {
         try
         {
-            return this.getLoadedObject();
+            return getLoadedObject();
         }
         catch (IOException e)
         {
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/BERTags.java b/bcprov/src/main/java/org/bouncycastle/asn1/BERTags.java
index 39b517e..2c6c28c 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/BERTags.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/BERTags.java
@@ -2,20 +2,26 @@
 
 public interface BERTags
 {
+    // 0x00: Reserved for use by the encoding rules
     public static final int BOOLEAN             = 0x01;
     public static final int INTEGER             = 0x02;
     public static final int BIT_STRING          = 0x03;
     public static final int OCTET_STRING        = 0x04;
     public static final int NULL                = 0x05;
     public static final int OBJECT_IDENTIFIER   = 0x06;
+    public static final int OBJECT_DESCRIPTOR   = 0x07;
     public static final int EXTERNAL            = 0x08;
+    public static final int REAL                = 0x09;
     public static final int ENUMERATED          = 0x0a; // decimal 10
+    public static final int EMBEDDED_PDV        = 0x0b; // decimal 11
+    public static final int UTF8_STRING         = 0x0c; // decimal 12
+    public static final int RELATIVE_OID        = 0x0d; // decimal 13
+    public static final int TIME                = 0x0e;
+    // 0x0f: Reserved for future editions of this Recommendation | International Standard
     public static final int SEQUENCE            = 0x10; // decimal 16
     public static final int SEQUENCE_OF         = 0x10; // for completeness - used to model a SEQUENCE of the same type.
     public static final int SET                 = 0x11; // decimal 17
     public static final int SET_OF              = 0x11; // for completeness - used to model a SET of the same type.
-
-
     public static final int NUMERIC_STRING      = 0x12; // decimal 18
     public static final int PRINTABLE_STRING    = 0x13; // decimal 19
     public static final int T61_STRING          = 0x14; // decimal 20
@@ -27,10 +33,23 @@
     public static final int VISIBLE_STRING      = 0x1a; // decimal 26
     public static final int GENERAL_STRING      = 0x1b; // decimal 27
     public static final int UNIVERSAL_STRING    = 0x1c; // decimal 28
+    public static final int UNRESTRICTED_STRING = 0x1d; // decimal 29
     public static final int BMP_STRING          = 0x1e; // decimal 30
-    public static final int UTF8_STRING         = 0x0c; // decimal 12
-    
+    public static final int DATE                = 0x1f;
+    public static final int TIME_OF_DAY         = 0x20;
+    public static final int DATE_TIME           = 0x21;
+    public static final int DURATION            = 0x22;
+    public static final int OBJECT_IDENTIFIER_IRI = 0x23;
+    public static final int RELATIVE_OID_IRI    = 0x24;
+    // 0x25..: Reserved for addenda to this Recommendation | International Standard
+
     public static final int CONSTRUCTED         = 0x20; // decimal 32
+
+    public static final int UNIVERSAL           = 0x00; // decimal 32
     public static final int APPLICATION         = 0x40; // decimal 64
-    public static final int TAGGED              = 0x80; // decimal 128
+    public static final int TAGGED              = 0x80; // decimal 128 - maybe should deprecate this.
+    public static final int CONTEXT_SPECIFIC    = 0x80; // decimal 128
+    public static final int PRIVATE             = 0xC0; // decimal 192
+
+    public static final int FLAGS               = 0xE0;
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/ConstructedBitStream.java b/bcprov/src/main/java/org/bouncycastle/asn1/ConstructedBitStream.java
new file mode 100644
index 0000000..cd020ea
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/ConstructedBitStream.java
@@ -0,0 +1,145 @@
+package org.bouncycastle.asn1;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+class ConstructedBitStream
+    extends InputStream
+{
+    private final ASN1StreamParser _parser;
+    private final boolean _octetAligned;
+
+    private boolean                _first = true;
+    private int                    _padBits = 0;
+
+    private ASN1BitStringParser    _currentParser;
+    private InputStream            _currentStream;
+
+    ConstructedBitStream(ASN1StreamParser parser, boolean octetAligned)
+    {
+        _parser = parser;
+        _octetAligned = octetAligned;
+    }
+
+    int getPadBits()
+    {
+        return _padBits;
+    }
+
+    public int read(byte[] b, int off, int len) throws IOException
+    {
+        if (_currentStream == null)
+        {
+            if (!_first)
+            {
+                return -1;
+            }
+
+            _currentParser = getNextParser();
+            if (_currentParser == null)
+            {
+                return -1;
+            }
+
+            _first = false;
+            _currentStream = _currentParser.getBitStream();
+            
+        }
+
+        int totalRead = 0;
+
+        for (;;)
+        {
+            int numRead = _currentStream.read(b, off + totalRead, len - totalRead);
+
+            if (numRead >= 0)
+            {
+                totalRead += numRead;
+
+                if (totalRead == len)
+                {
+                    return totalRead;
+                }
+            }
+            else
+            {
+                _padBits = _currentParser.getPadBits();
+                _currentParser = getNextParser();
+                if (_currentParser == null)
+                {
+                    _currentStream = null;
+                    return totalRead < 1 ? -1 : totalRead;
+                }
+
+                _currentStream = _currentParser.getBitStream();
+            }
+        }
+    }
+
+    public int read()
+        throws IOException
+    {
+        if (_currentStream == null)
+        {
+            if (!_first)
+            {
+                return -1;
+            }
+
+            _currentParser = getNextParser();
+            if (_currentParser == null)
+            {
+                return -1;
+            }
+
+            _first = false;
+            _currentStream = _currentParser.getBitStream();
+        }
+
+        for (;;)
+        {
+            int b = _currentStream.read();
+
+            if (b >= 0)
+            {
+                return b;
+            }
+
+            _padBits = _currentParser.getPadBits();
+            _currentParser = getNextParser();
+            if (_currentParser == null)
+            {
+                _currentStream = null;
+                return -1;
+            }
+
+            _currentStream = _currentParser.getBitStream();
+        }
+    }
+
+    private ASN1BitStringParser getNextParser() throws IOException
+    {
+        ASN1Encodable asn1Obj = _parser.readObject();
+        if (asn1Obj == null)
+        {
+            if (_octetAligned && _padBits != 0)
+            {
+                throw new IOException("expected octet-aligned bitstring, but found padBits: " + _padBits);
+            }
+
+            return null;
+        }
+
+        if (asn1Obj instanceof ASN1BitStringParser)
+        {
+            if (_padBits != 0)
+            {
+                throw new IOException("only the last nested bitstring can have padding");
+            }
+
+            return (ASN1BitStringParser)asn1Obj;
+        }
+
+        throw new IOException("unknown object encountered: " + asn1Obj.getClass());
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/DERApplicationSpecific.java b/bcprov/src/main/java/org/bouncycastle/asn1/DERApplicationSpecific.java
deleted file mode 100644
index eb54b20..0000000
--- a/bcprov/src/main/java/org/bouncycastle/asn1/DERApplicationSpecific.java
+++ /dev/null
@@ -1,124 +0,0 @@
-package org.bouncycastle.asn1;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-
-/**
- * A DER encoding version of an application specific object.
- */
-public class DERApplicationSpecific 
-    extends ASN1ApplicationSpecific
-{
-    DERApplicationSpecific(
-        boolean isConstructed,
-        int     tag,
-        byte[]  octets)
-    {
-        super(isConstructed, tag, octets);
-    }
-
-    /**
-     * Create an application specific object from the passed in data. This will assume
-     * the data does not represent a constructed object.
-     *
-     * @param tag the tag number for this object.
-     * @param octets the encoding of the object's body.
-     */
-    public DERApplicationSpecific(
-        int    tag,
-        byte[] octets)
-    {
-        this(false, tag, octets);
-    }
-
-    /**
-     * Create an application specific object with a tagging of explicit/constructed.
-     *
-     * @param tag the tag number for this object.
-     * @param object the object to be contained.
-     */
-    public DERApplicationSpecific(
-        int           tag,
-        ASN1Encodable object)
-        throws IOException 
-    {
-        this(true, tag, object);
-    }
-
-    /**
-     * Create an application specific object with the tagging style given by the value of constructed.
-     *
-     * @param constructed true if the object is constructed.
-     * @param tag the tag number for this object.
-     * @param object the object to be contained.
-     */
-    public DERApplicationSpecific(
-        boolean      constructed,
-        int          tag,
-        ASN1Encodable object)
-        throws IOException
-    {
-        super(constructed || object.toASN1Primitive().isConstructed(), tag, getEncoding(constructed, object));
-    }
-
-    private static byte[] getEncoding(boolean explicit, ASN1Encodable object)
-        throws IOException
-    {
-        byte[] data = object.toASN1Primitive().getEncoded(ASN1Encoding.DER);
-
-        if (explicit)
-        {
-            return data;
-        }
-        else
-        {
-            int lenBytes = getLengthOfHeader(data);
-            byte[] tmp = new byte[data.length - lenBytes];
-            System.arraycopy(data, lenBytes, tmp, 0, tmp.length);
-            return tmp;
-        }
-    }
-
-    /**
-     * Create an application specific object which is marked as constructed
-     *
-     * @param tagNo the tag number for this object.
-     * @param vec the objects making up the application specific object.
-     */
-    public DERApplicationSpecific(int tagNo, ASN1EncodableVector vec)
-    {
-        super(true, tagNo, getEncodedVector(vec));
-    }
-
-    private static byte[] getEncodedVector(ASN1EncodableVector vec)
-    {
-        ByteArrayOutputStream bOut = new ByteArrayOutputStream();
-
-        for (int i = 0; i != vec.size(); i++)
-        {
-            try
-            {
-                bOut.write(((ASN1Object)vec.get(i)).getEncoded(ASN1Encoding.DER));
-            }
-            catch (IOException e)
-            {
-                throw new ASN1ParsingException("malformed object: " + e, e);
-            }
-        }
-        return bOut.toByteArray();
-    }
-
-    /* (non-Javadoc)
-     * @see org.bouncycastle.asn1.ASN1Primitive#encode(org.bouncycastle.asn1.DEROutputStream)
-     */
-    void encode(ASN1OutputStream out, boolean withTag) throws IOException
-    {
-        int flags = BERTags.APPLICATION;
-        if (isConstructed)
-        {
-            flags |= BERTags.CONSTRUCTED;
-        }
-
-        out.writeEncoded(withTag, flags, tag, octets);
-    }
-}
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/DERBMPString.java b/bcprov/src/main/java/org/bouncycastle/asn1/DERBMPString.java
index 8daf842..31f54ac 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/DERBMPString.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/DERBMPString.java
@@ -1,9 +1,5 @@
 package org.bouncycastle.asn1;
 
-import java.io.IOException;
-
-import org.bouncycastle.util.Arrays;
-
 /**
  * DER BMPString object encodes BMP (<i>Basic Multilingual Plane</i>) subset
  * (aka UCS-2) of UNICODE (ISO 10646) characters in codepoints 0 to 65535.
@@ -13,203 +9,28 @@
  * </p>
  */
 public class DERBMPString
-    extends ASN1Primitive
-    implements ASN1String
+    extends ASN1BMPString
 {
-    private final char[]  string;
-
-    /**
-     * Return a BMP String from the given object.
-     *
-     * @param obj the object we want converted.
-     * @exception IllegalArgumentException if the object cannot be converted.
-     * @return a DERBMPString instance, or null.
-     */
-    public static DERBMPString getInstance(
-        Object  obj)
-    {
-        if (obj == null || obj instanceof DERBMPString)
-        {
-            return (DERBMPString)obj;
-        }
-
-        if (obj instanceof byte[])
-        {
-            try
-            {
-                return (DERBMPString)fromByteArray((byte[])obj);
-            }
-            catch (Exception e)
-            {
-                throw new IllegalArgumentException("encoding error in getInstance: " + e.toString());
-            }
-        }
-
-        throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
-    }
-
-    /**
-     * Return a BMP String from a tagged object.
-     *
-     * @param obj the tagged object holding the object we want
-     * @param explicit true if the object is meant to be explicitly
-     *              tagged false otherwise.
-     * @exception IllegalArgumentException if the tagged object cannot
-     *              be converted.
-     * @return a DERBMPString instance.
-     */
-    public static DERBMPString getInstance(
-        ASN1TaggedObject obj,
-        boolean          explicit)
-    {
-        ASN1Primitive o = obj.getObject();
-
-        if (explicit || o instanceof DERBMPString)
-        {
-            return getInstance(o);
-        }
-        else
-        {
-            return new DERBMPString(ASN1OctetString.getInstance(o).getOctets());
-        }
-    }
-
-    /**
-     * Basic constructor - byte encoded string.
-     * @param string the encoded BMP STRING to wrap.
-     */
-    DERBMPString(
-        byte[]   string)
-    {
-        if (string == null)
-        {
-            throw new NullPointerException("'string' cannot be null");
-        }
-
-        int byteLen = string.length;
-        if (0 != (byteLen & 1))
-        {
-            throw new IllegalArgumentException("malformed BMPString encoding encountered");
-        }
-
-        int charLen = byteLen / 2;
-        char[] cs = new char[charLen];
-
-        for (int i = 0; i != charLen; i++)
-        {
-            cs[i] = (char)((string[2 * i] << 8) | (string[2 * i + 1] & 0xff));
-        }
-
-        this.string = cs;
-    }
-
-    DERBMPString(char[] string)
-    {
-        if (string == null)
-        {
-            throw new NullPointerException("'string' cannot be null");
-        }
-
-        this.string = string;
-    }
-
     /**
      * Basic constructor
      * @param string a String to wrap as a BMP STRING.
      */
-    public DERBMPString(
-        String   string)
+    public DERBMPString(String string)
     {
-        if (string == null)
-        {
-            throw new NullPointerException("'string' cannot be null");
-        }
-
-        this.string = string.toCharArray();
+        super(string);
     }
 
-    public String getString()
+    /**
+     * Basic constructor - byte encoded string.
+     * @param contents the encoded BMP STRING to wrap.
+     */
+    DERBMPString(byte[] contents)
     {
-        return new String(string);
+        super(contents);
     }
 
-    public String toString()
+    DERBMPString(char[] string)
     {
-        return getString();
-    }
-
-    public int hashCode()
-    {
-        return Arrays.hashCode(string);
-    }
-
-    protected boolean asn1Equals(
-        ASN1Primitive o)
-    {
-        if (!(o instanceof DERBMPString))
-        {
-            return false;
-        }
-
-        DERBMPString  s = (DERBMPString)o;
-
-        return Arrays.areEqual(string, s.string);
-    }
-
-    boolean isConstructed()
-    {
-        return false;
-    }
-
-    int encodedLength()
-    {
-        return 1 + StreamUtil.calculateBodyLength(string.length * 2) + (string.length * 2);
-    }
-
-    void encode(
-        ASN1OutputStream out, boolean withTag)
-        throws IOException
-    {
-        int count = string.length;
-        if (withTag)
-        {
-            out.write(BERTags.BMP_STRING);
-        }
-        out.writeLength(count * 2);
-
-        byte[] buf = new byte[8];
-
-        int i = 0, limit = count & -4;
-        while (i < limit)
-        {
-            char c0 = string[i], c1 = string[i + 1], c2 = string[i + 2], c3 = string[i + 3];
-            i += 4;
-
-            buf[0] = (byte)(c0 >> 8);
-            buf[1] = (byte)c0;
-            buf[2] = (byte)(c1 >> 8);
-            buf[3] = (byte)c1;
-            buf[4] = (byte)(c2 >> 8);
-            buf[5] = (byte)c2;
-            buf[6] = (byte)(c3 >> 8);
-            buf[7] = (byte)c3;
-
-            out.write(buf, 0, 8);
-        }
-        if (i < count)
-        {
-            int bufPos = 0;
-            do
-            {
-                char c0 = string[i];
-                i += 1;
-
-                buf[bufPos++] = (byte)(c0 >> 8);
-                buf[bufPos++] = (byte)c0;
-            }
-            while (i < count);
-
-            out.write(buf, 0, bufPos);
-        }
+        super(string);
     }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/DERBitString.java b/bcprov/src/main/java/org/bouncycastle/asn1/DERBitString.java
index 0807aaf..26953ec 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/DERBitString.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/DERBitString.java
@@ -8,123 +8,69 @@
 public class DERBitString
     extends ASN1BitString
 {
-    /**
-     * return a Bit String from the passed in object
-     *
-     * @param obj a DERBitString or an object that can be converted into one.
-     * @exception IllegalArgumentException if the object cannot be converted.
-     * @return a DERBitString instance, or null.
-     */
-    public static DERBitString getInstance(
-        Object  obj)
+    public static DERBitString convert(ASN1BitString bitString)
     {
-        if (obj == null || obj instanceof DERBitString)
-        {
-            return (DERBitString)obj;
-        }
-        if (obj instanceof DLBitString)
-        {
-            return new DERBitString(((DLBitString)obj).data, ((DLBitString)obj).padBits);
-        }
-        if (obj instanceof byte[])
-        {
-            try
-            {
-                return (DERBitString)fromByteArray((byte[])obj);
-            }
-            catch (Exception e)
-            {
-                throw new IllegalArgumentException("encoding error in getInstance: " + e.toString());
-            }
-        }
-
-        throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
+        return (DERBitString)bitString.toDERObject();
     }
 
-    /**
-     * return a Bit String from a tagged object.
-     *
-     * @param obj the tagged object holding the object we want
-     * @param explicit true if the object is meant to be explicitly
-     *              tagged false otherwise.
-     * @exception IllegalArgumentException if the tagged object cannot
-     *               be converted.
-     * @return a DERBitString instance, or null.
-     */
-    public static DERBitString getInstance(
-        ASN1TaggedObject obj,
-        boolean          explicit)
-    {
-        ASN1Primitive o = obj.getObject();
-
-        if (explicit || o instanceof DERBitString)
-        {
-            return getInstance(o);
-        }
-        else
-        {
-            return fromOctetString(ASN1OctetString.getInstance(o).getOctets());
-        }
-    }
-
-    protected DERBitString(byte data, int padBits)
-    {
-        super(data, padBits);
-    }
-
-    /**
-     * @param data the octets making up the bit string.
-     * @param padBits the number of extra bits at the end of the string.
-     */
-    public DERBitString(
-        byte[]  data,
-        int     padBits)
-    {
-        super(data, padBits);
-    }
-
-    public DERBitString(
-        byte[]  data)
+    public DERBitString(byte[] data)
     {
         this(data, 0);
     }
 
-    public DERBitString(
-        int value)
+    public DERBitString(byte data, int padBits)
     {
+        super(data, padBits);
+    }
+
+    public DERBitString(byte[] data, int padBits)
+    {
+        super(data, padBits);
+    }
+
+    public DERBitString(int value)
+    {
+        // TODO[asn1] Unify in single allocation of 'contents'
         super(getBytes(value), getPadBits(value));
     }
 
-    public DERBitString(
-        ASN1Encodable obj)
-        throws IOException
+    public DERBitString(ASN1Encodable obj) throws IOException
     {
+        // TODO[asn1] Unify in single allocation of 'contents'
         super(obj.toASN1Primitive().getEncoded(ASN1Encoding.DER), 0);
     }
 
-    boolean isConstructed()
+    DERBitString(byte[] contents, boolean check)
+    {
+        super(contents, check);
+    }
+
+    boolean encodeConstructed()
     {
         return false;
     }
 
-    int encodedLength()
+    int encodedLength(boolean withTag)
     {
-        return 1 + StreamUtil.calculateBodyLength(data.length + 1) + data.length + 1;
+        return ASN1OutputStream.getLengthOfEncodingDL(withTag, contents.length);
     }
 
     void encode(ASN1OutputStream out, boolean withTag) throws IOException
     {
-        int len = data.length;
-        if (0 == len
-            || 0 == padBits
-            || (data[len - 1] == (byte)(data[len - 1] & (0xFF << padBits))))
+        int padBits = contents[0] & 0xFF;
+        int length = contents.length;
+        int last = length - 1;
+
+        byte lastOctet = contents[last];
+        byte lastOctetDER = (byte)(contents[last] & (0xFF << padBits));
+
+        if (lastOctet == lastOctetDER)
         {
-            out.writeEncoded(withTag, BERTags.BIT_STRING, (byte)padBits, data);
+            out.writeEncodingDL(withTag, BERTags.BIT_STRING, contents);
         }
         else
         {
-            byte der = (byte)(data[len - 1] & (0xFF << padBits));
-            out.writeEncoded(withTag, BERTags.BIT_STRING, (byte)padBits, data, 0, len - 1, der);
+            out.writeEncodingDL(withTag, BERTags.BIT_STRING, contents, 0, last, lastOctetDER);
         }
     }
 
@@ -138,21 +84,8 @@
         return this;
     }
 
-    static DERBitString fromOctetString(byte[] bytes)
+    static DERBitString fromOctetString(ASN1OctetString octetString)
     {
-        if (bytes.length < 1)
-        {
-            throw new IllegalArgumentException("truncated BIT STRING detected");
-        }
-
-        int padBits = bytes[0];
-        byte[] data = new byte[bytes.length - 1];
-
-        if (data.length != 0)
-        {
-            System.arraycopy(bytes, 1, data, 0, bytes.length - 1);
-        }
-
-        return new DERBitString(data, padBits);
+        return new DERBitString(octetString.getOctets(), true);
     }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/DEREncodableVector.java b/bcprov/src/main/java/org/bouncycastle/asn1/DEREncodableVector.java
deleted file mode 100644
index ff1059a..0000000
--- a/bcprov/src/main/java/org/bouncycastle/asn1/DEREncodableVector.java
+++ /dev/null
@@ -1,18 +0,0 @@
-package org.bouncycastle.asn1;
-
-/**
- * a general class for building up a vector of DER encodable objects -
- * this will eventually be superseded by ASN1EncodableVector so you should
- * use that class in preference.
- */
-public class DEREncodableVector
-    extends ASN1EncodableVector
-{
-    /**
-     * @deprecated use ASN1EncodableVector instead.
-     */
-    public DEREncodableVector()
-    {
-
-    }
-}
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/DEREnumerated.java b/bcprov/src/main/java/org/bouncycastle/asn1/DEREnumerated.java
deleted file mode 100644
index daa8777..0000000
--- a/bcprov/src/main/java/org/bouncycastle/asn1/DEREnumerated.java
+++ /dev/null
@@ -1,37 +0,0 @@
-package org.bouncycastle.asn1;
-
-import java.math.BigInteger;
-
-/**
- * @deprecated Use ASN1Enumerated instead of this.
- */
-public class DEREnumerated
-    extends ASN1Enumerated
-{
-    /**
-     * @param bytes the value of this enumerated as an encoded BigInteger (signed).
-     * @deprecated use ASN1Enumerated
-     */
-    DEREnumerated(byte[] bytes)
-    {
-        super(bytes);
-    }
-
-    /**
-     * @param value the value of this enumerated.
-     * @deprecated use ASN1Enumerated
-     */
-    public DEREnumerated(BigInteger value)
-    {
-        super(value);
-    }
-
-    /**
-     * @param value the value of this enumerated.
-     * @deprecated use ASN1Enumerated
-     */
-    public DEREnumerated(int value)
-    {
-        super(value);
-    }
-}
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/DERExternal.java b/bcprov/src/main/java/org/bouncycastle/asn1/DERExternal.java
index 02b0e76..fced6d2 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/DERExternal.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/DERExternal.java
@@ -1,8 +1,5 @@
 package org.bouncycastle.asn1;
 
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-
 /**
  * Class representing the DER-type External
  */
@@ -19,11 +16,30 @@
      * <li> Anything but {@link DERTaggedObject} + data {@link DERTaggedObject} (data value form)</li>
      * </ul>
      *
-     * @throws IllegalArgumentException if input size is wrong, or
+     * @throws IllegalArgumentException if input size is wrong, or input is not an acceptable format
+     * 
+     * @deprecated Use {@link DERExternal#DERExternal(DERSequence)} instead.
      */
     public DERExternal(ASN1EncodableVector vector)
     {
-        super(vector);
+        this(DERFactory.createSequence(vector));
+    }
+
+    /**
+     * Construct a DER EXTERNAL object, the input sequence must have exactly two elements on it.
+     * <p>
+     * Acceptable input formats are:
+     * <ul>
+     * <li> {@link ASN1ObjectIdentifier} + data {@link DERTaggedObject} (direct reference form)</li>
+     * <li> {@link ASN1Integer} + data {@link DERTaggedObject} (indirect reference form)</li>
+     * <li> Anything but {@link DERTaggedObject} + data {@link DERTaggedObject} (data value form)</li>
+     * </ul>
+     *
+     * @throws IllegalArgumentException if input size is wrong, or input is not an acceptable format
+     */
+    public DERExternal(DERSequence sequence)
+    {
+        super(sequence);
     }
 
     /**
@@ -34,9 +50,10 @@
      * @param dataValueDescriptor The data value descriptor or <code>null</code> if not set.
      * @param externalData The external data in its encoded form.
      */
-    public DERExternal(ASN1ObjectIdentifier directReference, ASN1Integer indirectReference, ASN1Primitive dataValueDescriptor, DERTaggedObject externalData)
+    public DERExternal(ASN1ObjectIdentifier directReference, ASN1Integer indirectReference,
+        ASN1Primitive dataValueDescriptor, DERTaggedObject externalData)
     {
-        this(directReference, indirectReference, dataValueDescriptor, externalData.getTagNo(), externalData.toASN1Primitive());
+        super(directReference, indirectReference, dataValueDescriptor, externalData);
     }
 
     /**
@@ -48,11 +65,33 @@
      * @param encoding The encoding to be used for the external data
      * @param externalData The external data
      */
-    public DERExternal(ASN1ObjectIdentifier directReference, ASN1Integer indirectReference, ASN1Primitive dataValueDescriptor, int encoding, ASN1Primitive externalData)
+    public DERExternal(ASN1ObjectIdentifier directReference, ASN1Integer indirectReference,
+        ASN1Primitive dataValueDescriptor, int encoding, ASN1Primitive externalData)
     {
         super(directReference, indirectReference, dataValueDescriptor, encoding, externalData);
     }
 
+    ASN1Sequence buildSequence()
+    {
+        ASN1EncodableVector v = new ASN1EncodableVector(4);
+        if (directReference != null)
+        {
+            v.add(directReference);
+        }
+        if (indirectReference != null)
+        {
+            v.add(indirectReference);
+        }
+        if (dataValueDescriptor != null)
+        {
+            v.add(dataValueDescriptor.toDERObject());
+        }
+
+        v.add(new DERTaggedObject(0 == encoding, encoding, externalContent));
+
+        return new DERSequence(v);
+    }
+
     ASN1Primitive toDERObject()
     {
         return this;
@@ -62,34 +101,4 @@
     {
         return this;
     }
-
-    int encodedLength()
-        throws IOException
-    {
-        return this.getEncoded().length;
-    }
-
-    /* (non-Javadoc)
-     * @see org.bouncycastle.asn1.ASN1Primitive#encode(org.bouncycastle.asn1.DEROutputStream)
-     */
-    void encode(ASN1OutputStream out, boolean withTag) throws IOException
-    {
-        ByteArrayOutputStream baos = new ByteArrayOutputStream();
-        if (directReference != null)
-        {
-            baos.write(directReference.getEncoded(ASN1Encoding.DER));
-        }
-        if (indirectReference != null)
-        {
-            baos.write(indirectReference.getEncoded(ASN1Encoding.DER));
-        }
-        if (dataValueDescriptor != null)
-        {
-            baos.write(dataValueDescriptor.getEncoded(ASN1Encoding.DER));
-        }
-        DERTaggedObject obj = new DERTaggedObject(true, encoding, externalContent);
-        baos.write(obj.getEncoded(ASN1Encoding.DER));
-
-        out.writeEncoded(withTag, BERTags.CONSTRUCTED, BERTags.EXTERNAL, baos.toByteArray());
-    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/DERExternalParser.java b/bcprov/src/main/java/org/bouncycastle/asn1/DERExternalParser.java
index afdf510..310acad 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/DERExternalParser.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/DERExternalParser.java
@@ -6,7 +6,7 @@
  * Parser DER EXTERNAL tagged objects.
  */
 public class DERExternalParser
-    implements ASN1Encodable, InMemoryRepresentable
+    implements ASN1ExternalParser
 {
     private ASN1StreamParser _parser;
 
@@ -35,14 +35,7 @@
     public ASN1Primitive getLoadedObject()
         throws IOException
     {
-        try
-        {
-            return new DLExternal(_parser.readVector());
-        }
-        catch (IllegalArgumentException e)
-        {
-            throw new ASN1Exception(e.getMessage(), e);
-        }
+        return parse(_parser);
     }
 
     /**
@@ -65,4 +58,16 @@
             throw new ASN1ParsingException("unable to get DER object", ioe);
         }
     }
+
+    static DLExternal parse(ASN1StreamParser sp) throws IOException
+    {
+        try
+        {
+            return new DLExternal(new DLSequence(sp.readVector()));
+        }
+        catch (IllegalArgumentException e)
+        {
+            throw new ASN1Exception(e.getMessage(), e);
+        }
+    }
 }
\ No newline at end of file
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/DERFactory.java b/bcprov/src/main/java/org/bouncycastle/asn1/DERFactory.java
index d7c7d86..00447be 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/DERFactory.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/DERFactory.java
@@ -2,10 +2,10 @@
 
 class DERFactory
 {
-    static final ASN1Sequence EMPTY_SEQUENCE = new DERSequence();
-    static final ASN1Set EMPTY_SET = new DERSet();
+    static final DERSequence EMPTY_SEQUENCE = new DERSequence();
+    static final DERSet EMPTY_SET = new DERSet();
 
-    static ASN1Sequence createSequence(ASN1EncodableVector v)
+    static DERSequence createSequence(ASN1EncodableVector v)
     {
         if (v.size() < 1)
         {
@@ -15,7 +15,7 @@
         return new DERSequence(v);
     }
 
-    static ASN1Set createSet(ASN1EncodableVector v)
+    static DERSet createSet(ASN1EncodableVector v)
     {
         if (v.size() < 1)
         {
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/DERGeneralString.java b/bcprov/src/main/java/org/bouncycastle/asn1/DERGeneralString.java
index bac214f..c60979e 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/DERGeneralString.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/DERGeneralString.java
@@ -1,10 +1,5 @@
 package org.bouncycastle.asn1;
 
-import java.io.IOException;
-
-import org.bouncycastle.util.Arrays;
-import org.bouncycastle.util.Strings;
-
 /**
  * ASN.1 GENERAL-STRING data type.
  * <p>
@@ -13,136 +8,20 @@
  * </p>
  */
 public class DERGeneralString 
-    extends ASN1Primitive
-    implements ASN1String
+    extends ASN1GeneralString
 {
-    private final byte[] string;
-
-    /**
-     * Return a GeneralString from the given object.
-     *
-     * @param obj the object we want converted.
-     * @exception IllegalArgumentException if the object cannot be converted.
-     * @return a DERBMPString instance, or null.
-     */
-    public static DERGeneralString getInstance(
-        Object obj) 
-    {
-        if (obj == null || obj instanceof DERGeneralString) 
-        {
-            return (DERGeneralString) obj;
-        }
-
-        if (obj instanceof byte[])
-        {
-            try
-            {
-                return (DERGeneralString)fromByteArray((byte[])obj);
-            }
-            catch (Exception e)
-            {
-                throw new IllegalArgumentException("encoding error in getInstance: " + e.toString());
-            }
-        }
-
-        throw new IllegalArgumentException("illegal object in getInstance: "
-                + obj.getClass().getName());
-    }
-
-    /**
-     * Return a GeneralString from a tagged object.
-     *
-     * @param obj the tagged object holding the object we want
-     * @param explicit true if the object is meant to be explicitly
-     *              tagged false otherwise.
-     * @exception IllegalArgumentException if the tagged object cannot
-     *              be converted.
-     * @return a DERGeneralString instance.
-     */
-    public static DERGeneralString getInstance(
-        ASN1TaggedObject obj, 
-        boolean explicit) 
-    {
-        ASN1Primitive o = obj.getObject();
-
-        if (explicit || o instanceof DERGeneralString)
-        {
-            return getInstance(o);
-        }
-        else
-        {
-            return new DERGeneralString(ASN1OctetString.getInstance(o).getOctets());
-        }
-    }
-
-    DERGeneralString(byte[] string)
-    {
-        this.string = string;
-    }
-
     /**
      * Construct a GeneralString from the passed in String.
      *
      * @param string the string to be contained in this object.
      */
-    public DERGeneralString(String string) 
+    public DERGeneralString(String string)
     {
-        this.string = Strings.toByteArray(string);
+        super(string);
     }
 
-    /**
-     * Return a Java String representation of our contained String.
-     *
-     * @return a Java String representing our contents.
-     */
-    public String getString() 
+    DERGeneralString(byte[] contents, boolean clone)
     {
-        return Strings.fromByteArray(string);
-    }
-
-    public String toString()
-    {
-        return getString();
-    }
-
-    /**
-     * Return a byte array representation of our contained String.
-     *
-     * @return a byte array representing our contents.
-     */
-    public byte[] getOctets() 
-    {
-        return Arrays.clone(string);
-    }
-
-    boolean isConstructed()
-    {
-        return false;
-    }
-
-    int encodedLength()
-    {
-        return 1 + StreamUtil.calculateBodyLength(string.length) + string.length;
-    }
-
-    void encode(ASN1OutputStream out, boolean withTag) throws IOException
-    {
-        out.writeEncoded(withTag, BERTags.GENERAL_STRING, string);
-    }
-
-    public int hashCode() 
-    {
-        return Arrays.hashCode(string);
-    }
-    
-    boolean asn1Equals(ASN1Primitive o)
-    {
-        if (!(o instanceof DERGeneralString)) 
-        {
-            return false;
-        }
-        DERGeneralString s = (DERGeneralString)o;
-
-        return Arrays.areEqual(string, s.string);
+        super(contents, clone);
     }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/DERGeneralizedTime.java b/bcprov/src/main/java/org/bouncycastle/asn1/DERGeneralizedTime.java
index 33b9abf..493a73e 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/DERGeneralizedTime.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/DERGeneralizedTime.java
@@ -42,39 +42,39 @@
 
     private byte[] getDERTime()
     {
-        if (time[time.length - 1] == 'Z')
+        if (contents[contents.length - 1] == 'Z')
         {
             if (!hasMinutes())
             {
-                byte[] derTime = new byte[time.length + 4];
+                byte[] derTime = new byte[contents.length + 4];
 
-                System.arraycopy(time, 0, derTime, 0, time.length - 1);
-                System.arraycopy(Strings.toByteArray("0000Z"), 0, derTime, time.length - 1, 5);
+                System.arraycopy(contents, 0, derTime, 0, contents.length - 1);
+                System.arraycopy(Strings.toByteArray("0000Z"), 0, derTime, contents.length - 1, 5);
 
                 return derTime;
             }
             else if (!hasSeconds())
             {
-                byte[] derTime = new byte[time.length + 2];
+                byte[] derTime = new byte[contents.length + 2];
 
-                System.arraycopy(time, 0, derTime, 0, time.length - 1);
-                System.arraycopy(Strings.toByteArray("00Z"), 0, derTime, time.length - 1, 3);
+                System.arraycopy(contents, 0, derTime, 0, contents.length - 1);
+                System.arraycopy(Strings.toByteArray("00Z"), 0, derTime, contents.length - 1, 3);
 
                 return derTime;
             }
             else if (hasFractionalSeconds())
             {
-                int ind = time.length - 2;
-                while (ind > 0 && time[ind] == '0')
+                int ind = contents.length - 2;
+                while (ind > 0 && contents[ind] == '0')
                 {
                     ind--;
                 }
 
-                if (time[ind] == '.')
+                if (contents[ind] == '.')
                 {
                     byte[] derTime = new byte[ind + 1];
 
-                    System.arraycopy(time, 0, derTime, 0, ind);
+                    System.arraycopy(contents, 0, derTime, 0, ind);
                     derTime[ind] = (byte)'Z';
 
                     return derTime;
@@ -83,7 +83,7 @@
                 {
                     byte[] derTime = new byte[ind + 2];
 
-                    System.arraycopy(time, 0, derTime, 0, ind + 1);
+                    System.arraycopy(contents, 0, derTime, 0, ind + 1);
                     derTime[ind + 1] = (byte)'Z';
 
                     return derTime;
@@ -91,25 +91,23 @@
             }
             else
             {
-                return time;
+                return contents;
             }
         }
         else
         {
-            return time; // TODO: is there a better way?
+            return contents; // TODO: is there a better way?
         }
     }
 
-    int encodedLength()
+    int encodedLength(boolean withTag)
     {
-        int length = getDERTime().length;
-
-        return 1 + StreamUtil.calculateBodyLength(length) + length;
+        return ASN1OutputStream.getLengthOfEncodingDL(withTag, getDERTime().length);
     }
 
     void encode(ASN1OutputStream out, boolean withTag) throws IOException
     {
-        out.writeEncoded(withTag, BERTags.GENERALIZED_TIME, getDERTime());
+        out.writeEncodingDL(withTag, BERTags.GENERALIZED_TIME, getDERTime());
     }
 
     ASN1Primitive toDERObject()
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/DERGraphicString.java b/bcprov/src/main/java/org/bouncycastle/asn1/DERGraphicString.java
index 2beb435..7b6f55f 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/DERGraphicString.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/DERGraphicString.java
@@ -1,122 +1,15 @@
 package org.bouncycastle.asn1;
 
-import java.io.IOException;
-
-import org.bouncycastle.util.Arrays;
-import org.bouncycastle.util.Strings;
-
 public class DERGraphicString
-    extends ASN1Primitive
-    implements ASN1String
+    extends ASN1GraphicString
 {
-    private final byte[] string;
-    
-    /**
-     * return a Graphic String from the passed in object
-     *
-     * @param obj a DERGraphicString or an object that can be converted into one.
-     * @exception IllegalArgumentException if the object cannot be converted.
-     * @return a DERGraphicString instance, or null.
-     */
-    public static DERGraphicString getInstance(
-        Object  obj)
+    public DERGraphicString(byte[] octets)
     {
-        if (obj == null || obj instanceof DERGraphicString)
-        {
-            return (DERGraphicString)obj;
-        }
-
-        if (obj instanceof byte[])
-        {
-            try
-            {
-                return (DERGraphicString)fromByteArray((byte[])obj);
-            }
-            catch (Exception e)
-            {
-                throw new IllegalArgumentException("encoding error in getInstance: " + e.toString());
-            }
-        }
-
-        throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
+        this(octets, true);
     }
 
-    /**
-     * return a Graphic String from a tagged object.
-     *
-     * @param obj the tagged object holding the object we want
-     * @param explicit true if the object is meant to be explicitly
-     *              tagged false otherwise.
-     * @exception IllegalArgumentException if the tagged object cannot
-     *               be converted.
-     * @return a DERGraphicString instance, or null.
-     */
-    public static DERGraphicString getInstance(
-        ASN1TaggedObject obj,
-        boolean          explicit)
+    DERGraphicString(byte[] contents, boolean clone)
     {
-        ASN1Primitive o = obj.getObject();
-
-        if (explicit || o instanceof DERGraphicString)
-        {
-            return getInstance(o);
-        }
-        else
-        {
-            return new DERGraphicString(ASN1OctetString.getInstance(o).getOctets());
-        }
-    }
-
-    /**
-     * basic constructor - with bytes.
-     * @param string the byte encoding of the characters making up the string.
-     */
-    public DERGraphicString(
-        byte[]   string)
-    {
-        this.string = Arrays.clone(string);
-    }
-    
-    public byte[] getOctets()
-    {
-        return Arrays.clone(string);
-    }
-
-    boolean isConstructed()
-    {
-        return false;
-    }
-
-    int encodedLength()
-    {
-        return 1 + StreamUtil.calculateBodyLength(string.length) + string.length;
-    }
-
-    void encode(ASN1OutputStream out, boolean withTag) throws IOException
-    {
-        out.writeEncoded(withTag, BERTags.GRAPHIC_STRING, string);
-    }
-
-    public int hashCode()
-    {
-        return Arrays.hashCode(string);
-    }
-
-    boolean asn1Equals(
-        ASN1Primitive o)
-    {
-        if (!(o instanceof DERGraphicString))
-        {
-            return false;
-        }
-
-        DERGraphicString  s = (DERGraphicString)o;
-
-        return Arrays.areEqual(string, s.string);
-    }
-
-    public String getString()
-    {
-        return Strings.fromByteArray(string);
+        super(contents, clone);
     }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/DERIA5String.java b/bcprov/src/main/java/org/bouncycastle/asn1/DERIA5String.java
index d238741..b2644aa 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/DERIA5String.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/DERIA5String.java
@@ -1,10 +1,5 @@
 package org.bouncycastle.asn1;
 
-import java.io.IOException;
-
-import org.bouncycastle.util.Arrays;
-import org.bouncycastle.util.Strings;
-
 /**
  * DER IA5String object - this is a ISO 646 (ASCII) string encoding code points 0 to 127.
  * <p>
@@ -12,83 +7,13 @@
  * </p>
  */
 public class DERIA5String
-    extends ASN1Primitive
-    implements ASN1String
+    extends ASN1IA5String
 {
-    private final byte[]  string;
-
-    /**
-     * Return an IA5 string from the passed in object
-     *
-     * @param obj a DERIA5String or an object that can be converted into one.
-     * @exception IllegalArgumentException if the object cannot be converted.
-     * @return a DERIA5String instance, or null.
-     */
-    public static DERIA5String getInstance(
-        Object  obj)
-    {
-        if (obj == null || obj instanceof DERIA5String)
-        {
-            return (DERIA5String)obj;
-        }
-
-        if (obj instanceof byte[])
-        {
-            try
-            {
-                return (DERIA5String)fromByteArray((byte[])obj);
-            }
-            catch (Exception e)
-            {
-                throw new IllegalArgumentException("encoding error in getInstance: " + e.toString());
-            }
-        }
-
-        throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
-    }
-
-    /**
-     * Return an IA5 String from a tagged object.
-     *
-     * @param obj the tagged object holding the object we want
-     * @param explicit true if the object is meant to be explicitly
-     *              tagged false otherwise.
-     * @exception IllegalArgumentException if the tagged object cannot
-     *               be converted.
-     * @return a DERIA5String instance, or null.
-     */
-    public static DERIA5String getInstance(
-        ASN1TaggedObject obj,
-        boolean          explicit)
-    {
-        ASN1Primitive o = obj.getObject();
-
-        if (explicit || o instanceof DERIA5String)
-        {
-            return getInstance(o);
-        }
-        else
-        {
-            return new DERIA5String(ASN1OctetString.getInstance(o).getOctets());
-        }
-    }
-
-    /**
-     * Basic constructor - with bytes.
-     * @param string the byte encoding of the characters making up the string.
-     */
-    DERIA5String(
-        byte[]   string)
-    {
-        this.string = string;
-    }
-
     /**
      * Basic constructor - without validation.
      * @param string the base string to use..
      */
-    public DERIA5String(
-        String   string)
+    public DERIA5String(String string)
     {
         this(string, false);
     }
@@ -101,90 +26,13 @@
      * @throws IllegalArgumentException if validate is true and the string
      * contains characters that should not be in an IA5String.
      */
-    public DERIA5String(
-        String   string,
-        boolean  validate)
+    public DERIA5String(String string, boolean validate)
     {
-        if (string == null)
-        {
-            throw new NullPointerException("'string' cannot be null");
-        }
-        if (validate && !isIA5String(string))
-        {
-            throw new IllegalArgumentException("'string' contains illegal characters");
-        }
-
-        this.string = Strings.toByteArray(string);
+        super(string, validate);
     }
 
-    public String getString()
+    DERIA5String(byte[] contents, boolean clone)
     {
-        return Strings.fromByteArray(string);
-    }
-
-    public String toString()
-    {
-        return getString();
-    }
-
-    public byte[] getOctets()
-    {
-        return Arrays.clone(string);
-    }
-
-    boolean isConstructed()
-    {
-        return false;
-    }
-
-    int encodedLength()
-    {
-        return 1 + StreamUtil.calculateBodyLength(string.length) + string.length;
-    }
-
-    void encode(ASN1OutputStream out, boolean withTag) throws IOException
-    {
-        out.writeEncoded(withTag, BERTags.IA5_STRING, string);
-    }
-
-    public int hashCode()
-    {
-        return Arrays.hashCode(string);
-    }
-
-    boolean asn1Equals(
-        ASN1Primitive o)
-    {
-        if (!(o instanceof DERIA5String))
-        {
-            return false;
-        }
-
-        DERIA5String  s = (DERIA5String)o;
-
-        return Arrays.areEqual(string, s.string);
-    }
-
-    /**
-     * return true if the passed in String can be represented without
-     * loss as an IA5String, false otherwise.
-     *
-     * @param str the string to check.
-     * @return true if character set in IA5String set, false otherwise.
-     */
-    public static boolean isIA5String(
-        String  str)
-    {
-        for (int i = str.length() - 1; i >= 0; i--)
-        {
-            char    ch = str.charAt(i);
-
-            if (ch > 0x007f)
-            {
-                return false;
-            }
-        }
-
-        return true;
+        super(contents, clone);
     }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/DERInteger.java b/bcprov/src/main/java/org/bouncycastle/asn1/DERInteger.java
deleted file mode 100644
index d2e850f..0000000
--- a/bcprov/src/main/java/org/bouncycastle/asn1/DERInteger.java
+++ /dev/null
@@ -1,30 +0,0 @@
-package org.bouncycastle.asn1;
-
-import java.math.BigInteger;
-
-/**
- * @deprecated  Use ASN1Integer instead of this,
- */
-public class DERInteger
-    extends ASN1Integer
-{
-    /**
-     * Constructor from a byte array containing a signed representation of the number.
-     *
-     * @param bytes a byte array containing the signed number.A copy is made of the byte array.
-     */
-    public DERInteger(byte[] bytes)
-    {
-        super(bytes, true);
-    }
-
-    public DERInteger(BigInteger value)
-    {
-        super(value);
-    }
-
-    public DERInteger(long value)
-    {
-        super(value);
-    }
-}
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/DERNull.java b/bcprov/src/main/java/org/bouncycastle/asn1/DERNull.java
index 38960a4..73e0ef0 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/DERNull.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/DERNull.java
@@ -18,18 +18,18 @@
     {
     }
 
-    boolean isConstructed()
+    boolean encodeConstructed()
     {
         return false;
     }
 
-    int encodedLength()
+    int encodedLength(boolean withTag)
     {
-        return 2;
+        return ASN1OutputStream.getLengthOfEncodingDL(withTag, 0);
     }
 
     void encode(ASN1OutputStream out, boolean withTag) throws IOException
     {
-        out.writeEncoded(withTag, BERTags.NULL, zeroBytes);
+        out.writeEncodingDL(withTag, BERTags.NULL, zeroBytes);
     }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/DERNumericString.java b/bcprov/src/main/java/org/bouncycastle/asn1/DERNumericString.java
index 9a4a968..0ce5074 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/DERNumericString.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/DERNumericString.java
@@ -1,10 +1,5 @@
 package org.bouncycastle.asn1;
 
-import java.io.IOException;
-
-import org.bouncycastle.util.Arrays;
-import org.bouncycastle.util.Strings;
-
 /**
  * DER NumericString object - this is an ascii string of characters {0,1,2,3,4,5,6,7,8,9, }.
  * ASN.1 NUMERIC-STRING object.
@@ -16,81 +11,12 @@
  * Explicit character set escape sequences are not allowed.
  */
 public class DERNumericString
-    extends ASN1Primitive
-    implements ASN1String
+    extends ASN1NumericString
 {
-    private final byte[]  string;
-
-    /**
-     * Return a Numeric string from the passed in object
-     *
-     * @param obj a DERNumericString or an object that can be converted into one.
-     * @exception IllegalArgumentException if the object cannot be converted.
-     * @return a DERNumericString instance, or null
-     */
-    public static DERNumericString getInstance(
-        Object  obj)
-    {
-        if (obj == null || obj instanceof DERNumericString)
-        {
-            return (DERNumericString)obj;
-        }
-
-        if (obj instanceof byte[])
-        {
-            try
-            {
-                return (DERNumericString)fromByteArray((byte[])obj);
-            }
-            catch (Exception e)
-            {
-                throw new IllegalArgumentException("encoding error in getInstance: " + e.toString());
-            }
-        }
-
-        throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
-    }
-
-    /**
-     * Return an Numeric String from a tagged object.
-     *
-     * @param obj the tagged object holding the object we want
-     * @param explicit true if the object is meant to be explicitly
-     *              tagged false otherwise.
-     * @exception IllegalArgumentException if the tagged object cannot
-     *               be converted.
-     * @return a DERNumericString instance, or null.
-     */
-    public static DERNumericString getInstance(
-        ASN1TaggedObject obj,
-        boolean          explicit)
-    {
-        ASN1Primitive o = obj.getObject();
-
-        if (explicit || o instanceof DERNumericString)
-        {
-            return getInstance(o);
-        }
-        else
-        {
-            return new DERNumericString(ASN1OctetString.getInstance(o).getOctets());
-        }
-    }
-
-    /**
-     * Basic constructor - with bytes.
-     */
-    DERNumericString(
-        byte[]   string)
-    {
-        this.string = string;
-    }
-
     /**
      * Basic constructor -  without validation..
      */
-    public DERNumericString(
-        String   string)
+    public DERNumericString(String string)
     {
         this(string, false);
     }
@@ -103,92 +29,13 @@
      * @throws IllegalArgumentException if validate is true and the string
      * contains characters that should not be in a NumericString.
      */
-    public DERNumericString(
-        String   string,
-        boolean  validate)
+    public DERNumericString(String string, boolean validate)
     {
-        if (validate && !isNumericString(string))
-        {
-            throw new IllegalArgumentException("string contains illegal characters");
-        }
-
-        this.string = Strings.toByteArray(string);
+        super(string, validate);
     }
 
-    public String getString()
+    DERNumericString(byte[] contents, boolean clone)
     {
-        return Strings.fromByteArray(string);
-    }
-
-    public String toString()
-    {
-        return getString();
-    }
-
-    public byte[] getOctets()
-    {
-        return Arrays.clone(string);
-    }
-
-    boolean isConstructed()
-    {
-        return false;
-    }
-
-    int encodedLength()
-    {
-        return 1 + StreamUtil.calculateBodyLength(string.length) + string.length;
-    }
-
-    void encode(ASN1OutputStream out, boolean withTag) throws IOException
-    {
-        out.writeEncoded(withTag, BERTags.NUMERIC_STRING, string);
-    }
-
-    public int hashCode()
-    {
-        return Arrays.hashCode(string);
-    }
-
-    boolean asn1Equals(
-        ASN1Primitive o)
-    {
-        if (!(o instanceof DERNumericString))
-        {
-            return false;
-        }
-
-        DERNumericString  s = (DERNumericString)o;
-
-        return Arrays.areEqual(string, s.string);
-    }
-
-    /**
-     * Return true if the string can be represented as a NumericString ('0'..'9', ' ')
-     *
-     * @param str string to validate.
-     * @return true if numeric, fale otherwise.
-     */
-    public static boolean isNumericString(
-        String  str)
-    {
-        for (int i = str.length() - 1; i >= 0; i--)
-        {
-            char    ch = str.charAt(i);
-
-            if (ch > 0x007f)
-            {
-                return false;
-            }
-
-            if (('0' <= ch && ch <= '9') || ch == ' ')
-            {
-                continue;
-            }
-
-            return false;
-        }
-
-        return true;
+        super(contents, clone);
     }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/DERObjectIdentifier.java b/bcprov/src/main/java/org/bouncycastle/asn1/DERObjectIdentifier.java
deleted file mode 100644
index acb2ada..0000000
--- a/bcprov/src/main/java/org/bouncycastle/asn1/DERObjectIdentifier.java
+++ /dev/null
@@ -1,24 +0,0 @@
-package org.bouncycastle.asn1;
-
-/**
- *
- * @deprecated Use ASN1ObjectIdentifier instead of this,
- */
-public class DERObjectIdentifier
-    extends ASN1ObjectIdentifier
-{
-    public DERObjectIdentifier(String identifier)
-    {
-        super(identifier);
-    }
-
-    DERObjectIdentifier(byte[] bytes)
-    {
-        super(bytes);
-    }
-
-    DERObjectIdentifier(ASN1ObjectIdentifier oid, String branch)
-    {
-        super(oid, branch);
-    }
-}
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/DEROctetString.java b/bcprov/src/main/java/org/bouncycastle/asn1/DEROctetString.java
index 2b535e8..6d11821 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/DEROctetString.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/DEROctetString.java
@@ -31,19 +31,19 @@
         super(obj.toASN1Primitive().getEncoded(ASN1Encoding.DER));
     }
 
-    boolean isConstructed()
+    boolean encodeConstructed()
     {
         return false;
     }
 
-    int encodedLength()
+    int encodedLength(boolean withTag)
     {
-        return 1 + StreamUtil.calculateBodyLength(string.length) + string.length;
+        return ASN1OutputStream.getLengthOfEncodingDL(withTag, string.length);
     }
 
     void encode(ASN1OutputStream out, boolean withTag) throws IOException
     {
-        out.writeEncoded(withTag, BERTags.OCTET_STRING, string);
+        out.writeEncodingDL(withTag, BERTags.OCTET_STRING, string);
     }
 
     ASN1Primitive toDERObject()
@@ -56,8 +56,13 @@
         return this;
     }
 
-    static void encode(ASN1OutputStream derOut, boolean withTag, byte[] buf, int off, int len) throws IOException
+    static void encode(ASN1OutputStream out, boolean withTag, byte[] buf, int off, int len) throws IOException
     {
-        derOut.writeEncoded(withTag, BERTags.OCTET_STRING, buf, off, len);
+        out.writeEncodingDL(withTag, BERTags.OCTET_STRING, buf, off, len);
+    }
+
+    static int encodedLength(boolean withTag, int contentsLength)
+    {
+        return ASN1OutputStream.getLengthOfEncodingDL(withTag, contentsLength);
     }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/DEROctetStringParser.java b/bcprov/src/main/java/org/bouncycastle/asn1/DEROctetStringParser.java
index 1c9c159..33e1bca 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/DEROctetStringParser.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/DEROctetStringParser.java
@@ -5,6 +5,8 @@
 
 /**
  * Parser for DER encoded OCTET STRINGS
+ * 
+ * @deprecated Check for 'ASN1OctetStringParser' instead 
  */
 public class DEROctetStringParser
     implements ASN1OctetStringParser
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/DEROutputStream.java b/bcprov/src/main/java/org/bouncycastle/asn1/DEROutputStream.java
index caa9766..5995b5d 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/DEROutputStream.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/DEROutputStream.java
@@ -16,18 +16,32 @@
         super(os);
     }
 
-    void writePrimitive(ASN1Primitive primitive, boolean withTag) throws IOException
-    {
-        primitive.toDERObject().encode(this, withTag);
-    }
-
     DEROutputStream getDERSubStream()
     {
         return this;
     }
 
-    ASN1OutputStream getDLSubStream()
+    void writeElements(ASN1Encodable[] elements)
+        throws IOException
     {
-        return this;
+        for (int i = 0, count = elements.length; i < count; ++i)
+        {
+            elements[i].toASN1Primitive().toDERObject().encode(this, true);
+        }
+    }
+
+    void writePrimitive(ASN1Primitive primitive, boolean withTag) throws IOException
+    {
+        primitive.toDERObject().encode(this, withTag);
+    }
+
+    void writePrimitives(ASN1Primitive[] primitives)
+        throws IOException
+    {
+        int count = primitives.length;
+        for (int i = 0; i < count; ++i)
+        {
+            primitives[i].toDERObject().encode(this, true);
+        }
     }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/DERPrintableString.java b/bcprov/src/main/java/org/bouncycastle/asn1/DERPrintableString.java
index f7721c2..a403b87 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/DERPrintableString.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/DERPrintableString.java
@@ -1,10 +1,5 @@
 package org.bouncycastle.asn1;
 
-import java.io.IOException;
-
-import org.bouncycastle.util.Arrays;
-import org.bouncycastle.util.Strings;
-
 /**
  * DER PrintableString object.
  * <p>
@@ -32,76 +27,8 @@
  * </p>
  */
 public class DERPrintableString
-    extends ASN1Primitive
-    implements ASN1String
+    extends ASN1PrintableString
 {
-    private final byte[]  string;
-
-    /**
-     * Return a printable string from the passed in object.
-     *
-     * @param obj a DERPrintableString or an object that can be converted into one.
-     * @exception IllegalArgumentException if the object cannot be converted.
-     * @return a DERPrintableString instance, or null.
-     */
-    public static DERPrintableString getInstance(
-        Object  obj)
-    {
-        if (obj == null || obj instanceof DERPrintableString)
-        {
-            return (DERPrintableString)obj;
-        }
-
-        if (obj instanceof byte[])
-        {
-            try
-            {
-                return (DERPrintableString)fromByteArray((byte[])obj);
-            }
-            catch (Exception e)
-            {
-                throw new IllegalArgumentException("encoding error in getInstance: " + e.toString());
-            }
-        }
-
-        throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
-    }
-
-    /**
-     * Return a Printable String from a tagged object.
-     *
-     * @param obj the tagged object holding the object we want
-     * @param explicit true if the object is meant to be explicitly
-     *              tagged false otherwise.
-     * @exception IllegalArgumentException if the tagged object cannot
-     *               be converted.
-     * @return a DERPrintableString instance, or null.
-     */
-    public static DERPrintableString getInstance(
-        ASN1TaggedObject obj,
-        boolean          explicit)
-    {
-        ASN1Primitive o = obj.getObject();
-
-        if (explicit || o instanceof DERPrintableString)
-        {
-            return getInstance(o);
-        }
-        else
-        {
-            return new DERPrintableString(ASN1OctetString.getInstance(o).getOctets());
-        }
-    }
-
-    /**
-     * Basic constructor - byte encoded string.
-     */
-    DERPrintableString(
-        byte[]   string)
-    {
-        this.string = string;
-    }
-
     /**
      * Basic constructor - this does not validate the string
      */
@@ -123,115 +50,11 @@
         String   string,
         boolean  validate)
     {
-        if (validate && !isPrintableString(string))
-        {
-            throw new IllegalArgumentException("string contains illegal characters");
-        }
-
-        this.string = Strings.toByteArray(string);
+        super(string, validate);
     }
 
-    public String getString()
+    DERPrintableString(byte[] contents, boolean clone)
     {
-        return Strings.fromByteArray(string);
-    }
-
-    public byte[] getOctets()
-    {
-        return Arrays.clone(string);
-    }
-
-    boolean isConstructed()
-    {
-        return false;
-    }
-
-    int encodedLength()
-    {
-        return 1 + StreamUtil.calculateBodyLength(string.length) + string.length;
-    }
-
-    void encode(ASN1OutputStream out, boolean withTag) throws IOException
-    {
-        out.writeEncoded(withTag, BERTags.PRINTABLE_STRING, string);
-    }
-
-    public int hashCode()
-    {
-        return Arrays.hashCode(string);
-    }
-
-    boolean asn1Equals(
-        ASN1Primitive o)
-    {
-        if (!(o instanceof DERPrintableString))
-        {
-            return false;
-        }
-
-        DERPrintableString  s = (DERPrintableString)o;
-
-        return Arrays.areEqual(string, s.string);
-    }
-
-    public String toString()
-    {
-        return getString();
-    }
-
-    /**
-     * return true if the passed in String can be represented without
-     * loss as a PrintableString, false otherwise.
-     *
-     * @return true if in printable set, false otherwise.
-     */
-    public static boolean isPrintableString(
-        String  str)
-    {
-        for (int i = str.length() - 1; i >= 0; i--)
-        {
-            char    ch = str.charAt(i);
-
-            if (ch > 0x007f)
-            {
-                return false;
-            }
-
-            if ('a' <= ch && ch <= 'z')
-            {
-                continue;
-            }
-
-            if ('A' <= ch && ch <= 'Z')
-            {
-                continue;
-            }
-
-            if ('0' <= ch && ch <= '9')
-            {
-                continue;
-            }
-
-            switch (ch)
-            {
-            case ' ':
-            case '\'':
-            case '(':
-            case ')':
-            case '+':
-            case '-':
-            case '.':
-            case ':':
-            case '=':
-            case '?':
-            case '/':
-            case ',':
-                continue;
-            }
-
-            return false;
-        }
-
-        return true;
+        super(contents, clone);
     }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/DERSequence.java b/bcprov/src/main/java/org/bouncycastle/asn1/DERSequence.java
index e547471..9bffbb2 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/DERSequence.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/DERSequence.java
@@ -16,7 +16,7 @@
         return (DERSequence)seq.toDERObject();
     }
 
-    private int bodyLength = -1;
+    private int contentsLength = -1;
 
     /**
      * Create an empty sequence
@@ -57,9 +57,9 @@
         super(elements, clone);
     }
 
-    private int getBodyLength() throws IOException
+    private int getContentsLength() throws IOException
     {
-        if (bodyLength < 0)
+        if (contentsLength < 0)
         {
             int count = elements.length;
             int totalLength = 0;
@@ -67,20 +67,18 @@
             for (int i = 0; i < count; ++i)
             {
                 ASN1Primitive derObject = elements[i].toASN1Primitive().toDERObject();
-                totalLength += derObject.encodedLength();
+                totalLength += derObject.encodedLength(true);
             }
 
-            this.bodyLength = totalLength;
+            this.contentsLength = totalLength;
         }
 
-        return bodyLength;
+        return contentsLength;
     }
 
-    int encodedLength() throws IOException
+    int encodedLength(boolean withTag) throws IOException
     {
-        int length = getBodyLength();
-
-        return 1 + StreamUtil.calculateBodyLength(length) + length;
+        return ASN1OutputStream.getLengthOfEncodingDL(withTag, getContentsLength());
     }
 
     /*
@@ -93,17 +91,14 @@
      */
     void encode(ASN1OutputStream out, boolean withTag) throws IOException
     {
-        if (withTag)
-        {
-            out.write(BERTags.SEQUENCE | BERTags.CONSTRUCTED);
-        }
+        out.writeIdentifier(withTag, BERTags.CONSTRUCTED | BERTags.SEQUENCE);
 
         DEROutputStream derOut = out.getDERSubStream();
 
         int count = elements.length;
-        if (bodyLength >= 0 || count > 16)
+        if (contentsLength >= 0 || count > 16)
         {
-            out.writeLength(getBodyLength());
+            out.writeDL(getContentsLength());
 
             for (int i = 0; i < count; ++i)
             {
@@ -120,11 +115,11 @@
             {
                 ASN1Primitive derObject = elements[i].toASN1Primitive().toDERObject();
                 derObjects[i] = derObject;
-                totalLength += derObject.encodedLength();
+                totalLength += derObject.encodedLength(true);
             }
 
-            this.bodyLength = totalLength;
-            out.writeLength(totalLength);
+            this.contentsLength = totalLength;
+            out.writeDL(totalLength);
 
             for (int i = 0; i < count; ++i)
             {
@@ -133,6 +128,27 @@
         }
     }
 
+    ASN1BitString toASN1BitString()
+    {
+        return new DERBitString(BERBitString.flattenBitStrings(getConstructedBitStrings()), false);
+    }
+
+    ASN1External toASN1External()
+    {
+        return new DERExternal(this);
+    }
+
+    ASN1OctetString toASN1OctetString()
+    {
+        return new DEROctetString(BEROctetString.flattenOctetStrings(getConstructedOctetStrings()));
+    }
+
+    ASN1Set toASN1Set()
+    {
+        // NOTE: DLSet is intentional, we don't want sorting
+        return new DLSet(false, toArrayInternal());
+    }
+
     ASN1Primitive toDERObject()
     {
         return this;
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/DERSequenceParser.java b/bcprov/src/main/java/org/bouncycastle/asn1/DERSequenceParser.java
deleted file mode 100644
index aed121a..0000000
--- a/bcprov/src/main/java/org/bouncycastle/asn1/DERSequenceParser.java
+++ /dev/null
@@ -1,58 +0,0 @@
-package org.bouncycastle.asn1;
-
-import java.io.IOException;
-
-/**
- * @deprecated Use DLSequenceParser instead
- */
-public class DERSequenceParser
-    implements ASN1SequenceParser
-{
-    private ASN1StreamParser _parser;
-
-    DERSequenceParser(ASN1StreamParser parser)
-    {
-        this._parser = parser;
-    }
-
-    /**
-     * Return the next object in the SEQUENCE.
-     *
-     * @return next object in SEQUENCE.
-     * @throws IOException if there is an issue loading the object.
-     */
-    public ASN1Encodable readObject()
-        throws IOException
-    {
-        return _parser.readObject();
-    }
-
-    /**
-     * Return an in memory, encodable, representation of the SEQUENCE.
-     *
-     * @return a DERSequence.
-     * @throws IOException if there is an issue loading the data.
-     */
-    public ASN1Primitive getLoadedObject()
-        throws IOException
-    {
-         return new DLSequence(_parser.readVector());
-    }
-
-    /**
-     * Return a DERSequence representing this parser and its contents.
-     *
-     * @return a DERSequence.
-     */
-    public ASN1Primitive toASN1Primitive()
-    {
-        try
-        {
-            return getLoadedObject();
-        }
-        catch (IOException e)
-        {
-            throw new IllegalStateException(e.getMessage());
-        }
-    }
-}
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/DERSet.java b/bcprov/src/main/java/org/bouncycastle/asn1/DERSet.java
index bc55bcb..f86689b 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/DERSet.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/DERSet.java
@@ -20,7 +20,7 @@
         return (DERSet)set.toDERObject();
     }
 
-    private int bodyLength = -1;
+    private int contentsLength = -1;
 
     /**
      * create an empty set
@@ -61,9 +61,9 @@
         super(checkSorted(isSorted), elements);
     }
 
-    private int getBodyLength() throws IOException
+    private int getContentsLength() throws IOException
     {
-        if (bodyLength < 0)
+        if (contentsLength < 0)
         {
             int count = elements.length;
             int totalLength = 0;
@@ -71,20 +71,18 @@
             for (int i = 0; i < count; ++i)
             {
                 ASN1Primitive derObject = elements[i].toASN1Primitive().toDERObject();
-                totalLength += derObject.encodedLength();
+                totalLength += derObject.encodedLength(true);
             }
 
-            this.bodyLength = totalLength;
+            this.contentsLength = totalLength;
         }
 
-        return bodyLength;
+        return contentsLength;
     }
 
-    int encodedLength() throws IOException
+    int encodedLength(boolean withTag) throws IOException
     {
-        int length = getBodyLength();
-
-        return 1 + StreamUtil.calculateBodyLength(length) + length;
+        return ASN1OutputStream.getLengthOfEncodingDL(withTag, getContentsLength());
     }
 
     /*
@@ -97,17 +95,14 @@
      */
     void encode(ASN1OutputStream out, boolean withTag) throws IOException
     {
-        if (withTag)
-        {
-            out.write(BERTags.SET | BERTags.CONSTRUCTED);
-        }
+        out.writeIdentifier(withTag, BERTags.CONSTRUCTED | BERTags.SET);
 
         DEROutputStream derOut = out.getDERSubStream();
 
         int count = elements.length;
-        if (bodyLength >= 0 || count > 16)
+        if (contentsLength >= 0 || count > 16)
         {
-            out.writeLength(getBodyLength());
+            out.writeDL(getContentsLength());
 
             for (int i = 0; i < count; ++i)
             {
@@ -124,11 +119,11 @@
             {
                 ASN1Primitive derObject = elements[i].toASN1Primitive().toDERObject();
                 derObjects[i] = derObject;
-                totalLength += derObject.encodedLength();
+                totalLength += derObject.encodedLength(true);
             }
 
-            this.bodyLength = totalLength;
-            out.writeLength(totalLength);
+            this.contentsLength = totalLength;
+            out.writeDL(totalLength);
 
             for (int i = 0; i < count; ++i)
             {
@@ -139,7 +134,7 @@
 
     ASN1Primitive toDERObject()
     {
-        return isSorted ? this : super.toDERObject();
+        return (sortedElements != null) ? this : super.toDERObject();
     }
 
     ASN1Primitive toDLObject()
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/DERSetParser.java b/bcprov/src/main/java/org/bouncycastle/asn1/DERSetParser.java
deleted file mode 100644
index 492f4a4..0000000
--- a/bcprov/src/main/java/org/bouncycastle/asn1/DERSetParser.java
+++ /dev/null
@@ -1,58 +0,0 @@
-package org.bouncycastle.asn1;
-
-import java.io.IOException;
-
-/**
- * @deprecated Use DLSetParser instead
- */
-public class DERSetParser
-    implements ASN1SetParser
-{
-    private ASN1StreamParser _parser;
-
-    DERSetParser(ASN1StreamParser parser)
-    {
-        this._parser = parser;
-    }
-
-    /**
-     * Return the next object in the SET.
-     *
-     * @return next object in SET.
-     * @throws IOException if there is an issue loading the object.
-     */
-    public ASN1Encodable readObject()
-        throws IOException
-    {
-        return _parser.readObject();
-    }
-
-    /**
-     * Return an in memory, encodable, representation of the SET.
-     *
-     * @return a DERSet.
-     * @throws IOException if there is an issue loading the data.
-     */
-    public ASN1Primitive getLoadedObject()
-        throws IOException
-    {
-        return new DLSet(_parser.readVector());
-    }
-
-    /**
-     * Return a DERSet representing this parser and its contents.
-     *
-     * @return a DERSet
-     */
-    public ASN1Primitive toASN1Primitive()
-    {
-        try
-        {
-            return getLoadedObject();
-        }
-        catch (IOException e)
-        {
-            throw new ASN1ParsingException(e.getMessage(), e);
-        }
-    }
-}
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/DERT61String.java b/bcprov/src/main/java/org/bouncycastle/asn1/DERT61String.java
index 018f636..ade137a 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/DERT61String.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/DERT61String.java
@@ -1,74 +1,20 @@
 package org.bouncycastle.asn1;
 
-import java.io.IOException;
-
-import org.bouncycastle.util.Arrays;
-import org.bouncycastle.util.Strings;
-
 /**
  * DER T61String (also the teletex string), try not to use this if you don't need to. The standard support the encoding for
  * this has been withdrawn.
  */
 public class DERT61String
-    extends ASN1Primitive
-    implements ASN1String
+    extends ASN1T61String
 {
-    private byte[] string;
-
     /**
-     * Return a T61 string from the passed in object.
+     * Basic constructor - with string 8 bit assumed.
      *
-     * @param obj a DERT61String or an object that can be converted into one.
-     * @exception IllegalArgumentException if the object cannot be converted.
-     * @return a DERT61String instance, or null
+     * @param string the string to be wrapped.
      */
-    public static DERT61String getInstance(
-        Object  obj)
+    public DERT61String(String string)
     {
-        if (obj == null || obj instanceof DERT61String)
-        {
-            return (DERT61String)obj;
-        }
-
-        if (obj instanceof byte[])
-        {
-            try
-            {
-                return (DERT61String)fromByteArray((byte[])obj);
-            }
-            catch (Exception e)
-            {
-                throw new IllegalArgumentException("encoding error in getInstance: " + e.toString());
-            }
-        }
-
-        throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
-    }
-
-    /**
-     * Return an T61 String from a tagged object.
-     *
-     * @param obj the tagged object holding the object we want
-     * @param explicit true if the object is meant to be explicitly
-     *              tagged false otherwise.
-     * @exception IllegalArgumentException if the tagged object cannot
-     *               be converted.
-     * @return a DERT61String instance, or null
-     */
-    public static DERT61String getInstance(
-        ASN1TaggedObject obj,
-        boolean          explicit)
-    {
-        ASN1Primitive o = obj.getObject();
-
-        if (explicit || o instanceof DERT61String)
-        {
-            return getInstance(o);
-        }
-        else
-        {
-            return new DERT61String(ASN1OctetString.getInstance(o).getOctets());
-        }
+        super(string);
     }
 
     /**
@@ -76,74 +22,13 @@
      *
      * @param string the byte encoding of the string to be wrapped.
      */
-    public DERT61String(
-        byte[]   string)
+    public DERT61String(byte[] string)
     {
-        this.string = Arrays.clone(string);
+        this(string, true);
     }
 
-    /**
-     * Basic constructor - with string 8 bit assumed.
-     *
-     * @param string the string to be wrapped.
-     */
-    public DERT61String(
-        String   string)
+    DERT61String(byte[] contents, boolean clone)
     {
-        this.string = Strings.toByteArray(string);
-    }
-
-    /**
-     * Decode the encoded string and return it, 8 bit encoding assumed.
-     * @return the decoded String
-     */
-    public String getString()
-    {
-        return Strings.fromByteArray(string);
-    }
-
-    public String toString()
-    {
-        return getString();
-    }
-
-    boolean isConstructed()
-    {
-        return false;
-    }
-
-    int encodedLength()
-    {
-        return 1 + StreamUtil.calculateBodyLength(string.length) + string.length;
-    }
-
-    void encode(ASN1OutputStream out, boolean withTag) throws IOException
-    {
-        out.writeEncoded(withTag, BERTags.T61_STRING, string);
-    }
-
-    /**
-     * Return the encoded string as a byte array.
-     * @return the actual bytes making up the encoded body of the T61 string.
-     */
-    public byte[] getOctets()
-    {
-        return Arrays.clone(string);
-    }
-
-    boolean asn1Equals(
-        ASN1Primitive o)
-    {
-        if (!(o instanceof DERT61String))
-        {
-            return false;
-        }
-
-        return Arrays.areEqual(string, ((DERT61String)o).string);
-    }
-    
-    public int hashCode()
-    {
-        return Arrays.hashCode(string);
+        super(contents, clone);
     }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/DERTaggedObject.java b/bcprov/src/main/java/org/bouncycastle/asn1/DERTaggedObject.java
index dfdfcd2..97ffb66 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/DERTaggedObject.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/DERTaggedObject.java
@@ -10,68 +10,94 @@
 public class DERTaggedObject
     extends ASN1TaggedObject
 {
-    /**
-     * @param explicit true if an explicitly tagged object.
-     * @param tagNo the tag number for this object.
-     * @param obj the tagged object.
-     */
-    public DERTaggedObject(
-        boolean       explicit,
-        int           tagNo,
-        ASN1Encodable obj)
-    {
-        super(explicit, tagNo, obj);
-    }
-
     public DERTaggedObject(int tagNo, ASN1Encodable encodable)
     {
         super(true, tagNo, encodable);
     }
 
-    boolean isConstructed()
+    public DERTaggedObject(int tagClass, int tagNo, ASN1Encodable obj)
     {
-        return explicit || obj.toASN1Primitive().toDERObject().isConstructed();
+        super(true, tagClass, tagNo, obj);
     }
 
-    int encodedLength()
-        throws IOException
+    /**
+     * @param explicit true if an explicitly tagged object.
+     * @param tagNo the tag number for this object.
+     * @param obj the tagged object.
+     */
+    public DERTaggedObject(boolean explicit, int tagNo, ASN1Encodable obj)
+    {
+        super(explicit, tagNo, obj);
+    }
+
+    public DERTaggedObject(boolean explicit, int tagClass, int tagNo, ASN1Encodable obj)
+    {
+        super(explicit, tagClass, tagNo, obj);
+    }
+
+    DERTaggedObject(int explicitness, int tagClass, int tagNo, ASN1Encodable obj)
+    {
+        super(explicitness, tagClass, tagNo, obj);
+    }
+
+    boolean encodeConstructed()
+    {
+        return isExplicit() || obj.toASN1Primitive().toDERObject().encodeConstructed();
+    }
+
+    int encodedLength(boolean withTag) throws IOException
     {
         ASN1Primitive primitive = obj.toASN1Primitive().toDERObject();
-        int length = primitive.encodedLength();
+        boolean explicit = isExplicit();
+
+        int length = primitive.encodedLength(explicit);
 
         if (explicit)
         {
-            return StreamUtil.calculateTagLength(tagNo) + StreamUtil.calculateBodyLength(length) + length;
+            length += ASN1OutputStream.getLengthOfDL(length);
         }
-        else
-        {
-            // header length already in calculation
-            length = length - 1;
 
-            return StreamUtil.calculateTagLength(tagNo) + length;
-        }
+        length += withTag ? ASN1OutputStream.getLengthOfIdentifier(tagNo) : 0;
+
+        return length;
     }
 
     void encode(ASN1OutputStream out, boolean withTag) throws IOException
     {
+//      assert out.getClass().isAssignableFrom(DEROutputStream.class);
+
         ASN1Primitive primitive = obj.toASN1Primitive().toDERObject();
+        boolean explicit = isExplicit();
 
-        int flags = BERTags.TAGGED;
-        if (explicit || primitive.isConstructed())
+        if (withTag)
         {
-            flags |= BERTags.CONSTRUCTED;
-        }
+            int flags = tagClass;
+            if (explicit || primitive.encodeConstructed())
+            {
+                flags |= BERTags.CONSTRUCTED;
+            }
 
-        out.writeTag(withTag, flags, tagNo);
+            out.writeIdentifier(true, flags, tagNo);
+        }
 
         if (explicit)
         {
-            out.writeLength(primitive.encodedLength());
+            out.writeDL(primitive.encodedLength(true));
         }
 
         primitive.encode(out.getDERSubStream(), explicit);
     }
 
+    ASN1Sequence rebuildConstructed(ASN1Primitive primitive)
+    {
+        return new DERSequence(primitive);
+    }
+
+    ASN1TaggedObject replaceTag(int tagClass, int tagNo)
+    {
+        return new DERTaggedObject(explicitness, tagClass, tagNo, obj);
+    }
+
     ASN1Primitive toDERObject()
     {
         return this;
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/DERTags.java b/bcprov/src/main/java/org/bouncycastle/asn1/DERTags.java
deleted file mode 100644
index 83fd7fd..0000000
--- a/bcprov/src/main/java/org/bouncycastle/asn1/DERTags.java
+++ /dev/null
@@ -1,9 +0,0 @@
-package org.bouncycastle.asn1;
-
-/**
- * @deprecated use BERTags
- */
-public interface DERTags
-    extends BERTags
-{
-}
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/DERUTF8String.java b/bcprov/src/main/java/org/bouncycastle/asn1/DERUTF8String.java
index ceb1409..1b08584 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/DERUTF8String.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/DERUTF8String.java
@@ -1,86 +1,11 @@
 package org.bouncycastle.asn1;
 
-import java.io.IOException;
-
-import org.bouncycastle.util.Arrays;
-import org.bouncycastle.util.Strings;
-
 /**
  * DER UTF8String object.
  */
 public class DERUTF8String
-    extends ASN1Primitive
-    implements ASN1String
+    extends ASN1UTF8String
 {
-    private final byte[]  string;
-
-    /**
-     * Return an UTF8 string from the passed in object.
-     *
-     * @param obj a DERUTF8String or an object that can be converted into one.
-     * @exception IllegalArgumentException
-     *                if the object cannot be converted.
-     * @return a DERUTF8String instance, or null
-     */
-    public static DERUTF8String getInstance(Object obj)
-    {
-        if (obj == null || obj instanceof DERUTF8String)
-        {
-            return (DERUTF8String)obj;
-        }
-
-        if (obj instanceof byte[])
-        {
-            try
-            {
-                return (DERUTF8String)fromByteArray((byte[])obj);
-            }
-            catch (Exception e)
-            {
-                throw new IllegalArgumentException("encoding error in getInstance: " + e.toString());
-            }
-        }
-
-        throw new IllegalArgumentException("illegal object in getInstance: "
-                + obj.getClass().getName());
-    }
-
-    /**
-     * Return an UTF8 String from a tagged object.
-     * 
-     * @param obj
-     *            the tagged object holding the object we want
-     * @param explicit
-     *            true if the object is meant to be explicitly tagged false
-     *            otherwise.
-     * @exception IllegalArgumentException
-     *                if the tagged object cannot be converted.
-     * @return a DERUTF8String instance, or null
-     */
-    public static DERUTF8String getInstance(
-        ASN1TaggedObject obj,
-        boolean explicit)
-    {
-        ASN1Primitive o = obj.getObject();
-
-        if (explicit || o instanceof DERUTF8String)
-        {
-            return getInstance(o);
-        }
-        else
-        {
-            return new DERUTF8String(ASN1OctetString.getInstance(o).getOctets());
-        }
-    }
-
-    /*
-     * Basic constructor - byte encoded string.
-     */
-    DERUTF8String(byte[] string)
-    {
-        this.string = string;
-    }
-
     /**
      * Basic constructor
      *
@@ -88,49 +13,11 @@
      */
     public DERUTF8String(String string)
     {
-        this.string = Strings.toUTF8ByteArray(string);
+        super(string);
     }
 
-    public String getString()
+    DERUTF8String(byte[] contents, boolean clone)
     {
-        return Strings.fromUTF8ByteArray(string);
-    }
-
-    public String toString()
-    {
-        return getString();
-    }
-
-    public int hashCode()
-    {
-        return Arrays.hashCode(string);
-    }
-
-    boolean asn1Equals(ASN1Primitive o)
-    {
-        if (!(o instanceof DERUTF8String))
-        {
-            return false;
-        }
-
-        DERUTF8String s = (DERUTF8String)o;
-
-        return Arrays.areEqual(string, s.string);
-    }
-
-    boolean isConstructed()
-    {
-        return false;
-    }
-
-    int encodedLength()
-        throws IOException
-    {
-        return 1 + StreamUtil.calculateBodyLength(string.length) + string.length;
-    }
-
-    void encode(ASN1OutputStream out, boolean withTag) throws IOException
-    {
-        out.writeEncoded(withTag, BERTags.UTF8_STRING, string);
+        super(contents, clone);
     }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/DERUniversalString.java b/bcprov/src/main/java/org/bouncycastle/asn1/DERUniversalString.java
index 474584b..0c50ef9 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/DERUniversalString.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/DERUniversalString.java
@@ -1,148 +1,24 @@
 package org.bouncycastle.asn1;
 
-import java.io.IOException;
-
-import org.bouncycastle.util.Arrays;
-
 /**
  * DER UniversalString object - encodes UNICODE (ISO 10646) characters using 32-bit format. In Java we
  * have no way of representing this directly so we rely on byte arrays to carry these.
  */
 public class DERUniversalString
-    extends ASN1Primitive
-    implements ASN1String
+    extends ASN1UniversalString
 {
-    private static final char[]  table = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
-    private final byte[] string;
-    
-    /**
-     * Return a Universal String from the passed in object.
-     *
-     * @param obj a DERUniversalString or an object that can be converted into one.
-     * @exception IllegalArgumentException if the object cannot be converted.
-     * @return a DERUniversalString instance, or null
-     */
-    public static DERUniversalString getInstance(
-        Object  obj)
-    {
-        if (obj == null || obj instanceof DERUniversalString)
-        {
-            return (DERUniversalString)obj;
-        }
-
-        if (obj instanceof byte[])
-        {
-            try
-            {
-                return (DERUniversalString)fromByteArray((byte[])obj);
-            }
-            catch (Exception e)
-            {
-                throw new IllegalArgumentException("encoding error getInstance: " + e.toString());
-            }
-        }
-
-        throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
-    }
-
-    /**
-     * Return a Universal String from a tagged object.
-     *
-     * @param obj the tagged object holding the object we want
-     * @param explicit true if the object is meant to be explicitly
-     *              tagged false otherwise.
-     * @exception IllegalArgumentException if the tagged object cannot
-     *               be converted.
-     * @return a DERUniversalString instance, or null
-     */
-    public static DERUniversalString getInstance(
-        ASN1TaggedObject obj,
-        boolean          explicit)
-    {
-        ASN1Primitive o = obj.getObject();
-
-        if (explicit || o instanceof DERUniversalString)
-        {
-            return getInstance(o);
-        }
-        else
-        {
-            return new DERUniversalString(ASN1OctetString.getInstance(o).getOctets());
-        }
-    }
-
     /**
      * Basic constructor - byte encoded string.
      *
      * @param string the byte encoding of the string to be carried in the UniversalString object,
      */
-    public DERUniversalString(
-        byte[]   string)
+    public DERUniversalString(byte[] string)
     {
-        this.string = Arrays.clone(string);
+        this(string, true);
     }
 
-    public String getString()
+    DERUniversalString(byte[] contents, boolean clone)
     {
-        StringBuffer buf = new StringBuffer("#");
-
-        byte[] string;
-        try
-        {
-            string = getEncoded();
-        }
-        catch (IOException e)
-        {
-           throw new ASN1ParsingException("internal error encoding UniversalString");
-        }
-
-        for (int i = 0; i != string.length; i++)
-        {
-            buf.append(table[(string[i] >>> 4) & 0xf]);
-            buf.append(table[string[i] & 0xf]);
-        }
-        
-        return buf.toString();
-    }
-
-    public String toString()
-    {
-        return getString();
-    }
-
-    public byte[] getOctets()
-    {
-        return Arrays.clone(string);
-    }
-
-    boolean isConstructed()
-    {
-        return false;
-    }
-
-    int encodedLength()
-    {
-        return 1 + StreamUtil.calculateBodyLength(string.length) + string.length;
-    }
-
-    void encode(ASN1OutputStream out, boolean withTag) throws IOException
-    {
-        out.writeEncoded(withTag, BERTags.UNIVERSAL_STRING, string);
-    }
-
-    boolean asn1Equals(
-        ASN1Primitive o)
-    {
-        if (!(o instanceof DERUniversalString))
-        {
-            return false;
-        }
-
-        return Arrays.areEqual(string, ((DERUniversalString)o).string);
-    }
-    
-    public int hashCode()
-    {
-        return Arrays.hashCode(string);
+        super(contents, clone);
     }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/DERVideotexString.java b/bcprov/src/main/java/org/bouncycastle/asn1/DERVideotexString.java
index 35458a8..141bab9 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/DERVideotexString.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/DERVideotexString.java
@@ -1,122 +1,15 @@
 package org.bouncycastle.asn1;
 
-import java.io.IOException;
-
-import org.bouncycastle.util.Arrays;
-import org.bouncycastle.util.Strings;
-
 public class DERVideotexString
-    extends ASN1Primitive
-    implements ASN1String
+    extends ASN1VideotexString
 {
-    private final byte[] string;
-    
-    /**
-     * return a Videotex String from the passed in object
-     *
-     * @param obj a DERVideotexString or an object that can be converted into one.
-     * @exception IllegalArgumentException if the object cannot be converted.
-     * @return a DERVideotexString instance, or null.
-     */
-    public static DERVideotexString getInstance(
-        Object  obj)
+    public DERVideotexString(byte[] octets)
     {
-        if (obj == null || obj instanceof DERVideotexString)
-        {
-            return (DERVideotexString)obj;
-        }
-
-        if (obj instanceof byte[])
-        {
-            try
-            {
-                return (DERVideotexString)fromByteArray((byte[])obj);
-            }
-            catch (Exception e)
-            {
-                throw new IllegalArgumentException("encoding error in getInstance: " + e.toString());
-            }
-        }
-
-        throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
+        this(octets, true);
     }
 
-    /**
-     * return a Videotex String from a tagged object.
-     *
-     * @param obj the tagged object holding the object we want
-     * @param explicit true if the object is meant to be explicitly
-     *              tagged false otherwise.
-     * @exception IllegalArgumentException if the tagged object cannot
-     *               be converted.
-     * @return a DERVideotexString instance, or null.
-     */
-    public static DERVideotexString getInstance(
-        ASN1TaggedObject obj,
-        boolean          explicit)
+    DERVideotexString(byte[] contents, boolean clone)
     {
-        ASN1Primitive o = obj.getObject();
-
-        if (explicit || o instanceof DERVideotexString)
-        {
-            return getInstance(o);
-        }
-        else
-        {
-            return new DERVideotexString(ASN1OctetString.getInstance(o).getOctets());
-        }
-    }
-
-    /**
-     * basic constructor - with bytes.
-     * @param string the byte encoding of the characters making up the string.
-     */
-    public DERVideotexString(
-        byte[]   string)
-    {
-        this.string = Arrays.clone(string);
-    }
-    
-    public byte[] getOctets()
-    {
-        return Arrays.clone(string);
-    }
-
-    boolean isConstructed()
-    {
-        return false;
-    }
-
-    int encodedLength()
-    {
-        return 1 + StreamUtil.calculateBodyLength(string.length) + string.length;
-    }
-
-    void encode(ASN1OutputStream out, boolean withTag) throws IOException
-    {
-        out.writeEncoded(withTag, BERTags.VIDEOTEX_STRING, string);
-    }
-
-    public int hashCode()
-    {
-        return Arrays.hashCode(string);
-    }
-
-    boolean asn1Equals(
-        ASN1Primitive o)
-    {
-        if (!(o instanceof DERVideotexString))
-        {
-            return false;
-        }
-
-        DERVideotexString  s = (DERVideotexString)o;
-
-        return Arrays.areEqual(string, s.string);
-    }
-
-    public String getString()
-    {
-        return Strings.fromByteArray(string);
+        super(contents, clone);
     }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/DERVisibleString.java b/bcprov/src/main/java/org/bouncycastle/asn1/DERVisibleString.java
index ddb692a..47dc787 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/DERVisibleString.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/DERVisibleString.java
@@ -1,10 +1,5 @@
 package org.bouncycastle.asn1;
 
-import java.io.IOException;
-
-import org.bouncycastle.util.Arrays;
-import org.bouncycastle.util.Strings;
-
 /**
  * DER VisibleString object encoding ISO 646 (ASCII) character code points 32 to 126.
  * <p>
@@ -12,130 +7,20 @@
  * </p>
  */
 public class DERVisibleString
-    extends ASN1Primitive
-    implements ASN1String
+    extends ASN1VisibleString
 {
-    private final byte[]  string;
-
-    /**
-     * Return a Visible String from the passed in object.
-     *
-     * @param obj a DERVisibleString or an object that can be converted into one.
-     * @exception IllegalArgumentException if the object cannot be converted.
-     * @return a DERVisibleString instance, or null
-     */
-    public static DERVisibleString getInstance(
-        Object  obj)
-    {
-        if (obj == null || obj instanceof DERVisibleString)
-        {
-            return (DERVisibleString)obj;
-        }
-
-        if (obj instanceof byte[])
-        {
-            try
-            {
-                return (DERVisibleString)fromByteArray((byte[])obj);
-            }
-            catch (Exception e)
-            {
-                throw new IllegalArgumentException("encoding error in getInstance: " + e.toString());
-            }
-        }
-
-        throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
-    }
-
-    /**
-     * Return a Visible String from a tagged object.
-     *
-     * @param obj the tagged object holding the object we want
-     * @param explicit true if the object is meant to be explicitly
-     *              tagged false otherwise.
-     * @exception IllegalArgumentException if the tagged object cannot
-     *               be converted.
-     * @return a DERVisibleString instance, or null
-     */
-    public static DERVisibleString getInstance(
-        ASN1TaggedObject obj,
-        boolean          explicit)
-    {
-        ASN1Primitive o = obj.getObject();
-
-        if (explicit || o instanceof DERVisibleString)
-        {
-            return getInstance(o);
-        }
-        else
-        {
-            return new DERVisibleString(ASN1OctetString.getInstance(o).getOctets());
-        }
-    }
-
-    /*
-     * Basic constructor - byte encoded string.
-     */
-    DERVisibleString(
-        byte[]   string)
-    {
-        this.string = string;
-    }
-
     /**
      * Basic constructor
      *
      * @param string the string to be carried in the VisibleString object,
      */
-    public DERVisibleString(
-        String   string)
+    public DERVisibleString(String string)
     {
-        this.string = Strings.toByteArray(string);
+        super(string);
     }
 
-    public String getString()
+    DERVisibleString(byte[] contents, boolean clone)
     {
-        return Strings.fromByteArray(string);
-    }
-
-    public String toString()
-    {
-        return getString();
-    }
-
-    public byte[] getOctets()
-    {
-        return Arrays.clone(string);
-    }
-
-    boolean isConstructed()
-    {
-        return false;
-    }
-
-    int encodedLength()
-    {
-        return 1 + StreamUtil.calculateBodyLength(string.length) + string.length;
-    }
-
-    void encode(ASN1OutputStream out, boolean withTag) throws IOException
-    {
-        out.writeEncoded(withTag, BERTags.VISIBLE_STRING, this.string);
-    }
-
-    boolean asn1Equals(
-        ASN1Primitive o)
-    {
-        if (!(o instanceof DERVisibleString))
-        {
-            return false;
-        }
-
-        return Arrays.areEqual(string, ((DERVisibleString)o).string);
-    }
-    
-    public int hashCode()
-    {
-        return Arrays.hashCode(string);
+        super(contents, clone);
     }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/DLApplicationSpecific.java b/bcprov/src/main/java/org/bouncycastle/asn1/DLApplicationSpecific.java
deleted file mode 100644
index 88059a7..0000000
--- a/bcprov/src/main/java/org/bouncycastle/asn1/DLApplicationSpecific.java
+++ /dev/null
@@ -1,124 +0,0 @@
-package org.bouncycastle.asn1;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-
-/**
- * A DER encoding version of an application specific object.
- */
-public class DLApplicationSpecific
-    extends ASN1ApplicationSpecific
-{
-    DLApplicationSpecific(
-        boolean isConstructed,
-        int     tag,
-        byte[]  octets)
-    {
-        super(isConstructed, tag, octets);
-    }
-
-    /**
-     * Create an application specific object from the passed in data. This will assume
-     * the data does not represent a constructed object.
-     *
-     * @param tag the tag number for this object.
-     * @param octets the encoding of the object's body.
-     */
-    public DLApplicationSpecific(
-        int    tag,
-        byte[] octets)
-    {
-        this(false, tag, octets);
-    }
-
-    /**
-     * Create an application specific object with a tagging of explicit/constructed.
-     *
-     * @param tag the tag number for this object.
-     * @param object the object to be contained.
-     */
-    public DLApplicationSpecific(
-        int           tag,
-        ASN1Encodable object)
-        throws IOException
-    {
-        this(true, tag, object);
-    }
-
-    /**
-     * Create an application specific object with the tagging style given by the value of constructed.
-     *
-     * @param constructed true if the object is constructed.
-     * @param tag the tag number for this object.
-     * @param object the object to be contained.
-     */
-    public DLApplicationSpecific(
-        boolean      constructed,
-        int          tag,
-        ASN1Encodable object)
-        throws IOException
-    {
-        super(constructed || object.toASN1Primitive().isConstructed(), tag, getEncoding(constructed, object));
-    }
-
-    private static byte[] getEncoding(boolean explicit, ASN1Encodable object)
-        throws IOException
-    {
-        byte[] data = object.toASN1Primitive().getEncoded(ASN1Encoding.DL);
-
-        if (explicit)
-        {
-            return data;
-        }
-        else
-        {
-            int lenBytes = getLengthOfHeader(data);
-            byte[] tmp = new byte[data.length - lenBytes];
-            System.arraycopy(data, lenBytes, tmp, 0, tmp.length);
-            return tmp;
-        }
-    }
-
-    /**
-     * Create an application specific object which is marked as constructed
-     *
-     * @param tagNo the tag number for this object.
-     * @param vec the objects making up the application specific object.
-     */
-    public DLApplicationSpecific(int tagNo, ASN1EncodableVector vec)
-    {
-        super(true, tagNo, getEncodedVector(vec));
-    }
-
-    private static byte[] getEncodedVector(ASN1EncodableVector vec)
-    {
-        ByteArrayOutputStream bOut = new ByteArrayOutputStream();
-
-        for (int i = 0; i != vec.size(); i++)
-        {
-            try
-            {
-                bOut.write(((ASN1Object)vec.get(i)).getEncoded(ASN1Encoding.DL));
-            }
-            catch (IOException e)
-            {
-                throw new ASN1ParsingException("malformed object: " + e, e);
-            }
-        }
-        return bOut.toByteArray();
-    }
-
-    /* (non-Javadoc)
-     * @see org.bouncycastle.asn1.ASN1Primitive#encode(org.bouncycastle.asn1.DEROutputStream)
-     */
-    void encode(ASN1OutputStream out, boolean withTag) throws IOException
-    {
-        int flags = BERTags.APPLICATION;
-        if (isConstructed)
-        {
-            flags |= BERTags.CONSTRUCTED;
-        }
-
-        out.writeEncoded(withTag, flags, tag, octets);
-    }
-}
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/DLBitString.java b/bcprov/src/main/java/org/bouncycastle/asn1/DLBitString.java
index 176dc8e..ecb0797 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/DLBitString.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/DLBitString.java
@@ -8,113 +8,51 @@
 public class DLBitString
     extends ASN1BitString
 {
-    /**
-     * return a Bit String that can be definite-length encoded from the passed in object.
-     *
-     * @param obj a DL or DER BitString or an object that can be converted into one.
-     * @exception IllegalArgumentException if the object cannot be converted.
-     * @return an ASN1BitString instance, or null.
-     */
-    public static ASN1BitString getInstance(
-        Object  obj)
-    {
-        if (obj == null || obj instanceof DLBitString)
-        {
-            return (DLBitString)obj;
-        }
-        if (obj instanceof DERBitString)
-        {
-            return (DERBitString)obj;
-        }
-        if (obj instanceof byte[])
-        {
-            try
-            {
-                return (ASN1BitString)fromByteArray((byte[])obj);
-            }
-            catch (Exception e)
-            {
-                throw new IllegalArgumentException("encoding error in getInstance: " + e.toString());
-            }
-        }
-
-        throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
-    }
-
-    /**
-     * return a Bit String from a tagged object.
-     *
-     * @param obj the tagged object holding the object we want
-     * @param explicit true if the object is meant to be explicitly
-     *              tagged false otherwise.
-     * @exception IllegalArgumentException if the tagged object cannot
-     *               be converted.
-     * @return an ASN1BitString instance, or null.
-     */
-    public static ASN1BitString getInstance(
-        ASN1TaggedObject obj,
-        boolean          explicit)
-    {
-        ASN1Primitive o = obj.getObject();
-
-        if (explicit || o instanceof DLBitString)
-        {
-            return getInstance(o);
-        }
-        else
-        {
-            return fromOctetString(ASN1OctetString.getInstance(o).getOctets());
-        }
-    }
-
-    protected DLBitString(byte data, int padBits)
-    {
-        super(data, padBits);
-    }
-
-    /**
-     * @param data the octets making up the bit string.
-     * @param padBits the number of extra bits at the end of the string.
-     */
-    public DLBitString(
-        byte[]  data,
-        int     padBits)
-    {
-        super(data, padBits);
-    }
-
-    public DLBitString(
-        byte[]  data)
+    public DLBitString(byte[] data)
     {
         this(data, 0);
     }
 
-    public DLBitString(
-        int value)
+    public DLBitString(byte data, int padBits)
     {
+        super(data, padBits);
+    }
+
+    public DLBitString(byte[] data, int padBits)
+    {
+        super(data, padBits);
+    }
+
+    public DLBitString(int value)
+    {
+        // TODO[asn1] Unify in single allocation of 'contents'
         super(getBytes(value), getPadBits(value));
     }
 
-    public DLBitString(
-        ASN1Encodable obj)
-        throws IOException
+    public DLBitString(ASN1Encodable obj) throws IOException
     {
+        // TODO[asn1] Unify in single allocation of 'contents'
         super(obj.toASN1Primitive().getEncoded(ASN1Encoding.DER), 0);
     }
 
-    boolean isConstructed()
+    DLBitString(byte[] contents, boolean check)
+    {
+        super(contents, check);
+    }
+
+    boolean encodeConstructed()
     {
         return false;
     }
 
-    int encodedLength()
+    int encodedLength(boolean withTag)
     {
-        return 1 + StreamUtil.calculateBodyLength(data.length + 1) + data.length + 1;
+        return ASN1OutputStream.getLengthOfEncodingDL(withTag, contents.length);
     }
 
     void encode(ASN1OutputStream out, boolean withTag) throws IOException
     {
-        out.writeEncoded(withTag, BERTags.BIT_STRING, (byte)padBits, data);
+        out.writeEncodingDL(withTag, BERTags.BIT_STRING, contents);
     }
 
     ASN1Primitive toDLObject()
@@ -122,21 +60,19 @@
         return this;
     }
 
-    static DLBitString fromOctetString(byte[] bytes)
+    static int encodedLength(boolean withTag, int contentsLength)
     {
-        if (bytes.length < 1)
-        {
-            throw new IllegalArgumentException("truncated BIT STRING detected");
-        }
+        return ASN1OutputStream.getLengthOfEncodingDL(withTag, contentsLength);
+    }
 
-        int padBits = bytes[0];
-        byte[] data = new byte[bytes.length - 1];
+    static void encode(ASN1OutputStream out, boolean withTag, byte[] buf, int off, int len) throws IOException
+    {
+        out.writeEncodingDL(withTag, BERTags.BIT_STRING, buf, off, len);
+    }
 
-        if (data.length != 0)
-        {
-            System.arraycopy(bytes, 1, data, 0, bytes.length - 1);
-        }
-
-        return new DLBitString(data, padBits);
+    static void encode(ASN1OutputStream out, boolean withTag, byte pad, byte[] buf, int off, int len)
+        throws IOException
+    {
+        out.writeEncodingDL(withTag, BERTags.BIT_STRING, pad, buf, off, len);
     }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/DLBitStringParser.java b/bcprov/src/main/java/org/bouncycastle/asn1/DLBitStringParser.java
new file mode 100644
index 0000000..ce92398
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/DLBitStringParser.java
@@ -0,0 +1,83 @@
+package org.bouncycastle.asn1;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Parser for a DL encoded BIT STRING.
+ * 
+ * @deprecated Check for 'ASN1BitStringParser' instead 
+ */
+public class DLBitStringParser
+    implements ASN1BitStringParser
+{
+    private final DefiniteLengthInputStream stream;
+    private int padBits = 0;
+
+    DLBitStringParser(
+        DefiniteLengthInputStream stream)
+    {
+        this.stream = stream;
+    }
+
+    public InputStream getBitStream() throws IOException
+    {
+        return getBitStream(false);
+    }
+
+    public InputStream getOctetStream() throws IOException
+    {
+        return getBitStream(true);
+    }
+
+    public int getPadBits()
+    {
+        return padBits;
+    }
+
+    public ASN1Primitive getLoadedObject()
+        throws IOException
+    {
+        return ASN1BitString.createPrimitive(stream.toByteArray());
+    }
+
+    public ASN1Primitive toASN1Primitive()
+    {
+        try
+        {
+            return getLoadedObject();
+        }
+        catch (IOException e)
+        {
+            throw new ASN1ParsingException("IOException converting stream to byte array: " + e.getMessage(), e);
+        }
+    }
+
+    private InputStream getBitStream(boolean octetAligned) throws IOException
+    {
+        int length = stream.getRemaining();
+        if (length < 1)
+        {
+            throw new IllegalStateException("content octets cannot be empty");
+        }
+
+        padBits = stream.read();
+        if (padBits > 0)
+        {
+            if (length < 2)
+            {
+                throw new IllegalStateException("zero length data with non-zero pad bits");
+            }
+            if (padBits > 7)
+            {
+                throw new IllegalStateException("pad bits cannot be greater than 7 or less than 0");
+            }
+            if (octetAligned)
+            {
+                throw new IOException("expected octet-aligned bitstring, but found padBits: " + padBits);
+            }
+        }
+
+        return stream;
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/DLExternal.java b/bcprov/src/main/java/org/bouncycastle/asn1/DLExternal.java
index 833c60e..8424fc5 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/DLExternal.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/DLExternal.java
@@ -1,8 +1,5 @@
 package org.bouncycastle.asn1;
 
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-
 /**
  * Class representing the Definite-Length-type External
  */
@@ -19,11 +16,30 @@
      * <li> Anything but {@link DERTaggedObject} + data {@link DERTaggedObject} (data value form)</li>
      * </ul>
      *
-     * @throws IllegalArgumentException if input size is wrong, or
+     * @throws IllegalArgumentException if input size is wrong, or input is not an acceptable format
+     * 
+     * @deprecated Use {@link DLExternal#DLExternal(DLSequence)} instead.
      */
     public DLExternal(ASN1EncodableVector vector)
     {
-        super(vector);
+        this(DLFactory.createSequence(vector));
+    }
+
+    /**
+     * Construct a Definite-Length EXTERNAL object, the input sequence must have exactly two elements on it.
+     * <p>
+     * Acceptable input formats are:
+     * <ul>
+     * <li> {@link ASN1ObjectIdentifier} + data {@link DERTaggedObject} (direct reference form)</li>
+     * <li> {@link ASN1Integer} + data {@link DERTaggedObject} (indirect reference form)</li>
+     * <li> Anything but {@link DERTaggedObject} + data {@link DERTaggedObject} (data value form)</li>
+     * </ul>
+     *
+     * @throws IllegalArgumentException if input size is wrong, or input is not an acceptable format
+     */
+    public DLExternal(DLSequence sequence)
+    {
+        super(sequence);
     }
 
     /**
@@ -34,9 +50,10 @@
      * @param dataValueDescriptor The data value descriptor or <code>null</code> if not set.
      * @param externalData The external data in its encoded form.
      */
-    public DLExternal(ASN1ObjectIdentifier directReference, ASN1Integer indirectReference, ASN1Primitive dataValueDescriptor, DERTaggedObject externalData)
+    public DLExternal(ASN1ObjectIdentifier directReference, ASN1Integer indirectReference,
+        ASN1Primitive dataValueDescriptor, DERTaggedObject externalData)
     {
-        this(directReference, indirectReference, dataValueDescriptor, externalData.getTagNo(), externalData.toASN1Primitive());
+        super(directReference, indirectReference, dataValueDescriptor, externalData);
     }
 
     /**
@@ -48,43 +65,35 @@
      * @param encoding The encoding to be used for the external data
      * @param externalData The external data
      */
-    public DLExternal(ASN1ObjectIdentifier directReference, ASN1Integer indirectReference, ASN1Primitive dataValueDescriptor, int encoding, ASN1Primitive externalData)
+    public DLExternal(ASN1ObjectIdentifier directReference, ASN1Integer indirectReference,
+        ASN1Primitive dataValueDescriptor, int encoding, ASN1Primitive externalData)
     {
         super(directReference, indirectReference, dataValueDescriptor, encoding, externalData);
     }
 
+    ASN1Sequence buildSequence()
+    {
+        ASN1EncodableVector v = new ASN1EncodableVector(4);
+        if (directReference != null)
+        {
+            v.add(directReference);
+        }
+        if (indirectReference != null)
+        {
+            v.add(indirectReference);
+        }
+        if (dataValueDescriptor != null)
+        {
+            v.add(dataValueDescriptor.toDLObject());
+        }
+
+        v.add(new DLTaggedObject(0 == encoding, encoding, externalContent));
+
+        return new DLSequence(v);
+    }
+
     ASN1Primitive toDLObject()
     {
         return this;
     }
-
-    int encodedLength()
-        throws IOException
-    {
-        return this.getEncoded().length;
-    }
-
-    /* (non-Javadoc)
-     * @see org.bouncycastle.asn1.ASN1Primitive#encode(org.bouncycastle.asn1.DEROutputStream)
-     */
-    void encode(ASN1OutputStream out, boolean withTag) throws IOException
-    {
-        ByteArrayOutputStream baos = new ByteArrayOutputStream();
-        if (directReference != null)
-        {
-            baos.write(directReference.getEncoded(ASN1Encoding.DL));
-        }
-        if (indirectReference != null)
-        {
-            baos.write(indirectReference.getEncoded(ASN1Encoding.DL));
-        }
-        if (dataValueDescriptor != null)
-        {
-            baos.write(dataValueDescriptor.getEncoded(ASN1Encoding.DL));
-        }
-        ASN1TaggedObject obj = new DLTaggedObject(true, encoding, externalContent);
-        baos.write(obj.getEncoded(ASN1Encoding.DL));
-        
-        out.writeEncoded(withTag, BERTags.CONSTRUCTED, BERTags.EXTERNAL, baos.toByteArray());
-    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/DLFactory.java b/bcprov/src/main/java/org/bouncycastle/asn1/DLFactory.java
index 0ce21ca..9ae4742 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/DLFactory.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/DLFactory.java
@@ -2,10 +2,10 @@
 
 class DLFactory
 {
-    static final ASN1Sequence EMPTY_SEQUENCE = new DLSequence();
-    static final ASN1Set EMPTY_SET = new DLSet();
+    static final DLSequence EMPTY_SEQUENCE = new DLSequence();
+    static final DLSet EMPTY_SET = new DLSet();
 
-    static ASN1Sequence createSequence(ASN1EncodableVector v)
+    static DLSequence createSequence(ASN1EncodableVector v)
     {
         if (v.size() < 1)
         {
@@ -15,7 +15,7 @@
         return new DLSequence(v);
     }
 
-    static ASN1Set createSet(ASN1EncodableVector v)
+    static DLSet createSet(ASN1EncodableVector v)
     {
         if (v.size() < 1)
         {
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/DLOutputStream.java b/bcprov/src/main/java/org/bouncycastle/asn1/DLOutputStream.java
index 9c2a88c..edefdbf 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/DLOutputStream.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/DLOutputStream.java
@@ -14,13 +14,32 @@
         super(os);
     }
 
+    DLOutputStream getDLSubStream()
+    {
+        return this;
+    }
+
+    void writeElements(ASN1Encodable[] elements)
+        throws IOException
+    {
+        for (int i = 0, count = elements.length; i < count; ++i)
+        {
+            elements[i].toASN1Primitive().toDLObject().encode(this, true);
+        }
+    }
+
     void writePrimitive(ASN1Primitive primitive, boolean withTag) throws IOException
     {
         primitive.toDLObject().encode(this, withTag);
     }
 
-    ASN1OutputStream getDLSubStream()
+    void writePrimitives(ASN1Primitive[] primitives)
+        throws IOException
     {
-        return this;
+        int count = primitives.length;
+        for (int i = 0; i < count; ++i)
+        {
+            primitives[i].toDLObject().encode(this, true);
+        }
     }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/DLSequence.java b/bcprov/src/main/java/org/bouncycastle/asn1/DLSequence.java
index b040ded..5cc4bb4 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/DLSequence.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/DLSequence.java
@@ -8,7 +8,7 @@
 public class DLSequence
     extends ASN1Sequence
 {
-    private int bodyLength = -1;
+    private int contentsLength = -1;
 
     /**
      * Create an empty sequence
@@ -49,9 +49,9 @@
         super(elements, clone);
     }
 
-    private int getBodyLength() throws IOException
+    private int getContentsLength() throws IOException
     {
-        if (bodyLength < 0)
+        if (contentsLength < 0)
         {
             int count = elements.length;
             int totalLength = 0;
@@ -59,20 +59,18 @@
             for (int i = 0; i < count; ++i)
             {
                 ASN1Primitive dlObject = elements[i].toASN1Primitive().toDLObject();
-                totalLength += dlObject.encodedLength();
+                totalLength += dlObject.encodedLength(true);
             }
 
-            this.bodyLength = totalLength;
+            this.contentsLength = totalLength;
         }
 
-        return bodyLength;
+        return contentsLength;
     }
 
-    int encodedLength() throws IOException
+    int encodedLength(boolean withTag) throws IOException
     {
-        int length = getBodyLength();
-
-        return 1 + StreamUtil.calculateBodyLength(length) + length;
+        return ASN1OutputStream.getLengthOfEncodingDL(withTag, getContentsLength());
     }
 
     /**
@@ -85,17 +83,14 @@
      */
     void encode(ASN1OutputStream out, boolean withTag) throws IOException
     {
-        if (withTag)
-        {
-            out.write(BERTags.SEQUENCE | BERTags.CONSTRUCTED);
-        }
+        out.writeIdentifier(withTag, BERTags.CONSTRUCTED | BERTags.SEQUENCE);
 
         ASN1OutputStream dlOut = out.getDLSubStream();
 
         int count = elements.length;
-        if (bodyLength >= 0 || count > 16)
+        if (contentsLength >= 0 || count > 16)
         {
-            out.writeLength(getBodyLength());
+            out.writeDL(getContentsLength());
 
             for (int i = 0; i < count; ++i)
             {
@@ -111,11 +106,11 @@
             {
                 ASN1Primitive dlObject = elements[i].toASN1Primitive().toDLObject();
                 dlObjects[i] = dlObject;
-                totalLength += dlObject.encodedLength();
+                totalLength += dlObject.encodedLength(true);
             }
 
-            this.bodyLength = totalLength;
-            out.writeLength(totalLength);
+            this.contentsLength = totalLength;
+            out.writeDL(totalLength);
 
             for (int i = 0; i < count; ++i)
             {
@@ -124,8 +119,29 @@
         }
     }
 
+    ASN1BitString toASN1BitString()
+    {
+        return new DLBitString(BERBitString.flattenBitStrings(getConstructedBitStrings()), false);
+    }
+
+    ASN1External toASN1External()
+    {
+        return new DLExternal(this);
+    }
+
+    ASN1OctetString toASN1OctetString()
+    {
+        // NOTE: There is no DLOctetString
+        return new DEROctetString(BEROctetString.flattenOctetStrings(getConstructedOctetStrings()));
+    }
+
+    ASN1Set toASN1Set()
+    {
+        return new DLSet(false, toArrayInternal());
+    }
+
     ASN1Primitive toDLObject()
     {
         return this;
     }
-}
\ No newline at end of file
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/DLSequenceParser.java b/bcprov/src/main/java/org/bouncycastle/asn1/DLSequenceParser.java
index 290a46e..7e54814 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/DLSequenceParser.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/DLSequenceParser.java
@@ -5,7 +5,7 @@
 /**
  * Parser class for DL SEQUENCEs.
  *
- * TODO The class is only publicly visible to support 'instanceof' checks; provide an alternative
+ * @deprecated Check for 'ASN1SequenceParser' instead
  */
 public class DLSequenceParser
     implements ASN1SequenceParser
@@ -38,7 +38,7 @@
     public ASN1Primitive getLoadedObject()
         throws IOException
     {
-         return new DLSequence(_parser.readVector());
+         return DLFactory.createSequence(_parser.readVector());
     }
 
     /**
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/DLSet.java b/bcprov/src/main/java/org/bouncycastle/asn1/DLSet.java
index 576197a..695b1e3 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/DLSet.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/DLSet.java
@@ -53,7 +53,7 @@
 public class DLSet
     extends ASN1Set
 {
-    private int bodyLength = -1;
+    private int contentsLength = -1;
 
     /**
      * create an empty set
@@ -91,9 +91,14 @@
         super(isSorted, elements);
     }
 
-    private int getBodyLength() throws IOException
+    DLSet(ASN1Encodable[] elements, ASN1Encodable[] sortedElements)
     {
-        if (bodyLength < 0)
+        super(elements, sortedElements);
+    }
+
+    private int getContentsLength() throws IOException
+    {
+        if (contentsLength < 0)
         {
             int count = elements.length;
             int totalLength = 0;
@@ -101,20 +106,18 @@
             for (int i = 0; i < count; ++i)
             {
                 ASN1Primitive dlObject = elements[i].toASN1Primitive().toDLObject();
-                totalLength += dlObject.encodedLength();
+                totalLength += dlObject.encodedLength(true);
             }
 
-            this.bodyLength = totalLength;
+            this.contentsLength = totalLength;
         }
 
-        return bodyLength;
+        return contentsLength;
     }
 
-    int encodedLength() throws IOException
+    int encodedLength(boolean withTag) throws IOException
     {
-        int length = getBodyLength();
-
-        return 1 + StreamUtil.calculateBodyLength(length) + length;
+        return ASN1OutputStream.getLengthOfEncodingDL(withTag, getContentsLength());
     }
 
     /**
@@ -127,17 +130,14 @@
      */
     void encode(ASN1OutputStream out, boolean withTag) throws IOException
     {
-        if (withTag)
-        {
-            out.write(BERTags.SET | BERTags.CONSTRUCTED);
-        }
+        out.writeIdentifier(withTag, BERTags.CONSTRUCTED | BERTags.SET);
 
         ASN1OutputStream dlOut = out.getDLSubStream();
 
         int count = elements.length;
-        if (bodyLength >= 0 || count > 16)
+        if (contentsLength >= 0 || count > 16)
         {
-            out.writeLength(getBodyLength());
+            out.writeDL(getContentsLength());
 
             for (int i = 0; i < count; ++i)
             {
@@ -153,11 +153,11 @@
             {
                 ASN1Primitive dlObject = elements[i].toASN1Primitive().toDLObject();
                 dlObjects[i] = dlObject;
-                totalLength += dlObject.encodedLength();
+                totalLength += dlObject.encodedLength(true);
             }
 
-            this.bodyLength = totalLength;
-            out.writeLength(totalLength);
+            this.contentsLength = totalLength;
+            out.writeDL(totalLength);
 
             for (int i = 0; i < count; ++i)
             {
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/DLSetParser.java b/bcprov/src/main/java/org/bouncycastle/asn1/DLSetParser.java
index 9f4421d..8a55a6d 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/DLSetParser.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/DLSetParser.java
@@ -5,7 +5,7 @@
 /**
  * Parser class for DL SETs.
  *
- * TODO The class is only publicly visible to support 'instanceof' checks; provide an alternative
+ * @deprecated Check for 'ASN1SetParser' instead
  */
 public class DLSetParser
     implements ASN1SetParser
@@ -38,7 +38,7 @@
     public ASN1Primitive getLoadedObject()
         throws IOException
     {
-        return new DLSet(_parser.readVector());
+        return DLFactory.createSet(_parser.readVector());
     }
 
     /**
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/DLTaggedObject.java b/bcprov/src/main/java/org/bouncycastle/asn1/DLTaggedObject.java
index 50a3a26..c4e1859 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/DLTaggedObject.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/DLTaggedObject.java
@@ -10,60 +10,92 @@
 public class DLTaggedObject
     extends ASN1TaggedObject
 {
+    public DLTaggedObject(int tagNo, ASN1Encodable encodable)
+    {
+        super(true, tagNo, encodable);
+    }
+
+    public DLTaggedObject(int tagClass, int tagNo, ASN1Encodable encodable)
+    {
+        super(true, tagClass, tagNo, encodable);
+    }
+
     /**
      * @param explicit true if an explicitly tagged object.
      * @param tagNo the tag number for this object.
      * @param obj the tagged object.
      */
-    public DLTaggedObject(
-        boolean explicit,
-        int tagNo,
-        ASN1Encodable obj)
+    public DLTaggedObject(boolean explicit, int tagNo, ASN1Encodable obj)
     {
         super(explicit, tagNo, obj);
     }
 
-    boolean isConstructed()
+    public DLTaggedObject(boolean explicit, int tagClass, int tagNo, ASN1Encodable obj)
     {
-        return explicit || obj.toASN1Primitive().toDLObject().isConstructed();
+        super(explicit, tagClass, tagNo, obj);
     }
 
-    int encodedLength()
-        throws IOException
+    DLTaggedObject(int explicitness, int tagClass, int tagNo, ASN1Encodable obj)
     {
-        int length = obj.toASN1Primitive().toDLObject().encodedLength();
+        super(explicitness, tagClass, tagNo, obj);
+    }
+
+    boolean encodeConstructed()
+    {
+        return isExplicit() || obj.toASN1Primitive().toDLObject().encodeConstructed();
+    }
+
+    int encodedLength(boolean withTag) throws IOException
+    {
+        ASN1Primitive primitive = obj.toASN1Primitive().toDLObject();
+        boolean explicit = isExplicit();
+
+        int length = primitive.encodedLength(explicit);
 
         if (explicit)
         {
-            return  StreamUtil.calculateTagLength(tagNo) + StreamUtil.calculateBodyLength(length) + length;
+            length += ASN1OutputStream.getLengthOfDL(length);
         }
-        else
-        {
-            // header length already in calculation
-            length = length - 1;
 
-            return StreamUtil.calculateTagLength(tagNo) + length;
-        }
+        length += withTag ? ASN1OutputStream.getLengthOfIdentifier(tagNo) : 0;
+
+        return length;
     }
 
     void encode(ASN1OutputStream out, boolean withTag) throws IOException
     {
+//        assert out.getClass().isAssignableFrom(DLOutputStream.class);
+
         ASN1Primitive primitive = obj.toASN1Primitive().toDLObject();
+        boolean explicit = isExplicit();
 
-        int flags = BERTags.TAGGED;
-        if (explicit || primitive.isConstructed())
+        if (withTag)
         {
-            flags |= BERTags.CONSTRUCTED;
-        }
+            int flags = tagClass;
+            if (explicit || primitive.encodeConstructed())
+            {
+                flags |= BERTags.CONSTRUCTED;
+            }
 
-        out.writeTag(withTag, flags, tagNo);
+            out.writeIdentifier(true, flags, tagNo);
+        }
 
         if (explicit)
         {
-            out.writeLength(primitive.encodedLength());
+            out.writeDL(primitive.encodedLength(true));
         }
 
-        out.getDLSubStream().writePrimitive(primitive, explicit);
+        primitive.encode(out.getDLSubStream(), explicit);
+    }
+
+    ASN1Sequence rebuildConstructed(ASN1Primitive primitive)
+    {
+        return new DLSequence(primitive);
+    }
+
+    ASN1TaggedObject replaceTag(int tagClass, int tagNo)
+    {
+        return new DLTaggedObject(explicitness, tagClass, tagNo, obj);
     }
 
     ASN1Primitive toDLObject()
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/DLTaggedObjectParser.java b/bcprov/src/main/java/org/bouncycastle/asn1/DLTaggedObjectParser.java
new file mode 100644
index 0000000..3addbaa
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/DLTaggedObjectParser.java
@@ -0,0 +1,73 @@
+package org.bouncycastle.asn1;
+
+import java.io.IOException;
+
+/**
+ * Parser for definite-length tagged objects.
+ */
+class DLTaggedObjectParser
+    extends BERTaggedObjectParser
+{
+    private final boolean _constructed;
+
+    DLTaggedObjectParser(int tagClass, int tagNo, boolean constructed, ASN1StreamParser parser)
+    {
+        super(tagClass, tagNo, parser);
+
+        _constructed = constructed;
+    }
+
+    /**
+     * Return an in-memory, encodable, representation of the tagged object.
+     *
+     * @return an ASN1TaggedObject.
+     * @throws IOException if there is an issue loading the data.
+     */
+    public ASN1Primitive getLoadedObject()
+        throws IOException
+    {
+        return _parser.loadTaggedDL(_tagClass, _tagNo, _constructed);
+    }
+
+    public ASN1Encodable parseBaseUniversal(boolean declaredExplicit, int baseTagNo) throws IOException
+    {
+        if (declaredExplicit)
+        {
+            if (!_constructed)
+            {
+                throw new IOException("Explicit tags must be constructed (see X.690 8.14.2)");
+            }
+
+            return _parser.parseObject(baseTagNo);
+        }
+
+        return _constructed
+            ?  _parser.parseImplicitConstructedDL(baseTagNo)
+            :  _parser.parseImplicitPrimitive(baseTagNo);
+    }
+
+    public ASN1Encodable parseExplicitBaseObject() throws IOException
+    {
+        if (!_constructed)
+        {
+            throw new IOException("Explicit tags must be constructed (see X.690 8.14.2)");
+        }
+
+        return _parser.readObject();
+    }
+
+    public ASN1TaggedObjectParser parseExplicitBaseTagged() throws IOException
+    {
+        if (!_constructed)
+        {
+            throw new IOException("Explicit tags must be constructed (see X.690 8.14.2)");
+        }
+
+        return _parser.parseTaggedObject();
+    }
+
+    public ASN1TaggedObjectParser parseImplicitBaseTagged(int baseTagClass, int baseTagNo) throws IOException
+    {
+        return new DLTaggedObjectParser(baseTagClass, baseTagNo, _constructed, _parser);
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/DefiniteLengthInputStream.java b/bcprov/src/main/java/org/bouncycastle/asn1/DefiniteLengthInputStream.java
index 359bcc2..3589c8b 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/DefiniteLengthInputStream.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/DefiniteLengthInputStream.java
@@ -25,18 +25,18 @@
     {
         super(in, limit);
 
-        if (length < 0)
+        if (length <= 0)
         {
-            throw new IllegalArgumentException("negative lengths not allowed");
+            if (length < 0)
+            {
+                throw new IllegalArgumentException("negative lengths not allowed");
+            }
+
+            setParentEofDetect(true);
         }
 
         this._originalLength = length;
         this._remaining = length;
-
-        if (length == 0)
-        {
-            setParentEofDetect(true);
-        }
     }
 
     int getRemaining()
@@ -111,7 +111,7 @@
             throw new IOException("corrupted stream - out of bounds length found: " + _remaining + " >= " + limit);
         }
 
-        if ((_remaining -= Streams.readFully(_in, buf)) != 0)
+        if ((_remaining -= Streams.readFully(_in, buf, 0, buf.length)) != 0)
         {
             throw new EOFException("DEF length " + _originalLength + " object truncated by " + _remaining);
         }
@@ -134,7 +134,7 @@
         }
 
         byte[] bytes = new byte[_remaining];
-        if ((_remaining -= Streams.readFully(_in, bytes)) != 0)
+        if ((_remaining -= Streams.readFully(_in, bytes, 0, bytes.length)) != 0)
         {
             throw new EOFException("DEF length " + _originalLength + " object truncated by " + _remaining);
         }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/LazyConstructionEnumeration.java b/bcprov/src/main/java/org/bouncycastle/asn1/LazyConstructionEnumeration.java
index c671e5b..4216e9e 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/LazyConstructionEnumeration.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/LazyConstructionEnumeration.java
@@ -40,7 +40,7 @@
         }
         catch (IOException e)
         {
-            throw new ASN1ParsingException("malformed DER construction: " + e, e);
+            throw new ASN1ParsingException("malformed ASN.1: " + e, e);
         }
     }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/LazyEncodedSequence.java b/bcprov/src/main/java/org/bouncycastle/asn1/LazyEncodedSequence.java
index 23c72f8..93e9ff0 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/LazyEncodedSequence.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/LazyEncodedSequence.java
@@ -17,18 +17,24 @@
         // NOTE: Initially, the actual 'elements' will be empty
         super();
 
+        if (null == encoded)
+        {
+            throw new NullPointerException("'encoded' cannot be null");
+        }
+
         this.encoded = encoded;
     }
 
-    public synchronized ASN1Encodable getObjectAt(int index)
+    public ASN1Encodable getObjectAt(int index)
     {
         force();
 
         return super.getObjectAt(index);
     }
 
-    public synchronized Enumeration getObjects()
+    public Enumeration getObjects()
     {
+        byte[] encoded = getContents();
         if (null != encoded)
         {
             return new LazyConstructionEnumeration(encoded);
@@ -37,28 +43,28 @@
         return super.getObjects();
     }
 
-    public synchronized int hashCode()
+    public int hashCode()
     {
         force();
 
         return super.hashCode();
     }
 
-    public synchronized Iterator<ASN1Encodable> iterator()
+    public Iterator<ASN1Encodable> iterator()
     {
         force();
 
         return super.iterator();
     }
 
-    public synchronized int size()
+    public int size()
     {
         force();
 
         return super.size();
     }
 
-    public synchronized ASN1Encodable[] toArray()
+    public ASN1Encodable[] toArray()
     {
         force();
 
@@ -72,27 +78,48 @@
         return super.toArrayInternal();
     }
 
-    synchronized int encodedLength()
+    int encodedLength(boolean withTag)
         throws IOException
     {
+        byte[] encoded = getContents();
         if (null != encoded)
         {
-            return 1 + StreamUtil.calculateBodyLength(encoded.length) + encoded.length;
+            return ASN1OutputStream.getLengthOfEncodingDL(withTag, encoded.length);
         }
 
-        return super.toDLObject().encodedLength();
+        return super.toDLObject().encodedLength(withTag);
     }
 
-    synchronized void encode(ASN1OutputStream out, boolean withTag) throws IOException
+    void encode(ASN1OutputStream out, boolean withTag) throws IOException
     {
+        byte[] encoded = getContents();
         if (null != encoded)
         {
-            out.writeEncoded(withTag, BERTags.SEQUENCE | BERTags.CONSTRUCTED, encoded);
+            out.writeEncodingDL(withTag, BERTags.CONSTRUCTED | BERTags.SEQUENCE, encoded);
+            return;
         }
-        else
-        {
-            super.toDLObject().encode(out, withTag);
-        }
+
+        super.toDLObject().encode(out, withTag);
+    }
+
+    ASN1BitString toASN1BitString()
+    {
+        return ((ASN1Sequence)toDLObject()).toASN1BitString();
+    }
+
+    ASN1External toASN1External()
+    {
+        return ((ASN1Sequence)toDLObject()).toASN1External();
+    }
+
+    ASN1OctetString toASN1OctetString()
+    {
+        return ((ASN1Sequence)toDLObject()).toASN1OctetString();
+    }
+
+    ASN1Set toASN1Set()
+    {
+        return ((ASN1Sequence)toDLObject()).toASN1Set();
     }
 
     synchronized ASN1Primitive toDERObject()
@@ -109,20 +136,28 @@
         return super.toDLObject();
     }
 
-    private void force()
+    private synchronized void force()
     {
         if (null != encoded)
         {
-            ASN1EncodableVector v = new ASN1EncodableVector();
-
-            Enumeration en = new LazyConstructionEnumeration(encoded);
-            while (en.hasMoreElements())
+            ASN1InputStream aIn = new ASN1InputStream(encoded, true);
+            try
             {
-                v.add((ASN1Primitive)en.nextElement());
-            }
+                ASN1EncodableVector v = aIn.readVector();
+                aIn.close();
 
-            this.elements = v.takeElements();
-            this.encoded = null;
+                this.elements = v.takeElements();
+                this.encoded = null;
+            }
+            catch (IOException e)
+            {
+                throw new ASN1ParsingException("malformed ASN.1: " + e, e);
+            }
         }
     }
+
+    private synchronized byte[] getContents()
+    {
+        return encoded;
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/DateUtil.java b/bcprov/src/main/java/org/bouncycastle/asn1/LocaleUtil.java
similarity index 79%
rename from bcprov/src/main/java/org/bouncycastle/asn1/DateUtil.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/LocaleUtil.java
index 83d8bb2..1a7821e 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/DateUtil.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/LocaleUtil.java
@@ -7,13 +7,17 @@
 import java.util.Locale;
 import java.util.Map;
 
-class DateUtil
-{
-    private static Long ZERO = longValueOf(0);
+import org.bouncycastle.util.Longs;
 
+/**
+ * ASN.1 uses an EN locale for its internal formatting. This class finds the nearest equivalent in the
+ * current JVM to ensure date formats are always respected.
+ */
+public class LocaleUtil
+{
     private static final Map localeCache = new HashMap();
 
-    static Locale EN_Locale = forEN();
+    public static Locale EN_Locale = forEN();
 
     private static Locale forEN()
     {
@@ -52,19 +56,12 @@
                 SimpleDateFormat dateF = new SimpleDateFormat("yyyyMMddHHmmssz");
                 long v = dateF.parse("19700101000000GMT+00:00").getTime();
 
-                if (v == 0)
-                {
-                    adj = ZERO;
-                }
-                else
-                {
-                    adj = longValueOf(v);
-                }
+                adj = longValueOf(v);
 
                 localeCache.put(locale, adj);
             }
 
-            if (adj != ZERO)
+            if (adj.longValue() != 0L)
             {
                 return new Date(date.getTime() - adj.longValue());
             }
@@ -75,6 +72,6 @@
 
     private static Long longValueOf(long v)
     {
-        return Long.valueOf(v);
+        return Longs.valueOf(v);
     }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/bc/BCObjectIdentifiers.java b/bcprov/src/main/java/org/bouncycastle/asn1/bc/BCObjectIdentifiers.java
index 31b14f2..2189edc 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/bc/BCObjectIdentifiers.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/bc/BCObjectIdentifiers.java
@@ -3,74 +3,102 @@
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
 
 /**
- *  Object Identifiers belonging to iso.org.dod.internet.private.enterprise.legion-of-the-bouncy-castle (1.3.6.1.4.1.22554)
+ * Object Identifiers belonging to iso.org.dod.internet.private.enterprise.legion-of-the-bouncy-castle (1.3.6.1.4.1.22554)
  */
 public interface BCObjectIdentifiers
 {
     /**
-     *  iso.org.dod.internet.private.enterprise.legion-of-the-bouncy-castle
-     *<p>
-     *  1.3.6.1.4.1.22554
+     * iso.org.dod.internet.private.enterprise.legion-of-the-bouncy-castle
+     * <p>
+     * 1.3.6.1.4.1.22554
      */
-    public static final ASN1ObjectIdentifier bc = new ASN1ObjectIdentifier("1.3.6.1.4.1.22554");
+    ASN1ObjectIdentifier bc = new ASN1ObjectIdentifier("1.3.6.1.4.1.22554");
 
     /**
      * pbe(1) algorithms
      * <p>
      * 1.3.6.1.4.1.22554.1
      */
-    public static final ASN1ObjectIdentifier bc_pbe        = bc.branch("1");
+    ASN1ObjectIdentifier bc_pbe = bc.branch("1");
 
     /**
      * SHA-1(1)
      * <p>
      * 1.3.6.1.4.1.22554.1.1
      */
-    public static final ASN1ObjectIdentifier bc_pbe_sha1   = bc_pbe.branch("1");
+    ASN1ObjectIdentifier bc_pbe_sha1 = bc_pbe.branch("1");
 
-    /** SHA-2.SHA-256; 1.3.6.1.4.1.22554.1.2.1 */
-    public static final ASN1ObjectIdentifier bc_pbe_sha256 = bc_pbe.branch("2.1");
-    /** SHA-2.SHA-384; 1.3.6.1.4.1.22554.1.2.2 */
-    public static final ASN1ObjectIdentifier bc_pbe_sha384 = bc_pbe.branch("2.2");
-    /** SHA-2.SHA-512; 1.3.6.1.4.1.22554.1.2.3 */
-    public static final ASN1ObjectIdentifier bc_pbe_sha512 = bc_pbe.branch("2.3");
-    /** SHA-2.SHA-224; 1.3.6.1.4.1.22554.1.2.4 */
-    public static final ASN1ObjectIdentifier bc_pbe_sha224 = bc_pbe.branch("2.4");
+    /**
+     * SHA-2.SHA-256; 1.3.6.1.4.1.22554.1.2.1
+     */
+    ASN1ObjectIdentifier bc_pbe_sha256 = bc_pbe.branch("2.1");
+    /**
+     * SHA-2.SHA-384; 1.3.6.1.4.1.22554.1.2.2
+     */
+    ASN1ObjectIdentifier bc_pbe_sha384 = bc_pbe.branch("2.2");
+    /**
+     * SHA-2.SHA-512; 1.3.6.1.4.1.22554.1.2.3
+     */
+    ASN1ObjectIdentifier bc_pbe_sha512 = bc_pbe.branch("2.3");
+    /**
+     * SHA-2.SHA-224; 1.3.6.1.4.1.22554.1.2.4
+     */
+    ASN1ObjectIdentifier bc_pbe_sha224 = bc_pbe.branch("2.4");
 
     /**
      * PKCS-5(1)|PKCS-12(2)
      */
-    /** SHA-1.PKCS5;  1.3.6.1.4.1.22554.1.1.1 */
-    public static final ASN1ObjectIdentifier bc_pbe_sha1_pkcs5    = bc_pbe_sha1.branch("1");
-    /** SHA-1.PKCS12; 1.3.6.1.4.1.22554.1.1.2 */
-    public static final ASN1ObjectIdentifier bc_pbe_sha1_pkcs12   = bc_pbe_sha1.branch("2");
+    /**
+     * SHA-1.PKCS5;  1.3.6.1.4.1.22554.1.1.1
+     */
+    ASN1ObjectIdentifier bc_pbe_sha1_pkcs5 = bc_pbe_sha1.branch("1");
+    /**
+     * SHA-1.PKCS12; 1.3.6.1.4.1.22554.1.1.2
+     */
+    ASN1ObjectIdentifier bc_pbe_sha1_pkcs12 = bc_pbe_sha1.branch("2");
 
-    /** SHA-256.PKCS12; 1.3.6.1.4.1.22554.1.2.1.1 */
-    public static final ASN1ObjectIdentifier bc_pbe_sha256_pkcs5  = bc_pbe_sha256.branch("1");
-    /** SHA-256.PKCS12; 1.3.6.1.4.1.22554.1.2.1.2 */
-    public static final ASN1ObjectIdentifier bc_pbe_sha256_pkcs12 = bc_pbe_sha256.branch("2");
+    /**
+     * SHA-256.PKCS5; 1.3.6.1.4.1.22554.1.2.1.1
+     */
+    ASN1ObjectIdentifier bc_pbe_sha256_pkcs5 = bc_pbe_sha256.branch("1");
+    /**
+     * SHA-256.PKCS12; 1.3.6.1.4.1.22554.1.2.1.2
+     */
+    ASN1ObjectIdentifier bc_pbe_sha256_pkcs12 = bc_pbe_sha256.branch("2");
 
     /**
      * AES(1) . (CBC-128(2)|CBC-192(22)|CBC-256(42))
      */
-    /** 1.3.6.1.4.1.22554.1.1.2.1.2 */
-    public static final ASN1ObjectIdentifier bc_pbe_sha1_pkcs12_aes128_cbc   = bc_pbe_sha1_pkcs12.branch("1.2");
-    /** 1.3.6.1.4.1.22554.1.1.2.1.22 */
-    public static final ASN1ObjectIdentifier bc_pbe_sha1_pkcs12_aes192_cbc   = bc_pbe_sha1_pkcs12.branch("1.22");
-    /** 1.3.6.1.4.1.22554.1.1.2.1.42 */
-    public static final ASN1ObjectIdentifier bc_pbe_sha1_pkcs12_aes256_cbc   = bc_pbe_sha1_pkcs12.branch("1.42");
+    /**
+     * 1.3.6.1.4.1.22554.1.1.2.1.2
+     */
+    ASN1ObjectIdentifier bc_pbe_sha1_pkcs12_aes128_cbc = bc_pbe_sha1_pkcs12.branch("1.2");
+    /**
+     * 1.3.6.1.4.1.22554.1.1.2.1.22
+     */
+    ASN1ObjectIdentifier bc_pbe_sha1_pkcs12_aes192_cbc = bc_pbe_sha1_pkcs12.branch("1.22");
+    /**
+     * 1.3.6.1.4.1.22554.1.1.2.1.42
+     */
+    ASN1ObjectIdentifier bc_pbe_sha1_pkcs12_aes256_cbc = bc_pbe_sha1_pkcs12.branch("1.42");
 
-    /** 1.3.6.1.4.1.22554.1.1.2.2.2 */
-    public static final ASN1ObjectIdentifier bc_pbe_sha256_pkcs12_aes128_cbc = bc_pbe_sha256_pkcs12.branch("1.2");
-    /** 1.3.6.1.4.1.22554.1.1.2.2.22 */
-    public static final ASN1ObjectIdentifier bc_pbe_sha256_pkcs12_aes192_cbc = bc_pbe_sha256_pkcs12.branch("1.22");
-    /** 1.3.6.1.4.1.22554.1.1.2.2.42 */
-    public static final ASN1ObjectIdentifier bc_pbe_sha256_pkcs12_aes256_cbc = bc_pbe_sha256_pkcs12.branch("1.42");
+    /**
+     * 1.3.6.1.4.1.22554.1.1.2.2.2
+     */
+    ASN1ObjectIdentifier bc_pbe_sha256_pkcs12_aes128_cbc = bc_pbe_sha256_pkcs12.branch("1.2");
+    /**
+     * 1.3.6.1.4.1.22554.1.1.2.2.22
+     */
+    ASN1ObjectIdentifier bc_pbe_sha256_pkcs12_aes192_cbc = bc_pbe_sha256_pkcs12.branch("1.22");
+    /**
+     * 1.3.6.1.4.1.22554.1.1.2.2.42
+     */
+    ASN1ObjectIdentifier bc_pbe_sha256_pkcs12_aes256_cbc = bc_pbe_sha256_pkcs12.branch("1.42");
 
     /**
      * signature(2) algorithms
      */
-    public static final ASN1ObjectIdentifier bc_sig        = bc.branch("2");
+    ASN1ObjectIdentifier bc_sig = bc.branch("2");
 
     // BEGIN Android-removed: Unsupported algorithms
     /*
@@ -81,101 +109,329 @@
     public static final ASN1ObjectIdentifier sphincs256_with_BLAKE512        = sphincs256.branch("1");
     public static final ASN1ObjectIdentifier sphincs256_with_SHA512          = sphincs256.branch("2");
     public static final ASN1ObjectIdentifier sphincs256_with_SHA3_512        = sphincs256.branch("3");
+    ASN1ObjectIdentifier sphincs256 = bc_sig.branch("1");
+    ASN1ObjectIdentifier sphincs256_with_BLAKE512 = sphincs256.branch("1");
+    ASN1ObjectIdentifier sphincs256_with_SHA512 = sphincs256.branch("2");
+    ASN1ObjectIdentifier sphincs256_with_SHA3_512 = sphincs256.branch("3");
+     */
 
     /**
      * XMSS
      */
-    public static final ASN1ObjectIdentifier xmss = bc_sig.branch("2");
-    public static final ASN1ObjectIdentifier xmss_SHA256ph = xmss.branch("1");
-    public static final ASN1ObjectIdentifier xmss_SHA512ph = xmss.branch("2");
-    public static final ASN1ObjectIdentifier xmss_SHAKE128ph = xmss.branch("3");
-    public static final ASN1ObjectIdentifier xmss_SHAKE256ph = xmss.branch("4");
-    public static final ASN1ObjectIdentifier xmss_SHA256 = xmss.branch("5");
-    public static final ASN1ObjectIdentifier xmss_SHA512 = xmss.branch("6");
-    public static final ASN1ObjectIdentifier xmss_SHAKE128 = xmss.branch("7");
-    public static final ASN1ObjectIdentifier xmss_SHAKE256 = xmss.branch("8");
+    ASN1ObjectIdentifier xmss = bc_sig.branch("2");
+    ASN1ObjectIdentifier xmss_SHA256ph = xmss.branch("1");
+    ASN1ObjectIdentifier xmss_SHA512ph = xmss.branch("2");
+    ASN1ObjectIdentifier xmss_SHAKE128_512ph = xmss.branch("3");
+    ASN1ObjectIdentifier xmss_SHAKE256_1024ph = xmss.branch("4");
+    ASN1ObjectIdentifier xmss_SHA256 = xmss.branch("5");
+    ASN1ObjectIdentifier xmss_SHA512 = xmss.branch("6");
+    ASN1ObjectIdentifier xmss_SHAKE128 = xmss.branch("7");
+    ASN1ObjectIdentifier xmss_SHAKE256 = xmss.branch("8");
+    ASN1ObjectIdentifier xmss_SHAKE128ph = xmss.branch("9");
+    ASN1ObjectIdentifier xmss_SHAKE256ph = xmss.branch("10");
 
     /**
      * XMSS^MT
      */
-    public static final ASN1ObjectIdentifier xmss_mt = bc_sig.branch("3");
-    public static final ASN1ObjectIdentifier xmss_mt_SHA256ph = xmss_mt.branch("1");
-    public static final ASN1ObjectIdentifier xmss_mt_SHA512ph = xmss_mt.branch("2");
-    public static final ASN1ObjectIdentifier xmss_mt_SHAKE128ph = xmss_mt.branch("3");
-    public static final ASN1ObjectIdentifier xmss_mt_SHAKE256ph = xmss_mt.branch("4");
-    public static final ASN1ObjectIdentifier xmss_mt_SHA256 = xmss_mt.branch("5");
-    public static final ASN1ObjectIdentifier xmss_mt_SHA512 = xmss_mt.branch("6");
-    public static final ASN1ObjectIdentifier xmss_mt_SHAKE128 = xmss_mt.branch("7");
-    public static final ASN1ObjectIdentifier xmss_mt_SHAKE256 = xmss_mt.branch("8");
-
-    // old OIDs.
-    /**
-     * @deprecated use xmss_SHA256ph
-     */
-    public static final ASN1ObjectIdentifier xmss_with_SHA256          = xmss_SHA256ph;
-    /**
-     * @deprecated use xmss_SHA512ph
-     */
-    public static final ASN1ObjectIdentifier xmss_with_SHA512 = xmss_SHA512ph;
-    /**
-     * @deprecated use xmss_SHAKE128ph
-     */
-    public static final ASN1ObjectIdentifier xmss_with_SHAKE128 = xmss_SHAKE128ph;
-    /**
-     * @deprecated use xmss_SHAKE256ph
-     */
-    public static final ASN1ObjectIdentifier xmss_with_SHAKE256        = xmss_SHAKE256ph;
-
-    /**
-     * @deprecated use xmss_mt_SHA256ph
-     */
-    public static final ASN1ObjectIdentifier xmss_mt_with_SHA256          = xmss_mt_SHA256ph;
-    /**
-     * @deprecated use xmss_mt_SHA512ph
-     */
-    public static final ASN1ObjectIdentifier xmss_mt_with_SHA512          = xmss_mt_SHA512ph;
-    /**
-     * @deprecated use xmss_mt_SHAKE128ph
-     */
-    public static final ASN1ObjectIdentifier xmss_mt_with_SHAKE128        = xmss_mt_SHAKE128;
-    /**
-     * @deprecated use xmss_mt_SHAKE256ph
-     */
-    public static final ASN1ObjectIdentifier xmss_mt_with_SHAKE256        = xmss_mt_SHAKE256;
+    ASN1ObjectIdentifier xmss_mt = bc_sig.branch("3");
+    ASN1ObjectIdentifier xmss_mt_SHA256ph = xmss_mt.branch("1");
+    ASN1ObjectIdentifier xmss_mt_SHA512ph = xmss_mt.branch("2");
+    ASN1ObjectIdentifier xmss_mt_SHAKE128_512ph = xmss_mt.branch("3");
+    ASN1ObjectIdentifier xmss_mt_SHAKE256_1024ph = xmss_mt.branch("4");
+    ASN1ObjectIdentifier xmss_mt_SHA256 = xmss_mt.branch("5");
+    ASN1ObjectIdentifier xmss_mt_SHA512 = xmss_mt.branch("6");
+    ASN1ObjectIdentifier xmss_mt_SHAKE128 = xmss_mt.branch("7");
+    ASN1ObjectIdentifier xmss_mt_SHAKE256 = xmss_mt.branch("8");
+    ASN1ObjectIdentifier xmss_mt_SHAKE128ph = xmss_mt.branch("9");
+    ASN1ObjectIdentifier xmss_mt_SHAKE256ph = xmss_mt.branch("10");
 
     /**
      * qTESLA
      */
-    public static final ASN1ObjectIdentifier qTESLA = bc_sig.branch("4");
+    ASN1ObjectIdentifier qTESLA = bc_sig.branch("4");
 
-    public static final ASN1ObjectIdentifier qTESLA_Rnd1_I = qTESLA.branch("1");
-    public static final ASN1ObjectIdentifier qTESLA_Rnd1_III_size = qTESLA.branch("2");
-    public static final ASN1ObjectIdentifier qTESLA_Rnd1_III_speed = qTESLA.branch("3");
-    public static final ASN1ObjectIdentifier qTESLA_Rnd1_p_I = qTESLA.branch("4");
-    public static final ASN1ObjectIdentifier qTESLA_Rnd1_p_III = qTESLA.branch("5");
+    ASN1ObjectIdentifier qTESLA_Rnd1_I = qTESLA.branch("1");
+    ASN1ObjectIdentifier qTESLA_Rnd1_III_size = qTESLA.branch("2");
+    ASN1ObjectIdentifier qTESLA_Rnd1_III_speed = qTESLA.branch("3");
+    ASN1ObjectIdentifier qTESLA_Rnd1_p_I = qTESLA.branch("4");
+    ASN1ObjectIdentifier qTESLA_Rnd1_p_III = qTESLA.branch("5");
 
 
-    public static final ASN1ObjectIdentifier qTESLA_p_I = qTESLA.branch("11");
-    public static final ASN1ObjectIdentifier qTESLA_p_III = qTESLA.branch("12");
+    ASN1ObjectIdentifier qTESLA_p_I = qTESLA.branch("11");
+    ASN1ObjectIdentifier qTESLA_p_III = qTESLA.branch("12");
+
+    /**
+     * SPHINCS+
+     */
+    ASN1ObjectIdentifier sphincsPlus = bc_sig.branch("5");
+    ASN1ObjectIdentifier sphincsPlus_sha2_128s_r3 = sphincsPlus.branch("1");
+    ASN1ObjectIdentifier sphincsPlus_sha2_128f_r3 = sphincsPlus.branch("2");
+    ASN1ObjectIdentifier sphincsPlus_shake_128s_r3 = sphincsPlus.branch("3");
+    ASN1ObjectIdentifier sphincsPlus_shake_128f_r3 = sphincsPlus.branch("4");
+    ASN1ObjectIdentifier sphincsPlus_haraka_128s_r3 = sphincsPlus.branch("5");
+    ASN1ObjectIdentifier sphincsPlus_haraka_128f_r3 = sphincsPlus.branch("6");
+
+    ASN1ObjectIdentifier sphincsPlus_sha2_192s_r3 = sphincsPlus.branch("7");
+    ASN1ObjectIdentifier sphincsPlus_sha2_192f_r3 = sphincsPlus.branch("8");
+    ASN1ObjectIdentifier sphincsPlus_shake_192s_r3 = sphincsPlus.branch("9");
+    ASN1ObjectIdentifier sphincsPlus_shake_192f_r3 = sphincsPlus.branch("10");
+    ASN1ObjectIdentifier sphincsPlus_haraka_192s_r3 = sphincsPlus.branch("11");
+    ASN1ObjectIdentifier sphincsPlus_haraka_192f_r3 = sphincsPlus.branch("12");
+
+    ASN1ObjectIdentifier sphincsPlus_sha2_256s_r3 = sphincsPlus.branch("13");
+    ASN1ObjectIdentifier sphincsPlus_sha2_256f_r3 = sphincsPlus.branch("14");
+    ASN1ObjectIdentifier sphincsPlus_shake_256s_r3 = sphincsPlus.branch("15");
+    ASN1ObjectIdentifier sphincsPlus_shake_256f_r3 = sphincsPlus.branch("16");
+    ASN1ObjectIdentifier sphincsPlus_haraka_256s_r3 = sphincsPlus.branch("17");
+    ASN1ObjectIdentifier sphincsPlus_haraka_256f_r3 = sphincsPlus.branch("18");
+
+    ASN1ObjectIdentifier sphincsPlus_sha2_128s_r3_simple = sphincsPlus.branch("19");
+    ASN1ObjectIdentifier sphincsPlus_sha2_128f_r3_simple = sphincsPlus.branch("20");
+    ASN1ObjectIdentifier sphincsPlus_shake_128s_r3_simple = sphincsPlus.branch("21");
+    ASN1ObjectIdentifier sphincsPlus_shake_128f_r3_simple = sphincsPlus.branch("22");
+    ASN1ObjectIdentifier sphincsPlus_haraka_128s_r3_simple = sphincsPlus.branch("23");
+    ASN1ObjectIdentifier sphincsPlus_haraka_128f_r3_simple = sphincsPlus.branch("24");
+
+    ASN1ObjectIdentifier sphincsPlus_sha2_192s_r3_simple = sphincsPlus.branch("25");
+    ASN1ObjectIdentifier sphincsPlus_sha2_192f_r3_simple = sphincsPlus.branch("26");
+    ASN1ObjectIdentifier sphincsPlus_shake_192s_r3_simple = sphincsPlus.branch("27");
+    ASN1ObjectIdentifier sphincsPlus_shake_192f_r3_simple = sphincsPlus.branch("28");
+    ASN1ObjectIdentifier sphincsPlus_haraka_192s_r3_simple = sphincsPlus.branch("29");
+    ASN1ObjectIdentifier sphincsPlus_haraka_192f_r3_simple = sphincsPlus.branch("30");
+
+    ASN1ObjectIdentifier sphincsPlus_sha2_256s_r3_simple = sphincsPlus.branch("31");
+    ASN1ObjectIdentifier sphincsPlus_sha2_256f_r3_simple = sphincsPlus.branch("32");
+    ASN1ObjectIdentifier sphincsPlus_shake_256s_r3_simple = sphincsPlus.branch("33");
+    ASN1ObjectIdentifier sphincsPlus_shake_256f_r3_simple = sphincsPlus.branch("34");
+    ASN1ObjectIdentifier sphincsPlus_haraka_256s_r3_simple = sphincsPlus.branch("35");
+    ASN1ObjectIdentifier sphincsPlus_haraka_256f_r3_simple = sphincsPlus.branch("36");
+
+
+    ASN1ObjectIdentifier sphincsPlus_interop = new ASN1ObjectIdentifier("1.3.9999.6");
+
+    ASN1ObjectIdentifier sphincsPlus_sha2_128f = new ASN1ObjectIdentifier("1.3.9999.6.4.13");
+    ASN1ObjectIdentifier sphincsPlus_sha2_128s = new ASN1ObjectIdentifier("1.3.9999.6.4.16");
+    ASN1ObjectIdentifier sphincsPlus_sha2_192f = new ASN1ObjectIdentifier("1.3.9999.6.5.10");
+    ASN1ObjectIdentifier sphincsPlus_sha2_192s = new ASN1ObjectIdentifier("1.3.9999.6.5.12");
+    ASN1ObjectIdentifier sphincsPlus_sha2_256f = new ASN1ObjectIdentifier("1.3.9999.6.6.10");
+    ASN1ObjectIdentifier sphincsPlus_sha2_256s = new ASN1ObjectIdentifier("1.3.9999.6.6.12");
+
+    ASN1ObjectIdentifier sphincsPlus_shake_128f = new ASN1ObjectIdentifier("1.3.9999.6.7.13");
+    ASN1ObjectIdentifier sphincsPlus_shake_128s = new ASN1ObjectIdentifier("1.3.9999.6.7.16");
+    ASN1ObjectIdentifier sphincsPlus_shake_192f = new ASN1ObjectIdentifier("1.3.9999.6.8.10");
+    ASN1ObjectIdentifier sphincsPlus_shake_192s = new ASN1ObjectIdentifier("1.3.9999.6.8.12");
+    ASN1ObjectIdentifier sphincsPlus_shake_256f = new ASN1ObjectIdentifier("1.3.9999.6.9.10");
+    ASN1ObjectIdentifier sphincsPlus_shake_256s = new ASN1ObjectIdentifier("1.3.9999.6.9.12");
+
+    /**
+     * Picnic
+     */
+    ASN1ObjectIdentifier picnic = bc_sig.branch("6");
+
+    ASN1ObjectIdentifier picnic_key = picnic.branch("1");
+
+    ASN1ObjectIdentifier picnicl1fs = picnic_key.branch("1");
+    ASN1ObjectIdentifier picnicl1ur = picnic_key.branch("2");
+    ASN1ObjectIdentifier picnicl3fs = picnic_key.branch("3");
+    ASN1ObjectIdentifier picnicl3ur = picnic_key.branch("4");
+    ASN1ObjectIdentifier picnicl5fs = picnic_key.branch("5");
+    ASN1ObjectIdentifier picnicl5ur = picnic_key.branch("6");
+    ASN1ObjectIdentifier picnic3l1 = picnic_key.branch("7");
+    ASN1ObjectIdentifier picnic3l3 = picnic_key.branch("8");
+    ASN1ObjectIdentifier picnic3l5 = picnic_key.branch("9");
+    ASN1ObjectIdentifier picnicl1full = picnic_key.branch("10");
+    ASN1ObjectIdentifier picnicl3full = picnic_key.branch("11");
+    ASN1ObjectIdentifier picnicl5full = picnic_key.branch("12");
+
+    ASN1ObjectIdentifier picnic_signature = picnic.branch("2");
+    ASN1ObjectIdentifier picnic_with_sha512 = picnic_signature.branch("1");
+    ASN1ObjectIdentifier picnic_with_shake256 = picnic_signature.branch("2");
+    ASN1ObjectIdentifier picnic_with_sha3_512 = picnic_signature.branch("3");
+
+
+    /*
+     * Falcon
+     */
+    ASN1ObjectIdentifier falcon = bc_sig.branch("7");
+
+    ASN1ObjectIdentifier falcon_512 = new ASN1ObjectIdentifier("1.3.9999.3.6");  // falcon.branch("1");
+    ASN1ObjectIdentifier falcon_1024 =  new ASN1ObjectIdentifier("1.3.9999.3.9"); // falcon.branch("2");
+
+    /*
+     * Dilithium
+     */
+    ASN1ObjectIdentifier dilithium = bc_sig.branch("8");
+
+    // OpenSSL OIDs
+    ASN1ObjectIdentifier dilithium2 = new ASN1ObjectIdentifier("1.3.6.1.4.1.2.267.12.4.4"); // dilithium.branch("1");
+    ASN1ObjectIdentifier dilithium3 = new ASN1ObjectIdentifier("1.3.6.1.4.1.2.267.12.6.5"); // dilithium.branch("2");
+    ASN1ObjectIdentifier dilithium5 = new ASN1ObjectIdentifier("1.3.6.1.4.1.2.267.12.8.7"); // dilithium.branch("3");
+    ASN1ObjectIdentifier dilithium2_aes = new ASN1ObjectIdentifier("1.3.6.1.4.1.2.267.11.4.4"); // dilithium.branch("4");
+    ASN1ObjectIdentifier dilithium3_aes = new ASN1ObjectIdentifier("1.3.6.1.4.1.2.267.11.6.5"); // dilithium.branch("5");
+    ASN1ObjectIdentifier dilithium5_aes = new ASN1ObjectIdentifier("1.3.6.1.4.1.2.267.11.8.7"); // dilithium.branch("6");
+
+    /*
+     * Rainbow
+     */
+    ASN1ObjectIdentifier rainbow = bc_sig.branch("9");
+
+    ASN1ObjectIdentifier rainbow_III_classic = rainbow.branch("1");
+    ASN1ObjectIdentifier rainbow_III_circumzenithal = rainbow.branch("2");
+    ASN1ObjectIdentifier rainbow_III_compressed = rainbow.branch("3");
+    ASN1ObjectIdentifier rainbow_V_classic = rainbow.branch("4");
+    ASN1ObjectIdentifier rainbow_V_circumzenithal = rainbow.branch("5");
+    ASN1ObjectIdentifier rainbow_V_compressed = rainbow.branch("6");
 
     /**
      * key_exchange(3) algorithms
-     *
-    public static final ASN1ObjectIdentifier bc_exch = bc.branch("3");
+     */
+    ASN1ObjectIdentifier bc_exch = bc.branch("3");
 
     /**
      * NewHope
-     *
-    public static final ASN1ObjectIdentifier newHope = bc_exch.branch("1");
-    */
-    // END Android-removed: Unsupported algorithms
+     */
+    ASN1ObjectIdentifier newHope = bc_exch.branch("1");
 
     /**
-     * X.509 extension(4) values
+     * X.509 extension/certificate types
      * <p>
      * 1.3.6.1.4.1.22554.4
      */
-    public static final ASN1ObjectIdentifier bc_ext        = bc.branch("4");
+    ASN1ObjectIdentifier bc_ext = bc.branch("4");
 
-    public static final ASN1ObjectIdentifier linkedCertificate = bc_ext.branch("1");
+    ASN1ObjectIdentifier linkedCertificate = bc_ext.branch("1");
+    ASN1ObjectIdentifier external_value = bc_ext.branch("2");
+
+    /**
+     * KEM(5) algorithms
+     */
+    ASN1ObjectIdentifier bc_kem = bc.branch("5");
+
+    /**
+     * Classic McEliece
+     */
+    ASN1ObjectIdentifier pqc_kem_mceliece = bc_kem.branch("1");
+
+    ASN1ObjectIdentifier mceliece348864_r3 = pqc_kem_mceliece.branch("1");
+    ASN1ObjectIdentifier mceliece348864f_r3 = pqc_kem_mceliece.branch("2");
+    ASN1ObjectIdentifier mceliece460896_r3 = pqc_kem_mceliece.branch("3");
+    ASN1ObjectIdentifier mceliece460896f_r3 = pqc_kem_mceliece.branch("4");
+    ASN1ObjectIdentifier mceliece6688128_r3 = pqc_kem_mceliece.branch("5");
+    ASN1ObjectIdentifier mceliece6688128f_r3 = pqc_kem_mceliece.branch("6");
+    ASN1ObjectIdentifier mceliece6960119_r3 = pqc_kem_mceliece.branch("7");
+    ASN1ObjectIdentifier mceliece6960119f_r3 = pqc_kem_mceliece.branch("8");
+    ASN1ObjectIdentifier mceliece8192128_r3 = pqc_kem_mceliece.branch("9");
+    ASN1ObjectIdentifier mceliece8192128f_r3 = pqc_kem_mceliece.branch("10");
+
+
+    /**
+     * Frodo
+     */
+    ASN1ObjectIdentifier pqc_kem_frodo = bc_kem.branch("2");
+
+    ASN1ObjectIdentifier frodokem640aes = pqc_kem_frodo.branch("1");
+    ASN1ObjectIdentifier frodokem640shake = pqc_kem_frodo.branch("2");
+    ASN1ObjectIdentifier frodokem976aes = pqc_kem_frodo.branch("3");
+    ASN1ObjectIdentifier frodokem976shake = pqc_kem_frodo.branch("4");
+    ASN1ObjectIdentifier frodokem1344aes = pqc_kem_frodo.branch("5");
+    ASN1ObjectIdentifier frodokem1344shake = pqc_kem_frodo.branch("6");
+
+    /**
+     * SABER
+     */
+    ASN1ObjectIdentifier pqc_kem_saber = bc_kem.branch("3");
+
+    ASN1ObjectIdentifier lightsaberkem128r3 = pqc_kem_saber.branch("1");
+    ASN1ObjectIdentifier saberkem128r3 = pqc_kem_saber.branch("2");
+    ASN1ObjectIdentifier firesaberkem128r3 = pqc_kem_saber.branch("3");
+    ASN1ObjectIdentifier lightsaberkem192r3 = pqc_kem_saber.branch("4");
+    ASN1ObjectIdentifier saberkem192r3 = pqc_kem_saber.branch("5");
+    ASN1ObjectIdentifier firesaberkem192r3 = pqc_kem_saber.branch("6");
+    ASN1ObjectIdentifier lightsaberkem256r3 = pqc_kem_saber.branch("7");
+    ASN1ObjectIdentifier saberkem256r3 = pqc_kem_saber.branch("8");
+    ASN1ObjectIdentifier firesaberkem256r3 = pqc_kem_saber.branch("9");
+    ASN1ObjectIdentifier ulightsaberkemr3 = pqc_kem_saber.branch("10");
+    ASN1ObjectIdentifier usaberkemr3 = pqc_kem_saber.branch("11");
+    ASN1ObjectIdentifier ufiresaberkemr3 = pqc_kem_saber.branch("12");
+    ASN1ObjectIdentifier lightsaberkem90sr3 = pqc_kem_saber.branch("13");
+    ASN1ObjectIdentifier saberkem90sr3 = pqc_kem_saber.branch("14");
+    ASN1ObjectIdentifier firesaberkem90sr3 = pqc_kem_saber.branch("15");
+    ASN1ObjectIdentifier ulightsaberkem90sr3 = pqc_kem_saber.branch("16");
+    ASN1ObjectIdentifier usaberkem90sr3 = pqc_kem_saber.branch("17");
+    ASN1ObjectIdentifier ufiresaberkem90sr3 = pqc_kem_saber.branch("18");
+
+    /**
+     * SIKE
+     */
+    ASN1ObjectIdentifier pqc_kem_sike = bc_kem.branch("4");
+
+    ASN1ObjectIdentifier sikep434 = pqc_kem_sike.branch("1");
+    ASN1ObjectIdentifier sikep503 = pqc_kem_sike.branch("2");
+    ASN1ObjectIdentifier sikep610 = pqc_kem_sike.branch("3");
+    ASN1ObjectIdentifier sikep751 = pqc_kem_sike.branch("4");
+    ASN1ObjectIdentifier sikep434_compressed = pqc_kem_sike.branch("5");
+    ASN1ObjectIdentifier sikep503_compressed = pqc_kem_sike.branch("6");
+    ASN1ObjectIdentifier sikep610_compressed = pqc_kem_sike.branch("7");
+    ASN1ObjectIdentifier sikep751_compressed = pqc_kem_sike.branch("8");
+
+    /**
+     * NTRU
+     */
+    ASN1ObjectIdentifier pqc_kem_ntru = bc_kem.branch("5");
+
+    ASN1ObjectIdentifier ntruhps2048509 = pqc_kem_ntru.branch("1");
+    ASN1ObjectIdentifier ntruhps2048677 = pqc_kem_ntru.branch("2");
+    ASN1ObjectIdentifier ntruhps4096821 = pqc_kem_ntru.branch("3");
+    ASN1ObjectIdentifier ntruhrss701 = pqc_kem_ntru.branch("4");
+
+    /**
+     * Kyber
+     */
+    ASN1ObjectIdentifier pqc_kem_kyber = bc_kem.branch("6");
+
+    ASN1ObjectIdentifier kyber512 = pqc_kem_kyber.branch("1");
+    ASN1ObjectIdentifier kyber768 = pqc_kem_kyber.branch("2");
+    ASN1ObjectIdentifier kyber1024 = pqc_kem_kyber.branch("3");
+    ASN1ObjectIdentifier kyber512_aes = pqc_kem_kyber.branch("4");
+    ASN1ObjectIdentifier kyber768_aes = pqc_kem_kyber.branch("5");
+    ASN1ObjectIdentifier kyber1024_aes = pqc_kem_kyber.branch("6");
+
+    /**
+     * NTRUPrime
+     */
+    ASN1ObjectIdentifier pqc_kem_ntruprime = bc_kem.branch("7");
+
+    ASN1ObjectIdentifier pqc_kem_ntrulprime = pqc_kem_ntruprime.branch("1");
+    ASN1ObjectIdentifier ntrulpr653 = pqc_kem_ntrulprime.branch("1");
+    ASN1ObjectIdentifier ntrulpr761 = pqc_kem_ntrulprime.branch("2");
+    ASN1ObjectIdentifier ntrulpr857 = pqc_kem_ntrulprime.branch("3");
+    ASN1ObjectIdentifier ntrulpr953 = pqc_kem_ntrulprime.branch("4");
+    ASN1ObjectIdentifier ntrulpr1013 = pqc_kem_ntrulprime.branch("5");
+    ASN1ObjectIdentifier ntrulpr1277 = pqc_kem_ntrulprime.branch("6");
+    
+    ASN1ObjectIdentifier pqc_kem_sntruprime = pqc_kem_ntruprime.branch("2");
+    ASN1ObjectIdentifier sntrup653 = pqc_kem_sntruprime.branch("1");
+    ASN1ObjectIdentifier sntrup761 = pqc_kem_sntruprime.branch("2");
+    ASN1ObjectIdentifier sntrup857 = pqc_kem_sntruprime.branch("3");
+    ASN1ObjectIdentifier sntrup953 = pqc_kem_sntruprime.branch("4");
+    ASN1ObjectIdentifier sntrup1013 = pqc_kem_sntruprime.branch("5");
+    ASN1ObjectIdentifier sntrup1277 = pqc_kem_sntruprime.branch("6");
+    
+    /**
+     * BIKE
+     **/
+    ASN1ObjectIdentifier pqc_kem_bike = bc_kem.branch("8");
+
+    ASN1ObjectIdentifier bike128 = pqc_kem_bike.branch("1");
+    ASN1ObjectIdentifier bike192 = pqc_kem_bike.branch("2");
+    ASN1ObjectIdentifier bike256 = pqc_kem_bike.branch("3");
+
+    /**
+     * HQC
+     **/
+    ASN1ObjectIdentifier pqc_kem_hqc = bc_kem.branch("9");
+
+    ASN1ObjectIdentifier hqc128 = pqc_kem_hqc.branch("1");
+    ASN1ObjectIdentifier hqc192 = pqc_kem_hqc.branch("2");
+    ASN1ObjectIdentifier hqc256 = pqc_kem_hqc.branch("3");
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/bc/ExternalValue.java b/bcprov/src/main/java/org/bouncycastle/asn1/bc/ExternalValue.java
new file mode 100644
index 0000000..9c31888
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/bc/ExternalValue.java
@@ -0,0 +1,117 @@
+package org.bouncycastle.asn1.bc;
+
+import org.bouncycastle.asn1.ASN1BitString;
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERBitString;
+import org.bouncycastle.asn1.DEROctetString;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.asn1.x509.GeneralName;
+import org.bouncycastle.asn1.x509.GeneralNames;
+import org.bouncycastle.util.Arrays;
+
+/**
+ * Based on External Keys And Signatures For Use In Internet PKI
+ * draft-ounsworth-pq-external-pubkeys-00
+ * <pre>
+ *  ExternalValue ::= SEQUENCE {
+ *      location GeneralNames,    # MUST refer to a DER encoded SubjectPublicKeyInfo/Signature  (may be Base64)
+ *      hashAlg AlgorithmIdentifier,
+ *      hashVal OCTET STRING }
+ * </pre>
+ */
+public class ExternalValue
+    extends ASN1Object
+{
+    private final GeneralNames location;
+    private final AlgorithmIdentifier hashAlg;
+    private final byte[] hashValue;
+
+    public ExternalValue(GeneralName location, AlgorithmIdentifier hashAlg, byte[] hashVal)
+    {
+        this.location = new GeneralNames(location);
+        this.hashAlg = hashAlg;
+        this.hashValue = Arrays.clone(hashVal);
+    }
+
+    private ExternalValue(ASN1Sequence seq)
+    {
+        if (seq.size() == 3)
+        {
+            location = GeneralNames.getInstance(seq.getObjectAt(0));
+            hashAlg = AlgorithmIdentifier.getInstance(seq.getObjectAt(1));
+            if (seq.getObjectAt(2) instanceof ASN1BitString)    // legacy implementation on 2021 draft
+            {
+                hashValue = ASN1BitString.getInstance(seq.getObjectAt(2)).getOctets();
+            }
+            else
+            {
+                hashValue = ASN1OctetString.getInstance(seq.getObjectAt(2)).getOctets();
+            }
+        }
+        else
+        {
+            throw new IllegalArgumentException("unknown sequence");
+        }
+    }
+
+    public static ExternalValue getInstance(Object o)
+    {
+        if (o instanceof ExternalValue)
+        {
+            return (ExternalValue)o;
+        }
+        else if (o != null)
+        {
+            return new ExternalValue(ASN1Sequence.getInstance(o));
+        }
+
+        return null;
+    }
+
+    public GeneralName getLocation()
+    {
+        return location.getNames()[0];
+    }
+
+    public GeneralName[] getLocations()
+    {
+        return location.getNames();
+    }
+
+    public AlgorithmIdentifier getHashAlg()
+    {
+        return hashAlg;
+    }
+
+    public byte[] getHashValue()
+    {
+        return Arrays.clone(hashValue);
+    }
+
+    /**
+     * Get the hash value as a BIT STRING.
+     *
+     * @return the hash value as a BIT STRING
+     * @deprecated use getHash(), the internal encoding is now an OCTET STRING
+     */
+    public ASN1BitString getHashVal()
+    {
+        return new DERBitString(hashValue);
+    }
+
+    public ASN1Primitive toASN1Primitive()
+    {
+        ASN1EncodableVector v = new ASN1EncodableVector();
+
+        v.add(location);
+        v.add(hashAlg);
+        v.add(new DEROctetString(hashValue));
+
+        return new DERSequence(v);
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/bc/LinkedCertificate.java b/bcprov/src/main/java/org/bouncycastle/asn1/bc/LinkedCertificate.java
new file mode 100644
index 0000000..d812087
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/bc/LinkedCertificate.java
@@ -0,0 +1,126 @@
+package org.bouncycastle.asn1.bc;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.DERTaggedObject;
+import org.bouncycastle.asn1.x500.X500Name;
+import org.bouncycastle.asn1.x509.DigestInfo;
+import org.bouncycastle.asn1.x509.GeneralName;
+import org.bouncycastle.asn1.x509.GeneralNames;
+
+/**
+ * Extension to tie an alternate certificate to the containing certificate.
+ * <pre>
+ *     LinkedCertificate := SEQUENCE {
+ *         digest        DigestInfo,                   -- digest of PQC certificate
+ *         certLocation  GeneralName,                  -- location of PQC certificate
+ *         certIssuer    [0] Name OPTIONAL,            -- issuer of PQC cert (if different from current certificate)
+ *         cACerts       [1] GeneralNames OPTIONAL,    -- CA certificates for PQC cert (one of more locations)
+ * }
+ * </pre>
+ */
+public class LinkedCertificate
+    extends ASN1Object
+{
+    private final DigestInfo digest;
+    private final GeneralName certLocation;
+
+    private X500Name certIssuer;
+    private GeneralNames cACerts;
+
+    public LinkedCertificate(DigestInfo digest, GeneralName certLocation)
+    {
+        this(digest, certLocation, null, null);
+    }
+
+    public LinkedCertificate(DigestInfo digest, GeneralName certLocation, X500Name certIssuer, GeneralNames cACerts)
+    {
+        this.digest = digest;
+        this.certLocation = certLocation;
+        this.certIssuer = certIssuer;
+        this.cACerts = cACerts;
+    }
+
+    private LinkedCertificate(ASN1Sequence seq)
+    {
+        this.digest = DigestInfo.getInstance(seq.getObjectAt(0));
+        this.certLocation = GeneralName.getInstance(seq.getObjectAt(1));
+
+        if (seq.size() > 2)
+        {
+            for (int i = 2; i != seq.size(); i++)
+            {
+                ASN1TaggedObject tagged =  ASN1TaggedObject.getInstance(seq.getObjectAt(i));
+
+                switch (tagged.getTagNo())
+                {
+                case 0:
+                    certIssuer = X500Name.getInstance(tagged, false);
+                    break;
+                case 1:
+                    cACerts = GeneralNames.getInstance(tagged, false);
+                    break;
+                default:
+                    throw new IllegalArgumentException("unknown tag in tagged field");
+                }
+            }
+        }
+    }
+
+    public static LinkedCertificate getInstance(Object o)
+    {
+        if (o instanceof LinkedCertificate)
+        {
+            return (LinkedCertificate)o;
+        }
+        else if (o != null)
+        {
+            return new LinkedCertificate(ASN1Sequence.getInstance(o));
+        }
+
+        return null;
+    }
+
+    public DigestInfo getDigest()
+    {
+        return digest;
+    }
+
+    public GeneralName getCertLocation()
+    {
+        return certLocation;
+    }
+
+    public X500Name getCertIssuer()
+    {
+        return certIssuer;
+    }
+
+    public GeneralNames getCACerts()
+    {
+        return cACerts;
+    }
+
+    public ASN1Primitive toASN1Primitive()
+    {
+        ASN1EncodableVector v = new ASN1EncodableVector(4);
+
+        v.add(digest);
+        v.add(certLocation);
+
+        if (certIssuer != null)
+        {
+            v.add(new DERTaggedObject(false, 0, certIssuer));
+        }
+        if (cACerts != null)
+        {
+            v.add(new DERTaggedObject(false, 1, cACerts));
+        }
+
+        return new DERSequence(v);
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cms/ContentInfo.java b/bcprov/src/main/java/org/bouncycastle/asn1/cms/ContentInfo.java
index be197d2..ffd3569 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/cms/ContentInfo.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/cms/ContentInfo.java
@@ -128,3 +128,4 @@
         return new BERSequence(v);
     }
 }
+
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/eac/EACObjectIdentifiers.java b/bcprov/src/main/java/org/bouncycastle/asn1/eac/EACObjectIdentifiers.java
deleted file mode 100644
index 35e65f5..0000000
--- a/bcprov/src/main/java/org/bouncycastle/asn1/eac/EACObjectIdentifiers.java
+++ /dev/null
@@ -1,110 +0,0 @@
-package org.bouncycastle.asn1.eac;
-
-import org.bouncycastle.asn1.ASN1ObjectIdentifier;
-
-/**
- * German Federal Office for Information Security
- * (Bundesamt f&uuml;r Sicherheit in der Informationstechnik)
- * <a href="https://www.bsi.bund.de/">https://www.bsi.bund.de/</a>
- * <p>
- * <a href="https://www.bsi.bund.de/EN/Publications/TechnicalGuidelines/TR03110/BSITR03110.html">BSI TR-03110</a>
- * Technical Guideline Advanced Security Mechanisms for Machine Readable Travel Documents
- * <p>
- * <a href="https://www.bsi.bund.de/SharedDocs/Downloads/EN/BSI/Publications/TechGuidelines/TR03110/TR-03110_v2.1_P3pdf.pdf">
- * Technical Guideline TR-03110-3</a>
- * Advanced Security Mechanisms for Machine Readable Travel Documents;
- * Part 3: Common Specifications.
- */
-public interface EACObjectIdentifiers
-{
-    /**
-     * <pre>
-     * bsi-de OBJECT IDENTIFIER ::= {
-     *     itu-t(0) identified-organization(4) etsi(0)
-     *     reserved(127) etsi-identified-organization(0) 7
-     * }
-     * </pre>
-     * OID: 0.4.0.127.0.7
-     */
-    static final ASN1ObjectIdentifier    bsi_de      = new ASN1ObjectIdentifier("0.4.0.127.0.7");
-
-    /**
-     * <pre>
-     * id-PK OBJECT IDENTIFIER ::= {
-     *     bsi-de protocols(2) smartcard(2) 1
-     * }
-     * </pre>
-     * OID: 0.4.0.127.0.7.2.2.1
-     */
-    static final ASN1ObjectIdentifier    id_PK      = bsi_de.branch("2.2.1");
-
-    /** OID: 0.4.0.127.0.7.2.2.1.1 */
-    static final ASN1ObjectIdentifier    id_PK_DH   = id_PK.branch("1");
-    /** OID: 0.4.0.127.0.7.2.2.1.2 */
-    static final ASN1ObjectIdentifier    id_PK_ECDH = id_PK.branch("2");
-
-    /**
-     * <pre>
-     * id-CA OBJECT IDENTIFIER ::= {
-     *     bsi-de protocols(2) smartcard(2) 3
-     * }
-     * </pre>
-     * OID: 0.4.0.127.0.7.2.2.3
-     */
-    static final ASN1ObjectIdentifier    id_CA                   = bsi_de.branch("2.2.3");
-    /** OID: 0.4.0.127.0.7.2.2.3.1 */
-    static final ASN1ObjectIdentifier    id_CA_DH                = id_CA.branch("1");
-    /** OID: 0.4.0.127.0.7.2.2.3.1.1 */
-    static final ASN1ObjectIdentifier    id_CA_DH_3DES_CBC_CBC   = id_CA_DH.branch("1");
-    /** OID: 0.4.0.127.0.7.2.2.3.2 */
-    static final ASN1ObjectIdentifier    id_CA_ECDH              = id_CA.branch("2");
-    /** OID: 0.4.0.127.0.7.2.2.3.2.1 */
-    static final ASN1ObjectIdentifier    id_CA_ECDH_3DES_CBC_CBC = id_CA_ECDH.branch("1");
-
-    /**
-     * <pre>
-     * id-TA OBJECT IDENTIFIER ::= {
-     *     bsi-de protocols(2) smartcard(2) 2
-     * }
-     * </pre>
-     * OID: 0.4.0.127.0.7.2.2.2
-     */
-    static final ASN1ObjectIdentifier    id_TA = bsi_de.branch("2.2.2");
-
-    /** OID: 0.4.0.127.0.7.2.2.2.1 */
-    static final ASN1ObjectIdentifier    id_TA_RSA              = id_TA.branch("1");
-    /** OID: 0.4.0.127.0.7.2.2.2.1.1 */
-    static final ASN1ObjectIdentifier    id_TA_RSA_v1_5_SHA_1   = id_TA_RSA.branch("1");
-    /** OID: 0.4.0.127.0.7.2.2.2.1.2 */
-    static final ASN1ObjectIdentifier    id_TA_RSA_v1_5_SHA_256 = id_TA_RSA.branch("2");
-    /** OID: 0.4.0.127.0.7.2.2.2.1.3 */
-    static final ASN1ObjectIdentifier    id_TA_RSA_PSS_SHA_1    = id_TA_RSA.branch("3");
-    /** OID: 0.4.0.127.0.7.2.2.2.1.4 */
-    static final ASN1ObjectIdentifier    id_TA_RSA_PSS_SHA_256  = id_TA_RSA.branch("4");
-    /** OID: 0.4.0.127.0.7.2.2.2.1.5 */
-    static final ASN1ObjectIdentifier    id_TA_RSA_v1_5_SHA_512 = id_TA_RSA.branch("5");
-    /** OID: 0.4.0.127.0.7.2.2.2.1.6 */
-    static final ASN1ObjectIdentifier    id_TA_RSA_PSS_SHA_512  = id_TA_RSA.branch("6");
-    /** OID: 0.4.0.127.0.7.2.2.2.2 */
-    static final ASN1ObjectIdentifier    id_TA_ECDSA            = id_TA.branch("2");
-    /** OID: 0.4.0.127.0.7.2.2.2.2.1 */
-    static final ASN1ObjectIdentifier    id_TA_ECDSA_SHA_1      = id_TA_ECDSA.branch("1");
-    /** OID: 0.4.0.127.0.7.2.2.2.2.2 */
-    static final ASN1ObjectIdentifier    id_TA_ECDSA_SHA_224    = id_TA_ECDSA.branch("2");
-    /** OID: 0.4.0.127.0.7.2.2.2.2.3 */
-    static final ASN1ObjectIdentifier    id_TA_ECDSA_SHA_256    = id_TA_ECDSA.branch("3");
-    /** OID: 0.4.0.127.0.7.2.2.2.2.4 */
-    static final ASN1ObjectIdentifier    id_TA_ECDSA_SHA_384    = id_TA_ECDSA.branch("4");
-    /** OID: 0.4.0.127.0.7.2.2.2.2.5 */
-    static final ASN1ObjectIdentifier    id_TA_ECDSA_SHA_512    = id_TA_ECDSA.branch("5");
-
-    /**
-     * <pre>
-     * id-EAC-ePassport OBJECT IDENTIFIER ::= {
-     *     bsi-de applications(3) mrtd(1) roles(2) 1
-     * }
-     * </pre>
-     * OID: 0.4.0.127.0.7.3.1.2.1
-     */
-    static final ASN1ObjectIdentifier id_EAC_ePassport = bsi_de.branch("3.1.2.1");
-}
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/gm/GMObjectIdentifiers.java b/bcprov/src/main/java/org/bouncycastle/asn1/gm/GMObjectIdentifiers.java
index ade6782..d0df5a9 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/gm/GMObjectIdentifiers.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/gm/GMObjectIdentifiers.java
@@ -4,6 +4,8 @@
 
 public interface GMObjectIdentifiers
 {
+    //1.2.156.10197: Chinese Cryptography Standardization Technology Committee (CCSTC)
+    //1.2.156.11235: China Broadband Wireless IP Standard Group
     ASN1ObjectIdentifier sm_scheme = new ASN1ObjectIdentifier("1.2.156.10197.1");
 
     ASN1ObjectIdentifier sm6_ecb = sm_scheme.branch("101.1");
@@ -46,7 +48,20 @@
     ASN1ObjectIdentifier sm2exchange = sm_scheme.branch("301.2");
     ASN1ObjectIdentifier sm2encrypt = sm_scheme.branch("301.3");
 
-    ASN1ObjectIdentifier wapip192v1 = sm_scheme.branch("301.101");
+    /**
+     * <Information security technology — Cryptographic application identifier criterion specification>
+     * <url>http&#058;//c.gb688.cn/bzgk/gb/showGb&#63;type=online&hcno=252CF0F72A7BE339A56DEA7D774E8994</url>,
+     * Page 21 only cover from 301.1 to 301.3
+     * */
+    ASN1ObjectIdentifier wapip192v1 =  sm_scheme.branch("301.101");
+    /**
+     * <WAPI certificate management—Part 5: Example of certificate format (draft)>
+     * <url>http&#058;//www.chinabwips.org.cn/zqyjgs1.htm</url> and
+     * <url>http&#058;//www.chinabwips.org.cn/doc/101.pdf</url>,
+     * Page 9 and page 10 states the OID of ECDSA-192 algorithm based on SHA-256 is 1.2.156.11235.1.1.1
+     * */
+    ASN1ObjectIdentifier wapi192v1 =  new ASN1ObjectIdentifier("1.2.156.11235.1.1.1");
+    ASN1ObjectIdentifier wapi192v1_parameters =  new ASN1ObjectIdentifier("1.2.156.11235.1.1.2.1");
 
     ASN1ObjectIdentifier sm2encrypt_recommendedParameters = sm2encrypt.branch("1");
     ASN1ObjectIdentifier sm2encrypt_specifiedParameters = sm2encrypt.branch("2");
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/isara/IsaraObjectIdentifiers.java b/bcprov/src/main/java/org/bouncycastle/asn1/isara/IsaraObjectIdentifiers.java
new file mode 100644
index 0000000..cf46920
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/isara/IsaraObjectIdentifiers.java
@@ -0,0 +1,22 @@
+package org.bouncycastle.asn1.isara;
+
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+
+public interface IsaraObjectIdentifiers
+{
+    /*
+    id-alg-xmss  OBJECT IDENTIFIER ::= { itu-t(0)
+             identified-organization(4) etsi(0) reserved(127)
+             etsi-identified-organization(0) isara(15) algorithms(1)
+             asymmetric(1) xmss(13) 0 }
+     */
+    static ASN1ObjectIdentifier id_alg_xmss = new ASN1ObjectIdentifier("0.4.0.127.0.15.1.1.13.0");
+
+    /*
+      id-alg-xmssmt  OBJECT IDENTIFIER ::= { itu-t(0)
+         identified-organization(4) etsi(0) reserved(127)
+         etsi-identified-organization(0) isara(15) algorithms(1)
+         asymmetric(1) xmssmt(14) 0 }
+     */
+    static ASN1ObjectIdentifier id_alg_xmssmt = new ASN1ObjectIdentifier("0.4.0.127.0.15.1.1.14.0");
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/misc/MiscObjectIdentifiers.java b/bcprov/src/main/java/org/bouncycastle/asn1/misc/MiscObjectIdentifiers.java
index 89b91d0..195978f 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/misc/MiscObjectIdentifiers.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/misc/MiscObjectIdentifiers.java
@@ -124,7 +124,7 @@
     ASN1ObjectIdentifier cryptlib_algorithm_blowfish_OFB = cryptlib_algorithm.branch("1.4");
 
     //
-    // Blake2b
+    // Blake2b/Blake2s
     //
     ASN1ObjectIdentifier blake2 = new ASN1ObjectIdentifier("1.3.6.1.4.1.1722.12.2");
 
@@ -138,6 +138,10 @@
     ASN1ObjectIdentifier id_blake2s224 = blake2.branch("2.7");
     ASN1ObjectIdentifier id_blake2s256 = blake2.branch("2.8");
 
+    ASN1ObjectIdentifier blake3 = blake2.branch("3");
+
+    ASN1ObjectIdentifier blake3_256 = blake3.branch("8");
+
     //
     // Scrypt
     ASN1ObjectIdentifier id_scrypt = new ASN1ObjectIdentifier("1.3.6.1.4.1.11591.4.11");
@@ -148,4 +152,15 @@
     //        iso(1)  identified-organization(3) dod(6) internet(1) private(4)
     //        enterprise(1) OpenCA(18227) Algorithms(2) id-alg-composite(1) }
     ASN1ObjectIdentifier id_alg_composite = new ASN1ObjectIdentifier("1.3.6.1.4.1.18227.2.1");
+
+    // -- To be replaced by IANA
+    //
+    //id-composite-key OBJECT IDENTIFIER ::= {
+    //
+    //    joint-iso-itu-t(2) country(16) us(840) organization(1) entrust(114027)
+    //
+    //    Algorithm(80) Composite(4) CompositeKey(1)
+    ASN1ObjectIdentifier id_composite_key = new ASN1ObjectIdentifier("2.16.840.1.114027.80.4.1");
+
+    ASN1ObjectIdentifier id_oracle_pkcs12_trusted_key_usage = new ASN1ObjectIdentifier("2.16.840.1.113894.746875.1.1");
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/misc/NetscapeCertType.java b/bcprov/src/main/java/org/bouncycastle/asn1/misc/NetscapeCertType.java
index 846a205..761b4c4 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/misc/NetscapeCertType.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/misc/NetscapeCertType.java
@@ -1,5 +1,6 @@
 package org.bouncycastle.asn1.misc;
 
+import org.bouncycastle.asn1.ASN1BitString;
 import org.bouncycastle.asn1.DERBitString;
 
 /**
@@ -42,13 +43,18 @@
     }
 
     public NetscapeCertType(
-        DERBitString usage)
+        ASN1BitString usage)
     {
         super(usage.getBytes(), usage.getPadBits());
     }
 
+    public boolean hasUsages(int usages)
+    {
+        return (intValue() & usages) == usages;
+    }
+
     public String toString()
     {
-        return "NetscapeCertType: 0x" + Integer.toHexString(data[0] & 0xff);
+        return "NetscapeCertType: 0x" + Integer.toHexString(intValue());
     }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/misc/NetscapeRevocationURL.java b/bcprov/src/main/java/org/bouncycastle/asn1/misc/NetscapeRevocationURL.java
index c0347da..1687913 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/misc/NetscapeRevocationURL.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/misc/NetscapeRevocationURL.java
@@ -1,12 +1,13 @@
 package org.bouncycastle.asn1.misc;
 
+import org.bouncycastle.asn1.ASN1IA5String;
 import org.bouncycastle.asn1.DERIA5String;
 
 public class NetscapeRevocationURL
     extends DERIA5String
 {
     public NetscapeRevocationURL(
-        DERIA5String str)
+        ASN1IA5String str)
     {
         super(str.getString());
     }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/misc/VerisignCzagExtension.java b/bcprov/src/main/java/org/bouncycastle/asn1/misc/VerisignCzagExtension.java
index f09880a..db13c31 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/misc/VerisignCzagExtension.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/misc/VerisignCzagExtension.java
@@ -1,12 +1,13 @@
 package org.bouncycastle.asn1.misc;
 
+import org.bouncycastle.asn1.ASN1IA5String;
 import org.bouncycastle.asn1.DERIA5String;
 
 public class VerisignCzagExtension
     extends DERIA5String
 {
     public VerisignCzagExtension(
-        DERIA5String str)
+        ASN1IA5String str)
     {
         super(str.getString());
     }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/nist/KMACwithSHAKE128_params.java b/bcprov/src/main/java/org/bouncycastle/asn1/nist/KMACwithSHAKE128_params.java
new file mode 100644
index 0000000..129034b
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/nist/KMACwithSHAKE128_params.java
@@ -0,0 +1,114 @@
+package org.bouncycastle.asn1.nist;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DEROctetString;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.util.Arrays;
+
+/**
+ * <pre>
+ *   KMACwithSHAKE128-params ::= SEQUENCE {
+ *      kMACOutputLength     INTEGER DEFAULT 256, -- Output length in bits
+ *      customizationString  OCTET STRING DEFAULT ''H
+ *    }
+ * </pre>
+ */
+public class KMACwithSHAKE128_params
+    extends ASN1Object
+{
+    private static final byte[] EMPTY_STRING = new byte[0];
+    private static final int DEF_LENGTH = 256;
+
+    private final int outputLength;
+    private final byte[] customizationString;
+
+    public KMACwithSHAKE128_params(int outputLength)
+    {
+        this.outputLength = outputLength;
+        this.customizationString = EMPTY_STRING;
+    }
+
+    public KMACwithSHAKE128_params(int outputLength, byte[] customizationString)
+    {
+        this.outputLength = outputLength;
+        this.customizationString = Arrays.clone(customizationString);
+    }
+
+    public static KMACwithSHAKE128_params getInstance(Object o)
+    {
+        if (o instanceof KMACwithSHAKE128_params)
+        {
+            return (KMACwithSHAKE128_params)o;
+        }
+        else if (o != null)
+        {
+            return new KMACwithSHAKE128_params(ASN1Sequence.getInstance(o));
+        }
+
+        return null;
+    }
+
+    private KMACwithSHAKE128_params(ASN1Sequence seq)
+    {
+        if (seq.size() > 2)
+        {
+            throw new IllegalArgumentException("sequence size greater than 2");
+        }
+
+        if (seq.size() == 2)
+        {
+            this.outputLength = ASN1Integer.getInstance(seq.getObjectAt(0)).intValueExact();
+            this.customizationString = Arrays.clone(ASN1OctetString.getInstance(seq.getObjectAt(1)).getOctets());
+        }
+        else if (seq.size() == 1)
+        {
+            if (seq.getObjectAt(0) instanceof ASN1Integer)
+            {
+                this.outputLength = ASN1Integer.getInstance(seq.getObjectAt(0)).intValueExact();
+                this.customizationString = EMPTY_STRING;
+            }
+            else
+            {
+                this.outputLength = DEF_LENGTH;
+                this.customizationString = Arrays.clone(ASN1OctetString.getInstance(seq.getObjectAt(0)).getOctets());
+            }
+        }
+        else
+        {
+            this.outputLength = DEF_LENGTH;
+            this.customizationString = EMPTY_STRING;
+        }
+    }
+
+    public int getOutputLength()
+    {
+        return outputLength;
+    }
+
+    public byte[] getCustomizationString()
+    {
+        return Arrays.clone(customizationString);
+    }
+
+    public ASN1Primitive toASN1Primitive()
+    {
+        ASN1EncodableVector v = new ASN1EncodableVector();
+
+        if (outputLength != DEF_LENGTH)
+        {
+            v.add(new ASN1Integer(outputLength));
+        }
+
+        if (customizationString.length != 0)
+        {
+            v.add(new DEROctetString(getCustomizationString()));
+        }
+
+        return new DERSequence(v);
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/nist/KMACwithSHAKE256_params.java b/bcprov/src/main/java/org/bouncycastle/asn1/nist/KMACwithSHAKE256_params.java
new file mode 100644
index 0000000..390c903
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/nist/KMACwithSHAKE256_params.java
@@ -0,0 +1,114 @@
+package org.bouncycastle.asn1.nist;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DEROctetString;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.util.Arrays;
+
+/**
+ * <pre>
+ *   KMACwithSHAKE256-params ::= SEQUENCE {
+ *      kMACOutputLength     INTEGER DEFAULT 512, -- Output length in bits
+ *      customizationString  OCTET STRING DEFAULT ''H
+ *    }
+ * </pre>
+ */
+public class KMACwithSHAKE256_params
+    extends ASN1Object
+{
+    private static final byte[] EMPTY_STRING = new byte[0];
+    private static final int DEF_LENGTH = 512;
+
+    private final int outputLength;
+    private final byte[] customizationString;
+
+    public KMACwithSHAKE256_params(int outputLength)
+    {
+        this.outputLength = outputLength;
+        this.customizationString = EMPTY_STRING;
+    }
+
+    public KMACwithSHAKE256_params(int outputLength, byte[] customizationString)
+    {
+        this.outputLength = outputLength;
+        this.customizationString = Arrays.clone(customizationString);
+    }
+
+    public static KMACwithSHAKE256_params getInstance(Object o)
+    {
+        if (o instanceof KMACwithSHAKE256_params)
+        {
+            return (KMACwithSHAKE256_params)o;
+        }
+        else if (o != null)
+        {
+            return new KMACwithSHAKE256_params(ASN1Sequence.getInstance(o));
+        }
+
+        return null;
+    }
+
+    private KMACwithSHAKE256_params(ASN1Sequence seq)
+    {
+        if (seq.size() > 2)
+        {
+            throw new IllegalArgumentException("sequence size greater than 2");
+        }
+
+        if (seq.size() == 2)
+        {
+            this.outputLength = ASN1Integer.getInstance(seq.getObjectAt(0)).intValueExact();
+            this.customizationString = Arrays.clone(ASN1OctetString.getInstance(seq.getObjectAt(1)).getOctets());
+        }
+        else if (seq.size() == 1)
+        {
+            if (seq.getObjectAt(0) instanceof ASN1Integer)
+            {
+                this.outputLength = ASN1Integer.getInstance(seq.getObjectAt(0)).intValueExact();
+                this.customizationString = EMPTY_STRING;
+            }
+            else
+            {
+                this.outputLength = DEF_LENGTH;
+                this.customizationString = Arrays.clone(ASN1OctetString.getInstance(seq.getObjectAt(0)).getOctets());
+            }
+        }
+        else
+        {
+            this.outputLength = DEF_LENGTH;
+            this.customizationString = EMPTY_STRING;
+        }
+    }
+
+    public int getOutputLength()
+    {
+        return outputLength;
+    }
+
+    public byte[] getCustomizationString()
+    {
+        return Arrays.clone(customizationString);
+    }
+    
+    public ASN1Primitive toASN1Primitive()
+    {
+        ASN1EncodableVector v = new ASN1EncodableVector();
+
+        if (outputLength != DEF_LENGTH)
+        {
+            v.add(new ASN1Integer(outputLength));
+        }
+
+        if (customizationString.length != 0)
+        {
+            v.add(new DEROctetString(getCustomizationString()));
+        }
+
+        return new DERSequence(v);
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/nist/NISTNamedCurves.java b/bcprov/src/main/java/org/bouncycastle/asn1/nist/NISTNamedCurves.java
index ba7e518..e2b624a 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/nist/NISTNamedCurves.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/nist/NISTNamedCurves.java
@@ -7,6 +7,7 @@
 import org.bouncycastle.asn1.sec.SECNamedCurves;
 import org.bouncycastle.asn1.sec.SECObjectIdentifiers;
 import org.bouncycastle.asn1.x9.X9ECParameters;
+import org.bouncycastle.asn1.x9.X9ECParametersHolder;
 import org.bouncycastle.util.Strings;
 
 /**
@@ -42,17 +43,16 @@
         defineCurve("P-192", SECObjectIdentifiers.secp192r1);
     }
 
-    public static X9ECParameters getByName(
-        String  name)
+    public static X9ECParameters getByName(String name)
     {
-        ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier)objIds.get(Strings.toUpperCase(name));
+        ASN1ObjectIdentifier oid = getOID(name);
+        return null != oid ? SECNamedCurves.getByOID(oid) : null;
+    }
 
-        if (oid != null)
-        {
-            return getByOID(oid);
-        }
-
-        return null;
+    public static X9ECParametersHolder getByNameLazy(String name)
+    {
+        ASN1ObjectIdentifier oid = getOID(name);
+        return null != oid ? SECNamedCurves.getByOIDLazy(oid) : null;
     }
 
     /**
@@ -61,10 +61,14 @@
      *
      * @param oid an object identifier representing a named curve, if present.
      */
-    public static X9ECParameters getByOID(
-        ASN1ObjectIdentifier  oid)
+    public static X9ECParameters getByOID(ASN1ObjectIdentifier oid)
     {
-        return SECNamedCurves.getByOID(oid);
+        return names.containsKey(oid) ? SECNamedCurves.getByOID(oid) : null;
+    }
+
+    public static X9ECParametersHolder getByOIDLazy(ASN1ObjectIdentifier oid)
+    {
+        return names.containsKey(oid) ? SECNamedCurves.getByOIDLazy(oid) : null;
     }
 
     /**
@@ -73,8 +77,7 @@
      *
      * @return the object identifier associated with name, if present.
      */
-    public static ASN1ObjectIdentifier getOID(
-        String  name)
+    public static ASN1ObjectIdentifier getOID(String name)
     {
         return (ASN1ObjectIdentifier)objIds.get(Strings.toUpperCase(name));
     }
@@ -82,8 +85,7 @@
     /**
      * return the named curve name represented by the given object identifier.
      */
-    public static String getName(
-        ASN1ObjectIdentifier  oid)
+    public static String getName(ASN1ObjectIdentifier oid)
     {
         return (String)names.get(oid);
     }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/nist/NISTObjectIdentifiers.java b/bcprov/src/main/java/org/bouncycastle/asn1/nist/NISTObjectIdentifiers.java
index 8de357a..572f6f4 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/nist/NISTObjectIdentifiers.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/nist/NISTObjectIdentifiers.java
@@ -58,69 +58,137 @@
     /** 2.16.840.1.101.3.4.2.19 */
     static final ASN1ObjectIdentifier    id_KmacWithSHAKE128 = hashAlgs.branch("19");
     /** 2.16.840.1.101.3.4.2.20 */
-    static final ASN1ObjectIdentifier    id_KmacWithSHAKE256 = hashAlgs.branch("20");
+    static final ASN1ObjectIdentifier id_KmacWithSHAKE256 = hashAlgs.branch("20");
 
-    /** 2.16.840.1.101.3.4.1 */
-    static final ASN1ObjectIdentifier    aes                     = nistAlgorithm.branch("1");
-    
-    /** 2.16.840.1.101.3.4.1.1 */
-    static final ASN1ObjectIdentifier    id_aes128_ECB           = aes.branch("1"); 
-    /** 2.16.840.1.101.3.4.1.2 */
-    static final ASN1ObjectIdentifier    id_aes128_CBC           = aes.branch("2");
-    /** 2.16.840.1.101.3.4.1.3 */
-    static final ASN1ObjectIdentifier    id_aes128_OFB           = aes.branch("3"); 
-    /** 2.16.840.1.101.3.4.1.4 */
-    static final ASN1ObjectIdentifier    id_aes128_CFB           = aes.branch("4"); 
-    /** 2.16.840.1.101.3.4.1.5 */
-    static final ASN1ObjectIdentifier    id_aes128_wrap          = aes.branch("5");
-    /** 2.16.840.1.101.3.4.1.6 */
-    static final ASN1ObjectIdentifier    id_aes128_GCM           = aes.branch("6");
-    /** 2.16.840.1.101.3.4.1.7 */
-    static final ASN1ObjectIdentifier    id_aes128_CCM           = aes.branch("7");
-    /** 2.16.840.1.101.3.4.1.28 */
-    static final ASN1ObjectIdentifier    id_aes128_wrap_pad      = aes.branch("8");
+    /**
+     * 2.16.840.1.101.3.4.1
+     */
+    static final ASN1ObjectIdentifier aes = nistAlgorithm.branch("1");
 
-    /** 2.16.840.1.101.3.4.1.21 */
-    static final ASN1ObjectIdentifier    id_aes192_ECB           = aes.branch("21"); 
-    /** 2.16.840.1.101.3.4.1.22 */
-    static final ASN1ObjectIdentifier    id_aes192_CBC           = aes.branch("22"); 
-    /** 2.16.840.1.101.3.4.1.23 */
-    static final ASN1ObjectIdentifier    id_aes192_OFB           = aes.branch("23"); 
-    /** 2.16.840.1.101.3.4.1.24 */
-    static final ASN1ObjectIdentifier    id_aes192_CFB           = aes.branch("24"); 
-    /** 2.16.840.1.101.3.4.1.25 */
-    static final ASN1ObjectIdentifier    id_aes192_wrap          = aes.branch("25");
-    /** 2.16.840.1.101.3.4.1.26 */
-    static final ASN1ObjectIdentifier    id_aes192_GCM           = aes.branch("26");
-    /** 2.16.840.1.101.3.4.1.27 */
-    static final ASN1ObjectIdentifier    id_aes192_CCM           = aes.branch("27");
-    /** 2.16.840.1.101.3.4.1.28 */
-    static final ASN1ObjectIdentifier    id_aes192_wrap_pad      = aes.branch("28");
+    /**
+     * 2.16.840.1.101.3.4.1.1
+     */
+    static final ASN1ObjectIdentifier id_aes128_ECB = aes.branch("1");
+    /**
+     * 2.16.840.1.101.3.4.1.2
+     */
+    static final ASN1ObjectIdentifier id_aes128_CBC = aes.branch("2");
+    /**
+     * 2.16.840.1.101.3.4.1.3
+     */
+    static final ASN1ObjectIdentifier id_aes128_OFB = aes.branch("3");
+    /**
+     * 2.16.840.1.101.3.4.1.4
+     */
+    static final ASN1ObjectIdentifier id_aes128_CFB = aes.branch("4");
+    /**
+     * 2.16.840.1.101.3.4.1.5
+     */
+    static final ASN1ObjectIdentifier id_aes128_wrap = aes.branch("5");
+    /**
+     * 2.16.840.1.101.3.4.1.6
+     */
+    static final ASN1ObjectIdentifier id_aes128_GCM = aes.branch("6");
+    /**
+     * 2.16.840.1.101.3.4.1.7
+     */
+    static final ASN1ObjectIdentifier id_aes128_CCM = aes.branch("7");
+    /**
+     * 2.16.840.1.101.3.4.1.8
+     */
+    static final ASN1ObjectIdentifier id_aes128_wrap_pad = aes.branch("8");
+    /**
+     * 2.16.840.1.101.3.4.1.9
+     */
+    static final ASN1ObjectIdentifier id_aes128_GMAC = aes.branch("9");
 
-    /** 2.16.840.1.101.3.4.1.41 */
-    static final ASN1ObjectIdentifier    id_aes256_ECB           = aes.branch("41"); 
-    /** 2.16.840.1.101.3.4.1.42 */
-    static final ASN1ObjectIdentifier    id_aes256_CBC           = aes.branch("42");
-    /** 2.16.840.1.101.3.4.1.43 */
-    static final ASN1ObjectIdentifier    id_aes256_OFB           = aes.branch("43"); 
-    /** 2.16.840.1.101.3.4.1.44 */
-    static final ASN1ObjectIdentifier    id_aes256_CFB           = aes.branch("44"); 
-    /** 2.16.840.1.101.3.4.1.45 */
-    static final ASN1ObjectIdentifier    id_aes256_wrap          = aes.branch("45"); 
-    /** 2.16.840.1.101.3.4.1.46 */
-    static final ASN1ObjectIdentifier    id_aes256_GCM           = aes.branch("46");
-    /** 2.16.840.1.101.3.4.1.47 */
-    static final ASN1ObjectIdentifier    id_aes256_CCM           = aes.branch("47");
-    /** 2.16.840.1.101.3.4.1.48 */
-    static final ASN1ObjectIdentifier    id_aes256_wrap_pad      = aes.branch("48");
+
+    /**
+     * 2.16.840.1.101.3.4.1.21
+     */
+    static final ASN1ObjectIdentifier id_aes192_ECB = aes.branch("21");
+    /**
+     * 2.16.840.1.101.3.4.1.22
+     */
+    static final ASN1ObjectIdentifier id_aes192_CBC = aes.branch("22");
+    /**
+     * 2.16.840.1.101.3.4.1.23
+     */
+    static final ASN1ObjectIdentifier id_aes192_OFB = aes.branch("23");
+    /**
+     * 2.16.840.1.101.3.4.1.24
+     */
+    static final ASN1ObjectIdentifier id_aes192_CFB = aes.branch("24");
+    /**
+     * 2.16.840.1.101.3.4.1.25
+     */
+    static final ASN1ObjectIdentifier id_aes192_wrap = aes.branch("25");
+    /**
+     * 2.16.840.1.101.3.4.1.26
+     */
+    static final ASN1ObjectIdentifier id_aes192_GCM = aes.branch("26");
+    /**
+     * 2.16.840.1.101.3.4.1.27
+     */
+    static final ASN1ObjectIdentifier id_aes192_CCM = aes.branch("27");
+    /**
+     * 2.16.840.1.101.3.4.1.28
+     */
+    static final ASN1ObjectIdentifier id_aes192_wrap_pad = aes.branch("28");
+
+    /**
+     * 2.16.840.1.101.3.4.1.29
+     */
+    static final ASN1ObjectIdentifier id_aes192_GMAC = aes.branch("29");
+
+
+    /**
+     * 2.16.840.1.101.3.4.1.41
+     */
+    static final ASN1ObjectIdentifier id_aes256_ECB = aes.branch("41");
+    /**
+     * 2.16.840.1.101.3.4.1.42
+     */
+    static final ASN1ObjectIdentifier id_aes256_CBC = aes.branch("42");
+    /**
+     * 2.16.840.1.101.3.4.1.43
+     */
+    static final ASN1ObjectIdentifier id_aes256_OFB = aes.branch("43");
+    /**
+     * 2.16.840.1.101.3.4.1.44
+     */
+    static final ASN1ObjectIdentifier id_aes256_CFB = aes.branch("44");
+    /**
+     * 2.16.840.1.101.3.4.1.45
+     */
+    static final ASN1ObjectIdentifier id_aes256_wrap = aes.branch("45");
+    /**
+     * 2.16.840.1.101.3.4.1.46
+     */
+    static final ASN1ObjectIdentifier id_aes256_GCM = aes.branch("46");
+    /**
+     * 2.16.840.1.101.3.4.1.47
+     */
+    static final ASN1ObjectIdentifier id_aes256_CCM = aes.branch("47");
+    /**
+     * 2.16.840.1.101.3.4.1.48
+     */
+    static final ASN1ObjectIdentifier id_aes256_wrap_pad = aes.branch("48");
+    /**
+     * 2.16.840.1.101.3.4.1.49
+     */
+    static final ASN1ObjectIdentifier id_aes256_GMAC = aes.branch("49");
+
 
     //
     // signatures
     //
-    /** 2.16.840.1.101.3.4.3 */
-    static final ASN1ObjectIdentifier    sigAlgs        = nistAlgorithm.branch("3");
+    /**
+     * 2.16.840.1.101.3.4.3
+     */
+    static final ASN1ObjectIdentifier sigAlgs = nistAlgorithm.branch("3");
 
-    static final ASN1ObjectIdentifier    id_dsa_with_sha2        = sigAlgs;
+    static final ASN1ObjectIdentifier id_dsa_with_sha2 = sigAlgs;
 
     /** 2.16.840.1.101.3.4.3.1 */
     static final ASN1ObjectIdentifier    dsa_with_sha224         = sigAlgs.branch("1");
@@ -150,12 +218,12 @@
     static final ASN1ObjectIdentifier    id_ecdsa_with_sha3_512       = sigAlgs.branch("12");
 
     // RSA PKCS #1 v1.5 Signature with SHA-3 family.
-    /** 2.16.840.1.101.3.4.3.9 */
+    /** 2.16.840.1.101.3.4.3.13 */
     static final ASN1ObjectIdentifier    id_rsassa_pkcs1_v1_5_with_sha3_224       = sigAlgs.branch("13");
-    /** 2.16.840.1.101.3.4.3.10 */
+    /** 2.16.840.1.101.3.4.3.14 */
     static final ASN1ObjectIdentifier    id_rsassa_pkcs1_v1_5_with_sha3_256       = sigAlgs.branch("14");
-    /** 2.16.840.1.101.3.4.3.11 */
+    /** 2.16.840.1.101.3.4.3.15 */
     static final ASN1ObjectIdentifier    id_rsassa_pkcs1_v1_5_with_sha3_384       = sigAlgs.branch("15");
-    /** 2.16.840.1.101.3.4.3.12 */
+    /** 2.16.840.1.101.3.4.3.16 */
     static final ASN1ObjectIdentifier    id_rsassa_pkcs1_v1_5_with_sha3_512       = sigAlgs.branch("16");
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/ocsp/CertStatus.java b/bcprov/src/main/java/org/bouncycastle/asn1/ocsp/CertStatus.java
index e471149..ed1eec6 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/ocsp/CertStatus.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/ocsp/CertStatus.java
@@ -2,9 +2,11 @@
 
 import org.bouncycastle.asn1.ASN1Choice;
 import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1Null;
 import org.bouncycastle.asn1.ASN1Object;
 import org.bouncycastle.asn1.ASN1Primitive;
 import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.ASN1Util;
 import org.bouncycastle.asn1.DERNull;
 import org.bouncycastle.asn1.DERTaggedObject;
 
@@ -42,22 +44,25 @@
     private CertStatus(
         ASN1TaggedObject    choice)
     {
-        this.tagNo = choice.getTagNo();
+        int tagNo = choice.getTagNo();
 
-        switch (choice.getTagNo())
+        switch (tagNo)
         {
         case 0:
-            value = DERNull.INSTANCE;
+            value = ASN1Null.getInstance(choice, false);
             break;
         case 1:
             value = RevokedInfo.getInstance(choice, false);
             break;
         case 2:
-            value = DERNull.INSTANCE;
+            // UnknownInfo ::= NULL
+            value = ASN1Null.getInstance(choice, false);
             break;
         default:
-            throw new IllegalArgumentException("Unknown tag encountered: " + choice.getTagNo());
+            throw new IllegalArgumentException("Unknown tag encountered: " + ASN1Util.getTagText(choice));
         }
+
+        this.tagNo = tagNo;
     }
 
     public static CertStatus getInstance(
@@ -79,7 +84,7 @@
         ASN1TaggedObject obj,
         boolean          explicit)
     {
-        return getInstance(obj.getObject()); // must be explicitly tagged
+        return getInstance(obj.getExplicitBaseTagged()); // must be explicitly tagged
     }
     
     public int getTagNo()
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/ocsp/CrlID.java b/bcprov/src/main/java/org/bouncycastle/asn1/ocsp/CrlID.java
index b9f5d7c..49afce6 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/ocsp/CrlID.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/ocsp/CrlID.java
@@ -4,6 +4,7 @@
 
 import org.bouncycastle.asn1.ASN1EncodableVector;
 import org.bouncycastle.asn1.ASN1GeneralizedTime;
+import org.bouncycastle.asn1.ASN1IA5String;
 import org.bouncycastle.asn1.ASN1Integer;
 import org.bouncycastle.asn1.ASN1Object;
 import org.bouncycastle.asn1.ASN1Primitive;
@@ -16,7 +17,7 @@
 public class CrlID
     extends ASN1Object
 {
-    private DERIA5String         crlUrl;
+    private ASN1IA5String        crlUrl;
     private ASN1Integer          crlNum;
     private ASN1GeneralizedTime  crlTime;
 
@@ -32,7 +33,7 @@
             switch (o.getTagNo())
             {
             case 0:
-                crlUrl = DERIA5String.getInstance(o, true);
+                crlUrl = ASN1IA5String.getInstance(o, true);
                 break;
             case 1:
                 crlNum = ASN1Integer.getInstance(o, true);
@@ -62,8 +63,18 @@
         return null;
     }
 
+    /**
+     * @deprecated Use {@link #getCrlUrlIA5()} instead.
+     */
     public DERIA5String getCrlUrl()
     {
+        return null == crlUrl || crlUrl instanceof DERIA5String
+            ?   (DERIA5String)crlUrl
+            :   new DERIA5String(crlUrl.getString(), false);
+    }
+
+    public ASN1IA5String getCrlUrlIA5()
+    {
         return crlUrl;
     }
 
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/ocsp/ResponderID.java b/bcprov/src/main/java/org/bouncycastle/asn1/ocsp/ResponderID.java
index 9719047..c851d79 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/ocsp/ResponderID.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/ocsp/ResponderID.java
@@ -60,7 +60,12 @@
         ASN1TaggedObject obj,
         boolean          explicit)
     {
-        return getInstance(obj.getObject()); // must be explicitly tagged
+        if (!explicit)
+        {
+            throw new IllegalArgumentException("choice item must be explicitly tagged");
+        }
+
+        return getInstance(obj.getExplicitBaseObject());
     }
 
     public byte[] getKeyHash()
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/ocsp/ResponseBytes.java b/bcprov/src/main/java/org/bouncycastle/asn1/ocsp/ResponseBytes.java
index ec6d926..eaeaa60 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/ocsp/ResponseBytes.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/ocsp/ResponseBytes.java
@@ -31,10 +31,7 @@
         this.response = response;
     }
 
-    /**
-     * @deprecated use getInstance()
-     */
-    public ResponseBytes(
+    private ResponseBytes(
         ASN1Sequence    seq)
     {
         responseType = (ASN1ObjectIdentifier)seq.getObjectAt(0);
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/ocsp/RevokedInfo.java b/bcprov/src/main/java/org/bouncycastle/asn1/ocsp/RevokedInfo.java
index 2976d4b..9a6e666 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/ocsp/RevokedInfo.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/ocsp/RevokedInfo.java
@@ -17,6 +17,11 @@
     private ASN1GeneralizedTime  revocationTime;
     private CRLReason           revocationReason;
 
+    public RevokedInfo(ASN1GeneralizedTime revocationTime)
+    {
+        this(revocationTime, null);
+    }
+
     public RevokedInfo(
         ASN1GeneralizedTime  revocationTime,
         CRLReason           revocationReason)
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/CRLBag.java b/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/CRLBag.java
index 47e1a54..1bb92f5 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/CRLBag.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/CRLBag.java
@@ -22,8 +22,8 @@
     private CRLBag(
         ASN1Sequence seq)
     {
-        this.crlId = (ASN1ObjectIdentifier)seq.getObjectAt(0);
-        this.crlValue = ((ASN1TaggedObject)seq.getObjectAt(1)).getObject();
+        this.crlId = ASN1ObjectIdentifier.getInstance(seq.getObjectAt(0));
+        this.crlValue = ASN1TaggedObject.getInstance(seq.getObjectAt(1)).getExplicitBaseObject();
     }
 
     public static CRLBag getInstance(Object o)
@@ -67,7 +67,7 @@
      *
      * x509CRL BAG-TYPE ::= {OCTET STRING IDENTIFIED BY {certTypes 1}
      * -- DER-encoded X.509 CRL stored in OCTET STRING
-	 *
+     *
      * CRLTypes BAG-TYPE ::= {
      * x509CRL,
      * ... -- For future extensions
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/CertBag.java b/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/CertBag.java
index f43f472..87f03de 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/CertBag.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/CertBag.java
@@ -20,7 +20,7 @@
         ASN1Sequence    seq)
     {
         this.certId = ASN1ObjectIdentifier.getInstance(seq.getObjectAt(0));
-        this.certValue = ASN1TaggedObject.getInstance(seq.getObjectAt(1)).getObject();
+        this.certValue = ASN1TaggedObject.getInstance(seq.getObjectAt(1)).getExplicitBaseObject();
     }
 
     public static CertBag getInstance(Object o)
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/CertificationRequest.java b/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/CertificationRequest.java
index 13587f8..2dbc10c 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/CertificationRequest.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/CertificationRequest.java
@@ -1,5 +1,6 @@
 package org.bouncycastle.asn1.pkcs;
 
+import org.bouncycastle.asn1.ASN1BitString;
 import org.bouncycastle.asn1.ASN1EncodableVector;
 import org.bouncycastle.asn1.ASN1Object;
 import org.bouncycastle.asn1.ASN1Primitive;
@@ -23,7 +24,7 @@
 {
     protected CertificationRequestInfo reqInfo = null;
     protected AlgorithmIdentifier sigAlgId = null;
-    protected DERBitString sigBits = null;
+    protected ASN1BitString sigBits = null;
 
     public static CertificationRequest getInstance(Object o)
     {
@@ -47,7 +48,7 @@
     public CertificationRequest(
         CertificationRequestInfo requestInfo,
         AlgorithmIdentifier     algorithm,
-        DERBitString            signature)
+        ASN1BitString            signature)
     {
         this.reqInfo = requestInfo;
         this.sigAlgId = algorithm;
@@ -75,7 +76,7 @@
         return sigAlgId;
     }
 
-    public DERBitString getSignature()
+    public ASN1BitString getSignature()
     {
         return sigBits;
     }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/CertificationRequestInfo.java b/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/CertificationRequestInfo.java
index 11fdf6c..b092bb8 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/CertificationRequestInfo.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/CertificationRequestInfo.java
@@ -97,10 +97,7 @@
         this(X500Name.getInstance(subject.toASN1Primitive()), pkInfo, attributes);
     }
 
-    /**
-     * @deprecated use getInstance().
-     */
-    public CertificationRequestInfo(
+    private CertificationRequestInfo(
         ASN1Sequence  seq)
     {
         version = (ASN1Integer)seq.getObjectAt(0);
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/ContentInfo.java b/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/ContentInfo.java
index a7b2bc7..d28e942 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/ContentInfo.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/ContentInfo.java
@@ -46,7 +46,7 @@
 
         if (e.hasMoreElements())
         {
-            content = ((ASN1TaggedObject)e.nextElement()).getObject();
+            content = ((ASN1TaggedObject)e.nextElement()).getExplicitBaseObject();
         }
 
         isBer = seq instanceof BERSequence;
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/EncryptedData.java b/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/EncryptedData.java
index e4d449b..8faafe5 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/EncryptedData.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/EncryptedData.java
@@ -55,9 +55,8 @@
     private EncryptedData(
         ASN1Sequence seq)
     {
-        int version = ((ASN1Integer)seq.getObjectAt(0)).intValueExact();
-
-        if (version != 0)
+        ASN1Integer version = (ASN1Integer)seq.getObjectAt(0);
+        if (!version.hasValue(0))
         {
             throw new IllegalArgumentException("sequence not version 0");
         }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/EncryptedPrivateKeyInfo.java b/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/EncryptedPrivateKeyInfo.java
index 82645ae..cc16037 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/EncryptedPrivateKeyInfo.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/EncryptedPrivateKeyInfo.java
@@ -10,6 +10,7 @@
 import org.bouncycastle.asn1.DEROctetString;
 import org.bouncycastle.asn1.DERSequence;
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.util.Arrays;
 
 public class EncryptedPrivateKeyInfo
     extends ASN1Object
@@ -31,7 +32,7 @@
         byte[]              encoding)
     {
         this.algId = algId;
-        this.data = new DEROctetString(encoding);
+        this.data = new DEROctetString(Arrays.clone(encoding));
     }
 
     public static EncryptedPrivateKeyInfo getInstance(
@@ -56,7 +57,7 @@
 
     public byte[] getEncryptedData()
     {
-        return data.getOctets();
+        return Arrays.clone(data.getOctets());
     }
 
     /**
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/PBKDF2Params.java b/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/PBKDF2Params.java
index 58fa82b..a59d16c 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/PBKDF2Params.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/PBKDF2Params.java
@@ -183,7 +183,7 @@
      */
     public byte[] getSalt()
     {
-        return octStr.getOctets();
+        return Arrays.clone(octStr.getOctets());
     }
 
     /**
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/PBMAC1Params.java b/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/PBMAC1Params.java
new file mode 100644
index 0000000..fdbaefd
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/PBMAC1Params.java
@@ -0,0 +1,88 @@
+package org.bouncycastle.asn1.pkcs;
+
+import java.util.Enumeration;
+
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+
+
+/**
+ * From https://datatracker.ietf.org/doc/html/rfc8018
+ *
+ * <pre>
+ * PBMAC1-params ::= SEQUENCE {
+ *     keyDerivationFunc AlgorithmIdentifier {{PBMAC1-KDFs}},
+ *     messageAuthScheme AlgorithmIdentifier {{PBMAC1-MACs}} }
+ * </pre>
+ */
+public class PBMAC1Params
+    extends ASN1Object
+    implements PKCSObjectIdentifiers
+{
+    private AlgorithmIdentifier func;
+    private AlgorithmIdentifier scheme;
+
+    public static PBMAC1Params getInstance(
+        Object  obj)
+    {
+        if (obj instanceof PBMAC1Params)
+        {
+            return (PBMAC1Params)obj;
+        }
+        if (obj != null)
+        {
+            return new PBMAC1Params(ASN1Sequence.getInstance(obj));
+        }
+
+        return null;
+    }
+
+    public PBMAC1Params(AlgorithmIdentifier keyDevFunc, AlgorithmIdentifier encScheme)
+    {
+        this.func = keyDevFunc;
+        this.scheme = encScheme;
+    }
+
+    private PBMAC1Params(
+        ASN1Sequence  obj)
+    {
+        Enumeration e = obj.getObjects();
+        ASN1Sequence  funcSeq = ASN1Sequence.getInstance(((ASN1Encodable)e.nextElement()).toASN1Primitive());
+
+        if (funcSeq.getObjectAt(0).equals(id_PBKDF2))
+        {
+            func = new AlgorithmIdentifier(id_PBKDF2, PBKDF2Params.getInstance(funcSeq.getObjectAt(1)));
+        }
+        else
+        {
+            func = AlgorithmIdentifier.getInstance(funcSeq);
+        }
+
+        scheme = AlgorithmIdentifier.getInstance(e.nextElement());
+    }
+
+    public AlgorithmIdentifier getKeyDerivationFunc()
+    {
+        return func;
+    }
+
+    public AlgorithmIdentifier getMessageAuthScheme()
+    {
+        return scheme;
+    }
+
+    public ASN1Primitive toASN1Primitive()
+    {
+        ASN1EncodableVector v = new ASN1EncodableVector(2);
+
+        v.add(func);
+        v.add(scheme);
+
+        return new DERSequence(v);
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/PKCSObjectIdentifiers.java b/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/PKCSObjectIdentifiers.java
index 7cf720f..057d825 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/PKCSObjectIdentifiers.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/PKCSObjectIdentifiers.java
@@ -76,10 +76,12 @@
     ASN1ObjectIdentifier    pbeWithSHA1AndDES_CBC   = pkcs_5.branch("10");
     /** PKCS#5: 1.2.840.113549.1.5.11 */
     ASN1ObjectIdentifier    pbeWithSHA1AndRC2_CBC   = pkcs_5.branch("11");
-    /** PKCS#5: 1.2.840.113549.1.5.13 */
-    ASN1ObjectIdentifier    id_PBES2                = pkcs_5.branch("13");
     /** PKCS#5: 1.2.840.113549.1.5.12 */
     ASN1ObjectIdentifier    id_PBKDF2               = pkcs_5.branch("12");
+    /** PKCS#5: 1.2.840.113549.1.5.13 */
+    ASN1ObjectIdentifier    id_PBES2                = pkcs_5.branch("13");
+    /** PKCS#5: 1.2.840.113549.1.5.14 */
+    ASN1ObjectIdentifier    id_PBMAC1               = pkcs_5.branch("14");
 
     //
     // encryptionAlgorithm OBJECT IDENTIFIER ::= {
@@ -135,6 +137,10 @@
     ASN1ObjectIdentifier    id_hmacWithSHA384      = digestAlgorithm.branch("10").intern();
     /**  1.2.840.113549.2.11 */
     ASN1ObjectIdentifier    id_hmacWithSHA512      = digestAlgorithm.branch("11").intern();
+    /**  1.2.840.113549.2.12 */
+    ASN1ObjectIdentifier    id_hmacWithSHA512_224  = digestAlgorithm.branch("12").intern();
+    /**  1.2.840.113549.2.13 */
+    ASN1ObjectIdentifier    id_hmacWithSHA512_256  = digestAlgorithm.branch("13").intern();
 
     //
     // pkcs-7 OBJECT IDENTIFIER ::= {
@@ -189,6 +195,8 @@
     ASN1ObjectIdentifier    pkcs_9_at_smimeCapabilities  = pkcs_9.branch("15").intern();
     /** PKCS#9: 1.2.840.113549.1.9.16 */
     ASN1ObjectIdentifier    id_smime                     = pkcs_9.branch("16").intern();
+    /** PKCS#9: 1.2.840.113549.1.9.16.2.46 */
+    ASN1ObjectIdentifier    pkcs_9_at_binarySigningTime  = pkcs_9.branch("16.2.46").intern();
 
     /** PKCS#9: 1.2.840.113549.1.9.20 */
     ASN1ObjectIdentifier    pkcs_9_at_friendlyName  = pkcs_9.branch("20").intern();
@@ -247,8 +255,20 @@
 
     /** S/MIME: Algorithm Identifiers ; 1.2.840.113549.1.9.16.3 */
     ASN1ObjectIdentifier id_alg                  = id_smime.branch("3");
+
+    /** PKCS#9: 1.2.840.113549.1.9.16.3.5 */
+    ASN1ObjectIdentifier    id_alg_ESDH             = id_alg.branch("5");
+    /** PKCS#9: 1.2.840.113549.1.9.16.3.6 */
+    ASN1ObjectIdentifier    id_alg_CMS3DESwrap      = id_alg.branch("6");
+    /** PKCS#9: 1.2.840.113549.1.9.16.3.7 */
+    ASN1ObjectIdentifier    id_alg_CMSRC2wrap       = id_alg.branch("7");
+    /** PKCS#9: 1.2.840.113549.1.9.16.3.8 */
+    ASN1ObjectIdentifier id_alg_zlibCompress        = id_alg.branch("8");
     /** PKCS#9: 1.2.840.113549.1.9.16.3.9 */
-    ASN1ObjectIdentifier id_alg_PWRI_KEK         = id_alg.branch("9");
+    ASN1ObjectIdentifier id_alg_PWRI_KEK            = id_alg.branch("9");
+    /** PKCS#9: 1.2.840.113549.1.9.16.3.10 */
+    ASN1ObjectIdentifier    id_alg_SSDH             = id_alg.branch("10");
+
     /**
      * <pre>
      * -- RSA-KEM Key Transport Algorithm  RFC 5990
@@ -466,14 +486,5 @@
      * @deprecated use pbeWithSHAAnd40BitRC2_CBC
      */
     ASN1ObjectIdentifier    pbewithSHAAnd40BitRC2_CBC = pkcs_12PbeIds.branch("6");
-
-    /** PKCS#9: 1.2.840.113549.1.9.16.3.6 */
-    ASN1ObjectIdentifier    id_alg_CMS3DESwrap = new ASN1ObjectIdentifier("1.2.840.113549.1.9.16.3.6");
-    /** PKCS#9: 1.2.840.113549.1.9.16.3.7 */
-    ASN1ObjectIdentifier    id_alg_CMSRC2wrap  = new ASN1ObjectIdentifier("1.2.840.113549.1.9.16.3.7");
-    /** PKCS#9: 1.2.840.113549.1.9.16.3.5 */
-    ASN1ObjectIdentifier    id_alg_ESDH  = new ASN1ObjectIdentifier("1.2.840.113549.1.9.16.3.5");
-    /** PKCS#9: 1.2.840.113549.1.9.16.3.10 */
-    ASN1ObjectIdentifier    id_alg_SSDH  = new ASN1ObjectIdentifier("1.2.840.113549.1.9.16.3.10");
 }
 
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/Pfx.java b/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/Pfx.java
index 5954313..8d51197 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/Pfx.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/Pfx.java
@@ -21,7 +21,7 @@
         ASN1Sequence   seq)
     {
         ASN1Integer version = ASN1Integer.getInstance(seq.getObjectAt(0));
-        if (version.intValueExact() != 3)
+        if (!version.hasValue(3))
         {
             throw new IllegalArgumentException("wrong version for PFX PDU");
         }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/PrivateKeyInfo.java b/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/PrivateKeyInfo.java
index 9ac8c5e..2a78c1f 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/PrivateKeyInfo.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/PrivateKeyInfo.java
@@ -164,7 +164,7 @@
                     throw new IllegalArgumentException("'publicKey' requires version v2(1) or later");
                 }
 
-                this.publicKey = DERBitString.getInstance(tagged, false);
+                this.publicKey = ASN1BitString.getInstance(tagged, false);
                 break;
             }
             default:
@@ -195,6 +195,11 @@
         return new DEROctetString(privateKey.getOctets());
     }
 
+    public int getPrivateKeyLength()
+    {
+        return privateKey.getOctetsLength();
+    }
+
     public ASN1Encodable parsePrivateKey()
         throws IOException
     {
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/RSAESOAEPparams.java b/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/RSAESOAEPparams.java
index 9e5c364..45b93fc 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/RSAESOAEPparams.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/RSAESOAEPparams.java
@@ -58,11 +58,7 @@
         this.pSourceAlgorithm = pSourceAlgorithm;
     }
 
-    /**
-     * @deprecated use getInstance()
-     * @param seq
-     */
-    public RSAESOAEPparams(
+    private RSAESOAEPparams(
         ASN1Sequence seq)
     {
         hashAlgorithm = DEFAULT_HASH_ALGORITHM;
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/SafeBag.java b/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/SafeBag.java
index 1d39416..d6b508e 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/SafeBag.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/SafeBag.java
@@ -57,7 +57,7 @@
         ASN1Sequence    seq)
     {
         this.bagId = (ASN1ObjectIdentifier)seq.getObjectAt(0);
-        this.bagValue = ((ASN1TaggedObject)seq.getObjectAt(1)).getObject();
+        this.bagValue = ((ASN1TaggedObject)seq.getObjectAt(1)).getExplicitBaseObject();
         if (seq.size() == 3)
         {
             this.bagAttributes = (ASN1Set)seq.getObjectAt(2);
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/sec/ECPrivateKey.java b/bcprov/src/main/java/org/bouncycastle/asn1/sec/ECPrivateKey.java
index d6e9d47..d37769f 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/sec/ECPrivateKey.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/sec/ECPrivateKey.java
@@ -3,6 +3,7 @@
 import java.math.BigInteger;
 import java.util.Enumeration;
 
+import org.bouncycastle.asn1.ASN1BitString;
 import org.bouncycastle.asn1.ASN1Encodable;
 import org.bouncycastle.asn1.ASN1EncodableVector;
 import org.bouncycastle.asn1.ASN1Integer;
@@ -11,7 +12,7 @@
 import org.bouncycastle.asn1.ASN1Primitive;
 import org.bouncycastle.asn1.ASN1Sequence;
 import org.bouncycastle.asn1.ASN1TaggedObject;
-import org.bouncycastle.asn1.DERBitString;
+import org.bouncycastle.asn1.BERTags;
 import org.bouncycastle.asn1.DEROctetString;
 import org.bouncycastle.asn1.DERSequence;
 import org.bouncycastle.asn1.DERTaggedObject;
@@ -91,7 +92,7 @@
      */
     public ECPrivateKey(
         BigInteger key,
-        DERBitString publicKey,
+        ASN1BitString publicKey,
         ASN1Encodable parameters)
     {
         this(key.bitLength(), key, publicKey, parameters);
@@ -108,7 +109,7 @@
     public ECPrivateKey(
         int orderBitLength,
         BigInteger key,
-        DERBitString publicKey,
+        ASN1BitString publicKey,
         ASN1Encodable parameters)
     {
         byte[] bytes = BigIntegers.asUnsignedByteArray((orderBitLength + 7) / 8, key);
@@ -137,18 +138,27 @@
 
         return new BigInteger(1, octs.getOctets());
     }
-
-    public DERBitString getPublicKey()
+    
+    public ASN1BitString getPublicKey()
     {
-        return (DERBitString)getObjectInTag(1);
+        return (ASN1BitString)getObjectInTag(1, BERTags.BIT_STRING);
     }
 
+    /**
+     * @deprecated Use {@link #getParametersObject()} instead and getInstance
+     *             methods or similar to get the object at the desired type.
+     */
     public ASN1Primitive getParameters()
     {
-        return getObjectInTag(0);
+        return getParametersObject().toASN1Primitive();
     }
 
-    private ASN1Primitive getObjectInTag(int tagNo)
+    public ASN1Object getParametersObject()
+    {
+        return getObjectInTag(0, -1);
+    }
+
+    private ASN1Object getObjectInTag(int tagNo, int baseTagNo)
     {
         Enumeration e = seq.getObjects();
 
@@ -159,9 +169,11 @@
             if (obj instanceof ASN1TaggedObject)
             {
                 ASN1TaggedObject tag = (ASN1TaggedObject)obj;
-                if (tag.getTagNo() == tagNo)
+                if (tag.hasContextTag(tagNo))
                 {
-                    return tag.getObject().toASN1Primitive();
+                    return baseTagNo < 0
+                        ?   tag.getExplicitBaseObject().toASN1Primitive()
+                        :   tag.getBaseUniversal(true, baseTagNo);
                 }
             }
         }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/sec/ECPrivateKeyStructure.java b/bcprov/src/main/java/org/bouncycastle/asn1/sec/ECPrivateKeyStructure.java
index d163987..5c33b1e 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/sec/ECPrivateKeyStructure.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/sec/ECPrivateKeyStructure.java
@@ -3,6 +3,7 @@
 import java.math.BigInteger;
 import java.util.Enumeration;
 
+import org.bouncycastle.asn1.ASN1BitString;
 import org.bouncycastle.asn1.ASN1Encodable;
 import org.bouncycastle.asn1.ASN1EncodableVector;
 import org.bouncycastle.asn1.ASN1Integer;
@@ -11,7 +12,6 @@
 import org.bouncycastle.asn1.ASN1Primitive;
 import org.bouncycastle.asn1.ASN1Sequence;
 import org.bouncycastle.asn1.ASN1TaggedObject;
-import org.bouncycastle.asn1.DERBitString;
 import org.bouncycastle.asn1.DEROctetString;
 import org.bouncycastle.asn1.DERSequence;
 import org.bouncycastle.asn1.DERTaggedObject;
@@ -54,7 +54,7 @@
 
     public ECPrivateKeyStructure(
         BigInteger    key,
-        DERBitString  publicKey,
+        ASN1BitString  publicKey,
         ASN1Encodable parameters)
     {
         byte[] bytes = BigIntegers.asUnsignedByteArray(key);
@@ -84,9 +84,9 @@
         return new BigInteger(1, octs.getOctets());
     }
 
-    public DERBitString getPublicKey()
+    public ASN1BitString getPublicKey()
     {
-        return (DERBitString)getObjectInTag(1);
+        return (ASN1BitString)getObjectInTag(1);
     }
 
     public ASN1Primitive getParameters()
@@ -107,7 +107,7 @@
                 ASN1TaggedObject tag = (ASN1TaggedObject)obj;
                 if (tag.getTagNo() == tagNo)
                 {
-                    return (ASN1Primitive)((ASN1Encodable)tag.getObject()).toASN1Primitive();
+                    return (ASN1Primitive)((ASN1Encodable)tag.getExplicitBaseObject()).toASN1Primitive();
                 }
             }
         }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/sec/SECNamedCurves.java b/bcprov/src/main/java/org/bouncycastle/asn1/sec/SECNamedCurves.java
index 03c09c4..92dfd32 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/sec/SECNamedCurves.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/sec/SECNamedCurves.java
@@ -46,22 +46,27 @@
      */
     static X9ECParametersHolder secp112r1 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             // p = (2^128 - 3) / 76439
             BigInteger p = fromHex("DB7C2ABF62E35E668076BEAD208B");
             BigInteger a = fromHex("DB7C2ABF62E35E668076BEAD2088");
             BigInteger b = fromHex("659EF8BA043916EEDE8911702B22");
-            byte[] S = Hex.decodeStrict("00F50B028E4D696E676875615175290472783FB1");
             BigInteger n = fromHex("DB7C2ABF62E35E7628DFAC6561C5");
             BigInteger h = BigInteger.valueOf(1);
 
-            ECCurve curve = configureCurve(new ECCurve.Fp(p, a, b, n, h));
+            return configureCurve(new ECCurve.Fp(p, a, b, n, h, true));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = Hex.decodeStrict("00F50B028E4D696E676875615175290472783FB1");
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "0409487239995A5EE76B55F9C2F098A89CE5AF8724C0A23E0E0FF77500");
 
-            return new X9ECParameters(curve, G, n, h, S);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
@@ -70,22 +75,27 @@
      */
     static X9ECParametersHolder secp112r2 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             // p = (2^128 - 3) / 76439
             BigInteger p = fromHex("DB7C2ABF62E35E668076BEAD208B");
             BigInteger a = fromHex("6127C24C05F38A0AAAF65C0EF02C");
             BigInteger b = fromHex("51DEF1815DB5ED74FCC34C85D709");
-            byte[] S = Hex.decodeStrict("002757A1114D696E6768756151755316C05E0BD4");
             BigInteger n = fromHex("36DF0AAFD8B8D7597CA10520D04B");
             BigInteger h = BigInteger.valueOf(4);
 
-            ECCurve curve = configureCurve(new ECCurve.Fp(p, a, b, n, h));
+            return configureCurve(new ECCurve.Fp(p, a, b, n, h, true));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = Hex.decodeStrict("002757A1114D696E6768756151755316C05E0BD4");
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "044BA30AB5E892B4E1649DD0928643ADCD46F5882E3747DEF36E956E97");
 
-            return new X9ECParameters(curve, G, n, h, S);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
@@ -94,22 +104,27 @@
      */
     static X9ECParametersHolder secp128r1 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             // p = 2^128 - 2^97 - 1
             BigInteger p = fromHex("FFFFFFFDFFFFFFFFFFFFFFFFFFFFFFFF");
             BigInteger a = fromHex("FFFFFFFDFFFFFFFFFFFFFFFFFFFFFFFC");
             BigInteger b = fromHex("E87579C11079F43DD824993C2CEE5ED3");
-            byte[] S = Hex.decodeStrict("000E0D4D696E6768756151750CC03A4473D03679");
             BigInteger n = fromHex("FFFFFFFE0000000075A30D1B9038A115");
             BigInteger h = BigInteger.valueOf(1);
 
-            ECCurve curve = configureCurve(new ECCurve.Fp(p, a, b, n, h));
+            return configureCurve(new ECCurve.Fp(p, a, b, n, h, true));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = Hex.decodeStrict("000E0D4D696E6768756151750CC03A4473D03679");
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "04161FF7528B899B2D0C28607CA52C5B86CF5AC8395BAFEB13C02DA292DDED7A83");
 
-            return new X9ECParameters(curve, G, n, h, S);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
@@ -118,22 +133,27 @@
      */
     static X9ECParametersHolder secp128r2 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             // p = 2^128 - 2^97 - 1
             BigInteger p = fromHex("FFFFFFFDFFFFFFFFFFFFFFFFFFFFFFFF");
             BigInteger a = fromHex("D6031998D1B3BBFEBF59CC9BBFF9AEE1");
             BigInteger b = fromHex("5EEEFCA380D02919DC2C6558BB6D8A5D");
-            byte[] S = Hex.decodeStrict("004D696E67687561517512D8F03431FCE63B88F4");
             BigInteger n = fromHex("3FFFFFFF7FFFFFFFBE0024720613B5A3");
             BigInteger h = BigInteger.valueOf(4);
 
-            ECCurve curve = configureCurve(new ECCurve.Fp(p, a, b, n, h));
+            return configureCurve(new ECCurve.Fp(p, a, b, n, h, true));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = Hex.decodeStrict("004D696E67687561517512D8F03431FCE63B88F4");
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "047B6AA5D85E572983E6FB32A7CDEBC14027B6916A894D3AEE7106FE805FC34B44");
 
-            return new X9ECParameters(curve, G, n, h, S);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
@@ -142,13 +162,12 @@
      */
     static X9ECParametersHolder secp160k1 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             // p = 2^160 - 2^32 - 2^14 - 2^12 - 2^9 - 2^8 - 2^7 - 2^3 - 2^2 - 1
             BigInteger p = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFAC73");
             BigInteger a = ECConstants.ZERO;
             BigInteger b = BigInteger.valueOf(7);
-            byte[] S = null;
             BigInteger n = fromHex("0100000000000000000001B8FA16DFAB9ACA16B6B3");
             BigInteger h = BigInteger.valueOf(1);
 
@@ -166,12 +185,18 @@
                     new BigInteger("96341f1138933bc2f503fd44", 16),
                     176));
 
-            ECCurve curve = configureCurveGLV(new ECCurve.Fp(p, a, b, n, h), glv);
+            return configureCurveGLV(new ECCurve.Fp(p, a, b, n, h, true), glv);
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = null;
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "043B4C382CE37AA192A4019E763036F4F5DD4D7EBB938CF935318FDCED6BC28286531733C3F03C4FEE");
 
-            return new X9ECParameters(curve, G, n, h, S);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
@@ -180,22 +205,27 @@
      */
     static X9ECParametersHolder secp160r1 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             // p = 2^160 - 2^31 - 1
             BigInteger p = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7FFFFFFF");
             BigInteger a = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7FFFFFFC");
             BigInteger b = fromHex("1C97BEFC54BD7A8B65ACF89F81D4D4ADC565FA45");
-            byte[] S = Hex.decodeStrict("1053CDE42C14D696E67687561517533BF3F83345");
             BigInteger n = fromHex("0100000000000000000001F4C8F927AED3CA752257");
             BigInteger h = BigInteger.valueOf(1);
 
-            ECCurve curve = configureCurve(new ECCurve.Fp(p, a, b, n, h));
+            return configureCurve(new ECCurve.Fp(p, a, b, n, h, true));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = Hex.decodeStrict("1053CDE42C14D696E67687561517533BF3F83345");
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "044A96B5688EF573284664698968C38BB913CBFC8223A628553168947D59DCC912042351377AC5FB32");
 
-            return new X9ECParameters(curve, G, n, h, S);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
@@ -204,22 +234,27 @@
      */
     static X9ECParametersHolder secp160r2 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             // p = 2^160 - 2^32 - 2^14 - 2^12 - 2^9 - 2^8 - 2^7 - 2^3 - 2^2 - 1
             BigInteger p = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFAC73");
             BigInteger a = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFAC70");
             BigInteger b = fromHex("B4E134D3FB59EB8BAB57274904664D5AF50388BA");
-            byte[] S = Hex.decodeStrict("B99B99B099B323E02709A4D696E6768756151751");
             BigInteger n = fromHex("0100000000000000000000351EE786A818F3A1A16B");
             BigInteger h = BigInteger.valueOf(1);
 
-            ECCurve curve = configureCurve(new ECCurve.Fp(p, a, b, n, h));
+            return configureCurve(new ECCurve.Fp(p, a, b, n, h, true));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = Hex.decodeStrict("B99B99B099B323E02709A4D696E6768756151751");
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "0452DCB034293A117E1F4FF11B30F7199D3144CE6DFEAFFEF2E331F296E071FA0DF9982CFEA7D43F2E");
 
-            return new X9ECParameters(curve, G, n, h, S);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
@@ -228,13 +263,12 @@
      */
     static X9ECParametersHolder secp192k1 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             // p = 2^192 - 2^32 - 2^12 - 2^8 - 2^7 - 2^6 - 2^3 - 1
             BigInteger p = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFEE37");
             BigInteger a = ECConstants.ZERO;
             BigInteger b = BigInteger.valueOf(3);
-            byte[] S = null;
             BigInteger n = fromHex("FFFFFFFFFFFFFFFFFFFFFFFE26F2FC170F69466A74DEFD8D");
             BigInteger h = BigInteger.valueOf(1);
 
@@ -252,12 +286,18 @@
                     new BigInteger("b3fb3400dec5c4adceb8655d4c94", 16),
                     208));
 
-            ECCurve curve = configureCurveGLV(new ECCurve.Fp(p, a, b, n, h), glv);
+            return configureCurveGLV(new ECCurve.Fp(p, a, b, n, h, true), glv);
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = null;
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "04DB4FF10EC057E9AE26B07D0280B7F4341DA5D1B1EAE06C7D9B2F2F6D9C5628A7844163D015BE86344082AA88D95E2F9D");
 
-            return new X9ECParameters(curve, G, n, h, S);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
@@ -266,22 +306,27 @@
      */
     static X9ECParametersHolder secp192r1 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             // p = 2^192 - 2^64 - 1
             BigInteger p = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFF");
             BigInteger a = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFC");
             BigInteger b = fromHex("64210519E59C80E70FA7E9AB72243049FEB8DEECC146B9B1");
-            byte[] S = Hex.decodeStrict("3045AE6FC8422F64ED579528D38120EAE12196D5");
             BigInteger n = fromHex("FFFFFFFFFFFFFFFFFFFFFFFF99DEF836146BC9B1B4D22831");
             BigInteger h = BigInteger.valueOf(1);
 
-            ECCurve curve = configureCurve(new ECCurve.Fp(p, a, b, n, h));
+            return configureCurve(new ECCurve.Fp(p, a, b, n, h, true));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = Hex.decodeStrict("3045AE6FC8422F64ED579528D38120EAE12196D5");
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "04188DA80EB03090F67CBF20EB43A18800F4FF0AFD82FF101207192B95FFC8DA78631011ED6B24CDD573F977A11E794811");
 
-            return new X9ECParameters(curve, G, n, h, S);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
@@ -290,13 +335,12 @@
      */
     static X9ECParametersHolder secp224k1 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             // p = 2^224 - 2^32 - 2^12 - 2^11 - 2^9 - 2^7 - 2^4 - 2 - 1
             BigInteger p = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFE56D");
             BigInteger a = ECConstants.ZERO;
             BigInteger b = BigInteger.valueOf(5);
-            byte[] S = null;
             BigInteger n = fromHex("010000000000000000000000000001DCE8D2EC6184CAF0A971769FB1F7");
             BigInteger h = BigInteger.valueOf(1);
 
@@ -314,12 +358,18 @@
                     new BigInteger("b8adf1378a6eb73409fa6c9c637ba7f5", 16),
                     240));
 
-            ECCurve curve = configureCurveGLV(new ECCurve.Fp(p, a, b, n, h), glv);
+            return configureCurveGLV(new ECCurve.Fp(p, a, b, n, h, true), glv);
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = null;
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "04A1455B334DF099DF30FC28A169A467E9E47075A90F7E650EB6B7A45C7E089FED7FBA344282CAFBD6F7E319F7C0B0BD59E2CA4BDB556D61A5");
 
-            return new X9ECParameters(curve, G, n, h, S);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
@@ -328,22 +378,27 @@
      */
     static X9ECParametersHolder secp224r1 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             // p = 2^224 - 2^96 + 1
             BigInteger p = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000000000000001");
             BigInteger a = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFE");
             BigInteger b = fromHex("B4050A850C04B3ABF54132565044B0B7D7BFD8BA270B39432355FFB4");
-            byte[] S = Hex.decodeStrict("BD71344799D5C7FCDC45B59FA3B9AB8F6A948BC5");
             BigInteger n = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFF16A2E0B8F03E13DD29455C5C2A3D");
             BigInteger h = BigInteger.valueOf(1);
 
-            ECCurve curve = configureCurve(new ECCurve.Fp(p, a, b, n, h));
+            return configureCurve(new ECCurve.Fp(p, a, b, n, h, true));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = Hex.decodeStrict("BD71344799D5C7FCDC45B59FA3B9AB8F6A948BC5");
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "04B70E0CBD6BB4BF7F321390B94A03C1D356C21122343280D6115C1D21BD376388B5F723FB4C22DFE6CD4375A05A07476444D5819985007E34");
 
-            return new X9ECParameters(curve, G, n, h, S);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
@@ -352,13 +407,12 @@
      */
     static X9ECParametersHolder secp256k1 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             // p = 2^256 - 2^32 - 2^9 - 2^8 - 2^7 - 2^6 - 2^4 - 1
             BigInteger p = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F");
             BigInteger a = ECConstants.ZERO;
             BigInteger b = BigInteger.valueOf(7);
-            byte[] S = null;
             BigInteger n = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141");
             BigInteger h = BigInteger.valueOf(1);
 
@@ -376,12 +430,18 @@
                     new BigInteger("e4437ed6010e88286f547fa90abfe4c42212", 16),
                     272));
 
-            ECCurve curve = configureCurveGLV(new ECCurve.Fp(p, a, b, n, h), glv);
+            return configureCurveGLV(new ECCurve.Fp(p, a, b, n, h, true), glv);
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = null;
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "0479BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8");
 
-            return new X9ECParameters(curve, G, n, h, S);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
@@ -390,22 +450,27 @@
      */
     static X9ECParametersHolder secp256r1 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             // p = 2^224 (2^32 - 1) + 2^192 + 2^96 - 1
             BigInteger p = fromHex("FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF");
             BigInteger a = fromHex("FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC");
             BigInteger b = fromHex("5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B");
-            byte[] S = Hex.decodeStrict("C49D360886E704936A6678E1139D26B7819F7E90");
             BigInteger n = fromHex("FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551");
             BigInteger h = BigInteger.valueOf(1);
 
-            ECCurve curve = configureCurve(new ECCurve.Fp(p, a, b, n, h));
+            return configureCurve(new ECCurve.Fp(p, a, b, n, h, true));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = Hex.decodeStrict("C49D360886E704936A6678E1139D26B7819F7E90");
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "046B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C2964FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5");
 
-            return new X9ECParameters(curve, G, n, h, S);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
@@ -414,23 +479,28 @@
      */
     static X9ECParametersHolder secp384r1 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             // p = 2^384 - 2^128 - 2^96 + 2^32 - 1
             BigInteger p = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFF");
             BigInteger a = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFC");
             BigInteger b = fromHex("B3312FA7E23EE7E4988E056BE3F82D19181D9C6EFE8141120314088F5013875AC656398D8A2ED19D2A85C8EDD3EC2AEF");
-            byte[] S = Hex.decodeStrict("A335926AA319A27A1D00896A6773A4827ACDAC73");
             BigInteger n = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC7634D81F4372DDF581A0DB248B0A77AECEC196ACCC52973");
             BigInteger h = BigInteger.valueOf(1);
 
-            ECCurve curve = configureCurve(new ECCurve.Fp(p, a, b, n, h));
+            return configureCurve(new ECCurve.Fp(p, a, b, n, h, true));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = Hex.decodeStrict("A335926AA319A27A1D00896A6773A4827ACDAC73");
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve, "04"
                 + "AA87CA22BE8B05378EB1C71EF320AD746E1D3B628BA79B9859F741E082542A385502F25DBF55296C3A545E3872760AB7"
                 + "3617DE4A96262C6F5D9E98BF9292DC29F8F41DBD289A147CE9DA3113B5F0B8C00A60B1CE1D7E819D7A431D7C90EA0E5F");
 
-            return new X9ECParameters(curve, G, n, h, S);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
@@ -439,23 +509,28 @@
      */
     static X9ECParametersHolder secp521r1 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             // p = 2^521 - 1
             BigInteger p = fromHex("01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF");
             BigInteger a = fromHex("01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC");
             BigInteger b = fromHex("0051953EB9618E1C9A1F929A21A0B68540EEA2DA725B99B315F3B8B489918EF109E156193951EC7E937B1652C0BD3BB1BF073573DF883D2C34F1EF451FD46B503F00");
-            byte[] S = Hex.decodeStrict("D09E8800291CB85396CC6717393284AAA0DA64BA");
             BigInteger n = fromHex("01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA51868783BF2F966B7FCC0148F709A5D03BB5C9B8899C47AEBB6FB71E91386409");
             BigInteger h = BigInteger.valueOf(1);
 
-            ECCurve curve = configureCurve(new ECCurve.Fp(p, a, b, n, h));
+            return configureCurve(new ECCurve.Fp(p, a, b, n, h, true));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = Hex.decodeStrict("D09E8800291CB85396CC6717393284AAA0DA64BA");
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve, "04"
                 + "00C6858E06B70404E9CD9E3ECB662395B4429C648139053FB521F828AF606B4D3DBAA14B5E77EFE75928FE1DC127A2FFA8DE3348B3C1856A429BF97E7E31C2E5BD66"
                 + "011839296A789A3BC0045C8A5FB42C7D1BD998F54449579B446817AFBD17273E662C97EE72995EF42640C550B9013FAD0761353C7086A272C24088BE94769FD16650");
 
-            return new X9ECParameters(curve, G, n, h, S);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
@@ -464,23 +539,28 @@
      */
     static X9ECParametersHolder sect113r1 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             int m = 113;
             int k = 9;
 
             BigInteger a = fromHex("003088250CA6E7C7FE649CE85820F7");
             BigInteger b = fromHex("00E8BEE4D3E2260744188BE0E9C723");
-            byte[] S = Hex.decodeStrict("10E723AB14D696E6768756151756FEBF8FCB49A9");
             BigInteger n = fromHex("0100000000000000D9CCEC8A39E56F");
             BigInteger h = BigInteger.valueOf(2);
 
-            ECCurve curve = configureCurve(new ECCurve.F2m(m, k, a, b, n, h));
+            return configureCurve(new ECCurve.F2m(m, k, a, b, n, h));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = Hex.decodeStrict("10E723AB14D696E6768756151756FEBF8FCB49A9");
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "04009D73616F35F4AB1407D73562C10F00A52830277958EE84D1315ED31886");
 
-            return new X9ECParameters(curve, G, n, h, S);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
@@ -489,23 +569,28 @@
      */
     static X9ECParametersHolder sect113r2 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             int m = 113;
             int k = 9;
 
             BigInteger a = fromHex("00689918DBEC7E5A0DD6DFC0AA55C7");
             BigInteger b = fromHex("0095E9A9EC9B297BD4BF36E059184F");
-            byte[] S = Hex.decodeStrict("10C0FB15760860DEF1EEF4D696E676875615175D");
             BigInteger n = fromHex("010000000000000108789B2496AF93");
             BigInteger h = BigInteger.valueOf(2);
 
-            ECCurve curve = configureCurve(new ECCurve.F2m(m, k, a, b, n, h));
+            return configureCurve(new ECCurve.F2m(m, k, a, b, n, h));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = Hex.decodeStrict("10C0FB15760860DEF1EEF4D696E676875615175D");
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "0401A57A6A7B26CA5EF52FCDB816479700B3ADC94ED1FE674C06E695BABA1D");
 
-            return new X9ECParameters(curve, G, n, h, S);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
@@ -514,7 +599,7 @@
      */
     static X9ECParametersHolder sect131r1 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             int m = 131;
             int k1 = 2;
@@ -523,16 +608,21 @@
 
             BigInteger a = fromHex("07A11B09A76B562144418FF3FF8C2570B8");
             BigInteger b = fromHex("0217C05610884B63B9C6C7291678F9D341");
-            byte[] S = Hex.decodeStrict("4D696E676875615175985BD3ADBADA21B43A97E2");
             BigInteger n = fromHex("0400000000000000023123953A9464B54D");
             BigInteger h = BigInteger.valueOf(2);
 
-            ECCurve curve = configureCurve(new ECCurve.F2m(m, k1, k2, k3, a, b, n, h));
+            return configureCurve(new ECCurve.F2m(m, k1, k2, k3, a, b, n, h));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = Hex.decodeStrict("4D696E676875615175985BD3ADBADA21B43A97E2");
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "040081BAF91FDF9833C40F9C181343638399078C6E7EA38C001F73C8134B1B4EF9E150");
 
-            return new X9ECParameters(curve, G, n, h, S);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
@@ -541,7 +631,7 @@
      */
     static X9ECParametersHolder sect131r2 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             int m = 131;
             int k1 = 2;
@@ -550,16 +640,21 @@
 
             BigInteger a = fromHex("03E5A88919D7CAFCBF415F07C2176573B2");
             BigInteger b = fromHex("04B8266A46C55657AC734CE38F018F2192");
-            byte[] S = Hex.decodeStrict("985BD3ADBAD4D696E676875615175A21B43A97E3");
             BigInteger n = fromHex("0400000000000000016954A233049BA98F");
             BigInteger h = BigInteger.valueOf(2);
 
-            ECCurve curve = configureCurve(new ECCurve.F2m(m, k1, k2, k3, a, b, n, h));
+            return configureCurve(new ECCurve.F2m(m, k1, k2, k3, a, b, n, h));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = Hex.decodeStrict("985BD3ADBAD4D696E676875615175A21B43A97E3");
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "040356DCD8F2F95031AD652D23951BB366A80648F06D867940A5366D9E265DE9EB240F");
 
-            return new X9ECParameters(curve, G, n, h, S);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
@@ -568,7 +663,7 @@
      */
     static X9ECParametersHolder sect163k1 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             int m = 163;
             int k1 = 3;
@@ -577,16 +672,21 @@
 
             BigInteger a = BigInteger.valueOf(1);
             BigInteger b = BigInteger.valueOf(1);
-            byte[] S = null;
             BigInteger n = fromHex("04000000000000000000020108A2E0CC0D99F8A5EF");
             BigInteger h = BigInteger.valueOf(2);
 
-            ECCurve curve = configureCurve(new ECCurve.F2m(m, k1, k2, k3, a, b, n, h));
+            return configureCurve(new ECCurve.F2m(m, k1, k2, k3, a, b, n, h));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = null;
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "0402FE13C0537BBC11ACAA07D793DE4E6D5E5C94EEE80289070FB05D38FF58321F2E800536D538CCDAA3D9");
 
-            return new X9ECParameters(curve, G, n, h, S);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
@@ -595,7 +695,7 @@
      */
     static X9ECParametersHolder sect163r1 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             int m = 163;
             int k1 = 3;
@@ -604,16 +704,21 @@
 
             BigInteger a = fromHex("07B6882CAAEFA84F9554FF8428BD88E246D2782AE2");
             BigInteger b = fromHex("0713612DCDDCB40AAB946BDA29CA91F73AF958AFD9");
-            byte[] S = Hex.decodeStrict("24B7B137C8A14D696E6768756151756FD0DA2E5C");
             BigInteger n = fromHex("03FFFFFFFFFFFFFFFFFFFF48AAB689C29CA710279B");
             BigInteger h = BigInteger.valueOf(2);
 
-            ECCurve curve = configureCurve(new ECCurve.F2m(m, k1, k2, k3, a, b, n, h));
+            return configureCurve(new ECCurve.F2m(m, k1, k2, k3, a, b, n, h));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = Hex.decodeStrict("24B7B137C8A14D696E6768756151756FD0DA2E5C");
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "040369979697AB43897789566789567F787A7876A65400435EDB42EFAFB2989D51FEFCE3C80988F41FF883");
 
-            return new X9ECParameters(curve, G, n, h, S);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
@@ -622,7 +727,7 @@
      */
     static X9ECParametersHolder sect163r2 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             int m = 163;
             int k1 = 3;
@@ -631,16 +736,21 @@
 
             BigInteger a = BigInteger.valueOf(1);
             BigInteger b = fromHex("020A601907B8C953CA1481EB10512F78744A3205FD");
-            byte[] S = Hex.decodeStrict("85E25BFE5C86226CDB12016F7553F9D0E693A268");
             BigInteger n = fromHex("040000000000000000000292FE77E70C12A4234C33");
             BigInteger h = BigInteger.valueOf(2);
 
-            ECCurve curve = configureCurve(new ECCurve.F2m(m, k1, k2, k3, a, b, n, h));
+            return configureCurve(new ECCurve.F2m(m, k1, k2, k3, a, b, n, h));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = Hex.decodeStrict("85E25BFE5C86226CDB12016F7553F9D0E693A268");
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "0403F0EBA16286A2D57EA0991168D4994637E8343E3600D51FBC6C71A0094FA2CDD545B11C5C0C797324F1");
 
-            return new X9ECParameters(curve, G, n, h, S);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
@@ -649,23 +759,28 @@
      */
     static X9ECParametersHolder sect193r1 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             int m = 193;
             int k = 15;
 
             BigInteger a = fromHex("0017858FEB7A98975169E171F77B4087DE098AC8A911DF7B01");
             BigInteger b = fromHex("00FDFB49BFE6C3A89FACADAA7A1E5BBC7CC1C2E5D831478814");
-            byte[] S = Hex.decodeStrict("103FAEC74D696E676875615175777FC5B191EF30");
             BigInteger n = fromHex("01000000000000000000000000C7F34A778F443ACC920EBA49");
             BigInteger h = BigInteger.valueOf(2);
 
-            ECCurve curve = configureCurve(new ECCurve.F2m(m, k, a, b, n, h));
+            return configureCurve(new ECCurve.F2m(m, k, a, b, n, h));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = Hex.decodeStrict("103FAEC74D696E676875615175777FC5B191EF30");
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "0401F481BC5F0FF84A74AD6CDF6FDEF4BF6179625372D8C0C5E10025E399F2903712CCF3EA9E3A1AD17FB0B3201B6AF7CE1B05");
 
-            return new X9ECParameters(curve, G, n, h, S);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
@@ -674,23 +789,28 @@
      */
     static X9ECParametersHolder sect193r2 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             int m = 193;
             int k = 15;
 
             BigInteger a = fromHex("0163F35A5137C2CE3EA6ED8667190B0BC43ECD69977702709B");
             BigInteger b = fromHex("00C9BB9E8927D4D64C377E2AB2856A5B16E3EFB7F61D4316AE");
-            byte[] S = Hex.decodeStrict("10B7B4D696E676875615175137C8A16FD0DA2211");
             BigInteger n = fromHex("010000000000000000000000015AAB561B005413CCD4EE99D5");
             BigInteger h = BigInteger.valueOf(2);
 
-            ECCurve curve = configureCurve(new ECCurve.F2m(m, k, a, b, n, h));
+            return configureCurve(new ECCurve.F2m(m, k, a, b, n, h));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = Hex.decodeStrict("10B7B4D696E676875615175137C8A16FD0DA2211");
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "0400D9B67D192E0367C803F39E1A7E82CA14A651350AAE617E8F01CE94335607C304AC29E7DEFBD9CA01F596F927224CDECF6C");
 
-            return new X9ECParameters(curve, G, n, h, S);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
@@ -699,23 +819,28 @@
      */
     static X9ECParametersHolder sect233k1 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             int m = 233;
             int k = 74;
 
             BigInteger a = ECConstants.ZERO;
             BigInteger b = BigInteger.valueOf(1);
-            byte[] S = null;
             BigInteger n = fromHex("8000000000000000000000000000069D5BB915BCD46EFB1AD5F173ABDF");
             BigInteger h = BigInteger.valueOf(4);
 
-            ECCurve curve = configureCurve(new ECCurve.F2m(m, k, a, b, n, h));
+            return configureCurve(new ECCurve.F2m(m, k, a, b, n, h));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = null;
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "04017232BA853A7E731AF129F22FF4149563A419C26BF50A4C9D6EEFAD612601DB537DECE819B7F70F555A67C427A8CD9BF18AEB9B56E0C11056FAE6A3");
 
-            return new X9ECParameters(curve, G, n, h, S);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
@@ -724,23 +849,28 @@
      */
     static X9ECParametersHolder sect233r1 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             int m = 233;
             int k = 74;
 
             BigInteger a = BigInteger.valueOf(1);
             BigInteger b = fromHex("0066647EDE6C332C7F8C0923BB58213B333B20E9CE4281FE115F7D8F90AD");
-            byte[] S = Hex.decodeStrict("74D59FF07F6B413D0EA14B344B20A2DB049B50C3");
             BigInteger n = fromHex("01000000000000000000000000000013E974E72F8A6922031D2603CFE0D7");
             BigInteger h = BigInteger.valueOf(2);
 
-            ECCurve curve = configureCurve(new ECCurve.F2m(m, k, a, b, n, h));
+            return configureCurve(new ECCurve.F2m(m, k, a, b, n, h));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = Hex.decodeStrict("74D59FF07F6B413D0EA14B344B20A2DB049B50C3");
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "0400FAC9DFCBAC8313BB2139F1BB755FEF65BC391F8B36F8F8EB7371FD558B01006A08A41903350678E58528BEBF8A0BEFF867A7CA36716F7E01F81052");
 
-            return new X9ECParameters(curve, G, n, h, S);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
@@ -749,23 +879,28 @@
      */
     static X9ECParametersHolder sect239k1 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             int m = 239;
             int k = 158;
 
             BigInteger a = ECConstants.ZERO;
             BigInteger b = BigInteger.valueOf(1);
-            byte[] S = null;
             BigInteger n = fromHex("2000000000000000000000000000005A79FEC67CB6E91F1C1DA800E478A5");
             BigInteger h = BigInteger.valueOf(4);
 
-            ECCurve curve = configureCurve(new ECCurve.F2m(m, k, a, b, n, h));
+            return configureCurve(new ECCurve.F2m(m, k, a, b, n, h));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = null;
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "0429A0B6A887A983E9730988A68727A8B2D126C44CC2CC7B2A6555193035DC76310804F12E549BDB011C103089E73510ACB275FC312A5DC6B76553F0CA");
 
-            return new X9ECParameters(curve, G, n, h, S);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
@@ -774,7 +909,7 @@
      */
     static X9ECParametersHolder sect283k1 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             int m = 283;
             int k1 = 5;
@@ -783,17 +918,22 @@
 
             BigInteger a = ECConstants.ZERO;
             BigInteger b = BigInteger.valueOf(1);
-            byte[] S = null;
             BigInteger n = fromHex("01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE9AE2ED07577265DFF7F94451E061E163C61");
             BigInteger h = BigInteger.valueOf(4);
 
-            ECCurve curve = configureCurve(new ECCurve.F2m(m, k1, k2, k3, a, b, n, h));
+            return configureCurve(new ECCurve.F2m(m, k1, k2, k3, a, b, n, h));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = null;
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve, "04"
                 + "0503213F78CA44883F1A3B8162F188E553CD265F23C1567A16876913B0C2AC2458492836"
                 + "01CCDA380F1C9E318D90F95D07E5426FE87E45C0E8184698E45962364E34116177DD2259");
 
-            return new X9ECParameters(curve, G, n, h, S);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
@@ -802,7 +942,7 @@
      */
     static X9ECParametersHolder sect283r1 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             int m = 283;
             int k1 = 5;
@@ -811,17 +951,22 @@
 
             BigInteger a = BigInteger.valueOf(1);
             BigInteger b = fromHex("027B680AC8B8596DA5A4AF8A19A0303FCA97FD7645309FA2A581485AF6263E313B79A2F5");
-            byte[] S = Hex.decodeStrict("77E2B07370EB0F832A6DD5B62DFC88CD06BB84BE");
             BigInteger n = fromHex("03FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEF90399660FC938A90165B042A7CEFADB307");
             BigInteger h = BigInteger.valueOf(2);
 
-            ECCurve curve = configureCurve(new ECCurve.F2m(m, k1, k2, k3, a, b, n, h));
+            return configureCurve(new ECCurve.F2m(m, k1, k2, k3, a, b, n, h));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = Hex.decodeStrict("77E2B07370EB0F832A6DD5B62DFC88CD06BB84BE");
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve, "04"
                 + "05F939258DB7DD90E1934F8C70B0DFEC2EED25B8557EAC9C80E2E198F8CDBECD86B12053"
                 + "03676854FE24141CB98FE6D4B20D02B4516FF702350EDDB0826779C813F0DF45BE8112F4");
 
-            return new X9ECParameters(curve, G, n, h, S);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
@@ -830,24 +975,29 @@
      */
     static X9ECParametersHolder sect409k1 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             int m = 409;
             int k = 87;
 
             BigInteger a = ECConstants.ZERO;
             BigInteger b = BigInteger.valueOf(1);
-            byte[] S = null;
             BigInteger n = fromHex("7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE5F83B2D4EA20400EC4557D5ED3E3E7CA5B4B5C83B8E01E5FCF");
             BigInteger h = BigInteger.valueOf(4);
 
-            ECCurve curve = configureCurve(new ECCurve.F2m(m, k, a, b, n, h));
+            return configureCurve(new ECCurve.F2m(m, k, a, b, n, h));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = null;
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve, "04"
                 + "0060F05F658F49C1AD3AB1890F7184210EFD0987E307C84C27ACCFB8F9F67CC2C460189EB5AAAA62EE222EB1B35540CFE9023746"
                 + "01E369050B7C4E42ACBA1DACBF04299C3460782F918EA427E6325165E9EA10E3DA5F6C42E9C55215AA9CA27A5863EC48D8E0286B");
 
-            return new X9ECParameters(curve, G, n, h, S);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
@@ -856,24 +1006,29 @@
      */
     static X9ECParametersHolder sect409r1 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             int m = 409;
             int k = 87;
 
             BigInteger a = BigInteger.valueOf(1);
             BigInteger b = fromHex("0021A5C2C8EE9FEB5C4B9A753B7B476B7FD6422EF1F3DD674761FA99D6AC27C8A9A197B272822F6CD57A55AA4F50AE317B13545F");
-            byte[] S = Hex.decodeStrict("4099B5A457F9D69F79213D094C4BCD4D4262210B");
             BigInteger n = fromHex("010000000000000000000000000000000000000000000000000001E2AAD6A612F33307BE5FA47C3C9E052F838164CD37D9A21173");
             BigInteger h = BigInteger.valueOf(2);
 
-            ECCurve curve = configureCurve(new ECCurve.F2m(m, k, a, b, n, h));
+            return configureCurve(new ECCurve.F2m(m, k, a, b, n, h));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = Hex.decodeStrict("4099B5A457F9D69F79213D094C4BCD4D4262210B");
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve, "04"
                 + "015D4860D088DDB3496B0C6064756260441CDE4AF1771D4DB01FFE5B34E59703DC255A868A1180515603AEAB60794E54BB7996A7"
                 + "0061B1CFAB6BE5F32BBFA78324ED106A7636B9C5A7BD198D0158AA4F5488D08F38514F1FDF4B4F40D2181B3681C364BA0273C706");
 
-            return new X9ECParameters(curve, G, n, h, S);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
@@ -882,7 +1037,7 @@
      */
     static X9ECParametersHolder sect571k1 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             int m = 571;
             int k1 = 2;
@@ -891,17 +1046,22 @@
 
             BigInteger a = ECConstants.ZERO;
             BigInteger b = BigInteger.valueOf(1);
-            byte[] S = null;
             BigInteger n = fromHex("020000000000000000000000000000000000000000000000000000000000000000000000131850E1F19A63E4B391A8DB917F4138B630D84BE5D639381E91DEB45CFE778F637C1001");
             BigInteger h = BigInteger.valueOf(4);
 
-            ECCurve curve = configureCurve(new ECCurve.F2m(m, k1, k2, k3, a, b, n, h));
+            return configureCurve(new ECCurve.F2m(m, k1, k2, k3, a, b, n, h));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = null;
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve, "04"
                 + "026EB7A859923FBC82189631F8103FE4AC9CA2970012D5D46024804801841CA44370958493B205E647DA304DB4CEB08CBBD1BA39494776FB988B47174DCA88C7E2945283A01C8972"
                 + "0349DC807F4FBF374F4AEADE3BCA95314DD58CEC9F307A54FFC61EFC006D8A2C9D4979C0AC44AEA74FBEBBB9F772AEDCB620B01A7BA7AF1B320430C8591984F601CD4C143EF1C7A3");
 
-            return new X9ECParameters(curve, G, n, h, S);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
@@ -910,7 +1070,7 @@
      */
     static X9ECParametersHolder sect571r1 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             int m = 571;
             int k1 = 2;
@@ -919,17 +1079,22 @@
 
             BigInteger a = BigInteger.valueOf(1);
             BigInteger b = fromHex("02F40E7E2221F295DE297117B7F3D62F5C6A97FFCB8CEFF1CD6BA8CE4A9A18AD84FFABBD8EFA59332BE7AD6756A66E294AFD185A78FF12AA520E4DE739BACA0C7FFEFF7F2955727A");
-            byte[] S = Hex.decodeStrict("2AA058F73A0E33AB486B0F610410C53A7F132310");
             BigInteger n = fromHex("03FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE661CE18FF55987308059B186823851EC7DD9CA1161DE93D5174D66E8382E9BB2FE84E47");
             BigInteger h = BigInteger.valueOf(2);
 
-            ECCurve curve = configureCurve(new ECCurve.F2m(m, k1, k2, k3, a, b, n, h));
+            return configureCurve(new ECCurve.F2m(m, k1, k2, k3, a, b, n, h));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = Hex.decodeStrict("2AA058F73A0E33AB486B0F610410C53A7F132310");
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve, "04"
                 + "0303001D34B856296C16C0D40D3CD7750A93D1D2955FA80AA5F40FC8DB7B2ABDBDE53950F4C0D293CDD711A35B67FB1499AE60038614F1394ABFA3B4C850D927E1E7769C8EEC2D19"
                 + "037BF27342DA639B6DCCFFFEB73D69D78C6C27A6009CBBCA1980F8533921E8A684423E43BAB08A576291AF8F461BB2A8B3531D2F0485C19B16E2F1516E23DD3C1A4827AF1B8AC15B");
 
-            return new X9ECParameters(curve, G, n, h, S);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
@@ -983,26 +1148,35 @@
         defineCurve("sect571r1", SECObjectIdentifiers.sect571r1, sect571r1); 
     }
 
-    public static X9ECParameters getByName(
-        String name)
+    public static X9ECParameters getByName(String name)
     {
         ASN1ObjectIdentifier oid = getOID(name);
         return oid == null ? null : getByOID(oid);
     }
 
+    public static X9ECParametersHolder getByNameLazy(String name)
+    {
+        ASN1ObjectIdentifier oid = getOID(name);
+        return oid == null ? null : getByOIDLazy(oid);
+    }
+
     /**
      * return the X9ECParameters object for the named curve represented by
      * the passed in object identifier. Null if the curve isn't present.
      *
      * @param oid an object identifier representing a named curve, if present.
      */
-    public static X9ECParameters getByOID(
-        ASN1ObjectIdentifier oid)
+    public static X9ECParameters getByOID(ASN1ObjectIdentifier oid)
     {
-        X9ECParametersHolder holder = (X9ECParametersHolder)curves.get(oid);
+        X9ECParametersHolder holder = getByOIDLazy(oid);
         return holder == null ? null : holder.getParameters();
     }
 
+    public static X9ECParametersHolder getByOIDLazy(ASN1ObjectIdentifier oid)
+    {
+        return (X9ECParametersHolder)curves.get(oid);
+    }
+
     /**
      * return the object identifier signified by the passed in name. Null
      * if there is no object identifier associated with name.
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/util/ASN1Dump.java b/bcprov/src/main/java/org/bouncycastle/asn1/util/ASN1Dump.java
index a8e07fe..4ff5e2d 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/util/ASN1Dump.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/util/ASN1Dump.java
@@ -1,42 +1,41 @@
 package org.bouncycastle.asn1.util;
 
-import java.io.IOException;
-import java.util.Enumeration;
-
-import org.bouncycastle.asn1.ASN1ApplicationSpecific;
+import org.bouncycastle.asn1.ASN1BMPString;
+import org.bouncycastle.asn1.ASN1BitString;
 import org.bouncycastle.asn1.ASN1Boolean;
 import org.bouncycastle.asn1.ASN1Encodable;
 import org.bouncycastle.asn1.ASN1Enumerated;
 import org.bouncycastle.asn1.ASN1External;
 import org.bouncycastle.asn1.ASN1GeneralizedTime;
+import org.bouncycastle.asn1.ASN1GraphicString;
+import org.bouncycastle.asn1.ASN1IA5String;
 import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Null;
+import org.bouncycastle.asn1.ASN1NumericString;
+import org.bouncycastle.asn1.ASN1ObjectDescriptor;
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
 import org.bouncycastle.asn1.ASN1OctetString;
 import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1PrintableString;
+import org.bouncycastle.asn1.ASN1RelativeOID;
 import org.bouncycastle.asn1.ASN1Sequence;
 import org.bouncycastle.asn1.ASN1Set;
+import org.bouncycastle.asn1.ASN1T61String;
 import org.bouncycastle.asn1.ASN1TaggedObject;
 import org.bouncycastle.asn1.ASN1UTCTime;
-import org.bouncycastle.asn1.BERApplicationSpecific;
+import org.bouncycastle.asn1.ASN1UTF8String;
+import org.bouncycastle.asn1.ASN1Util;
+import org.bouncycastle.asn1.ASN1VideotexString;
+import org.bouncycastle.asn1.ASN1VisibleString;
 import org.bouncycastle.asn1.BEROctetString;
 import org.bouncycastle.asn1.BERSequence;
 import org.bouncycastle.asn1.BERSet;
 import org.bouncycastle.asn1.BERTaggedObject;
-import org.bouncycastle.asn1.BERTags;
-import org.bouncycastle.asn1.DERApplicationSpecific;
-import org.bouncycastle.asn1.DERBMPString;
 import org.bouncycastle.asn1.DERBitString;
-import org.bouncycastle.asn1.DERGraphicString;
-import org.bouncycastle.asn1.DERIA5String;
-import org.bouncycastle.asn1.DERNull;
-import org.bouncycastle.asn1.DERPrintableString;
 import org.bouncycastle.asn1.DERSequence;
 import org.bouncycastle.asn1.DERSet;
-import org.bouncycastle.asn1.DERT61String;
-import org.bouncycastle.asn1.DERUTF8String;
-import org.bouncycastle.asn1.DERVideotexString;
-import org.bouncycastle.asn1.DERVisibleString;
-import org.bouncycastle.asn1.DLApplicationSpecific;
+import org.bouncycastle.asn1.DERTaggedObject;
+import org.bouncycastle.asn1.DLBitString;
 import org.bouncycastle.util.Strings;
 import org.bouncycastle.util.encoders.Hex;
 
@@ -60,11 +59,14 @@
         StringBuffer    buf)
     {
         String nl = Strings.lineSeparator();
-        if (obj instanceof ASN1Sequence)
+        if (obj instanceof ASN1Null)
         {
-            Enumeration     e = ((ASN1Sequence)obj).getObjects();
-            String          tab = indent + TAB;
-
+            buf.append(indent);
+            buf.append("NULL");
+            buf.append(nl);
+        }
+        else if (obj instanceof ASN1Sequence)
+        {
             buf.append(indent);
             if (obj instanceof BERSequence)
             {
@@ -78,64 +80,19 @@
             {
                 buf.append("Sequence");
             }
-
             buf.append(nl);
 
-            while (e.hasMoreElements())
+            ASN1Sequence sequence = (ASN1Sequence)obj;
+            String elementsIndent = indent + TAB;
+
+            for (int i = 0, count = sequence.size(); i < count; ++i)
             {
-                Object  o = e.nextElement();
-
-                if (o == null || o.equals(DERNull.INSTANCE))
-                {
-                    buf.append(tab);
-                    buf.append("NULL");
-                    buf.append(nl);
-                }
-                else if (o instanceof ASN1Primitive)
-                {
-                    _dumpAsString(tab, verbose, (ASN1Primitive)o, buf);
-                }
-                else
-                {
-                    _dumpAsString(tab, verbose, ((ASN1Encodable)o).toASN1Primitive(), buf);
-                }
+                _dumpAsString(elementsIndent, verbose, sequence.getObjectAt(i).toASN1Primitive(), buf);
             }
         }
-        else if (obj instanceof ASN1TaggedObject)
-        {
-            String          tab = indent + TAB;
-
-            buf.append(indent);
-            if (obj instanceof BERTaggedObject)
-            {
-                buf.append("BER Tagged [");
-            }
-            else
-            {
-                buf.append("Tagged [");
-            }
-
-            ASN1TaggedObject o = (ASN1TaggedObject)obj;
-
-            buf.append(Integer.toString(o.getTagNo()));
-            buf.append(']');
-
-            if (!o.isExplicit())
-            {
-                buf.append(" IMPLICIT ");
-            }
-
-            buf.append(nl);
-
-            _dumpAsString(tab, verbose, o.getObject(), buf);
-        }
         else if (obj instanceof ASN1Set)
         {
-            Enumeration     e = ((ASN1Set)obj).getObjects();
-            String          tab = indent + TAB;
-
             buf.append(indent);
-
             if (obj instanceof BERSet)
             {
                 buf.append("BER Set");
@@ -150,26 +107,45 @@
             }
             buf.append(nl);
 
-            while (e.hasMoreElements())
-            {
-                Object  o = e.nextElement();
+            ASN1Set set = (ASN1Set)obj;
+            String elementsIndent = indent + TAB;
 
-                if (o == null)
-                {
-                    buf.append(tab);
-                    buf.append("NULL");
-                    buf.append(nl);
-                }
-                else if (o instanceof ASN1Primitive)
-                {
-                    _dumpAsString(tab, verbose, (ASN1Primitive)o, buf);
-                }
-                else
-                {
-                    _dumpAsString(tab, verbose, ((ASN1Encodable)o).toASN1Primitive(), buf);
-                }
+            for (int i = 0, count = set.size(); i < count; ++i)
+            {
+                _dumpAsString(elementsIndent, verbose, set.getObjectAt(i).toASN1Primitive(), buf);
             }
         }
+        else if (obj instanceof ASN1TaggedObject)
+        {
+            buf.append(indent);
+            if (obj instanceof BERTaggedObject)
+            {
+                buf.append("BER Tagged ");
+            }
+            else if (obj instanceof DERTaggedObject)
+            {
+                buf.append("DER Tagged ");
+            }
+            else
+            {
+                buf.append("Tagged ");
+            }
+
+            ASN1TaggedObject o = (ASN1TaggedObject)obj;
+
+            buf.append(ASN1Util.getTagText(o));
+
+            if (!o.isExplicit())
+            {
+                buf.append(" IMPLICIT ");
+            }
+
+            buf.append(nl);
+
+            String baseIndent = indent + TAB;
+
+            _dumpAsString(baseIndent, verbose, o.getBaseObject().toASN1Primitive(), buf);
+        }
         else if (obj instanceof ASN1OctetString)
         {
             ASN1OctetString oct = (ASN1OctetString)obj;
@@ -195,6 +171,10 @@
         {
             buf.append(indent + "ObjectIdentifier(" + ((ASN1ObjectIdentifier)obj).getId() + ")" + nl);
         }
+        else if (obj instanceof ASN1RelativeOID)
+        {
+            buf.append(indent + "RelativeOID(" + ((ASN1RelativeOID)obj).getId() + ")" + nl);
+        }
         else if (obj instanceof ASN1Boolean)
         {
             buf.append(indent + "Boolean(" + ((ASN1Boolean)obj).isTrue() + ")" + nl);
@@ -203,50 +183,70 @@
         {
             buf.append(indent + "Integer(" + ((ASN1Integer)obj).getValue() + ")" + nl);
         }
-        else if (obj instanceof DERBitString)
+        else if (obj instanceof ASN1BitString)
         {
-            DERBitString bt = (DERBitString)obj;
-            buf.append(indent + "DER Bit String" + "[" + bt.getBytes().length + ", " + bt.getPadBits() + "] ");
+            ASN1BitString bitString = (ASN1BitString)obj;
+
+            byte[] bytes = bitString.getBytes();
+            int padBits = bitString.getPadBits();
+
+            if (bitString instanceof DERBitString)
+            {
+                buf.append(indent + "DER Bit String" + "[" + bytes.length + ", " + padBits + "] ");
+            }
+            else if (bitString instanceof DLBitString)
+            {
+                buf.append(indent + "DL Bit String" + "[" + bytes.length + ", " + padBits + "] ");
+            }
+            else
+            {
+                buf.append(indent + "BER Bit String" + "[" + bytes.length + ", " + padBits + "] ");
+            }
+
             if (verbose)
             {
-                buf.append(dumpBinaryDataAsString(indent, bt.getBytes()));
+                buf.append(dumpBinaryDataAsString(indent, bytes));
             }
             else
             {
                 buf.append(nl);
             }
         }
-        else if (obj instanceof DERIA5String)
+        else if (obj instanceof ASN1IA5String)
         {
-            buf.append(indent + "IA5String(" + ((DERIA5String)obj).getString() + ") " + nl);
+            buf.append(indent + "IA5String(" + ((ASN1IA5String)obj).getString() + ") " + nl);
         }
-        else if (obj instanceof DERUTF8String)
+        else if (obj instanceof ASN1UTF8String)
         {
-            buf.append(indent + "UTF8String(" + ((DERUTF8String)obj).getString() + ") " + nl);
+            buf.append(indent + "UTF8String(" + ((ASN1UTF8String)obj).getString() + ") " + nl);
         }
-        else if (obj instanceof DERPrintableString)
+        else if (obj instanceof ASN1NumericString)
         {
-            buf.append(indent + "PrintableString(" + ((DERPrintableString)obj).getString() + ") " + nl);
+            buf.append(indent + "NumericString(" + ((ASN1NumericString)obj).getString() + ") " + nl);
         }
-        else if (obj instanceof DERVisibleString)
+        else if (obj instanceof ASN1PrintableString)
         {
-            buf.append(indent + "VisibleString(" + ((DERVisibleString)obj).getString() + ") " + nl);
+            buf.append(indent + "PrintableString(" + ((ASN1PrintableString)obj).getString() + ") " + nl);
         }
-        else if (obj instanceof DERBMPString)
+        else if (obj instanceof ASN1VisibleString)
         {
-            buf.append(indent + "BMPString(" + ((DERBMPString)obj).getString() + ") " + nl);
+            buf.append(indent + "VisibleString(" + ((ASN1VisibleString)obj).getString() + ") " + nl);
         }
-        else if (obj instanceof DERT61String)
+        else if (obj instanceof ASN1BMPString)
         {
-            buf.append(indent + "T61String(" + ((DERT61String)obj).getString() + ") " + nl);
+            buf.append(indent + "BMPString(" + ((ASN1BMPString)obj).getString() + ") " + nl);
         }
-        else if (obj instanceof DERGraphicString)
+        else if (obj instanceof ASN1T61String)
         {
-            buf.append(indent + "GraphicString(" + ((DERGraphicString)obj).getString() + ") " + nl);
+            buf.append(indent + "T61String(" + ((ASN1T61String)obj).getString() + ") " + nl);
         }
-        else if (obj instanceof DERVideotexString)
+        else if (obj instanceof ASN1GraphicString)
         {
-            buf.append(indent + "VideotexString(" + ((DERVideotexString)obj).getString() + ") " + nl);
+            buf.append(indent + "GraphicString(" + ((ASN1GraphicString)obj).getString() + ") " + nl);
+        }
+        else if (obj instanceof ASN1VideotexString)
+        {
+            buf.append(indent + "VideotexString(" + ((ASN1VideotexString)obj).getString() + ") " + nl);
         }
         else if (obj instanceof ASN1UTCTime)
         {
@@ -256,23 +256,16 @@
         {
             buf.append(indent + "GeneralizedTime(" + ((ASN1GeneralizedTime)obj).getTime() + ") " + nl);
         }
-        else if (obj instanceof BERApplicationSpecific)
-        {
-            buf.append(outputApplicationSpecific("BER", indent, verbose, obj, nl));
-        }
-        else if (obj instanceof DERApplicationSpecific)
-        {
-            buf.append(outputApplicationSpecific("DER", indent, verbose, obj, nl));
-        }
-        else if (obj instanceof DLApplicationSpecific)
-        {
-            buf.append(outputApplicationSpecific("", indent, verbose, obj, nl));
-        }
         else if (obj instanceof ASN1Enumerated)
         {
             ASN1Enumerated en = (ASN1Enumerated) obj;
             buf.append(indent + "DER Enumerated(" + en.getValue() + ")" + nl);
         }
+        else if (obj instanceof ASN1ObjectDescriptor)
+        {
+            ASN1ObjectDescriptor od = (ASN1ObjectDescriptor)obj;
+            buf.append(indent + "ObjectDescriptor(" + od.getBaseGraphicString().getString() + ") " + nl);
+        }
         else if (obj instanceof ASN1External)
         {
             ASN1External ext = (ASN1External) obj;
@@ -298,32 +291,6 @@
             buf.append(indent + obj.toString() + nl);
         }
     }
-    
-    private static String outputApplicationSpecific(String type, String indent, boolean verbose, ASN1Primitive obj, String nl)
-    {
-        ASN1ApplicationSpecific app = ASN1ApplicationSpecific.getInstance(obj);
-        StringBuffer buf = new StringBuffer();
-
-        if (app.isConstructed())
-        {
-            try
-            {
-                ASN1Sequence s = ASN1Sequence.getInstance(app.getObject(BERTags.SEQUENCE));
-                buf.append(indent + type + " ApplicationSpecific[" + app.getApplicationTag() + "]" + nl);
-                for (Enumeration e = s.getObjects(); e.hasMoreElements();)
-                {
-                    _dumpAsString(indent + TAB, verbose, (ASN1Primitive)e.nextElement(), buf);
-                }
-            }
-            catch (IOException e)
-            {
-                buf.append(e);
-            }
-            return buf.toString();
-        }
-
-        return indent + type + " ApplicationSpecific[" + app.getApplicationTag() + "] (" + Strings.fromByteArray(Hex.encode(app.getContents())) + ")" + nl;
-    }
 
     /**
      * dump out a DER object as a formatted string, in non-verbose mode.
@@ -348,21 +315,22 @@
         Object   obj,
         boolean  verbose)
     {
-        StringBuffer buf = new StringBuffer();
-
+        ASN1Primitive primitive;
         if (obj instanceof ASN1Primitive)
         {
-            _dumpAsString("", verbose, (ASN1Primitive)obj, buf);
+            primitive = (ASN1Primitive)obj;
         }
         else if (obj instanceof ASN1Encodable)
         {
-            _dumpAsString("", verbose, ((ASN1Encodable)obj).toASN1Primitive(), buf);
+            primitive = ((ASN1Encodable)obj).toASN1Primitive();
         }
         else
         {
             return "unknown object type " + obj.toString();
         }
 
+        StringBuffer buf = new StringBuffer();
+        _dumpAsString("", verbose, primitive, buf);
         return buf.toString();
     }
 
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x500/DirectoryString.java b/bcprov/src/main/java/org/bouncycastle/asn1/x500/DirectoryString.java
index 39312de..3d09921 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/x500/DirectoryString.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/x500/DirectoryString.java
@@ -1,16 +1,17 @@
 package org.bouncycastle.asn1.x500;
 
+import org.bouncycastle.asn1.ASN1BMPString;
 import org.bouncycastle.asn1.ASN1Choice;
 import org.bouncycastle.asn1.ASN1Encodable;
 import org.bouncycastle.asn1.ASN1Object;
 import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1PrintableString;
 import org.bouncycastle.asn1.ASN1String;
+import org.bouncycastle.asn1.ASN1T61String;
 import org.bouncycastle.asn1.ASN1TaggedObject;
-import org.bouncycastle.asn1.DERBMPString;
-import org.bouncycastle.asn1.DERPrintableString;
-import org.bouncycastle.asn1.DERT61String;
+import org.bouncycastle.asn1.ASN1UTF8String;
+import org.bouncycastle.asn1.ASN1UniversalString;
 import org.bouncycastle.asn1.DERUTF8String;
-import org.bouncycastle.asn1.DERUniversalString;
 
 /**
  * The DirectoryString CHOICE object.
@@ -28,29 +29,29 @@
             return (DirectoryString)o;
         }
 
-        if (o instanceof DERT61String)
+        if (o instanceof ASN1T61String)
         {
-            return new DirectoryString((DERT61String)o);
+            return new DirectoryString((ASN1T61String)o);
         }
 
-        if (o instanceof DERPrintableString)
+        if (o instanceof ASN1PrintableString)
         {
-            return new DirectoryString((DERPrintableString)o);
+            return new DirectoryString((ASN1PrintableString)o);
         }
 
-        if (o instanceof DERUniversalString)
+        if (o instanceof ASN1UniversalString)
         {
-            return new DirectoryString((DERUniversalString)o);
+            return new DirectoryString((ASN1UniversalString)o);
         }
 
-        if (o instanceof DERUTF8String)
+        if (o instanceof ASN1UTF8String)
         {
-            return new DirectoryString((DERUTF8String)o);
+            return new DirectoryString((ASN1UTF8String)o);
         }
 
-        if (o instanceof DERBMPString)
+        if (o instanceof ASN1BMPString)
         {
-            return new DirectoryString((DERBMPString)o);
+            return new DirectoryString((ASN1BMPString)o);
         }
 
         throw new IllegalArgumentException("illegal object in getInstance: " + o.getClass().getName());
@@ -63,35 +64,35 @@
             throw new IllegalArgumentException("choice item must be explicitly tagged");
         }
 
-        return getInstance(o.getObject());
+        return getInstance(o.getExplicitBaseObject());
     }
 
     private DirectoryString(
-        DERT61String string)
+        ASN1T61String string)
     {
         this.string = string;
     }
 
     private DirectoryString(
-        DERPrintableString string)
+        ASN1PrintableString string)
     {
         this.string = string;
     }
 
     private DirectoryString(
-        DERUniversalString string)
+        ASN1UniversalString string)
     {
         this.string = string;
     }
 
     private DirectoryString(
-        DERUTF8String string)
+        ASN1UTF8String string)
     {
         this.string = string;
     }
 
     private DirectoryString(
-        DERBMPString string)
+        ASN1BMPString string)
     {
         this.string = string;
     }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x500/RDN.java b/bcprov/src/main/java/org/bouncycastle/asn1/x500/RDN.java
index 1475b4a..a5f092d 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/x500/RDN.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/x500/RDN.java
@@ -1,12 +1,11 @@
 package org.bouncycastle.asn1.x500;
 
 import org.bouncycastle.asn1.ASN1Encodable;
-import org.bouncycastle.asn1.ASN1EncodableVector;
 import org.bouncycastle.asn1.ASN1Object;
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
 import org.bouncycastle.asn1.ASN1Primitive;
 import org.bouncycastle.asn1.ASN1Set;
-import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
 import org.bouncycastle.asn1.DERSet;
 
 /**
@@ -19,6 +18,7 @@
 
     private RDN(ASN1Set values)
     {
+        // TODO Require minimum size of 1?
         this.values = values;
     }
 
@@ -36,6 +36,11 @@
         return null;
     }
 
+    public static RDN getInstance(ASN1TaggedObject taggedObject, boolean declaredExplicit)
+    {
+        return new RDN(ASN1Set.getInstance(taggedObject, declaredExplicit));
+    }
+
     /**
      * Create a single valued RDN.
      *
@@ -44,12 +49,7 @@
      */
     public RDN(ASN1ObjectIdentifier oid, ASN1Encodable value)
     {
-        ASN1EncodableVector v = new ASN1EncodableVector(2);
-
-        v.add(oid);
-        v.add(value);
-
-        this.values = new DERSet(new DERSequence(v));
+        this(new AttributeTypeAndValue(oid, value));
     }
 
     public RDN(AttributeTypeAndValue attrTAndV)
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x500/X500Name.java b/bcprov/src/main/java/org/bouncycastle/asn1/x500/X500Name.java
index 50efe50..bf6fdd7 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/x500/X500Name.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/x500/X500Name.java
@@ -1,7 +1,5 @@
 package org.bouncycastle.asn1.x500;
 
-import java.util.Enumeration;
-
 import org.bouncycastle.asn1.ASN1Choice;
 import org.bouncycastle.asn1.ASN1Encodable;
 import org.bouncycastle.asn1.ASN1Object;
@@ -111,18 +109,18 @@
         X500NameStyle style,
         ASN1Sequence  seq)
     {
+        int count = seq.size();
+
         this.style = style;
-        this.rdns = new RDN[seq.size()];
+        this.rdns = new RDN[count];
 
         boolean inPlace = true;
-
-        int index = 0;
-        for (Enumeration e = seq.getObjects(); e.hasMoreElements();)
+        for (int index = 0; index < count; ++index)
         {
-            Object element = e.nextElement();
+            ASN1Encodable element = seq.getObjectAt(index);
             RDN rdn = RDN.getInstance(element);
             inPlace &= (rdn == element);
-            rdns[index++] = rdn;
+            rdns[index] = rdn;
         }
 
         if (inPlace)
@@ -229,6 +227,11 @@
         return res;
     }
 
+    public int size()
+    {
+        return rdns.length;
+    }
+
     public ASN1Primitive toASN1Primitive()
     {
         return rdnSeq;
@@ -265,14 +268,14 @@
         
         ASN1Primitive derO = ((ASN1Encodable)obj).toASN1Primitive();
 
-        if (this.toASN1Primitive().equals(derO))
+        if (toASN1Primitive().equals(derO))
         {
             return true;
         }
 
         try
         {
-            return style.areEqual(this, new X500Name(ASN1Sequence.getInstance(((ASN1Encodable)obj).toASN1Primitive())));
+            return style.areEqual(this, getInstance(obj));
         }
         catch (Exception e)
         {
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x500/style/AbstractX500NameStyle.java b/bcprov/src/main/java/org/bouncycastle/asn1/x500/style/AbstractX500NameStyle.java
index 8d801b3..6f20e23 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/x500/style/AbstractX500NameStyle.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/x500/style/AbstractX500NameStyle.java
@@ -131,19 +131,22 @@
 
     public boolean areEqual(X500Name name1, X500Name name2)
     {
-        RDN[] rdns1 = name1.getRDNs();
-        RDN[] rdns2 = name2.getRDNs();
-
-        if (rdns1.length != rdns2.length)
+        if (name1.size() != name2.size())
         {
             return false;
         }
 
+        RDN[] rdns1 = name1.getRDNs();
+        RDN[] rdns2 = name2.getRDNs();
+
         boolean reverse = false;
 
-        if (rdns1[0].getFirst() != null && rdns2[0].getFirst() != null)
+        AttributeTypeAndValue first1 = rdns1[0].getFirst();
+        AttributeTypeAndValue first2 = rdns2[0].getFirst();
+
+        if (first1 != null && first2 != null)
         {
-            reverse = !rdns1[0].getFirst().getType().equals(rdns2[0].getFirst().getType());  // guess forward
+            reverse = !first1.getType().equals(first2.getType());  // guess forward
         }
 
         for (int i = 0; i != rdns1.length; i++)
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x500/style/BCStrictStyle.java b/bcprov/src/main/java/org/bouncycastle/asn1/x500/style/BCStrictStyle.java
index eb627c0..dd5f15f 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/x500/style/BCStrictStyle.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/x500/style/BCStrictStyle.java
@@ -15,14 +15,14 @@
 
     public boolean areEqual(X500Name name1, X500Name name2)
     {
-        RDN[] rdns1 = name1.getRDNs();
-        RDN[] rdns2 = name2.getRDNs();
-
-        if (rdns1.length != rdns2.length)
+        if (name1.size() != name2.size())
         {
             return false;
         }
 
+        RDN[] rdns1 = name1.getRDNs();
+        RDN[] rdns2 = name2.getRDNs();
+
         for (int i = 0; i != rdns1.length; i++)
         {
             if (!rdnAreEqual(rdns1[i], rdns2[i]))
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x500/style/BCStyle.java b/bcprov/src/main/java/org/bouncycastle/asn1/x500/style/BCStyle.java
index a7be3eb..dfece78 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/x500/style/BCStyle.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/x500/style/BCStyle.java
@@ -289,9 +289,9 @@
         defaultLookUp = copyHashTable(DefaultLookUp);
     }
 
-    protected ASN1Encodable encodeStringValue(ASN1ObjectIdentifier oid,
-    		String value) {
-    	if (oid.equals(EmailAddress) || oid.equals(DC))
+    protected ASN1Encodable encodeStringValue(ASN1ObjectIdentifier oid, String value)
+    {
+        if (oid.equals(EmailAddress) || oid.equals(DC))
         {
             return new DERIA5String(value);
         }
@@ -299,18 +299,18 @@
         {
             return new ASN1GeneralizedTime(value);
         }
-        else if (oid.equals(C) || oid.equals(SN) || oid.equals(DN_QUALIFIER)
+        else if (oid.equals(C) || oid.equals(SERIALNUMBER) || oid.equals(DN_QUALIFIER)
             || oid.equals(TELEPHONE_NUMBER))
         {
             return new DERPrintableString(value);
         }
-    	
-    	return super.encodeStringValue(oid, value);
+        
+        return super.encodeStringValue(oid, value);
     }
 
     public String oidToDisplayName(ASN1ObjectIdentifier oid)
     {
-        return (String)DefaultSymbols.get(oid);
+        return (String)defaultSymbols.get(oid);
     }
 
     public String[] oidToAttrNames(ASN1ObjectIdentifier oid)
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x500/style/IETFUtils.java b/bcprov/src/main/java/org/bouncycastle/asn1/x500/style/IETFUtils.java
index 5ddd322..03d5b73 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/x500/style/IETFUtils.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/x500/style/IETFUtils.java
@@ -10,7 +10,7 @@
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
 import org.bouncycastle.asn1.ASN1Primitive;
 import org.bouncycastle.asn1.ASN1String;
-import org.bouncycastle.asn1.DERUniversalString;
+import org.bouncycastle.asn1.ASN1UniversalString;
 import org.bouncycastle.asn1.x500.AttributeTypeAndValue;
 import org.bouncycastle.asn1.x500.RDN;
 import org.bouncycastle.asn1.x500.X500NameBuilder;
@@ -22,12 +22,15 @@
 {
     private static String unescape(String elt)
     {
-        if (elt.length() == 0 || (elt.indexOf('\\') < 0 && elt.indexOf('"') < 0))
+        if (elt.length() == 0)
+        {
+            return elt;
+        }
+        if (elt.indexOf('\\') < 0 && elt.indexOf('"') < 0)
         {
             return elt.trim();
         }
 
-        char[] elts = elt.toCharArray();
         boolean escaped = false;
         boolean quoted = false;
         StringBuffer buf = new StringBuffer(elt.length());
@@ -35,9 +38,9 @@
 
         // if it's an escaped hash string and not an actual encoding in string form
         // we need to leave it escaped.
-        if (elts[0] == '\\')
+        if (elt.charAt(0) == '\\')
         {
-            if (elts[1] == '#')
+            if (elt.charAt(1) == '#')
             {
                 start = 2;
                 buf.append("\\#");
@@ -48,9 +51,9 @@
         int     lastEscaped = 0;
         char    hex1 = 0;
 
-        for (int i = start; i != elts.length; i++)
+        for (int i = start; i != elt.length(); i++)
         {
-            char c = elts[i];
+            char c = elt.charAt(i);
 
             if (c != ' ')
             {
@@ -66,8 +69,8 @@
                 else
                 {
                     buf.append(c);
+                    escaped = false;
                 }
-                escaped = false;
             }
             else if (c == '\\' && !(escaped || quoted))
             {
@@ -128,81 +131,93 @@
 
     public static RDN[] rDNsFromString(String name, X500NameStyle x500Style)
     {
-        X500NameTokenizer nTok = new X500NameTokenizer(name);
+        X500NameTokenizer tokenizer = new X500NameTokenizer(name);
         X500NameBuilder builder = new X500NameBuilder(x500Style);
 
-        while (nTok.hasMoreTokens())
+        addRDNs(x500Style, builder, tokenizer);
+
+        // TODO There's an unnecessary clone of the RDNs array happening here
+        return builder.build().getRDNs();
+    }
+
+    private static void addRDNs(X500NameStyle style, X500NameBuilder builder, X500NameTokenizer tokenizer)
+    {
+        String token;
+        while ((token = tokenizer.nextToken()) != null)
         {
-            String  token = nTok.nextToken();
-
-            if (token.indexOf('+') > 0)
+            if (token.indexOf('+') >= 0)
             {
-                X500NameTokenizer   pTok = new X500NameTokenizer(token, '+');
-                X500NameTokenizer   vTok = new X500NameTokenizer(pTok.nextToken(), '=');
-
-                String              attr = vTok.nextToken();
-
-                if (!vTok.hasMoreTokens())
-                {
-                    throw new IllegalArgumentException("badly formatted directory string");
-                }
-
-                String               value = vTok.nextToken();
-                ASN1ObjectIdentifier oid = x500Style.attrNameToOID(attr.trim());
-
-                if (pTok.hasMoreTokens())
-                {
-                    Vector oids = new Vector();
-                    Vector values = new Vector();
-
-                    oids.addElement(oid);
-                    values.addElement(unescape(value));
-
-                    while (pTok.hasMoreTokens())
-                    {
-                        vTok = new X500NameTokenizer(pTok.nextToken(), '=');
-
-                        attr = vTok.nextToken();
-
-                        if (!vTok.hasMoreTokens())
-                        {
-                            throw new IllegalArgumentException("badly formatted directory string");
-                        }
-
-                        value = vTok.nextToken();
-                        oid = x500Style.attrNameToOID(attr.trim());
-
-
-                        oids.addElement(oid);
-                        values.addElement(unescape(value));
-                    }
-
-                    builder.addMultiValuedRDN(toOIDArray(oids), toValueArray(values));
-                }
-                else
-                {
-                    builder.addRDN(oid, unescape(value));
-                }
+                addMultiValuedRDN(style, builder, new X500NameTokenizer(token, '+'));
             }
             else
             {
-                X500NameTokenizer   vTok = new X500NameTokenizer(token, '=');
-
-                String              attr = vTok.nextToken();
-
-                if (!vTok.hasMoreTokens())
-                {
-                    throw new IllegalArgumentException("badly formatted directory string");
-                }
-
-                String               value = vTok.nextToken();
-                ASN1ObjectIdentifier oid = x500Style.attrNameToOID(attr.trim());
-
-                builder.addRDN(oid, unescape(value));
+                addRDN(style, builder, token);
             }
         }
+    }
 
-        return builder.build().getRDNs();
+    private static void addMultiValuedRDN(X500NameStyle style, X500NameBuilder builder, X500NameTokenizer tokenizer)
+    {
+        String token = tokenizer.nextToken();
+        if (token == null)
+        {
+            throw new IllegalArgumentException("badly formatted directory string");
+        }
+
+        if (!tokenizer.hasMoreTokens())
+        {
+            addRDN(style, builder, token);
+            return;
+        }
+
+        Vector oids = new Vector();
+        Vector values = new Vector();
+
+        do
+        {
+            collectAttributeTypeAndValue(style, oids, values, token);
+            token = tokenizer.nextToken();
+        }
+        while (token != null);
+
+        builder.addMultiValuedRDN(toOIDArray(oids), toValueArray(values));
+    }
+
+    private static void addRDN(X500NameStyle style, X500NameBuilder builder, String token)
+    {
+        X500NameTokenizer tokenizer = new X500NameTokenizer(token, '=');
+
+        String typeToken = nextToken(tokenizer, true);
+        String valueToken = nextToken(tokenizer, false);
+
+        ASN1ObjectIdentifier oid = style.attrNameToOID(typeToken.trim());
+        String value = unescape(valueToken);
+
+        builder.addRDN(oid, value);
+    }
+
+    private static void collectAttributeTypeAndValue(X500NameStyle style, Vector oids, Vector values, String token)
+    {
+        X500NameTokenizer tokenizer = new X500NameTokenizer(token, '=');
+
+        String typeToken = nextToken(tokenizer, true);
+        String valueToken = nextToken(tokenizer, false);
+
+        ASN1ObjectIdentifier oid = style.attrNameToOID(typeToken.trim());
+        String value = unescape(valueToken);
+
+        oids.addElement(oid);
+        values.addElement(value);
+    }
+
+    private static String nextToken(X500NameTokenizer tokenizer, boolean expectMoreTokens)
+    {
+        String token = tokenizer.nextToken();
+        if (token == null || tokenizer.hasMoreTokens() != expectMoreTokens)
+        {
+            throw new IllegalArgumentException("badly formatted directory string");
+        }
+        return token;
     }
 
     private static String[] toValueArray(Vector values)
@@ -354,7 +369,7 @@
     {
         StringBuffer vBuf = new StringBuffer();
 
-        if (value instanceof ASN1String && !(value instanceof DERUniversalString))
+        if (value instanceof ASN1String && !(value instanceof ASN1UniversalString))
         {
             String v = ((ASN1String)value).getString();
             if (v.length() > 0 && v.charAt(0) == '#')
@@ -369,6 +384,7 @@
             try
             {
                 vBuf.append('#');
+                // -DM Hex.toHexString
                 vBuf.append(Hex.toHexString(value.toASN1Primitive().getEncoded(ASN1Encoding.DER)));
             }
             catch (IOException e)
@@ -423,7 +439,7 @@
 
         int endBuf = vBuf.length() - 1;
 
-        while (endBuf >= 0 && vBuf.charAt(endBuf) == ' ')
+        while (endBuf >= start && vBuf.charAt(endBuf) == ' ')
         {
             vBuf.insert(endBuf, '\\');
             endBuf--;
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x500/style/RFC4519Style.java b/bcprov/src/main/java/org/bouncycastle/asn1/x500/style/RFC4519Style.java
index 2a40fb0..f07112f 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/x500/style/RFC4519Style.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/x500/style/RFC4519Style.java
@@ -11,7 +11,7 @@
 import org.bouncycastle.asn1.x500.X500NameStyle;
 
 public class RFC4519Style
-	extends AbstractX500NameStyle
+    extends AbstractX500NameStyle
 {
     public static final ASN1ObjectIdentifier businessCategory = new ASN1ObjectIdentifier("2.5.4.15").intern();
     public static final ASN1ObjectIdentifier c = new ASN1ObjectIdentifier("2.5.4.6").intern();
@@ -175,9 +175,9 @@
         defaultLookUp = copyHashTable(DefaultLookUp);
     }
 
-    protected ASN1Encodable encodeStringValue(ASN1ObjectIdentifier oid,
-    		String value) {
-    	if (oid.equals(dc))
+    protected ASN1Encodable encodeStringValue(ASN1ObjectIdentifier oid, String value)
+    {
+        if (oid.equals(dc))
         {
             return new DERIA5String(value);
         }
@@ -187,12 +187,12 @@
             return new DERPrintableString(value);
         }
 
-    	return super.encodeStringValue(oid, value);
+        return super.encodeStringValue(oid, value);
     }
 
     public String oidToDisplayName(ASN1ObjectIdentifier oid)
     {
-        return (String)DefaultSymbols.get(oid);
+        return (String)defaultSymbols.get(oid);
     }
 
     public String[] oidToAttrNames(ASN1ObjectIdentifier oid)
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x500/style/X500NameTokenizer.java b/bcprov/src/main/java/org/bouncycastle/asn1/x500/style/X500NameTokenizer.java
index b7e52f2..30dbf7a 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/x500/style/X500NameTokenizer.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/x500/style/X500NameTokenizer.java
@@ -8,83 +8,78 @@
  */
 public class X500NameTokenizer
 {
-    private String          value;
-    private int             index;
-    private char            separator;
-    private StringBuffer    buf = new StringBuffer();
+    private final String value;
+    private final char separator;
 
-    public X500NameTokenizer(
-        String  oid)
+    private int index;
+
+    public X500NameTokenizer(String oid)
     {
         this(oid, ',');
     }
-    
-    public X500NameTokenizer(
-        String  oid,
-        char    separator)
+
+    public X500NameTokenizer(String oid, char separator)
     {
+        if (oid == null)
+        {
+            throw new NullPointerException();
+        }
+        if (separator == '"' || separator == '\\')
+        {
+            throw new IllegalArgumentException("reserved separator character");
+        }
+
         this.value = oid;
-        this.index = -1;
         this.separator = separator;
+        this.index = oid.length() < 1 ? 0 : -1;
     }
 
     public boolean hasMoreTokens()
     {
-        return (index != value.length());
+        return index < value.length();
     }
 
     public String nextToken()
     {
-        if (index == value.length())
+        if (index >= value.length())
         {
             return null;
         }
 
-        int     end = index + 1;
         boolean quoted = false;
         boolean escaped = false;
 
-        buf.setLength(0);
-
-        while (end != value.length())
+        int beginIndex = index + 1;
+        while (++index < value.length())
         {
-            char    c = value.charAt(end);
+            char c = value.charAt(index);
 
-            if (c == '"')
+            if (escaped)
             {
-                if (!escaped)
-                {
-                    quoted = !quoted;
-                }
-                buf.append(c);
                 escaped = false;
             }
-            else
+            else if (c == '"')
             {
-                if (escaped || quoted)
-                {
-                    buf.append(c);
-                    escaped = false;
-                }
-                else if (c == '\\')
-                {
-                    buf.append(c);
-                    escaped = true;
-                }
-                else if (c == separator)
-                {
-                    break;
-                }
-                else
-                {
-                    buf.append(c);
-                }
+                quoted = !quoted;
             }
-            end++;
+            else if (quoted)
+            {
+            }
+            else if (c == '\\')
+            {
+                escaped = true;
+            }
+            else if (c == separator)
+            {
+                return value.substring(beginIndex, index);
+            }
         }
 
-        index = end;
+        if (escaped || quoted)
+        {
+            throw new IllegalArgumentException("badly formatted directory string");
+        }
 
-        return buf.toString();
+        return value.substring(beginIndex, index);
     }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x509/AltSignatureAlgorithm.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/AltSignatureAlgorithm.java
new file mode 100644
index 0000000..08f05a0
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/x509/AltSignatureAlgorithm.java
@@ -0,0 +1,94 @@
+package org.bouncycastle.asn1.x509;
+
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+
+/**
+ * X.509 Section 9.8.3.
+ * <br/>
+ * This extension may be used as a public-key certificate extension, a CRL extension or an AVL extension. It shall contain
+ * the algorithm identifier for the alternative digital signature algorithm used by the signer when creating an alternative
+ * digital signature and by the relying party when validating the alternative digital signature.
+ * <pre>
+ * altSignatureAlgorithm EXTENSION ::= {
+ *     SYNTAX AltSignatureAlgorithm
+ *     IDENTIFIED BY id-ce-altSignatureAlgorithm }
+ *
+ * AltSignatureAlgorithm ::= AlgorithmIdentifier{{SupportedAlgorithms}}
+ * </pre>
+ * When the altSignatureAlgorithm extension is included in a particular value that is an instance of a data type that
+ * supports extensions, the altSignatureValue extension shall also be included.
+ * <br/>
+ * NOTE 1 – By having a separate altSignatureAlgorithm extension, instead of having it combined with the
+ * altSignatureValue extension, the alternative digital signature algorithm is protected by the alternative signature.
+ * This extension may be flagged either as critical or as non-critical.
+ * <br/>
+ * NOTE 2 – It is recommended that it be flagged as non-critical. Flagging it as critical would require all relying parties to understand
+ * the extension and the alternative public-key algorithms
+ */
+public class AltSignatureAlgorithm
+    extends ASN1Object
+{
+    private final AlgorithmIdentifier algorithm;
+
+    public static AltSignatureAlgorithm getInstance(
+        ASN1TaggedObject obj,
+        boolean          explicit)
+    {
+        return getInstance(AlgorithmIdentifier.getInstance(obj, explicit));
+    }
+
+    public static AltSignatureAlgorithm getInstance(
+        Object obj)
+    {
+        if (obj instanceof AltSignatureAlgorithm)
+        {
+            return (AltSignatureAlgorithm)obj;
+        }
+        else if (obj != null)
+        {
+            return new AltSignatureAlgorithm(AlgorithmIdentifier.getInstance(obj));
+        }
+
+        return null;
+    }
+
+    public static AltSignatureAlgorithm fromExtensions(Extensions extensions)
+    {
+        return getInstance(Extensions.getExtensionParsedValue(extensions, Extension.altSignatureAlgorithm));
+    }
+
+    public AltSignatureAlgorithm(AlgorithmIdentifier algorithm)
+    {
+        this.algorithm = algorithm;
+    }
+
+    public AltSignatureAlgorithm(ASN1ObjectIdentifier algorithm)
+    {
+        this(algorithm, null);
+    }
+
+    public AltSignatureAlgorithm(ASN1ObjectIdentifier algorithm, ASN1Encodable parameters)
+    {
+        this.algorithm = new AlgorithmIdentifier(algorithm, parameters);
+    }
+
+    /**
+     * Return the algorithm identifier representing the alternate signature algorithm
+     * used to generate the alternate signature algorithm value extension.
+     *
+     * @return alternate signature algorithm identifier.
+     */
+    public AlgorithmIdentifier getAlgorithm()
+    {
+        return algorithm;
+    }
+
+    public ASN1Primitive toASN1Primitive()
+    {
+        return algorithm.toASN1Primitive();
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x509/AltSignatureValue.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/AltSignatureValue.java
new file mode 100644
index 0000000..96e7f67
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/x509/AltSignatureValue.java
@@ -0,0 +1,94 @@
+package org.bouncycastle.asn1.x509;
+
+import org.bouncycastle.asn1.ASN1BitString;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERBitString;
+
+/**
+ * X.509 Section 9.8.4.
+ * <br/>
+ * This extension may be used as a public-key certificate extension, a CRL extension or an AVL extension.
+ * This alternative signature shall be created by the issuer using its alternative private key, and it shall be verified using the
+ * alternative public key of the issuer.
+ * <pre>
+ * altSignatureValue EXTENSION ::= {
+ *     SYNTAX AltSignatureValue
+ *     IDENTIFIED BY id-ce-altSignatureValue }
+ *
+ * AltSignatureValue ::= BIT STRING
+ * </pre>
+ * This extension can only be created by a signer holding a multiple cryptographic algorithms public-key certificate. When
+ * creating the alternative digital signature on an issued public-key certificate or CRL, the signer shall use its alternative
+ * private key.
+ * <br/>
+ * The procedures for creating and validating alternative digital signatures are specified in:
+ * <ul>
+ * <li>clause 7.2.2 for public-key certificates;</li>
+ * <li>clause 7.10.3 for CRLs: and</li>
+ * <li>clause 11.4 for AVLs.</li>
+ * </ul>
+ */
+public class AltSignatureValue
+    extends ASN1Object
+{
+    private final ASN1BitString signature;
+
+    public static AltSignatureValue getInstance(
+        ASN1TaggedObject obj,
+        boolean          explicit)
+    {
+        return getInstance(ASN1BitString.getInstance(obj, explicit));
+    }
+
+    public static AltSignatureValue getInstance(
+        Object obj)
+    {
+        if (obj instanceof AltSignatureValue)
+        {
+            return (AltSignatureValue)obj;
+        }
+        else if (obj != null)
+        {
+            return new AltSignatureValue(ASN1BitString.getInstance(obj));
+        }
+
+        return null;
+    }
+
+    public static AltSignatureValue fromExtensions(Extensions extensions)
+    {
+        return getInstance(Extensions.getExtensionParsedValue(extensions, Extension.altSignatureValue));
+    }
+
+    private AltSignatureValue(ASN1BitString signature)
+    {
+        this.signature = signature;
+    }
+
+    /**
+     * Base constructor.
+     *
+     * @param signature  a signature value, based on the enclosing certificate.
+     */
+    public AltSignatureValue(byte[] signature)
+    {
+        this.signature = new DERBitString(signature);
+    }
+
+    /**
+     * Return the alternate signature to verify the certificate.
+     *
+     * @return certificate's alternate signature.
+     */
+    public ASN1BitString getSignature()
+    {
+        return signature;
+    }
+
+    public ASN1Primitive toASN1Primitive()
+    {
+        return signature;
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x509/AttCertIssuer.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/AttCertIssuer.java
index 21907c6..4c53c2a 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/x509/AttCertIssuer.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/x509/AttCertIssuer.java
@@ -46,7 +46,7 @@
         ASN1TaggedObject obj,
         boolean          explicit)
     {
-        return getInstance(obj.getObject()); // must be explicitly tagged
+        return getInstance(obj.getExplicitBaseObject()); // must be explicitly tagged
     }
 
     /**
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x509/AttributeCertificate.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/AttributeCertificate.java
index 7042e1e..7eb26be 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/x509/AttributeCertificate.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/x509/AttributeCertificate.java
@@ -1,5 +1,6 @@
 package org.bouncycastle.asn1.x509;
 
+import org.bouncycastle.asn1.ASN1BitString;
 import org.bouncycastle.asn1.ASN1EncodableVector;
 import org.bouncycastle.asn1.ASN1Object;
 import org.bouncycastle.asn1.ASN1Primitive;
@@ -12,7 +13,7 @@
 {
     AttributeCertificateInfo    acinfo;
     AlgorithmIdentifier         signatureAlgorithm;
-    DERBitString                signatureValue;
+    ASN1BitString               signatureValue;
 
     /**
      * @param obj
@@ -35,28 +36,23 @@
     public AttributeCertificate(
         AttributeCertificateInfo    acinfo,
         AlgorithmIdentifier         signatureAlgorithm,
-        DERBitString                signatureValue)
+        ASN1BitString               signatureValue)
     {
         this.acinfo = acinfo;
         this.signatureAlgorithm = signatureAlgorithm;
         this.signatureValue = signatureValue;
     }
 
-    /**
-     * @deprecated use getInstance() method.
-     */
-    public AttributeCertificate(
-        ASN1Sequence    seq)
+    private AttributeCertificate(ASN1Sequence seq)
     {
         if (seq.size() != 3)
         {
-            throw new IllegalArgumentException("Bad sequence size: "
-                    + seq.size());
+            throw new IllegalArgumentException("Bad sequence size: " + seq.size());
         }
 
         this.acinfo = AttributeCertificateInfo.getInstance(seq.getObjectAt(0));
         this.signatureAlgorithm = AlgorithmIdentifier.getInstance(seq.getObjectAt(1));
-        this.signatureValue = DERBitString.getInstance(seq.getObjectAt(2));
+        this.signatureValue = ASN1BitString.getInstance(seq.getObjectAt(2));
     }
     
     public AttributeCertificateInfo getAcinfo()
@@ -69,7 +65,7 @@
         return signatureAlgorithm;
     }
 
-    public DERBitString getSignatureValue()
+    public ASN1BitString getSignatureValue()
     {
         return signatureValue;
     }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x509/AttributeCertificateInfo.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/AttributeCertificateInfo.java
index 431cb29..a494d62 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/x509/AttributeCertificateInfo.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/x509/AttributeCertificateInfo.java
@@ -1,5 +1,6 @@
 package org.bouncycastle.asn1.x509;
 
+import org.bouncycastle.asn1.ASN1BitString;
 import org.bouncycastle.asn1.ASN1Encodable;
 import org.bouncycastle.asn1.ASN1EncodableVector;
 import org.bouncycastle.asn1.ASN1Integer;
@@ -7,7 +8,6 @@
 import org.bouncycastle.asn1.ASN1Primitive;
 import org.bouncycastle.asn1.ASN1Sequence;
 import org.bouncycastle.asn1.ASN1TaggedObject;
-import org.bouncycastle.asn1.DERBitString;
 import org.bouncycastle.asn1.DERSequence;
 
 public class AttributeCertificateInfo
@@ -17,10 +17,10 @@
     private Holder                  holder;
     private AttCertIssuer           issuer;
     private AlgorithmIdentifier     signature;
-    private ASN1Integer              serialNumber;
+    private ASN1Integer             serialNumber;
     private AttCertValidityPeriod   attrCertValidityPeriod;
     private ASN1Sequence            attributes;
-    private DERBitString            issuerUniqueID;
+    private ASN1BitString           issuerUniqueID;
     private Extensions              extensions;
 
     public static AttributeCertificateInfo getInstance(
@@ -76,9 +76,9 @@
         {
             ASN1Encodable    obj = seq.getObjectAt(i);
 
-            if (obj instanceof DERBitString)
+            if (obj instanceof ASN1BitString)
             {
-                this.issuerUniqueID = DERBitString.getInstance(seq.getObjectAt(i));
+                this.issuerUniqueID = ASN1BitString.getInstance(seq.getObjectAt(i));
             }
             else if (obj instanceof ASN1Sequence || obj instanceof Extensions)
             {
@@ -122,7 +122,7 @@
         return attributes;
     }
 
-    public DERBitString getIssuerUniqueID()
+    public ASN1BitString getIssuerUniqueID()
     {
         return issuerUniqueID;
     }
@@ -154,7 +154,7 @@
     {
         ASN1EncodableVector v = new ASN1EncodableVector(9);
 
-        if (version.intValueExact() != 0)
+        if (!version.hasValue(0))
         {
             v.add(version);
         }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x509/AuthorityKeyIdentifier.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/AuthorityKeyIdentifier.java
index 356a2fe..d82751d 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/x509/AuthorityKeyIdentifier.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/x509/AuthorityKeyIdentifier.java
@@ -17,6 +17,7 @@
 // Android-changed: Use Android digests
 // import org.bouncycastle.crypto.digests.SHA1Digest;
 import org.bouncycastle.crypto.digests.AndroidDigestFactory;
+import org.bouncycastle.util.Arrays;
 import org.bouncycastle.util.encoders.Hex;
 
 /**
@@ -173,7 +174,7 @@
         GeneralNames            name,
         BigInteger              serialNumber)
     {
-        this.keyidentifier = (keyIdentifier != null) ? new DEROctetString(keyIdentifier) : null;
+        this.keyidentifier = (keyIdentifier != null) ? new DEROctetString(Arrays.clone(keyIdentifier)) : null;
         this.certissuer = name;
         this.certserno = (serialNumber != null) ? new ASN1Integer(serialNumber) : null;
     }
@@ -230,6 +231,7 @@
 
     public String toString()
     {
+        // -DM Hex.toHexString
         String keyID = (keyidentifier != null) ? Hex.toHexString(keyidentifier.getOctets()) : "null";
 
         return "AuthorityKeyIdentifier: KeyID(" + keyID + ")";
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x509/BasicConstraints.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/BasicConstraints.java
index fa4c53d..4773c7f 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/x509/BasicConstraints.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/x509/BasicConstraints.java
@@ -122,6 +122,11 @@
         return null;
     }
 
+    public ASN1Integer getPathLenConstraintInteger()
+    {
+        return pathLenConstraint;
+    }
+
     /**
      * Produce an object suitable for an ASN1OutputStream.
      * <pre>
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x509/CRLNumber.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/CRLNumber.java
index 95425ba..baac2b0 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/x509/CRLNumber.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/x509/CRLNumber.java
@@ -5,6 +5,7 @@
 import org.bouncycastle.asn1.ASN1Integer;
 import org.bouncycastle.asn1.ASN1Object;
 import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.util.BigIntegers;
 
 /**
  * The CRLNumber object.
@@ -20,6 +21,10 @@
     public CRLNumber(
         BigInteger number)
     {
+        if (BigIntegers.ZERO.compareTo(number) > 0)
+        {
+            throw new IllegalArgumentException("Invalid CRL number : not in (0..MAX)");
+        }
         this.number = number;
     }
 
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x509/CRLReason.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/CRLReason.java
index a62122e..9bad7b6 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/x509/CRLReason.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/x509/CRLReason.java
@@ -51,7 +51,7 @@
     /**
      * @deprecated use lower case version
      */
-    public static final int CESSATION_OF_OPERATION  = 5;
+    public static final int CESSATION_OF_OPERATION = 5;
     /**
      * @deprecated use lower case version
      */
@@ -74,7 +74,7 @@
     public static final int cACompromise = 2;
     public static final int affiliationChanged = 3;
     public static final int superseded = 4;
-    public static final int cessationOfOperation  = 5;
+    public static final int cessationOfOperation = 5;
     public static final int certificateHold = 6;
     // 7 -> unknown
     public static final int removeFromCRL = 8;
@@ -82,11 +82,11 @@
     public static final int aACompromise = 10;
 
     private static final String[] reasonString =
-    {
-        "unspecified", "keyCompromise", "cACompromise", "affiliationChanged",
-        "superseded", "cessationOfOperation", "certificateHold", "unknown",
-        "removeFromCRL", "privilegeWithdrawn", "aACompromise"
-    };
+        {
+            "unspecified", "keyCompromise", "cACompromise", "affiliationChanged",
+            "superseded", "cessationOfOperation", "certificateHold", "unknown",
+            "removeFromCRL", "privilegeWithdrawn", "aACompromise"
+        };
 
     private static final Hashtable table = new Hashtable();
 
@@ -109,6 +109,10 @@
     private CRLReason(
         int reason)
     {
+        if (reason < 0)
+        {
+            throw new IllegalArgumentException("Invalid CRL reason : not in (0..MAX)");
+        }
         value = new ASN1Enumerated(reason);
     }
 
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x509/Certificate.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/Certificate.java
index 4ca14d4..e64a197 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/x509/Certificate.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/x509/Certificate.java
@@ -1,11 +1,11 @@
 package org.bouncycastle.asn1.x509;
 
+import org.bouncycastle.asn1.ASN1BitString;
 import org.bouncycastle.asn1.ASN1Integer;
 import org.bouncycastle.asn1.ASN1Object;
 import org.bouncycastle.asn1.ASN1Primitive;
 import org.bouncycastle.asn1.ASN1Sequence;
 import org.bouncycastle.asn1.ASN1TaggedObject;
-import org.bouncycastle.asn1.DERBitString;
 import org.bouncycastle.asn1.x500.X500Name;
 
 /**
@@ -24,7 +24,7 @@
     ASN1Sequence  seq;
     TBSCertificate tbsCert;
     AlgorithmIdentifier     sigAlgId;
-    DERBitString            sig;
+    ASN1BitString            sig;
 
     public static Certificate getInstance(
         ASN1TaggedObject obj,
@@ -61,7 +61,7 @@
             tbsCert = TBSCertificate.getInstance(seq.getObjectAt(0));
             sigAlgId = AlgorithmIdentifier.getInstance(seq.getObjectAt(1));
 
-            sig = DERBitString.getInstance(seq.getObjectAt(2));
+            sig = ASN1BitString.getInstance(seq.getObjectAt(2));
         }
         else
         {
@@ -119,7 +119,7 @@
         return sigAlgId;
     }
 
-    public DERBitString getSignature()
+    public ASN1BitString getSignature()
     {
         return sig;
     }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x509/CertificateList.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/CertificateList.java
index b21e3cd..c4c14d9 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/x509/CertificateList.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/x509/CertificateList.java
@@ -3,6 +3,7 @@
 
 import java.util.Enumeration;
 
+import org.bouncycastle.asn1.ASN1BitString;
 import org.bouncycastle.asn1.ASN1EncodableVector;
 import org.bouncycastle.asn1.ASN1Object;
 import org.bouncycastle.asn1.ASN1Primitive;
@@ -30,7 +31,7 @@
 {
     TBSCertList            tbsCertList;
     AlgorithmIdentifier    sigAlgId;
-    DERBitString           sig;
+    ASN1BitString          sig;
     boolean                isHashCodeSet = false;
     int                    hashCodeValue;
 
@@ -56,18 +57,14 @@
         return null;
     }
 
-    /**
-     * @deprecated use getInstance() method.
-     * @param seq
-     */
-    public CertificateList(
+    private CertificateList(
         ASN1Sequence seq)
     {
         if (seq.size() == 3)
         {
             tbsCertList = TBSCertList.getInstance(seq.getObjectAt(0));
             sigAlgId = AlgorithmIdentifier.getInstance(seq.getObjectAt(1));
-            sig = DERBitString.getInstance(seq.getObjectAt(2));
+            sig = ASN1BitString.getInstance(seq.getObjectAt(2));
         }
         else
         {
@@ -95,7 +92,7 @@
         return sigAlgId;
     }
 
-    public DERBitString getSignature()
+    public ASN1BitString getSignature()
     {
         return sig;
     }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x509/DeltaCertificateDescriptor.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/DeltaCertificateDescriptor.java
new file mode 100644
index 0000000..d562595
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/x509/DeltaCertificateDescriptor.java
@@ -0,0 +1,274 @@
+package org.bouncycastle.asn1.x509;
+
+import java.util.Enumeration;
+
+import org.bouncycastle.asn1.ASN1BitString;
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.DERTaggedObject;
+import org.bouncycastle.asn1.x500.X500Name;
+
+/**
+ * <pre>
+ *     DeltaCertificateDescriptor ::= SEQUENCE {
+ *      serialNumber          CertificateSerialNumber,
+ *      signature             [0] IMPLICIT AlgorithmIdentifier
+ *           {SIGNATURE_ALGORITHM, {...}} OPTIONAL,
+ *      issuer                [1] IMPLICIT Name OPTIONAL,
+ *      validity              [2] IMPLICIT Validity OPTIONAL,
+ *      subject               [3] IMPLICIT Name OPTIONAL,
+ *      subjectPublicKeyInfo  SubjectPublicKeyInfo,
+ *      extensions            [4] IMPLICIT Extensions{CertExtensions}
+ *           OPTIONAL,
+ *      signatureValue        BIT STRING
+ *    }
+ *    </pre>
+ */
+public class DeltaCertificateDescriptor
+    extends ASN1Object
+{
+    private final ASN1Integer serialNumber;
+
+    private AlgorithmIdentifier signature;
+    private X500Name issuer;
+    private ASN1Sequence validity;
+    private X500Name subject;
+    private SubjectPublicKeyInfo subjectPublicKeyInfo;
+    private Extensions extensions;
+
+    private final ASN1BitString signatureValue;
+
+    public static DeltaCertificateDescriptor getInstance(
+        Object  obj)
+    {
+        if (obj instanceof DeltaCertificateDescriptor)
+        {
+            return (DeltaCertificateDescriptor)obj;
+        }
+        else if (obj != null)
+        {
+            return new DeltaCertificateDescriptor(ASN1Sequence.getInstance(obj));
+        }
+
+        return null;
+    }
+
+    /**
+     * Retrieve a DeltaCertificateDescriptor for a passed in Extensions object, if present.
+     *
+     * @param extensions the extensions object to be examined.
+     * @return  the DeltaCertificateDescriptor, null if the extension is not present.
+     */
+    public static DeltaCertificateDescriptor fromExtensions(Extensions extensions)
+    {
+        return getInstance(Extensions.getExtensionParsedValue(extensions, Extension.deltaCertificateDescriptor));
+    }
+
+    private DeltaCertificateDescriptor(ASN1Sequence seq)
+    {
+        this.serialNumber = ASN1Integer.getInstance(seq.getObjectAt(0));
+
+        int idx = 1;
+        ASN1Encodable next = seq.getObjectAt(idx);
+        while (next instanceof ASN1TaggedObject)
+        {
+            ASN1TaggedObject tagged = ASN1TaggedObject.getInstance(next);
+            switch (tagged.getTagNo())
+            {
+            case 0:
+                signature = AlgorithmIdentifier.getInstance(tagged, false);
+                break;
+            case 1:
+                issuer = X500Name.getInstance(tagged, true);   // issuer
+                break;
+            case 2:
+                validity = ASN1Sequence.getInstance(tagged, false);
+                break;
+            case 3:
+                subject = X500Name.getInstance(tagged, true);   // subject
+                break;
+            }
+            next = seq.getObjectAt(idx++);
+        }
+
+        subjectPublicKeyInfo = subjectPublicKeyInfo.getInstance(next);
+
+        next = seq.getObjectAt(idx);
+        while (next instanceof ASN1TaggedObject)
+        {
+            ASN1TaggedObject tagged = ASN1TaggedObject.getInstance(next);
+            switch (tagged.getTagNo())
+            {
+            case 4:
+                extensions = Extensions.getInstance(tagged, false); 
+                break;
+            }
+            next = seq.getObjectAt(idx++);
+        }
+
+        signatureValue = ASN1BitString.getInstance(next);
+    }
+
+    public ASN1Integer getSerialNumber()
+    {
+        return serialNumber;
+    }
+
+    public AlgorithmIdentifier getSignature()
+    {
+        return signature;
+    }
+
+    public X500Name getIssuer()
+    {
+        return issuer;
+    }
+
+    public ASN1Sequence getValidity()
+    {
+        return validity;
+    }
+
+    public X500Name getSubject()
+    {
+        return subject;
+    }
+
+    public SubjectPublicKeyInfo getSubjectPublicKeyInfo()
+    {
+        return subjectPublicKeyInfo;
+    }
+
+    public Extensions getExtensions()
+    {
+        return extensions;
+    }
+
+    public ASN1BitString getSignatureValue()
+    {
+        return signatureValue;
+    }
+
+    public DeltaCertificateDescriptor trimTo(TBSCertificate baseTbsCertificate, Extensions tbsExtensions)
+    {
+        AlgorithmIdentifier signature = baseTbsCertificate.signature;
+        X500Name issuer = baseTbsCertificate.issuer;
+        ASN1Sequence validity = new DERSequence(new ASN1Encodable[]
+        {
+            baseTbsCertificate.startDate, baseTbsCertificate.endDate
+        });
+        X500Name subject = baseTbsCertificate.subject;
+        ASN1Sequence s = ASN1Sequence.getInstance(toASN1Primitive());
+        ASN1EncodableVector v = new ASN1EncodableVector();
+
+        Enumeration en = s.getObjects();
+        v.add((ASN1Encodable)en.nextElement());
+
+        ASN1Encodable next = (ASN1Encodable)en.nextElement();
+        while (next instanceof ASN1TaggedObject)
+        {
+            ASN1TaggedObject tagged = ASN1TaggedObject.getInstance(next);
+            switch (tagged.getTagNo())
+            {
+            case 0:
+                AlgorithmIdentifier sig = AlgorithmIdentifier.getInstance(tagged, false);
+                if (!sig.equals(signature))
+                {
+                    v.add(next);
+                }
+                break;
+            case 1:
+                X500Name iss = X500Name.getInstance(tagged, true);   // issuer
+                if (!iss.equals(issuer))
+                {
+                    v.add(next);
+                }
+                break;
+            case 2:
+                ASN1Sequence val = ASN1Sequence.getInstance(tagged, false);
+                if (!val.equals(validity))
+                {
+                    v.add(next);
+                }
+                break;
+            case 3:
+                X500Name sub = X500Name.getInstance(tagged, true);   // subject
+                if (!sub.equals(subject))
+                {
+                    v.add(next);
+                }
+                break;
+            }
+            next = (ASN1Encodable)en.nextElement();
+        }
+
+        v.add(next);
+
+        next = (ASN1Encodable)en.nextElement();
+        while (next instanceof ASN1TaggedObject)
+        {
+            ASN1TaggedObject tagged = ASN1TaggedObject.getInstance(next);
+            switch (tagged.getTagNo())
+            {
+            case 4:
+                Extensions deltaExts = Extensions.getInstance(tagged, false);
+                ExtensionsGenerator deltaExtGen = new ExtensionsGenerator();
+                for (Enumeration extEn = deltaExts.oids(); extEn.hasMoreElements(); )
+                {
+                    Extension deltaExt = deltaExts.getExtension((ASN1ObjectIdentifier)extEn.nextElement());
+                    Extension primaryExt = tbsExtensions.getExtension(deltaExt.getExtnId());
+
+                    if (primaryExt != null)
+                    {
+                        if (!deltaExt.equals(primaryExt))
+                        {
+                            deltaExtGen.addExtension(deltaExt);
+                        }
+                    }
+                }
+
+                DeltaCertificateDescriptor trimmedDeltaCertDesc;
+                if (!deltaExtGen.isEmpty())
+                {
+                    v.add(new DERTaggedObject(false, 4, deltaExtGen.generate()));
+                }
+            }
+            next = (ASN1Encodable)en.nextElement();
+        }
+
+        v.add(next);
+
+        return new DeltaCertificateDescriptor(new DERSequence(v));
+    }
+
+    private void addOptional(ASN1EncodableVector v, int tag, boolean explicit, ASN1Object obj)
+    {
+        if (obj != null)
+        {
+             v.add(new DERTaggedObject(explicit, tag, obj));
+        }
+    }
+
+    public ASN1Primitive toASN1Primitive()
+    {
+        ASN1EncodableVector v = new ASN1EncodableVector(7);
+
+        v.add(serialNumber);
+        addOptional(v, 0, false, signature);
+        addOptional(v, 1, true, issuer); // CHOICE
+        addOptional(v, 2, false, validity);
+        addOptional(v, 3, true, subject);  // CHOICE
+        v.add(subjectPublicKeyInfo);
+        addOptional(v, 4, false, extensions);
+        v.add(signatureValue);
+
+        return new DERSequence(v);
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x509/DistributionPoint.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/DistributionPoint.java
index f001700..368b12a 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/x509/DistributionPoint.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/x509/DistributionPoint.java
@@ -1,11 +1,11 @@
 package org.bouncycastle.asn1.x509;
 
+import org.bouncycastle.asn1.ASN1BitString;
 import org.bouncycastle.asn1.ASN1EncodableVector;
 import org.bouncycastle.asn1.ASN1Object;
 import org.bouncycastle.asn1.ASN1Primitive;
 import org.bouncycastle.asn1.ASN1Sequence;
 import org.bouncycastle.asn1.ASN1TaggedObject;
-import org.bouncycastle.asn1.DERBitString;
 import org.bouncycastle.asn1.DERSequence;
 import org.bouncycastle.asn1.DERTaggedObject;
 import org.bouncycastle.util.Strings;
@@ -59,10 +59,11 @@
             switch (t.getTagNo())
             {
             case 0:
+                // CHOICE so explicit
                 distributionPoint = DistributionPointName.getInstance(t, true);
                 break;
             case 1:
-                reasons = new ReasonFlags(DERBitString.getInstance(t, false));
+                reasons = new ReasonFlags(ASN1BitString.getInstance(t, false));
                 break;
             case 2:
                 cRLIssuer = GeneralNames.getInstance(t, false);
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x509/Extension.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/Extension.java
index 5008028..95cf846 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/x509/Extension.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/x509/Extension.java
@@ -12,6 +12,7 @@
 import org.bouncycastle.asn1.ASN1Sequence;
 import org.bouncycastle.asn1.DEROctetString;
 import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.util.Arrays;
 
 /**
  * an object for the elements in the X.509 V3 extension block.
@@ -179,6 +180,26 @@
      */
     public static final ASN1ObjectIdentifier expiredCertsOnCRL = new ASN1ObjectIdentifier("2.5.29.60").intern();
 
+    /**
+     * the subject’s alternative public key information
+     */
+    public static final ASN1ObjectIdentifier subjectAltPublicKeyInfo = new ASN1ObjectIdentifier("2.5.29.72").intern();
+
+    /**
+     * the algorithm identifier for the alternative digital signature algorithm.
+     */
+    public static final ASN1ObjectIdentifier altSignatureAlgorithm = new ASN1ObjectIdentifier("2.5.29.73").intern();
+
+    /**
+     * alternative signature shall be created by the issuer using its alternative private key.
+     */
+    public static final ASN1ObjectIdentifier altSignatureValue = new ASN1ObjectIdentifier("2.5.29.74").intern();
+
+    /**
+     * delta certificate extension - prototype value will change!
+     */
+    public static final ASN1ObjectIdentifier deltaCertificateDescriptor = new ASN1ObjectIdentifier("2.16.840.1.114027.80.6.1");
+
     private ASN1ObjectIdentifier extnId;
     private boolean             critical;
     private ASN1OctetString      value;
@@ -210,7 +231,7 @@
         boolean critical,
         byte[] value)
     {
-        this(extnId, critical, new DEROctetString(value));
+        this(extnId, critical, new DEROctetString(Arrays.clone(value)));
     }
 
     /**
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x509/Extensions.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/Extensions.java
index 0795a08..d25187a 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/x509/Extensions.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/x509/Extensions.java
@@ -12,6 +12,7 @@
 import org.bouncycastle.asn1.ASN1Sequence;
 import org.bouncycastle.asn1.ASN1TaggedObject;
 import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.util.Properties;
 
 /**
  * <pre>
@@ -70,6 +71,11 @@
     private Extensions(
         ASN1Sequence seq)
     {
+        if (seq.size() == 0)
+        {
+            throw new IllegalArgumentException("empty extension sequence found");
+        }
+
         Enumeration e = seq.getObjects();
 
         while (e.hasMoreElements())
@@ -78,7 +84,10 @@
 
             if (extensions.containsKey(ext.getExtnId()))
             {
-                throw new IllegalArgumentException("repeated extension found: " + ext.getExtnId());
+                if (!Properties.isOverrideSet("org.bouncycastle.x509.ignore_repeated_extensions"))
+                {
+                    throw new IllegalArgumentException("repeated extension found: " + ext.getExtnId());
+                }
             }
             
             extensions.put(ext.getExtnId(), ext);
@@ -106,6 +115,11 @@
     public Extensions(
         Extension[] extensions)
     {
+        if (extensions == null || extensions.length == 0)
+        {
+            throw new IllegalArgumentException("extension array cannot be null or empty");
+        }
+
         for (int i = 0; i != extensions.length; i++)
         {
             Extension ext = extensions[i];
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x509/ExtensionsGenerator.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/ExtensionsGenerator.java
index e4e2ffe..e5726d3 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/x509/ExtensionsGenerator.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/x509/ExtensionsGenerator.java
@@ -1,13 +1,22 @@
 package org.bouncycastle.asn1.x509;
 
 import java.io.IOException;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashSet;
 import java.util.Hashtable;
+import java.util.Set;
 import java.util.Vector;
 
 import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1EncodableVector;
 import org.bouncycastle.asn1.ASN1Encoding;
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1ParsingException;
+import org.bouncycastle.asn1.ASN1Sequence;
 import org.bouncycastle.asn1.DEROctetString;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.util.Arrays;
 
 /**
  * Generator for X.509 extensions
@@ -16,6 +25,18 @@
 {
     private Hashtable extensions = new Hashtable();
     private Vector extOrdering = new Vector();
+    private static final Set dupsAllowed;
+
+
+    static
+    {
+        Set dups = new HashSet();
+        dups.add(Extension.subjectAlternativeName);
+        dups.add(Extension.issuerAlternativeName);
+        dups.add(Extension.subjectDirectoryAttributes);
+        dups.add(Extension.certificateIssuer);
+        dupsAllowed = Collections.unmodifiableSet(dups);
+    }
 
     /**
      * Reset the generator
@@ -30,14 +51,14 @@
      * Add an extension with the given oid and the passed in value to be included
      * in the OCTET STRING associated with the extension.
      *
-     * @param oid  OID for the extension.
-     * @param critical  true if critical, false otherwise.
-     * @param value the ASN.1 object to be included in the extension.
+     * @param oid      OID for the extension.
+     * @param critical true if critical, false otherwise.
+     * @param value    the ASN.1 object to be included in the extension.
      */
     public void addExtension(
         ASN1ObjectIdentifier oid,
-        boolean              critical,
-        ASN1Encodable        value)
+        boolean critical,
+        ASN1Encodable value)
         throws IOException
     {
         this.addExtension(oid, critical, value.toASN1Primitive().getEncoded(ASN1Encoding.DER));
@@ -47,22 +68,52 @@
      * Add an extension with the given oid and the passed in byte array to be wrapped in the
      * OCTET STRING associated with the extension.
      *
-     * @param oid OID for the extension.
+     * @param oid      OID for the extension.
      * @param critical true if critical, false otherwise.
-     * @param value the byte array to be wrapped.
+     * @param value    the byte array to be wrapped.
      */
     public void addExtension(
         ASN1ObjectIdentifier oid,
-        boolean             critical,
-        byte[]              value)
+        boolean critical,
+        byte[] value)
     {
         if (extensions.containsKey(oid))
         {
-            throw new IllegalArgumentException("extension " + oid + " already added");
-        }
+            if (dupsAllowed.contains(oid))
+            {
+                Extension existingExtension = (Extension)extensions.get(oid);
+                ASN1Sequence seq1 = ASN1Sequence.getInstance(DEROctetString.getInstance(existingExtension.getExtnValue()).getOctets());
+                ASN1Sequence seq2 = ASN1Sequence.getInstance(value);
 
-        extOrdering.addElement(oid);
-        extensions.put(oid, new Extension(oid, critical, new DEROctetString(value)));
+                ASN1EncodableVector items = new ASN1EncodableVector(seq1.size() + seq2.size());
+                for (Enumeration en = seq1.getObjects(); en.hasMoreElements();)
+                {
+                    items.add((ASN1Encodable)en.nextElement());
+                }
+                for (Enumeration en = seq2.getObjects(); en.hasMoreElements();)
+                {
+                    items.add((ASN1Encodable)en.nextElement());
+                }
+                
+                try
+                {
+                    extensions.put(oid, new Extension(oid, critical, new DERSequence(items).getEncoded()));
+                }
+                catch (IOException e)
+                {
+                    throw new ASN1ParsingException(e.getMessage(), e);
+                }
+            }
+            else
+            {
+                throw new IllegalArgumentException("extension " + oid + " already added");
+            }
+        }
+        else
+        {
+            extOrdering.addElement(oid);
+            extensions.put(oid, new Extension(oid, critical, new DEROctetString(Arrays.clone(value))));
+        }
     }
 
     /**
@@ -86,14 +137,14 @@
      * Replace an extension with the given oid and the passed in value to be included
      * in the OCTET STRING associated with the extension.
      *
-     * @param oid  OID for the extension.
-     * @param critical  true if critical, false otherwise.
-     * @param value the ASN.1 object to be included in the extension.
+     * @param oid      OID for the extension.
+     * @param critical true if critical, false otherwise.
+     * @param value    the ASN.1 object to be included in the extension.
      */
     public void replaceExtension(
         ASN1ObjectIdentifier oid,
-        boolean              critical,
-        ASN1Encodable        value)
+        boolean critical,
+        ASN1Encodable value)
         throws IOException
     {
         this.replaceExtension(oid, critical, value.toASN1Primitive().getEncoded(ASN1Encoding.DER));
@@ -103,14 +154,14 @@
      * Replace an extension with the given oid and the passed in byte array to be wrapped in the
      * OCTET STRING associated with the extension.
      *
-     * @param oid OID for the extension.
+     * @param oid      OID for the extension.
      * @param critical true if critical, false otherwise.
-     * @param value the byte array to be wrapped.
+     * @param value    the byte array to be wrapped.
      */
     public void replaceExtension(
         ASN1ObjectIdentifier oid,
-        boolean             critical,
-        byte[]              value)
+        boolean critical,
+        byte[] value)
     {
         this.replaceExtension(new Extension(oid, critical, value));
     }
@@ -156,7 +207,7 @@
      */
     public boolean hasExtension(ASN1ObjectIdentifier oid)
     {
-         return extensions.containsKey(oid);
+        return extensions.containsKey(oid);
     }
 
     /**
@@ -167,7 +218,7 @@
      */
     public Extension getExtension(ASN1ObjectIdentifier oid)
     {
-         return (Extension)extensions.get(oid);
+        return (Extension)extensions.get(oid);
     }
 
     /**
@@ -183,7 +234,7 @@
     /**
      * Generate an Extensions object based on the current state of the generator.
      *
-     * @return  an X09Extensions object.
+     * @return an X09Extensions object.
      */
     public Extensions generate()
     {
@@ -196,4 +247,15 @@
 
         return new Extensions(exts);
     }
+
+    public void addExtension(Extensions extensions)
+    {
+        ASN1ObjectIdentifier[] oids = extensions.getExtensionOIDs();
+        for (int i = 0; i != oids.length; i++)
+        {
+            ASN1ObjectIdentifier ident = oids[i];
+            Extension ext = extensions.getExtension(ident);
+            addExtension(ASN1ObjectIdentifier.getInstance(ident), ext.isCritical(), ext.getExtnValue().getOctets());
+        }
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x509/GeneralName.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/GeneralName.java
index 50b88d8..20c1910 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/x509/GeneralName.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/x509/GeneralName.java
@@ -5,6 +5,7 @@
 
 import org.bouncycastle.asn1.ASN1Choice;
 import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1IA5String;
 import org.bouncycastle.asn1.ASN1Object;
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
 import org.bouncycastle.asn1.ASN1OctetString;
@@ -194,7 +195,7 @@
             case dNSName:
             case rfc822Name:
             case uniformResourceIdentifier:
-                return new GeneralName(tag, DERIA5String.getInstance(tagObj, false));
+                return new GeneralName(tag, ASN1IA5String.getInstance(tagObj, false));
 
             case directoryName:
                 return new GeneralName(tag, X500Name.getInstance(tagObj, true));
@@ -227,6 +228,11 @@
         ASN1TaggedObject tagObj,
         boolean          explicit)
     {
+        if (!explicit)
+        {
+            throw new IllegalArgumentException("choice item must be explicitly tagged");
+        }
+
         return GeneralName.getInstance(ASN1TaggedObject.getInstance(tagObj, true));
     }
 
@@ -251,7 +257,7 @@
         case rfc822Name:
         case dNSName:
         case uniformResourceIdentifier:
-            buf.append(DERIA5String.getInstance(obj).getString());
+            buf.append(ASN1IA5String.getInstance(obj).getString());
             break;
         case directoryName:
             buf.append(X500Name.getInstance(obj).toString());
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x509/GeneralSubtree.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/GeneralSubtree.java
index db06da4..53e782d 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/x509/GeneralSubtree.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/x509/GeneralSubtree.java
@@ -203,7 +203,7 @@
 
         v.add(base);
 
-        if (minimum != null && !minimum.hasValue(ZERO))
+        if (minimum != null && !minimum.hasValue(0))
         {
             v.add(new DERTaggedObject(false, 0, minimum));
         }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x509/Holder.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/Holder.java
index 9a457b8..cdd3975 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/x509/Holder.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/x509/Holder.java
@@ -87,7 +87,7 @@
         default:
             throw new IllegalArgumentException("unknown tag in Holder");
         }
-        version = 0;
+        version = V1_CERTIFICATE_HOLDER;
     }
 
     /**
@@ -123,7 +123,7 @@
                 throw new IllegalArgumentException("unknown tag in Holder");
             }
         }
-        version = 1;
+        version = V2_CERTIFICATE_HOLDER;
     }
 
     public Holder(IssuerSerial baseCertificateID)
@@ -209,7 +209,7 @@
 
     public ASN1Primitive toASN1Primitive()
     {
-        if (version == 1)
+        if (version == V2_CERTIFICATE_HOLDER)
         {
             ASN1EncodableVector v = new ASN1EncodableVector(3);
 
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x509/IssuerSerial.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/IssuerSerial.java
index 4d8e323..df3af14 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/x509/IssuerSerial.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/x509/IssuerSerial.java
@@ -2,22 +2,22 @@
 
 import java.math.BigInteger;
 
+import org.bouncycastle.asn1.ASN1BitString;
 import org.bouncycastle.asn1.ASN1EncodableVector;
 import org.bouncycastle.asn1.ASN1Integer;
 import org.bouncycastle.asn1.ASN1Object;
 import org.bouncycastle.asn1.ASN1Primitive;
 import org.bouncycastle.asn1.ASN1Sequence;
 import org.bouncycastle.asn1.ASN1TaggedObject;
-import org.bouncycastle.asn1.DERBitString;
 import org.bouncycastle.asn1.DERSequence;
 import org.bouncycastle.asn1.x500.X500Name;
 
 public class IssuerSerial
     extends ASN1Object
 {
-    GeneralNames            issuer;
-    ASN1Integer              serial;
-    DERBitString            issuerUID;
+    GeneralNames  issuer;
+    ASN1Integer   serial;
+    ASN1BitString issuerUID;
 
     public static IssuerSerial getInstance(
             Object  obj)
@@ -55,7 +55,7 @@
 
         if (seq.size() == 3)
         {
-            issuerUID = DERBitString.getInstance(seq.getObjectAt(2));
+            issuerUID = ASN1BitString.getInstance(seq.getObjectAt(2));
         }
     }
 
@@ -91,7 +91,7 @@
         return serial;
     }
 
-    public DERBitString getIssuerUID()
+    public ASN1BitString getIssuerUID()
     {
         return issuerUID;
     }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x509/IssuingDistributionPoint.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/IssuingDistributionPoint.java
index 687fc99..ef78f62 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/x509/IssuingDistributionPoint.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/x509/IssuingDistributionPoint.java
@@ -1,5 +1,6 @@
 package org.bouncycastle.asn1.x509;
 
+import org.bouncycastle.asn1.ASN1BitString;
 import org.bouncycastle.asn1.ASN1Boolean;
 import org.bouncycastle.asn1.ASN1EncodableVector;
 import org.bouncycastle.asn1.ASN1Object;
@@ -152,7 +153,7 @@
             switch (o.getTagNo())
             {
             case 0:
-                                                    // CHOICE so explicit
+                // CHOICE so explicit
                 distributionPoint = DistributionPointName.getInstance(o, true);
                 break;
             case 1:
@@ -162,7 +163,7 @@
                 onlyContainsCACerts = ASN1Boolean.getInstance(o, false).isTrue();
                 break;
             case 3:
-                onlySomeReasons = new ReasonFlags(ReasonFlags.getInstance(o, false));
+                onlySomeReasons = new ReasonFlags(ASN1BitString.getInstance(o, false));
                 break;
             case 4:
                 indirectCRL = ASN1Boolean.getInstance(o, false).isTrue();
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x509/KeyPurposeId.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/KeyPurposeId.java
index 18e056d..29d6316 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/x509/KeyPurposeId.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/x509/KeyPurposeId.java
@@ -107,6 +107,30 @@
      */
     public static final KeyPurposeId id_kp_capwapWTP = new KeyPurposeId(id_kp.branch("19"));
 
+
+    /**
+     * id-kp-cmcCA OBJECT IDENTIFIER ::= {
+     *          iso(1) identified-organization(3) dod(6) internet(1)
+     *          security(5) mechanisms(5) pkix(7) kp(3) 27 }
+     */
+    public static final KeyPurposeId id_kp_cmcCA = new KeyPurposeId(id_kp.branch("27"));
+
+    /**
+     * id-kp-cmcRA OBJECT IDENTIFIER ::= {
+     *          iso(1) identified-organization(3) dod(6) internet(1)
+     *          security(5) mechanisms(5) pkix(7) kp(3) 28 }
+     */
+    public static final KeyPurposeId id_kp_cmcRA = new KeyPurposeId(id_kp.branch("28"));
+
+    /**
+     * id-kp-cmKGA OBJECT IDENTIFIER ::= {
+     *          iso(1) identified-organization(3) dod(6) internet(1)
+     *          security(5) mechanisms(5) pkix(7) kp(3) 32 }
+     */
+    public static final KeyPurposeId id_kp_cmKGA = new KeyPurposeId(id_kp.branch("32"));
+
+
+
     //
     // microsoft key purpose ids
     //
@@ -140,15 +164,6 @@
         this.id = id;
     }
 
-    /**
-     * @param id string representation of an OID.
-     * @deprecated use getInstance and an OID or one of the constants above.
-     */
-    public KeyPurposeId(String id)
-    {
-        this(new ASN1ObjectIdentifier(id));
-    }
-
     public static KeyPurposeId getInstance(Object o)
     {
         if (o instanceof KeyPurposeId)
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x509/KeyUsage.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/KeyUsage.java
index 8301013..78d2318 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/x509/KeyUsage.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/x509/KeyUsage.java
@@ -1,5 +1,6 @@
 package org.bouncycastle.asn1.x509;
 
+import org.bouncycastle.asn1.ASN1BitString;
 import org.bouncycastle.asn1.ASN1Object;
 import org.bouncycastle.asn1.ASN1Primitive;
 import org.bouncycastle.asn1.DERBitString;
@@ -34,7 +35,7 @@
     public static final int        encipherOnly     = (1 << 0);
     public static final int        decipherOnly     = (1 << 15);
 
-    private DERBitString bitString;
+    private ASN1BitString bitString;
 
     public static KeyUsage getInstance(Object obj)   // needs to be DERBitString for other VMs
     {
@@ -44,7 +45,7 @@
         }
         else if (obj != null)
         {
-            return new KeyUsage(DERBitString.getInstance(obj));
+            return new KeyUsage(ASN1BitString.getInstance(obj));
         }
 
         return null;
@@ -69,7 +70,7 @@
     }
 
     private KeyUsage(
-        DERBitString bitString)
+        ASN1BitString bitString)
     {
         this.bitString = bitString;
     }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x509/ObjectDigestInfo.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/ObjectDigestInfo.java
index d055705..6f83541 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/x509/ObjectDigestInfo.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/x509/ObjectDigestInfo.java
@@ -1,5 +1,6 @@
 package org.bouncycastle.asn1.x509;
 
+import org.bouncycastle.asn1.ASN1BitString;
 import org.bouncycastle.asn1.ASN1EncodableVector;
 import org.bouncycastle.asn1.ASN1Enumerated;
 import org.bouncycastle.asn1.ASN1Object;
@@ -54,7 +55,7 @@
 
     AlgorithmIdentifier digestAlgorithm;
 
-    DERBitString objectDigest;
+    ASN1BitString objectDigest;
 
     public static ObjectDigestInfo getInstance(
         Object obj)
@@ -129,7 +130,7 @@
 
         digestAlgorithm = AlgorithmIdentifier.getInstance(seq.getObjectAt(1 + offset));
 
-        objectDigest = DERBitString.getInstance(seq.getObjectAt(2 + offset));
+        objectDigest = ASN1BitString.getInstance(seq.getObjectAt(2 + offset));
     }
 
     public ASN1Enumerated getDigestedObjectType()
@@ -147,7 +148,7 @@
         return digestAlgorithm;
     }
 
-    public DERBitString getObjectDigest()
+    public ASN1BitString getObjectDigest()
     {
         return objectDigest;
     }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x509/OtherName.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/OtherName.java
index 93cbafc..989d0d1 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/x509/OtherName.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/x509/OtherName.java
@@ -67,7 +67,7 @@
     private OtherName(ASN1Sequence seq)
     {
         this.typeID = ASN1ObjectIdentifier.getInstance(seq.getObjectAt(0));
-        this.value = ASN1TaggedObject.getInstance(seq.getObjectAt(1)).getObject(); // explicitly tagged
+        this.value = ASN1TaggedObject.getInstance(seq.getObjectAt(1)).getExplicitBaseObject();
     }
 
     public ASN1ObjectIdentifier getTypeID()
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x509/PKIXNameConstraintValidator.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/PKIXNameConstraintValidator.java
index 22bf7ce..b918f98 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/x509/PKIXNameConstraintValidator.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/x509/PKIXNameConstraintValidator.java
@@ -9,9 +9,9 @@
 import java.util.Map;
 import java.util.Set;
 
+import org.bouncycastle.asn1.ASN1IA5String;
 import org.bouncycastle.asn1.ASN1OctetString;
 import org.bouncycastle.asn1.ASN1Sequence;
-import org.bouncycastle.asn1.DERIA5String;
 import org.bouncycastle.asn1.x500.RDN;
 import org.bouncycastle.asn1.x500.X500Name;
 import org.bouncycastle.asn1.x500.style.IETFUtils;
@@ -1822,22 +1822,22 @@
     private static String extractHostFromURL(String url)
     {
         // see RFC 1738
-        // remove ':' after protocol, e.g. http:
+        // remove ':' after protocol, e.g. https:
         String sub = url.substring(url.indexOf(':') + 1);
-        // extract host from Common Internet Scheme Syntax, e.g. http://
+        // extract host from Common Internet Scheme Syntax, e.g. https://
         if (sub.indexOf("//") != -1)
         {
             sub = sub.substring(sub.indexOf("//") + 2);
         }
-        // first remove port, e.g. http://test.com:21
+        // first remove port, e.g. https://test.com:21
         if (sub.lastIndexOf(':') != -1)
         {
             sub = sub.substring(0, sub.lastIndexOf(':'));
         }
-        // remove user and password, e.g. http://john:[email protected]
+        // remove user and password, e.g. https://john:[email protected]
         sub = sub.substring(sub.indexOf(':') + 1);
         sub = sub.substring(sub.indexOf('@') + 1);
-        // remove local parts, e.g. http://test.com/bla
+        // remove local parts, e.g. https://test.com/bla
         if (sub.indexOf('/') != -1)
         {
             sub = sub.substring(0, sub.indexOf('/'));
@@ -1847,7 +1847,7 @@
 
     private String extractNameAsString(GeneralName name)
     {
-        return DERIA5String.getInstance(name.getName()).getString();
+        return ASN1IA5String.getInstance(name.getName()).getString();
     }
 
     /**
@@ -2076,6 +2076,7 @@
             temp.append(":");
             try
             {
+                // -DM Hex.toHexString
                 temp.append(Hex.toHexString(name.getValue().toASN1Primitive().getEncoded()));
             }
             catch (IOException e)
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x509/PolicyQualifierInfo.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/PolicyQualifierInfo.java
index 6c6b89c..9b4a54f 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/x509/PolicyQualifierInfo.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/x509/PolicyQualifierInfo.java
@@ -114,4 +114,9 @@
 
       return new DERSequence(dev);
    }
+
+    public String toString()
+    {
+        return "PolicyQualifierInfo[" + policyQualifierId + ", " + qualifier + "]";
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x509/ReasonFlags.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/ReasonFlags.java
index 612e2c5..5a405f0 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/x509/ReasonFlags.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/x509/ReasonFlags.java
@@ -1,5 +1,6 @@
 package org.bouncycastle.asn1.x509;
 
+import org.bouncycastle.asn1.ASN1BitString;
 import org.bouncycastle.asn1.DERBitString;
 
 /**
@@ -78,7 +79,7 @@
     }
 
     public ReasonFlags(
-        DERBitString reasons)
+        ASN1BitString reasons)
     {
         super(reasons.getBytes(), reasons.getPadBits());
     }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x509/SubjectAltPublicKeyInfo.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/SubjectAltPublicKeyInfo.java
new file mode 100644
index 0000000..19e79fd
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/x509/SubjectAltPublicKeyInfo.java
@@ -0,0 +1,108 @@
+package org.bouncycastle.asn1.x509;
+
+import org.bouncycastle.asn1.ASN1BitString;
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERSequence;
+
+/**
+ * X.509 Section 9.8.2.
+ * <br/>
+ * This public-key certificate extension, when present, shall contain the subject’s alternative public key information
+ * <pre>
+ * subjectAltPublicKeyInfo EXTENSION ::= {
+ *      SYNTAX SubjectAltPublicKeyInfo
+ *      IDENTIFIED BY id-ce-subjectAltPublicKeyInfo }
+ *
+ * SubjectAltPublicKeyInfo ::= SEQUENCE {
+ *     algorithm AlgorithmIdentifier{{SupportedAlgorithms}},
+ *     subjectAltPublicKey BIT STRING }
+ * </pre>
+ * The SubjectAltPublicKeyInfo data type has the following components:
+ * <ul>
+ * <li>the algorithm subcomponent, which shall hold the algorithm that this public key is an instance of</li>
+ * <li>the subjectAltPublicKey subcomponent, which shall hold the alternative public key</li>
+ * </ul>
+ * This extension may be flagged as critical or as non-critical.
+ * <br/>
+ * NOTE – It is recommended that it be flagged as non-critical. Flagging it as critical would require relying parties to understand this
+ * extension and the alternative public-key algorithm.
+ */
+public class SubjectAltPublicKeyInfo
+    extends ASN1Object
+{
+    private AlgorithmIdentifier algorithm;
+    private ASN1BitString subjectAltPublicKey;
+
+    public static SubjectAltPublicKeyInfo getInstance(
+        ASN1TaggedObject obj,
+        boolean          explicit)
+    {
+        return getInstance(ASN1Sequence.getInstance(obj, explicit));
+    }
+
+    public static SubjectAltPublicKeyInfo getInstance(
+        Object obj)
+    {
+        if (obj instanceof SubjectAltPublicKeyInfo)
+        {
+            return (SubjectAltPublicKeyInfo)obj;
+        }
+        else if (obj != null)
+        {
+            return new SubjectAltPublicKeyInfo(ASN1Sequence.getInstance(obj));
+        }
+
+        return null;
+    }
+
+    public static SubjectAltPublicKeyInfo fromExtensions(Extensions extensions)
+    {
+        return getInstance(Extensions.getExtensionParsedValue(extensions, Extension.subjectAltPublicKeyInfo));
+    }
+
+    private SubjectAltPublicKeyInfo(ASN1Sequence s)
+    {
+        if (s.size() != 2)
+        {
+            throw new IllegalArgumentException("extension should contain only 2 elements");
+        }
+        algorithm = AlgorithmIdentifier.getInstance(s.getObjectAt(0));
+        subjectAltPublicKey = ASN1BitString.getInstance(s.getObjectAt(1));
+    }
+
+    public SubjectAltPublicKeyInfo(AlgorithmIdentifier algorithm, ASN1BitString subjectAltPublicKey)
+    {
+        this.algorithm = algorithm;
+        this.subjectAltPublicKey = subjectAltPublicKey;
+    }
+
+    public SubjectAltPublicKeyInfo(SubjectPublicKeyInfo subjectPublicKeyInfo)
+    {
+        this.algorithm = subjectPublicKeyInfo.getAlgorithm();
+        this.subjectAltPublicKey = subjectPublicKeyInfo.getPublicKeyData();
+    }
+
+    public AlgorithmIdentifier getAlgorithm()
+    {
+        return algorithm;
+    }
+
+    public ASN1BitString getSubjectAltPublicKey()
+    {
+        return subjectAltPublicKey;
+    }
+
+    public ASN1Primitive toASN1Primitive()
+    {
+        ASN1EncodableVector v = new ASN1EncodableVector();
+
+        v.add(algorithm);
+        v.add(subjectAltPublicKey);
+
+        return new DERSequence(v);
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x509/SubjectPublicKeyInfo.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/SubjectPublicKeyInfo.java
index 5ad2952..9c2241c 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/x509/SubjectPublicKeyInfo.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/x509/SubjectPublicKeyInfo.java
@@ -3,6 +3,7 @@
 import java.io.IOException;
 import java.util.Enumeration;
 
+import org.bouncycastle.asn1.ASN1BitString;
 import org.bouncycastle.asn1.ASN1Encodable;
 import org.bouncycastle.asn1.ASN1EncodableVector;
 import org.bouncycastle.asn1.ASN1Object;
@@ -22,7 +23,7 @@
     extends ASN1Object
 {
     private AlgorithmIdentifier     algId;
-    private DERBitString            keyData;
+    private ASN1BitString           keyData;
 
     public static SubjectPublicKeyInfo getInstance(
         ASN1TaggedObject obj,
@@ -48,6 +49,14 @@
 
     public SubjectPublicKeyInfo(
         AlgorithmIdentifier algId,
+        ASN1BitString publicKey)
+    {
+        this.keyData = publicKey;
+        this.algId = algId;
+    }
+
+    public SubjectPublicKeyInfo(
+        AlgorithmIdentifier algId,
         ASN1Encodable       publicKey)
         throws IOException
     {
@@ -75,10 +84,10 @@
                     + seq.size());
         }
 
-        Enumeration         e = seq.getObjects();
+        Enumeration e = seq.getObjects();
 
         this.algId = AlgorithmIdentifier.getInstance(e.nextElement());
-        this.keyData = DERBitString.getInstance(e.nextElement());
+        this.keyData = ASN1BitString.getInstance(e.nextElement());
     }
 
     public AlgorithmIdentifier getAlgorithm()
@@ -129,7 +138,7 @@
      *
      * @return the public key as the raw bit string...
      */
-    public DERBitString getPublicKeyData()
+    public ASN1BitString getPublicKeyData()
     {
         return keyData;
     }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x509/TBSCertList.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/TBSCertList.java
index abaa909..deda9dd 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/x509/TBSCertList.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/x509/TBSCertList.java
@@ -102,7 +102,7 @@
         }
     }
 
-    private class RevokedCertificatesEnumeration
+    private static class RevokedCertificatesEnumeration
         implements Enumeration
     {
         private final Enumeration en;
@@ -123,7 +123,7 @@
         }
     }
 
-    private class EmptyEnumeration
+    private static class EmptyEnumeration
         implements Enumeration
     {
         public boolean hasMoreElements()
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x509/TBSCertificate.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/TBSCertificate.java
index 6ae198a..7676329 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/x509/TBSCertificate.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/x509/TBSCertificate.java
@@ -1,18 +1,15 @@
 package org.bouncycastle.asn1.x509;
 
-import java.math.BigInteger;
-
+import org.bouncycastle.asn1.ASN1BitString;
 import org.bouncycastle.asn1.ASN1EncodableVector;
 import org.bouncycastle.asn1.ASN1Integer;
 import org.bouncycastle.asn1.ASN1Object;
 import org.bouncycastle.asn1.ASN1Primitive;
 import org.bouncycastle.asn1.ASN1Sequence;
 import org.bouncycastle.asn1.ASN1TaggedObject;
-import org.bouncycastle.asn1.DERBitString;
 import org.bouncycastle.asn1.DERSequence;
 import org.bouncycastle.asn1.DERTaggedObject;
 import org.bouncycastle.asn1.x500.X500Name;
-import org.bouncycastle.util.BigIntegers;
 import org.bouncycastle.util.Properties;
 
 /**
@@ -47,8 +44,8 @@
     Time                    startDate, endDate;
     X500Name                subject;
     SubjectPublicKeyInfo    subjectPublicKeyInfo;
-    DERBitString            issuerUniqueId;
-    DERBitString            subjectUniqueId;
+    ASN1BitString           issuerUniqueId;
+    ASN1BitString           subjectUniqueId;
     Extensions              extensions;
 
     public static TBSCertificate getInstance(
@@ -96,15 +93,15 @@
         boolean isV1 = false;
         boolean isV2 = false;
  
-        if (version.hasValue(BigInteger.valueOf(0)))
+        if (version.hasValue(0))
         {
             isV1 = true;
         }
-        else if (version.hasValue(BigInteger.valueOf(1)))
+        else if (version.hasValue(1))
         {
             isV2 = true;
         }
-        else if (!version.hasValue(BigInteger.valueOf(2)))
+        else if (!version.hasValue(2))
         {
             throw new IllegalArgumentException("version number not recognised");
         }
@@ -142,10 +139,10 @@
             switch (extra.getTagNo())
             {
             case 1:
-                issuerUniqueId = DERBitString.getInstance(extra, false);
+                issuerUniqueId = ASN1BitString.getInstance(extra, false);
                 break;
             case 2:
-                subjectUniqueId = DERBitString.getInstance(extra, false);
+                subjectUniqueId = ASN1BitString.getInstance(extra, false);
                 break;
             case 3:
                 if (isV2)
@@ -206,12 +203,12 @@
         return subjectPublicKeyInfo;
     }
 
-    public DERBitString getIssuerUniqueId()
+    public ASN1BitString getIssuerUniqueId()
     {
         return issuerUniqueId;
     }
 
-    public DERBitString getSubjectUniqueId()
+    public ASN1BitString getSubjectUniqueId()
     {
         return subjectUniqueId;
     }
@@ -238,7 +235,7 @@
         ASN1EncodableVector v = new ASN1EncodableVector();
 
         // DEFAULT Zero
-        if (!version.hasValue(BigIntegers.ZERO))
+        if (!version.hasValue(0))
         {
             v.add(new DERTaggedObject(true, 0, version));
         }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x509/TBSCertificateStructure.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/TBSCertificateStructure.java
index b635d59..440ca5d 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/x509/TBSCertificateStructure.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/x509/TBSCertificateStructure.java
@@ -1,11 +1,11 @@
 package org.bouncycastle.asn1.x509;
 
+import org.bouncycastle.asn1.ASN1BitString;
 import org.bouncycastle.asn1.ASN1Integer;
 import org.bouncycastle.asn1.ASN1Object;
 import org.bouncycastle.asn1.ASN1Primitive;
 import org.bouncycastle.asn1.ASN1Sequence;
 import org.bouncycastle.asn1.ASN1TaggedObject;
-import org.bouncycastle.asn1.DERBitString;
 import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
 import org.bouncycastle.asn1.x500.X500Name;
 
@@ -43,8 +43,8 @@
     Time                    startDate, endDate;
     X500Name                subject;
     SubjectPublicKeyInfo    subjectPublicKeyInfo;
-    DERBitString            issuerUniqueId;
-    DERBitString            subjectUniqueId;
+    ASN1BitString           issuerUniqueId;
+    ASN1BitString           subjectUniqueId;
     X509Extensions          extensions;
 
     public static TBSCertificateStructure getInstance(
@@ -116,10 +116,10 @@
             switch (extra.getTagNo())
             {
             case 1:
-                issuerUniqueId = DERBitString.getInstance(extra, false);
+                issuerUniqueId = ASN1BitString.getInstance(extra, false);
                 break;
             case 2:
-                subjectUniqueId = DERBitString.getInstance(extra, false);
+                subjectUniqueId = ASN1BitString.getInstance(extra, false);
                 break;
             case 3:
                 extensions = X509Extensions.getInstance(extra);
@@ -172,12 +172,12 @@
         return subjectPublicKeyInfo;
     }
 
-    public DERBitString getIssuerUniqueId()
+    public ASN1BitString getIssuerUniqueId()
     {
         return issuerUniqueId;
     }
 
-    public DERBitString getSubjectUniqueId()
+    public ASN1BitString getSubjectUniqueId()
     {
         return subjectUniqueId;
     }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x509/Time.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/Time.java
index a99dddf..0efba33 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/x509/Time.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/x509/Time.java
@@ -16,6 +16,7 @@
 import org.bouncycastle.asn1.ASN1UTCTime;
 import org.bouncycastle.asn1.DERGeneralizedTime;
 import org.bouncycastle.asn1.DERUTCTime;
+import org.bouncycastle.asn1.LocaleUtil;
 
 public class Time
     extends ASN1Object
@@ -27,7 +28,12 @@
         ASN1TaggedObject obj,
         boolean          explicit)
     {
-        return getInstance(obj.getObject()); // must be explicitly tagged
+        if (!explicit)
+        {
+            throw new IllegalArgumentException("choice item must be explicitly tagged");
+        }
+
+        return getInstance(obj.getExplicitBaseObject());
     }
 
     public Time(
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x509/V2Form.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/V2Form.java
index bd4c59c..6063439 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/x509/V2Form.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/x509/V2Form.java
@@ -67,10 +67,7 @@
         this.objectDigestInfo = objectDigestInfo;
     }
 
-    /**
-     * @deprecated use getInstance().
-     */
-    public V2Form(
+    private V2Form(
         ASN1Sequence seq)
     {
         if (seq.size() > 3)
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x509/V3TBSCertificateGenerator.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/V3TBSCertificateGenerator.java
index c4ebdc1..4434d73 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/x509/V3TBSCertificateGenerator.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/x509/V3TBSCertificateGenerator.java
@@ -2,6 +2,8 @@
 
 import org.bouncycastle.asn1.ASN1EncodableVector;
 import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Sequence;
 import org.bouncycastle.asn1.ASN1UTCTime;
 import org.bouncycastle.asn1.DERBitString;
 import org.bouncycastle.asn1.DERSequence;
@@ -155,20 +157,34 @@
         }
     }
 
-    public TBSCertificate generateTBSCertificate()
+    public ASN1Sequence generatePreTBSCertificate()
     {
-        if ((serialNumber == null) || (signature == null)
+        if (signature != null)
+        {
+            throw new IllegalStateException("signature field should not be set in PreTBSCertificate");
+        }
+        if ((serialNumber == null)
             || (issuer == null) || (startDate == null) || (endDate == null)
             || (subject == null && !altNamePresentAndCritical) || (subjectPublicKeyInfo == null))
         {
             throw new IllegalStateException("not all mandatory fields set in V3 TBScertificate generator");
         }
 
+        return generateTBSStructure();
+    }
+
+    private ASN1Sequence generateTBSStructure()
+    {
         ASN1EncodableVector v = new ASN1EncodableVector(10);
 
         v.add(version);
         v.add(serialNumber);
-        v.add(signature);
+
+        if (signature != null)
+        {
+            v.add(signature);
+        }
+        
         v.add(issuer);
 
         //
@@ -208,6 +224,18 @@
             v.add(new DERTaggedObject(true, 3, extensions));
         }
 
-        return TBSCertificate.getInstance(new DERSequence(v));
+        return new DERSequence(v);
+    }
+
+    public TBSCertificate generateTBSCertificate()
+    {
+        if ((serialNumber == null) || (signature == null)
+            || (issuer == null) || (startDate == null) || (endDate == null)
+            || (subject == null && !altNamePresentAndCritical) || (subjectPublicKeyInfo == null))
+        {
+            throw new IllegalStateException("not all mandatory fields set in V3 TBScertificate generator");
+        }
+
+        return TBSCertificate.getInstance(generateTBSStructure());
     }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x509/X509CertificateStructure.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/X509CertificateStructure.java
index 6830030..7c25580 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/x509/X509CertificateStructure.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/x509/X509CertificateStructure.java
@@ -1,11 +1,11 @@
 package org.bouncycastle.asn1.x509;
 
+import org.bouncycastle.asn1.ASN1BitString;
 import org.bouncycastle.asn1.ASN1Integer;
 import org.bouncycastle.asn1.ASN1Object;
 import org.bouncycastle.asn1.ASN1Primitive;
 import org.bouncycastle.asn1.ASN1Sequence;
 import org.bouncycastle.asn1.ASN1TaggedObject;
-import org.bouncycastle.asn1.DERBitString;
 import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
 import org.bouncycastle.asn1.x500.X500Name;
 
@@ -27,7 +27,7 @@
     ASN1Sequence  seq;
     TBSCertificateStructure tbsCert;
     AlgorithmIdentifier     sigAlgId;
-    DERBitString            sig;
+    ASN1BitString sig;
 
     public static X509CertificateStructure getInstance(
         ASN1TaggedObject obj,
@@ -64,7 +64,7 @@
             tbsCert = TBSCertificateStructure.getInstance(seq.getObjectAt(0));
             sigAlgId = AlgorithmIdentifier.getInstance(seq.getObjectAt(1));
 
-            sig = DERBitString.getInstance(seq.getObjectAt(2));
+            sig = ASN1BitString.getInstance(seq.getObjectAt(2));
         }
         else
         {
@@ -117,7 +117,7 @@
         return sigAlgId;
     }
 
-    public DERBitString getSignature()
+    public ASN1BitString getSignature()
     {
         return sig;
     }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x509/X509DefaultEntryConverter.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/X509DefaultEntryConverter.java
index 0ae0f80..c888754 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/x509/X509DefaultEntryConverter.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/x509/X509DefaultEntryConverter.java
@@ -8,6 +8,7 @@
 import org.bouncycastle.asn1.DERIA5String;
 import org.bouncycastle.asn1.DERPrintableString;
 import org.bouncycastle.asn1.DERUTF8String;
+import org.bouncycastle.asn1.x500.style.BCStyle;
 
 /**
  * The default converter for X509 DN entries when going from their
@@ -45,16 +46,16 @@
             {
                 value = value.substring(1);
             }
-            if (oid.equals(X509Name.EmailAddress) || oid.equals(X509Name.DC))
+            if (oid.equals(BCStyle.EmailAddress) || oid.equals(BCStyle.DC))
             {
                 return new DERIA5String(value);
             }
-            else if (oid.equals(X509Name.DATE_OF_BIRTH))  // accept time string as well as # (for compatibility)
+            else if (oid.equals(BCStyle.DATE_OF_BIRTH))  // accept time string as well as # (for compatibility)
             {
                 return new DERGeneralizedTime(value);
             }
-            else if (oid.equals(X509Name.C) || oid.equals(X509Name.SN) || oid.equals(X509Name.DN_QUALIFIER)
-                || oid.equals(X509Name.TELEPHONE_NUMBER))
+            else if (oid.equals(BCStyle.C) || oid.equals(BCStyle.SERIALNUMBER) || oid.equals(BCStyle.DN_QUALIFIER)
+                || oid.equals(BCStyle.TELEPHONE_NUMBER))
             {
                  return new DERPrintableString(value);
             }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x509/X509Extensions.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/X509Extensions.java
index 86e3b24..b43ecd3 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/x509/X509Extensions.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/x509/X509Extensions.java
@@ -12,10 +12,11 @@
 import org.bouncycastle.asn1.ASN1Primitive;
 import org.bouncycastle.asn1.ASN1Sequence;
 import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.BERTags;
 import org.bouncycastle.asn1.DERSequence;
 
 /**
- * @deprecated use {@link Extensions}
+ * @deprecated use {@link Extension} and  {@link Extensions}
  */
 public class X509Extensions
     extends ASN1Object
@@ -236,7 +237,9 @@
 
         if (obj instanceof ASN1TaggedObject)
         {
-            return getInstance(((ASN1TaggedObject)obj).getObject());
+            ASN1TaggedObject taggedObject = ASN1TaggedObject.getInstance(obj, BERTags.CONTEXT_SPECIFIC);
+
+            return getInstance(taggedObject.getBaseObject().toASN1Primitive());
         }
 
         throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x509/X509Name.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/X509Name.java
index 9f8d9b9..c931797 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/x509/X509Name.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/x509/X509Name.java
@@ -15,9 +15,9 @@
 import org.bouncycastle.asn1.ASN1Set;
 import org.bouncycastle.asn1.ASN1String;
 import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.ASN1UniversalString;
 import org.bouncycastle.asn1.DERSequence;
 import org.bouncycastle.asn1.DERSet;
-import org.bouncycastle.asn1.DERUniversalString;
 import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
 import org.bouncycastle.asn1.x500.X500Name;
 import org.bouncycastle.util.Strings;
@@ -425,7 +425,7 @@
                    ordering.addElement(ASN1ObjectIdentifier.getInstance(s.getObjectAt(0)));
                    
                    ASN1Encodable value = s.getObjectAt(1);
-                   if (value instanceof ASN1String && !(value instanceof DERUniversalString))
+                   if (value instanceof ASN1String && !(value instanceof ASN1UniversalString))
                    {
                        String v = ((ASN1String)value).getString();
                        if (v.length() > 0 && v.charAt(0) == '#')
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x509/X509NameEntryConverter.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/X509NameEntryConverter.java
index b5de2e0..42e50c5 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/x509/X509NameEntryConverter.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/x509/X509NameEntryConverter.java
@@ -4,7 +4,7 @@
 
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
 import org.bouncycastle.asn1.ASN1Primitive;
-import org.bouncycastle.asn1.DERPrintableString;
+import org.bouncycastle.asn1.ASN1PrintableString;
 import org.bouncycastle.util.encoders.Hex;
 
 /**
@@ -71,7 +71,7 @@
     protected boolean canBePrintable(
         String  str)
     {
-        return DERPrintableString.isPrintableString(str);
+        return ASN1PrintableString.isPrintableString(str);
     }
     
     /**
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x509/X509ObjectIdentifiers.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/X509ObjectIdentifiers.java
index 02857c7..c1eab27 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/x509/X509ObjectIdentifiers.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/x509/X509ObjectIdentifiers.java
@@ -107,4 +107,11 @@
     static final ASN1ObjectIdentifier ocspAccessMethod = id_ad_ocsp;
     /** OID for crl uri in AuthorityInformationAccess extension */
     static final ASN1ObjectIdentifier crlAccessMethod  = id_ad_caIssuers;
+
+
+    /**
+     *  id-PasswordBasedMac OBJECT IDENTIFIER ::= { iso(1) member-body(2)
+     *          us(840) nt(113533) nsn(7) algorithms(66) 13 }
+     */
+    static final ASN1ObjectIdentifier id_PasswordBasedMac = new ASN1ObjectIdentifier("1.2.840.113533.7.66.13");
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x9/DHValidationParms.java b/bcprov/src/main/java/org/bouncycastle/asn1/x9/DHValidationParms.java
index 8def60c..053dc2c 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/x9/DHValidationParms.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/x9/DHValidationParms.java
@@ -1,12 +1,12 @@
 package org.bouncycastle.asn1.x9;
 
+import org.bouncycastle.asn1.ASN1BitString;
 import org.bouncycastle.asn1.ASN1EncodableVector;
 import org.bouncycastle.asn1.ASN1Integer;
 import org.bouncycastle.asn1.ASN1Object;
 import org.bouncycastle.asn1.ASN1Primitive;
 import org.bouncycastle.asn1.ASN1Sequence;
 import org.bouncycastle.asn1.ASN1TaggedObject;
-import org.bouncycastle.asn1.DERBitString;
 import org.bouncycastle.asn1.DERSequence;
 
 /**
@@ -14,7 +14,7 @@
  */
 public class DHValidationParms extends ASN1Object
 {
-    private DERBitString seed;
+    private ASN1BitString seed;
     private ASN1Integer pgenCounter;
 
     public static DHValidationParms getInstance(ASN1TaggedObject obj, boolean explicit)
@@ -36,7 +36,7 @@
         return null;
     }
 
-    public DHValidationParms(DERBitString seed, ASN1Integer pgenCounter)
+    public DHValidationParms(ASN1BitString seed, ASN1Integer pgenCounter)
     {
         if (seed == null)
         {
@@ -58,11 +58,11 @@
             throw new IllegalArgumentException("Bad sequence size: " + seq.size());
         }
 
-        this.seed = DERBitString.getInstance(seq.getObjectAt(0));
+        this.seed = ASN1BitString.getInstance(seq.getObjectAt(0));
         this.pgenCounter = ASN1Integer.getInstance(seq.getObjectAt(1));
     }
 
-    public DERBitString getSeed()
+    public ASN1BitString getSeed()
     {
         return this.seed;
     }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x9/ECNamedCurveTable.java b/bcprov/src/main/java/org/bouncycastle/asn1/x9/ECNamedCurveTable.java
index 1d68df7..32a4845 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/x9/ECNamedCurveTable.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/x9/ECNamedCurveTable.java
@@ -69,6 +69,47 @@
         return ecP;
     }
 
+    public static X9ECParametersHolder getByNameLazy(String name)
+    {
+        X9ECParametersHolder holder = X962NamedCurves.getByNameLazy(name);
+
+        if (null == holder)
+        {
+            holder = SECNamedCurves.getByNameLazy(name);
+        }
+
+        if (null == holder)
+        {
+            holder = NISTNamedCurves.getByNameLazy(name);
+        }
+
+        // BEGIN Android-removed: Unsupported curves
+        /*
+        if (null == holder)
+        {
+            holder = TeleTrusTNamedCurves.getByNameLazy(name);
+        }
+
+        if (null == holder)
+        {
+            holder = ANSSINamedCurves.getByNameLazy(name);
+        }
+
+        if (null == holder)
+        {
+            holder = ECGOST3410NamedCurves.getByNameLazy(name);
+        }
+
+        if (null == holder)
+        {
+            holder = GMNamedCurves.getByNameLazy(name);
+        }
+        */
+        // END Android-removed: Unsupported curves
+
+        return holder;
+    }
+
     /**
      * return the object identifier signified by the passed in name. Null
      * if there is no object identifier associated with name.
@@ -222,6 +263,44 @@
         return ecP;
     }
 
+    public static X9ECParametersHolder getByOIDLazy(ASN1ObjectIdentifier oid)
+    {
+        X9ECParametersHolder holder = X962NamedCurves.getByOIDLazy(oid);
+
+        if (null == holder)
+        {
+            holder = SECNamedCurves.getByOIDLazy(oid);
+        }
+
+        // NOTE: All the NIST curves are currently from SEC, so no point in redundant OID lookup
+
+        // BEGIN Android-removed: Unsupported curves
+        /*
+        if (null == holder)
+        {
+            holder = TeleTrusTNamedCurves.getByOIDLazy(oid);
+        }
+
+        if (null == holder)
+        {
+            holder = ANSSINamedCurves.getByOIDLazy(oid);
+        }
+
+        if (null == holder)
+        {
+            holder = ECGOST3410NamedCurves.getByOIDLazy(oid);
+        }
+
+        if (null == holder)
+        {
+            holder = GMNamedCurves.getByOIDLazy(oid);
+        }
+        */
+        // END Android-removed: Unsupported curves
+
+        return holder;
+    }
+
     /**
      * return an enumeration of the names of the available curves.
      *
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x9/ValidationParams.java b/bcprov/src/main/java/org/bouncycastle/asn1/x9/ValidationParams.java
index 466e265..47d16ca 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/x9/ValidationParams.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/x9/ValidationParams.java
@@ -2,6 +2,7 @@
 
 import java.math.BigInteger;
 
+import org.bouncycastle.asn1.ASN1BitString;
 import org.bouncycastle.asn1.ASN1EncodableVector;
 import org.bouncycastle.asn1.ASN1Integer;
 import org.bouncycastle.asn1.ASN1Object;
@@ -23,7 +24,7 @@
 public class ValidationParams
     extends ASN1Object
 {
-    private DERBitString seed;
+    private ASN1BitString seed;
     private ASN1Integer pgenCounter;
 
     public static ValidationParams getInstance(ASN1TaggedObject obj, boolean explicit)
@@ -78,7 +79,7 @@
             throw new IllegalArgumentException("Bad sequence size: " + seq.size());
         }
 
-        this.seed = DERBitString.getInstance(seq.getObjectAt(0));
+        this.seed = ASN1BitString.getInstance(seq.getObjectAt(0));
         this.pgenCounter = ASN1Integer.getInstance(seq.getObjectAt(1));
     }
 
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x9/X962NamedCurves.java b/bcprov/src/main/java/org/bouncycastle/asn1/x9/X962NamedCurves.java
index 3d1ddd5..38690fd 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/x9/X962NamedCurves.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/x9/X962NamedCurves.java
@@ -35,141 +35,183 @@
 
     static X9ECParametersHolder prime192v1 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             BigInteger n = fromHex("ffffffffffffffffffffffff99def836146bc9b1b4d22831");
             BigInteger h = BigInteger.valueOf(1);
 
-            ECCurve curve = configureCurve(new ECCurve.Fp(
+            return configureCurve(new ECCurve.Fp(
                 fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFF"),
                 fromHex("fffffffffffffffffffffffffffffffefffffffffffffffc"),
                 fromHex("64210519e59c80e70fa7e9ab72243049feb8deecc146b9b1"),
-                n, h));
+                n, h, true));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = Hex.decodeStrict("3045AE6FC8422f64ED579528D38120EAE12196D5");
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "03188da80eb03090f67cbf20eb43a18800f4ff0afd82ff1012");
 
-            return new X9ECParameters(curve, G, n, h, Hex.decodeStrict("3045AE6FC8422f64ED579528D38120EAE12196D5"));
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
     static X9ECParametersHolder prime192v2 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             BigInteger n = fromHex("fffffffffffffffffffffffe5fb1a724dc80418648d8dd31");
             BigInteger h = BigInteger.valueOf(1);
 
-            ECCurve curve = configureCurve(new ECCurve.Fp(
+            return configureCurve(new ECCurve.Fp(
                 fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFF"),
                 fromHex("fffffffffffffffffffffffffffffffefffffffffffffffc"),
                 fromHex("cc22d6dfb95c6b25e49c0d6364a4e5980c393aa21668d953"),
-                n, h));
+                n, h, true));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = Hex.decodeStrict("31a92ee2029fd10d901b113e990710f0d21ac6b6");
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "03eea2bae7e1497842f2de7769cfe9c989c072ad696f48034a");
 
-            return new X9ECParameters(curve, G, n, h, Hex.decodeStrict("31a92ee2029fd10d901b113e990710f0d21ac6b6"));
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
     static X9ECParametersHolder prime192v3 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             BigInteger n = fromHex("ffffffffffffffffffffffff7a62d031c83f4294f640ec13");
             BigInteger h = BigInteger.valueOf(1);
 
-            ECCurve curve = configureCurve(new ECCurve.Fp(
+            return configureCurve(new ECCurve.Fp(
                 fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFF"),
                 fromHex("fffffffffffffffffffffffffffffffefffffffffffffffc"),
                 fromHex("22123dc2395a05caa7423daeccc94760a7d462256bd56916"),
-                n, h));
+                n, h, true));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = Hex.decodeStrict("c469684435deb378c4b65ca9591e2a5763059a2e");
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "027d29778100c65a1da1783716588dce2b8b4aee8e228f1896");
 
-            return new X9ECParameters(curve, G, n, h, Hex.decodeStrict("c469684435deb378c4b65ca9591e2a5763059a2e"));
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
     static X9ECParametersHolder prime239v1 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             BigInteger n = fromHex("7fffffffffffffffffffffff7fffff9e5e9a9f5d9071fbd1522688909d0b");
             BigInteger h = BigInteger.valueOf(1);
 
-            ECCurve curve = configureCurve(new ECCurve.Fp(
+            return configureCurve(new ECCurve.Fp(
                 new BigInteger("883423532389192164791648750360308885314476597252960362792450860609699839"),
                 fromHex("7fffffffffffffffffffffff7fffffffffff8000000000007ffffffffffc"),
                 fromHex("6b016c3bdcf18941d0d654921475ca71a9db2fb27d1d37796185c2942c0a"),
-                n, h));
+                n, h, true));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = Hex.decodeStrict("e43bb460f0b80cc0c0b075798e948060f8321b7d");
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "020ffa963cdca8816ccc33b8642bedf905c3d358573d3f27fbbd3b3cb9aaaf");
 
-            return new X9ECParameters(curve, G, n, h, Hex.decodeStrict("e43bb460f0b80cc0c0b075798e948060f8321b7d"));
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
     static X9ECParametersHolder prime239v2 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             BigInteger n = fromHex("7fffffffffffffffffffffff800000cfa7e8594377d414c03821bc582063");
             BigInteger h = BigInteger.valueOf(1);
 
-            ECCurve curve = configureCurve(new ECCurve.Fp(
+            return configureCurve(new ECCurve.Fp(
                 new BigInteger("883423532389192164791648750360308885314476597252960362792450860609699839"),
                 fromHex("7fffffffffffffffffffffff7fffffffffff8000000000007ffffffffffc"),
                 fromHex("617fab6832576cbbfed50d99f0249c3fee58b94ba0038c7ae84c8c832f2c"),
-                n, h));
+                n, h, true));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = Hex.decodeStrict("e8b4011604095303ca3b8099982be09fcb9ae616");
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "0238af09d98727705120c921bb5e9e26296a3cdcf2f35757a0eafd87b830e7");
 
-            return new X9ECParameters(curve, G, n, h, Hex.decodeStrict("e8b4011604095303ca3b8099982be09fcb9ae616"));
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
     static X9ECParametersHolder prime239v3 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             BigInteger n = fromHex("7fffffffffffffffffffffff7fffff975deb41b3a6057c3c432146526551");
             BigInteger h = BigInteger.valueOf(1);
 
-            ECCurve curve = configureCurve(new ECCurve.Fp(
+            return configureCurve(new ECCurve.Fp(
                 new BigInteger("883423532389192164791648750360308885314476597252960362792450860609699839"),
                 fromHex("7fffffffffffffffffffffff7fffffffffff8000000000007ffffffffffc"),
                 fromHex("255705fa2a306654b1f4cb03d6a750a30c250102d4988717d9ba15ab6d3e"),
-                n, h));
+                n, h, true));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = Hex.decodeStrict("7d7374168ffe3471b60a857686a19475d3bfa2ff");
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "036768ae8e18bb92cfcf005c949aa2c6d94853d0e660bbf854b1c9505fe95a");
 
-            return new X9ECParameters(curve, G, n, h, Hex.decodeStrict("7d7374168ffe3471b60a857686a19475d3bfa2ff"));
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
     static X9ECParametersHolder prime256v1 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             BigInteger n = fromHex("ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551");
             BigInteger h = BigInteger.valueOf(1);
 
-            ECCurve curve = configureCurve(new ECCurve.Fp(
+            return configureCurve(new ECCurve.Fp(
                 new BigInteger("115792089210356248762697446949407573530086143415290314195533631308867097853951"),
                 fromHex("ffffffff00000001000000000000000000000000fffffffffffffffffffffffc"),
                 fromHex("5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b"),
-                n, h));
+                n, h, true));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = Hex.decodeStrict("c49d360886e704936a6678e1139d26b7819f7e90");
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "036b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296");
 
-            return new X9ECParameters(curve, G, n, h, Hex.decodeStrict("c49d360886e704936a6678e1139d26b7819f7e90"));
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
@@ -178,337 +220,433 @@
      */
     static X9ECParametersHolder c2pnb163v1 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             BigInteger n = fromHex("0400000000000000000001E60FC8821CC74DAEAFC1");
             BigInteger h = BigInteger.valueOf(2);
 
-            ECCurve curve = configureCurve(new ECCurve.F2m(
+            return configureCurve(new ECCurve.F2m(
                 163,
                 1, 2, 8,
                 fromHex("072546B5435234A422E0789675F432C89435DE5242"),
                 fromHex("00C9517D06D5240D3CFF38C74B20B6CD4D6F9DD4D9"),
                 n, h));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = Hex.decodeStrict("D2C0FB15760860DEF1EEF4D696E6768756151754");
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "0307AF69989546103D79329FCC3D74880F33BBE803CB");
 
-            return new X9ECParameters(curve, G, n, h, Hex.decodeStrict("D2C0FB15760860DEF1EEF4D696E6768756151754"));
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
     static X9ECParametersHolder c2pnb163v2 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             BigInteger n = fromHex("03FFFFFFFFFFFFFFFFFFFDF64DE1151ADBB78F10A7");
             BigInteger h = BigInteger.valueOf(2);
 
-            ECCurve curve = configureCurve(new ECCurve.F2m(
+            return configureCurve(new ECCurve.F2m(
                 163,
                 1, 2, 8,
                 fromHex("0108B39E77C4B108BED981ED0E890E117C511CF072"),
                 fromHex("0667ACEB38AF4E488C407433FFAE4F1C811638DF20"),
                 n, h));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = null;
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "030024266E4EB5106D0A964D92C4860E2671DB9B6CC5");
 
-            return new X9ECParameters(curve, G, n, h);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
     static X9ECParametersHolder c2pnb163v3 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             BigInteger n = fromHex("03FFFFFFFFFFFFFFFFFFFE1AEE140F110AFF961309");
             BigInteger h = BigInteger.valueOf(2);
 
-            ECCurve curve = configureCurve(new ECCurve.F2m(
+            return configureCurve(new ECCurve.F2m(
                 163,
                 1, 2, 8,
                 fromHex("07A526C63D3E25A256A007699F5447E32AE456B50E"),
                 fromHex("03F7061798EB99E238FD6F1BF95B48FEEB4854252B"),
                 n, h));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = null;
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "0202F9F87B7C574D0BDECF8A22E6524775F98CDEBDCB");
 
-            return new X9ECParameters(curve, G, n, h);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
     static X9ECParametersHolder c2pnb176w1 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             BigInteger n = fromHex("010092537397ECA4F6145799D62B0A19CE06FE26AD");
             BigInteger h = BigInteger.valueOf(0xFF6E);
 
-            ECCurve curve = configureCurve(new ECCurve.F2m(
+            return configureCurve(new ECCurve.F2m(
                 176,
                 1, 2, 43,
                 fromHex("E4E6DB2995065C407D9D39B8D0967B96704BA8E9C90B"),
                 fromHex("5DDA470ABE6414DE8EC133AE28E9BBD7FCEC0AE0FFF2"),
                 n, h));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = null;
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "038D16C2866798B600F9F08BB4A8E860F3298CE04A5798");
 
-            return new X9ECParameters(curve, G, n, h);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
     static X9ECParametersHolder c2tnb191v1 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             BigInteger n = fromHex("40000000000000000000000004A20E90C39067C893BBB9A5");
             BigInteger h = BigInteger.valueOf(2);
 
-            ECCurve curve = configureCurve(new ECCurve.F2m(
+            return configureCurve(new ECCurve.F2m(
                 191,
                 9,
                 fromHex("2866537B676752636A68F56554E12640276B649EF7526267"),
                 fromHex("2E45EF571F00786F67B0081B9495A3D95462F5DE0AA185EC"),
                 n, h));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = Hex.decodeStrict("4E13CA542744D696E67687561517552F279A8C84");
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "0236B3DAF8A23206F9C4F299D7B21A9C369137F2C84AE1AA0D");
 
-            return new X9ECParameters(curve, G, n, h, Hex.decodeStrict("4E13CA542744D696E67687561517552F279A8C84"));
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
     static X9ECParametersHolder c2tnb191v2 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             BigInteger n = fromHex("20000000000000000000000050508CB89F652824E06B8173");
             BigInteger h = BigInteger.valueOf(4);
 
-            ECCurve curve = configureCurve(new ECCurve.F2m(
+            return configureCurve(new ECCurve.F2m(
                 191,
                 9,
                 fromHex("401028774D7777C7B7666D1366EA432071274F89FF01E718"),
                 fromHex("0620048D28BCBD03B6249C99182B7C8CD19700C362C46A01"),
                 n, h));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = null;
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "023809B2B7CC1B28CC5A87926AAD83FD28789E81E2C9E3BF10");
 
-            return new X9ECParameters(curve, G, n, h);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
     static X9ECParametersHolder c2tnb191v3 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             BigInteger n = fromHex("155555555555555555555555610C0B196812BFB6288A3EA3");
             BigInteger h = BigInteger.valueOf(6);
 
-            ECCurve curve = configureCurve(new ECCurve.F2m(
+            return configureCurve(new ECCurve.F2m(
                 191,
                 9,
                 fromHex("6C01074756099122221056911C77D77E77A777E7E7E77FCB"),
                 fromHex("71FE1AF926CF847989EFEF8DB459F66394D90F32AD3F15E8"),
                 n, h));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = null;
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "03375D4CE24FDE434489DE8746E71786015009E66E38A926DD");
 
-            return new X9ECParameters(curve, G, n, h);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
     static X9ECParametersHolder c2pnb208w1 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             BigInteger n = fromHex("0101BAF95C9723C57B6C21DA2EFF2D5ED588BDD5717E212F9D");
             BigInteger h = BigInteger.valueOf(0xFE48);
 
-            ECCurve curve = configureCurve(new ECCurve.F2m(
+            return configureCurve(new ECCurve.F2m(
                 208,
                 1, 2, 83,
                 BigInteger.valueOf(0),
                 fromHex("C8619ED45A62E6212E1160349E2BFA844439FAFC2A3FD1638F9E"),
                 n, h));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = null;
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "0289FDFBE4ABE193DF9559ECF07AC0CE78554E2784EB8C1ED1A57A");
 
-            return new X9ECParameters(curve, G, n, h);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
     static X9ECParametersHolder c2tnb239v1 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             BigInteger n = fromHex("2000000000000000000000000000000F4D42FFE1492A4993F1CAD666E447");
             BigInteger h = BigInteger.valueOf(4);
 
-            ECCurve curve = configureCurve(new ECCurve.F2m(
+            return configureCurve(new ECCurve.F2m(
                 239,
                 36,
                 fromHex("32010857077C5431123A46B808906756F543423E8D27877578125778AC76"),
                 fromHex("790408F2EEDAF392B012EDEFB3392F30F4327C0CA3F31FC383C422AA8C16"),
                 n, h));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = null;
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "0257927098FA932E7C0A96D3FD5B706EF7E5F5C156E16B7E7C86038552E91D");
 
-            return new X9ECParameters(curve, G, n, h);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
     static X9ECParametersHolder c2tnb239v2 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             BigInteger n = fromHex("1555555555555555555555555555553C6F2885259C31E3FCDF154624522D");
             BigInteger h = BigInteger.valueOf(6);
 
-            ECCurve curve = configureCurve(new ECCurve.F2m(
+            return configureCurve(new ECCurve.F2m(
                 239,
                 36,
                 fromHex("4230017757A767FAE42398569B746325D45313AF0766266479B75654E65F"),
                 fromHex("5037EA654196CFF0CD82B2C14A2FCF2E3FF8775285B545722F03EACDB74B"),
                 n, h));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = null;
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "0228F9D04E900069C8DC47A08534FE76D2B900B7D7EF31F5709F200C4CA205");
 
-            return new X9ECParameters(curve, G, n, h);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
     static X9ECParametersHolder c2tnb239v3 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             BigInteger n = fromHex("0CCCCCCCCCCCCCCCCCCCCCCCCCCCCCAC4912D2D9DF903EF9888B8A0E4CFF");
             BigInteger h = BigInteger.valueOf(10);
 
-            ECCurve curve = configureCurve(new ECCurve.F2m(
+            return configureCurve(new ECCurve.F2m(
                 239,
                 36,
                 fromHex("01238774666A67766D6676F778E676B66999176666E687666D8766C66A9F"),
                 fromHex("6A941977BA9F6A435199ACFC51067ED587F519C5ECB541B8E44111DE1D40"),
                 n, h));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = null;
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "0370F6E9D04D289C4E89913CE3530BFDE903977D42B146D539BF1BDE4E9C92");
 
-            return new X9ECParameters(curve, G, n, h);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
     static X9ECParametersHolder c2pnb272w1 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             BigInteger n = fromHex("0100FAF51354E0E39E4892DF6E319C72C8161603FA45AA7B998A167B8F1E629521");
             BigInteger h = BigInteger.valueOf(0xFF06);
 
-            ECCurve curve = configureCurve(new ECCurve.F2m(
+            return configureCurve(new ECCurve.F2m(
                 272,
                 1, 3, 56,
                 fromHex("91A091F03B5FBA4AB2CCF49C4EDD220FB028712D42BE752B2C40094DBACDB586FB20"),
                 fromHex("7167EFC92BB2E3CE7C8AAAFF34E12A9C557003D7C73A6FAF003F99F6CC8482E540F7"),
                 n, h));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = null;
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "026108BABB2CEEBCF787058A056CBE0CFE622D7723A289E08A07AE13EF0D10D171DD8D");
 
-            return new X9ECParameters(curve, G, n, h);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
     static X9ECParametersHolder c2pnb304w1 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             BigInteger n = fromHex("0101D556572AABAC800101D556572AABAC8001022D5C91DD173F8FB561DA6899164443051D");
             BigInteger h = BigInteger.valueOf(0xFE2E);
 
-            ECCurve curve = configureCurve(new ECCurve.F2m(
+            return configureCurve(new ECCurve.F2m(
                 304,
                 1, 2, 11,
                 fromHex("FD0D693149A118F651E6DCE6802085377E5F882D1B510B44160074C1288078365A0396C8E681"),
                 fromHex("BDDB97E555A50A908E43B01C798EA5DAA6788F1EA2794EFCF57166B8C14039601E55827340BE"),
                 n, h));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = null;
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "02197B07845E9BE2D96ADB0F5F3C7F2CFFBD7A3EB8B6FEC35C7FD67F26DDF6285A644F740A2614");
 
-            return new X9ECParameters(curve, G, n, h);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
     static X9ECParametersHolder c2tnb359v1 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             BigInteger n = fromHex("01AF286BCA1AF286BCA1AF286BCA1AF286BCA1AF286BC9FB8F6B85C556892C20A7EB964FE7719E74F490758D3B");
             BigInteger h = BigInteger.valueOf(0x4C);
 
-            ECCurve curve = configureCurve(new ECCurve.F2m(
+            return configureCurve(new ECCurve.F2m(
                 359,
                 68,
                 fromHex("5667676A654B20754F356EA92017D946567C46675556F19556A04616B567D223A5E05656FB549016A96656A557"),
                 fromHex("2472E2D0197C49363F1FE7F5B6DB075D52B6947D135D8CA445805D39BC345626089687742B6329E70680231988"),
                 n, h));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = null;
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "033C258EF3047767E7EDE0F1FDAA79DAEE3841366A132E163ACED4ED2401DF9C6BDCDE98E8E707C07A2239B1B097");
 
-            return new X9ECParameters(curve, G, n, h);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
     static X9ECParametersHolder c2pnb368w1 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             BigInteger n = fromHex("010090512DA9AF72B08349D98A5DD4C7B0532ECA51CE03E2D10F3B7AC579BD87E909AE40A6F131E9CFCE5BD967");
             BigInteger h = BigInteger.valueOf(0xFF70);
 
-            ECCurve curve = configureCurve(new ECCurve.F2m(
+            return configureCurve(new ECCurve.F2m(
                 368,
                 1, 2, 85,
                 fromHex("E0D2EE25095206F5E2A4F9ED229F1F256E79A0E2B455970D8D0D865BD94778C576D62F0AB7519CCD2A1A906AE30D"),
                 fromHex("FC1217D4320A90452C760A58EDCD30C8DD069B3C34453837A34ED50CB54917E1C2112D84D164F444F8F74786046A"),
                 n, h));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = null;
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "021085E2755381DCCCE3C1557AFA10C2F0C0C2825646C5B34A394CBCFA8BC16B22E7E789E927BE216F02E1FB136A5F");
 
-            return new X9ECParameters(curve, G, n, h);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
     static X9ECParametersHolder c2tnb431r1 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             BigInteger n = fromHex("0340340340340340340340340340340340340340340340340340340323C313FAB50589703B5EC68D3587FEC60D161CC149C1AD4A91");
             BigInteger h = BigInteger.valueOf(0x2760);
 
-            ECCurve curve = configureCurve(new ECCurve.F2m(
+            return configureCurve(new ECCurve.F2m(
                 431,
                 120,
                 fromHex("1A827EF00DD6FC0E234CAF046C6A5D8A85395B236CC4AD2CF32A0CADBDC9DDF620B0EB9906D0957F6C6FEACD615468DF104DE296CD8F"),
                 fromHex("10D9B4A3D9047D8B154359ABFB1B7F5485B04CEB868237DDC9DEDA982A679A5A919B626D4E50A8DD731B107A9962381FB5D807BF2618"),
                 n, h));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = null;
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "02120FC05D3C67A99DE161D2F4092622FECA701BE4F50F4758714E8A87BBF2A658EF8C21E7C5EFE965361F6C2999C0C247B0DBD70CE6B7");
 
-            return new X9ECParameters(curve, G, n, h);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
@@ -551,17 +689,16 @@
         defineCurve("c2tnb431r1", X9ObjectIdentifiers.c2tnb431r1, c2tnb431r1);
     }
 
-    public static X9ECParameters getByName(
-        String name)
+    public static X9ECParameters getByName(String name)
     {
-        ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier)objIds.get(Strings.toLowerCase(name));
+        ASN1ObjectIdentifier oid = getOID(name);
+        return oid == null ? null : getByOID(oid);
+    }
 
-        if (oid != null)
-        {
-            return getByOID(oid);
-        }
-
-        return null;
+    public static X9ECParametersHolder getByNameLazy(String name)
+    {
+        ASN1ObjectIdentifier oid = getOID(name);
+        return oid == null ? null : getByOIDLazy(oid);
     }
 
     /**
@@ -570,17 +707,15 @@
      *
      * @param oid an object identifier representing a named curve, if present.
      */
-    public static X9ECParameters getByOID(
-        ASN1ObjectIdentifier oid)
+    public static X9ECParameters getByOID(ASN1ObjectIdentifier oid)
     {
-        X9ECParametersHolder holder = (X9ECParametersHolder)curves.get(oid);
+        X9ECParametersHolder holder = getByOIDLazy(oid);
+        return holder == null ? null : holder.getParameters();
+    }
 
-        if (holder != null)
-        {
-            return holder.getParameters();
-        }
-
-        return null;
+    public static X9ECParametersHolder getByOIDLazy(ASN1ObjectIdentifier oid)
+    {
+        return (X9ECParametersHolder)curves.get(oid);
     }
 
     /**
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x9/X962Parameters.java b/bcprov/src/main/java/org/bouncycastle/asn1/x9/X962Parameters.java
index eb067e6..b7b29be 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/x9/X962Parameters.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/x9/X962Parameters.java
@@ -48,7 +48,12 @@
         ASN1TaggedObject obj,
         boolean          explicit)
     {
-        return getInstance(obj.getObject()); // must be explicitly tagged
+        if (!explicit)
+        {
+            throw new IllegalArgumentException("choice item must be explicitly tagged");
+        }
+
+        return getInstance(obj.getExplicitBaseObject());
     }
     
     public X962Parameters(
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x9/X9ECParameters.java b/bcprov/src/main/java/org/bouncycastle/asn1/x9/X9ECParameters.java
index 5a1c0be..31f5d42 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/x9/X9ECParameters.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/x9/X9ECParameters.java
@@ -36,7 +36,7 @@
         ASN1Sequence  seq)
     {
         if (!(seq.getObjectAt(0) instanceof ASN1Integer)
-            || !((ASN1Integer)seq.getObjectAt(0)).hasValue(ONE))
+            || !((ASN1Integer)seq.getObjectAt(0)).hasValue(1))
         {
             throw new IllegalArgumentException("bad version in X9ECParameters");
         }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x9/X9ECParametersHolder.java b/bcprov/src/main/java/org/bouncycastle/asn1/x9/X9ECParametersHolder.java
index 2dd8ff1..e2b7ea2 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/x9/X9ECParametersHolder.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/x9/X9ECParametersHolder.java
@@ -1,12 +1,25 @@
 package org.bouncycastle.asn1.x9;
 
+import org.bouncycastle.math.ec.ECCurve;
+
 /**
  * A holding class that allows for X9ECParameters to be lazily constructed.
  */
 public abstract class X9ECParametersHolder
 {
+    private ECCurve curve;
     private X9ECParameters params;
 
+    public synchronized ECCurve getCurve()
+    {
+        if (curve == null)
+        {
+            curve = createCurve();
+        }
+
+        return curve;
+    }
+
     public synchronized X9ECParameters getParameters()
     {
         if (params == null)
@@ -17,5 +30,10 @@
         return params;
     }
 
+    protected ECCurve createCurve()
+    {
+        return createParameters().getCurve();
+    }
+
     protected abstract X9ECParameters createParameters();
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x9/X9FieldElement.java b/bcprov/src/main/java/org/bouncycastle/asn1/x9/X9FieldElement.java
index 4cba82d..1612a99 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/x9/X9FieldElement.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/x9/X9FieldElement.java
@@ -1,9 +1,6 @@
 package org.bouncycastle.asn1.x9;
 
-import java.math.BigInteger;
-
 import org.bouncycastle.asn1.ASN1Object;
-import org.bouncycastle.asn1.ASN1OctetString;
 import org.bouncycastle.asn1.ASN1Primitive;
 import org.bouncycastle.asn1.DEROctetString;
 import org.bouncycastle.math.ec.ECFieldElement;
@@ -23,22 +20,6 @@
         this.f = f;
     }
 
-    /**
-     * @deprecated Will be removed
-     */
-    public X9FieldElement(BigInteger p, ASN1OctetString s)
-    {
-        this(new ECFieldElement.Fp(p, new BigInteger(1, s.getOctets())));
-    }
-
-    /**
-     * @deprecated Will be removed
-     */
-    public X9FieldElement(int m, int k1, int k2, int k3, ASN1OctetString s)
-    {
-        this(new ECFieldElement.F2m(m, k1, k2, k3, new BigInteger(1, s.getOctets())));
-    }
-
     public ECFieldElement getValue()
     {
         return f;
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/AlphabetMapper.java b/bcprov/src/main/java/org/bouncycastle/crypto/AlphabetMapper.java
new file mode 100644
index 0000000..f40d989
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/AlphabetMapper.java
@@ -0,0 +1,32 @@
+package org.bouncycastle.crypto;
+
+/**
+ * Base interface for mapping from an alphabet to a set of indexes
+ * suitable for use with FPE.
+ */
+public interface AlphabetMapper
+{
+    /**
+     * Return the number of characters in the alphabet.
+     *
+     * @return the radix for the alphabet.
+     */
+    int getRadix();
+
+    /**
+     * Return the passed in char[] as a byte array of indexes (indexes
+     * can be more than 1 byte)
+     *
+     * @param input characters to be mapped.
+     * @return an index array.
+     */
+    byte[] convertToIndexes(char[] input);
+
+    /**
+     * Return a char[] for this alphabet based on the indexes passed.
+     *
+     * @param input input array of indexes.
+     * @return an array of char corresponding to the index values.
+     */
+    char[] convertToChars(byte[] input);
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/BlockCipher.java b/bcprov/src/main/java/org/bouncycastle/crypto/BlockCipher.java
index 3cfa25a..370225f 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/BlockCipher.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/BlockCipher.java
@@ -36,16 +36,16 @@
      * Process one block of input from the array in and write it to
      * the out array.
      *
-     * @param in the array containing the input data.
+     * @param input the array containing the input data.
      * @param inOff offset into the in array the data starts at.
-     * @param out the array the output data will be copied into.
+     * @param output the array the output data will be copied into.
      * @param outOff the offset into the out array the output will start at.
-     * @exception DataLengthException if there isn't enough data in in, or
+     * @exception DataLengthException if there isn't enough data in input , or
      * space in out.
      * @exception IllegalStateException if the cipher isn't initialised.
      * @return the number of bytes processed and produced.
      */
-    public int processBlock(byte[] in, int inOff, byte[] out, int outOff)
+    public int processBlock(byte[] input, int inOff, byte[] output, int outOff)
         throws DataLengthException, IllegalStateException;
 
     /**
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/BufferedBlockCipher.java b/bcprov/src/main/java/org/bouncycastle/crypto/BufferedBlockCipher.java
index 8ab2cdc..6a29741 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/BufferedBlockCipher.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/BufferedBlockCipher.java
@@ -14,8 +14,9 @@
     protected byte[]        buf;
     protected int           bufOff;
 
-    protected boolean       forEncryption;
-    protected BlockCipher   cipher;
+    protected boolean          forEncryption;
+    protected BlockCipher      cipher;
+    protected MultiBlockCipher mbCipher;
 
     protected boolean       partialBlockOkay;
     protected boolean       pgpCFB;
@@ -23,7 +24,7 @@
     /**
      * constructor for subclasses
      */
-    protected BufferedBlockCipher()
+    BufferedBlockCipher()
     {
     }
 
@@ -31,13 +32,24 @@
      * Create a buffered block cipher without padding.
      *
      * @param cipher the underlying block cipher this buffering object wraps.
+     * @deprecated use the constructor on DefaultBufferedBlockCipher.
      */
     public BufferedBlockCipher(
         BlockCipher     cipher)
     {
         this.cipher = cipher;
 
-        buf = new byte[cipher.getBlockSize()];
+        if (cipher instanceof MultiBlockCipher)
+        {
+            this.mbCipher = (MultiBlockCipher)cipher;
+            buf = new byte[mbCipher.getMultiBlockSize()];
+        }
+        else
+        {
+            this.mbCipher = null;
+            buf = new byte[cipher.getBlockSize()];
+        }
+
         bufOff = 0;
 
         //
@@ -143,6 +155,11 @@
     public int getOutputSize(
         int length)
     {
+        if (pgpCFB && forEncryption)
+        {
+            return length + bufOff + (cipher.getBlockSize() + 2);
+        }
+
         // Note: Can assume partialBlockOkay is true for purposes of this calculation
         return length + bufOff;
     }
@@ -225,12 +242,29 @@
             len -= gapLen;
             inOff += gapLen;
 
-            while (len > buf.length)
+            if (mbCipher != null)
             {
-                resultLen += cipher.processBlock(in, inOff, out, outOff + resultLen);
+                int blockCount = len / mbCipher.getMultiBlockSize();
 
-                len -= blockSize;
-                inOff += blockSize;
+                if (blockCount > 0)
+                {
+                    resultLen += mbCipher.processBlocks(in, inOff, blockCount, out, outOff + resultLen);
+
+                    int processed = blockCount * mbCipher.getMultiBlockSize();
+
+                    len -= processed;
+                    inOff += processed;
+                }
+            }
+            else
+            {
+                while (len > buf.length)
+                {
+                    resultLen += cipher.processBlock(in, inOff, out, outOff + resultLen);
+
+                    len -= blockSize;
+                    inOff += blockSize;
+                }
             }
         }
 
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/CipherKeyGenerator.java b/bcprov/src/main/java/org/bouncycastle/crypto/CipherKeyGenerator.java
index 451f8e8..9b41d71 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/CipherKeyGenerator.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/CipherKeyGenerator.java
@@ -2,6 +2,8 @@
 
 import java.security.SecureRandom;
 
+import org.bouncycastle.crypto.constraints.DefaultServiceProperties;
+
 /**
  * The base class for symmetric, or secret, cipher key generators.
  */
@@ -20,6 +22,8 @@
     {
         this.random = param.getRandom();
         this.strength = (param.getStrength() + 7) / 8;
+
+        CryptoServicesRegistrar.checkConstraints(new DefaultServiceProperties("SymKeyGen", param.getStrength()));
     }
 
     /**
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/CryptoServiceConstraintsException.java b/bcprov/src/main/java/org/bouncycastle/crypto/CryptoServiceConstraintsException.java
new file mode 100644
index 0000000..015557a
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/CryptoServiceConstraintsException.java
@@ -0,0 +1,10 @@
+package org.bouncycastle.crypto;
+
+public class CryptoServiceConstraintsException
+    extends RuntimeException
+{
+    public CryptoServiceConstraintsException(String msg)
+    {
+        super(msg);
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/CryptoServiceProperties.java b/bcprov/src/main/java/org/bouncycastle/crypto/CryptoServiceProperties.java
new file mode 100644
index 0000000..07a5440
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/CryptoServiceProperties.java
@@ -0,0 +1,12 @@
+package org.bouncycastle.crypto;
+
+public interface CryptoServiceProperties
+{
+    int bitsOfSecurity();
+
+    String getServiceName();
+
+    CryptoServicePurpose getPurpose();
+
+    Object getParams();
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/CryptoServicePurpose.java b/bcprov/src/main/java/org/bouncycastle/crypto/CryptoServicePurpose.java
new file mode 100644
index 0000000..2789326
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/CryptoServicePurpose.java
@@ -0,0 +1,15 @@
+package org.bouncycastle.crypto;
+
+public enum CryptoServicePurpose
+{
+    AGREEMENT,
+    ENCRYPTION,
+    DECRYPTION,
+    KEYGEN,
+    SIGNING,         // for signatures (and digests)
+    VERIFYING,
+    AUTHENTICATION,  // for MACs (and digests)
+    VERIFICATION,
+    PRF,
+    ANY
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/CryptoServicesConstraints.java b/bcprov/src/main/java/org/bouncycastle/crypto/CryptoServicesConstraints.java
new file mode 100644
index 0000000..1fc4488
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/CryptoServicesConstraints.java
@@ -0,0 +1,6 @@
+package org.bouncycastle.crypto;
+
+public interface CryptoServicesConstraints
+{
+    void check(CryptoServiceProperties service);
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/CryptoServicesPermission.java b/bcprov/src/main/java/org/bouncycastle/crypto/CryptoServicesPermission.java
index 8a62a8d..608436a 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/CryptoServicesPermission.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/CryptoServicesPermission.java
@@ -25,6 +25,11 @@
      */
     public static final String DEFAULT_RANDOM = "defaultRandomConfig";
 
+    /**
+     * Enable the setting of the constraints.
+     */
+    public static final String CONSTRAINTS = "constraints";
+
     private final Set<String> actions = new HashSet<String>();
 
     public CryptoServicesPermission(String name)
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/CryptoServicesRegistrar.java b/bcprov/src/main/java/org/bouncycastle/crypto/CryptoServicesRegistrar.java
index 6b28394..90d3c85 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/CryptoServicesRegistrar.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/CryptoServicesRegistrar.java
@@ -8,12 +8,16 @@
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.Map;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.logging.Logger;
 
 import org.bouncycastle.asn1.x9.X9ECParameters;
 import org.bouncycastle.crypto.params.DHParameters;
 import org.bouncycastle.crypto.params.DHValidationParameters;
 import org.bouncycastle.crypto.params.DSAParameters;
 import org.bouncycastle.crypto.params.DSAValidationParameters;
+import org.bouncycastle.util.Properties;
+import org.bouncycastle.util.Strings;
 import org.bouncycastle.util.encoders.Hex;
 
 /**
@@ -21,15 +25,28 @@
  */
 public final class CryptoServicesRegistrar
 {
+    private static final Logger LOG = Logger.getLogger(CryptoServicesRegistrar.class.getName());
+
     private static final Permission CanSetDefaultProperty = new CryptoServicesPermission(CryptoServicesPermission.GLOBAL_CONFIG);
     private static final Permission CanSetThreadProperty = new CryptoServicesPermission(CryptoServicesPermission.THREAD_LOCAL_CONFIG);
     private static final Permission CanSetDefaultRandom = new CryptoServicesPermission(CryptoServicesPermission.DEFAULT_RANDOM);
+    private static final Permission CanSetConstraints = new CryptoServicesPermission(CryptoServicesPermission.CONSTRAINTS);
 
     private static final ThreadLocal<Map<String, Object[]>> threadProperties = new ThreadLocal<Map<String, Object[]>>();
     private static final Map<String, Object[]> globalProperties = Collections.synchronizedMap(new HashMap<String, Object[]>());
+    private static final SecureRandomProvider defaultRandomProviderImpl = new ThreadLocalSecureRandomProvider();
 
-    private static final Object cacheLock = new Object();
-    private static SecureRandom defaultSecureRandom;
+    private static final CryptoServicesConstraints noConstraintsImpl = new CryptoServicesConstraints()
+    {
+        public void check(CryptoServiceProperties service)
+        {
+            // anything goes.
+        }
+    };
+
+    private static final AtomicReference<SecureRandomProvider> defaultSecureRandomProvider = new AtomicReference<SecureRandomProvider>();
+    private static final boolean preconfiguredConstraints;
+    private static final AtomicReference<CryptoServicesConstraints> servicesConstraints = new AtomicReference<CryptoServicesConstraints>();
 
     static
     {
@@ -43,56 +60,59 @@
 
         DSAParameters def768Params = new DSAParameters(
             new BigInteger("e9e642599d355f37c97ffd3567120b8e25c9cd43e927b3a9670fbec5" +
-                           "d890141922d2c3b3ad2480093799869d1e846aab49fab0ad26d2ce6a" +
-                           "22219d470bce7d777d4a21fbe9c270b57f607002f3cef8393694cf45" +
-                           "ee3688c11a8c56ab127a3daf", 16),
+                "d890141922d2c3b3ad2480093799869d1e846aab49fab0ad26d2ce6a" +
+                "22219d470bce7d777d4a21fbe9c270b57f607002f3cef8393694cf45" +
+                "ee3688c11a8c56ab127a3daf", 16),
             new BigInteger("9cdbd84c9f1ac2f38d0f80f42ab952e7338bf511", 16),
             new BigInteger("30470ad5a005fb14ce2d9dcd87e38bc7d1b1c5facbaecbe95f190aa7" +
-                           "a31d23c4dbbcbe06174544401a5b2c020965d8c2bd2171d366844577" +
-                           "1f74ba084d2029d83c1c158547f3a9f1a2715be23d51ae4d3e5a1f6a" +
-                           "7064f316933a346d3f529252", 16),
+                "a31d23c4dbbcbe06174544401a5b2c020965d8c2bd2171d366844577" +
+                "1f74ba084d2029d83c1c158547f3a9f1a2715be23d51ae4d3e5a1f6a" +
+                "7064f316933a346d3f529252", 16),
             new DSAValidationParameters(Hex.decodeStrict("77d0f8c4dad15eb8c4f2f8d6726cefd96d5bb399"), 263));
 
         DSAParameters def1024Params = new DSAParameters(
             new BigInteger("fd7f53811d75122952df4a9c2eece4e7f611b7523cef4400c31e3f80" +
-                            "b6512669455d402251fb593d8d58fabfc5f5ba30f6cb9b556cd7813b" +
-                            "801d346ff26660b76b9950a5a49f9fe8047b1022c24fbba9d7feb7c6" +
-                            "1bf83b57e7c6a8a6150f04fb83f6d3c51ec3023554135a169132f675" +
-                            "f3ae2b61d72aeff22203199dd14801c7", 16),
+                "b6512669455d402251fb593d8d58fabfc5f5ba30f6cb9b556cd7813b" +
+                "801d346ff26660b76b9950a5a49f9fe8047b1022c24fbba9d7feb7c6" +
+                "1bf83b57e7c6a8a6150f04fb83f6d3c51ec3023554135a169132f675" +
+                "f3ae2b61d72aeff22203199dd14801c7", 16),
             new BigInteger("9760508f15230bccb292b982a2eb840bf0581cf5", 16),
             new BigInteger("f7e1a085d69b3ddecbbcab5c36b857b97994afbbfa3aea82f9574c0b" +
-                            "3d0782675159578ebad4594fe67107108180b449167123e84c281613" +
-                            "b7cf09328cc8a6e13c167a8b547c8d28e0a3ae1e2bb3a675916ea37f" +
-                            "0bfa213562f1fb627a01243bcca4f1bea8519089a883dfe15ae59f06" +
-                            "928b665e807b552564014c3bfecf492a", 16),
+                "3d0782675159578ebad4594fe67107108180b449167123e84c281613" +
+                "b7cf09328cc8a6e13c167a8b547c8d28e0a3ae1e2bb3a675916ea37f" +
+                "0bfa213562f1fb627a01243bcca4f1bea8519089a883dfe15ae59f06" +
+                "928b665e807b552564014c3bfecf492a", 16),
             new DSAValidationParameters(Hex.decodeStrict("8d5155894229d5e689ee01e6018a237e2cae64cd"), 92));
 
         DSAParameters def2048Params = new DSAParameters(
             new BigInteger("95475cf5d93e596c3fcd1d902add02f427f5f3c7210313bb45fb4d5b" +
-                            "b2e5fe1cbd678cd4bbdd84c9836be1f31c0777725aeb6c2fc38b85f4" +
-                            "8076fa76bcd8146cc89a6fb2f706dd719898c2083dc8d896f84062e2" +
-                            "c9c94d137b054a8d8096adb8d51952398eeca852a0af12df83e475aa" +
-                            "65d4ec0c38a9560d5661186ff98b9fc9eb60eee8b030376b236bc73b" +
-                            "e3acdbd74fd61c1d2475fa3077b8f080467881ff7e1ca56fee066d79" +
-                            "506ade51edbb5443a563927dbc4ba520086746175c8885925ebc64c6" +
-                            "147906773496990cb714ec667304e261faee33b3cbdf008e0c3fa906" +
-                            "50d97d3909c9275bf4ac86ffcb3d03e6dfc8ada5934242dd6d3bcca2" +
-                            "a406cb0b", 16),
+                "b2e5fe1cbd678cd4bbdd84c9836be1f31c0777725aeb6c2fc38b85f4" +
+                "8076fa76bcd8146cc89a6fb2f706dd719898c2083dc8d896f84062e2" +
+                "c9c94d137b054a8d8096adb8d51952398eeca852a0af12df83e475aa" +
+                "65d4ec0c38a9560d5661186ff98b9fc9eb60eee8b030376b236bc73b" +
+                "e3acdbd74fd61c1d2475fa3077b8f080467881ff7e1ca56fee066d79" +
+                "506ade51edbb5443a563927dbc4ba520086746175c8885925ebc64c6" +
+                "147906773496990cb714ec667304e261faee33b3cbdf008e0c3fa906" +
+                "50d97d3909c9275bf4ac86ffcb3d03e6dfc8ada5934242dd6d3bcca2" +
+                "a406cb0b", 16),
             new BigInteger("f8183668ba5fc5bb06b5981e6d8b795d30b8978d43ca0ec572e37e09939a9773", 16),
             new BigInteger("42debb9da5b3d88cc956e08787ec3f3a09bba5f48b889a74aaf53174" +
-                            "aa0fbe7e3c5b8fcd7a53bef563b0e98560328960a9517f4014d3325f" +
-                            "c7962bf1e049370d76d1314a76137e792f3f0db859d095e4a5b93202" +
-                            "4f079ecf2ef09c797452b0770e1350782ed57ddf794979dcef23cb96" +
-                            "f183061965c4ebc93c9c71c56b925955a75f94cccf1449ac43d586d0" +
-                            "beee43251b0b2287349d68de0d144403f13e802f4146d882e057af19" +
-                            "b6f6275c6676c8fa0e3ca2713a3257fd1b27d0639f695e347d8d1cf9" +
-                            "ac819a26ca9b04cb0eb9b7b035988d15bbac65212a55239cfc7e58fa" +
-                            "e38d7250ab9991ffbc97134025fe8ce04c4399ad96569be91a546f49" +
-                            "78693c7a", 16),
+                "aa0fbe7e3c5b8fcd7a53bef563b0e98560328960a9517f4014d3325f" +
+                "c7962bf1e049370d76d1314a76137e792f3f0db859d095e4a5b93202" +
+                "4f079ecf2ef09c797452b0770e1350782ed57ddf794979dcef23cb96" +
+                "f183061965c4ebc93c9c71c56b925955a75f94cccf1449ac43d586d0" +
+                "beee43251b0b2287349d68de0d144403f13e802f4146d882e057af19" +
+                "b6f6275c6676c8fa0e3ca2713a3257fd1b27d0639f695e347d8d1cf9" +
+                "ac819a26ca9b04cb0eb9b7b035988d15bbac65212a55239cfc7e58fa" +
+                "e38d7250ab9991ffbc97134025fe8ce04c4399ad96569be91a546f49" +
+                "78693c7a", 16),
             new DSAValidationParameters(Hex.decodeStrict("b0b4417601b59cbc9d8ac8f935cadaec4f5fbb2f23785609ae466748d9b5a536"), 497));
 
         localSetGlobalProperty(Property.DSA_DEFAULT_PARAMS, def512Params, def768Params, def1024Params, def2048Params);
         localSetGlobalProperty(Property.DH_DEFAULT_PARAMS, toDH(def512Params), toDH(def768Params), toDH(def1024Params), toDH(def2048Params));
+
+        servicesConstraints.set(getDefaultConstraints());
+        preconfiguredConstraints = (servicesConstraints.get() != noConstraintsImpl);
     }
 
     private CryptoServicesRegistrar()
@@ -107,25 +127,9 @@
      */
     public static SecureRandom getSecureRandom()
     {
-        synchronized (cacheLock)
-        {
-            if (null != defaultSecureRandom)
-            {
-                return defaultSecureRandom;
-            }
-        }
+        defaultSecureRandomProvider.compareAndSet(null, defaultRandomProviderImpl);
 
-        SecureRandom tmp = new SecureRandom();
-
-        synchronized (cacheLock)
-        {
-            if (null == defaultSecureRandom)
-            {
-                defaultSecureRandom = tmp;
-            }
-
-            return defaultSecureRandom;
-        }
+        return defaultSecureRandomProvider.get().get();
     }
 
     /**
@@ -144,13 +148,83 @@
      *
      * @param secureRandom the SecureRandom to use as the default.
      */
-    public static void setSecureRandom(SecureRandom secureRandom)
+    public static void setSecureRandom(final SecureRandom secureRandom)
     {
         checkPermission(CanSetDefaultRandom);
 
-        synchronized (cacheLock)
+        if (secureRandom == null)
         {
-            defaultSecureRandom = secureRandom;
+            defaultSecureRandomProvider.set(defaultRandomProviderImpl);
+        }
+        else
+        {
+            defaultSecureRandomProvider.set(new SecureRandomProvider()
+            {
+                public SecureRandom get()
+                {
+                    return secureRandom;
+                }
+            });
+        }
+    }
+
+    /**
+     * Set a default secure random provider to be used where none is otherwise provided.
+     *
+     * @param secureRandomProvider a provider SecureRandom to use when a default SecureRandom is requested.
+     */
+    public static void setSecureRandomProvider(SecureRandomProvider secureRandomProvider)
+    {
+        checkPermission(CanSetDefaultRandom);
+
+        defaultSecureRandomProvider.set(secureRandomProvider);
+    }
+
+    /**
+     * Return the current algorithm/services constraints.
+     *
+     * @return the algorithm/services constraints.
+     */
+    public static CryptoServicesConstraints getServicesConstraints()
+    {
+        return servicesConstraints.get();
+    }
+
+    /**
+     * Check a service to make sure it meets the current constraints.
+     *
+     * @param cryptoService the service to be checked.
+     * @throws CryptoServiceConstraintsException if the service violates the current constraints.
+     */
+    public static void checkConstraints(CryptoServiceProperties cryptoService)
+    {
+        servicesConstraints.get().check(cryptoService);
+    }
+
+    /**
+     * Set the current algorithm constraints.
+     */
+    public static void setServicesConstraints(CryptoServicesConstraints constraints)
+    {
+        checkPermission(CanSetConstraints);
+
+        CryptoServicesConstraints newConstraints = (constraints == null) ? noConstraintsImpl : constraints;
+
+        if (preconfiguredConstraints)
+        {
+            if (Properties.isOverrideSet("org.bouncycastle.constraints.allow_override"))
+            {
+                servicesConstraints.set(newConstraints);
+            }
+            else
+            {
+                LOG.warning("attempt to override pre-configured constraints ignored");
+            }
+        }
+        else
+        {
+            // TODO: should this only be allowed once?
+            servicesConstraints.set(newConstraints);
         }
     }
 
@@ -159,7 +233,7 @@
      * configuration first and then on the global configuration in no local configuration exists.
      *
      * @param property the property to look up.
-     * @param <T> the type to be returned
+     * @param <T>      the type to be returned
      * @return null if the property is not set, the default value otherwise,
      */
     public static <T> T getProperty(Property property)
@@ -195,7 +269,7 @@
      * DSA_DEFAULT_PARAMS.
      *
      * @param property the name of the property to look up.
-     * @param <T> the base type of the array to be returned.
+     * @param <T>      the base type of the array to be returned.
      * @return null if the property is not set, an array of the current values otherwise.
      */
     public static <T> T[] getSizedProperty(Property property)
@@ -215,8 +289,8 @@
      * DSA_DEFAULT_PARAMS.
      *
      * @param property the name of the property to look up.
-     * @param size the size (in bits) of the defining value in the property type.
-     * @param <T> the type of the value to be returned.
+     * @param size     the size (in bits) of the defining value in the property type.
+     * @param <T>      the type of the value to be returned.
      * @return the current value for the size, null if there is no value set,
      */
     public static <T> T getSizedProperty(Property property, int size)
@@ -261,9 +335,9 @@
      * one value can be passed in for a sized property. If more than one value is provided the
      * first value in the argument list becomes the default value.
      *
-     * @param property the name of the property to set.
+     * @param property      the name of the property to set.
      * @param propertyValue the values to assign to the property.
-     * @param <T> the base type of the property value.
+     * @param <T>           the base type of the property value.
      */
     public static <T> void setThreadProperty(Property property, T... propertyValue)
     {
@@ -282,9 +356,9 @@
      * one value can be passed in for a sized property. If more than one value is provided the
      * first value in the argument list becomes the default value.
      *
-     * @param property the name of the property to set.
+     * @param property      the name of the property to set.
      * @param propertyValue the values to assign to the property.
-     * @param <T> the base type of the property value.
+     * @param <T>           the base type of the property value.
      */
     public static <T> void setGlobalProperty(Property property, T... propertyValue)
     {
@@ -323,7 +397,7 @@
      * Clear the global value for the passed in property.
      *
      * @param property the property to be cleared.
-     * @param <T> the base type of the property value
+     * @param <T>      the base type of the property value
      * @return an array of T if a value was previously set, null otherwise.
      */
     public static <T> T[] clearGlobalProperty(Property property)
@@ -340,7 +414,7 @@
      * Clear the thread local value for the passed in property.
      *
      * @param property the property to be cleared.
-     * @param <T> the base type of the property value
+     * @param <T>      the base type of the property value
      * @return an array of T if a value was previously set, null otherwise.
      */
     public static <T> T[] clearThreadProperty(Property property)
@@ -415,6 +489,13 @@
         return m;
     }
 
+    private static CryptoServicesConstraints getDefaultConstraints()
+    {
+        // TODO: return one based on system/security properties if set.
+
+        return noConstraintsImpl;
+    }
+
     /**
      * Available properties that can be set.
      */
@@ -427,11 +508,11 @@
         /**
          * The default parameters for a particular size of Diffie-Hellman key.This is a sized property.
          */
-        public static final Property DH_DEFAULT_PARAMS= new Property("dhDefaultParams", DHParameters.class);
+        public static final Property DH_DEFAULT_PARAMS = new Property("dhDefaultParams", DHParameters.class);
         /**
          * The default parameters for a particular size of DSA key. This is a sized property.
          */
-        public static final Property DSA_DEFAULT_PARAMS= new Property("dsaDefaultParams", DSAParameters.class);
+        public static final Property DSA_DEFAULT_PARAMS = new Property("dsaDefaultParams", DSAParameters.class);
         private final String name;
         private final Class type;
 
@@ -441,4 +522,20 @@
             this.type = type;
         }
     }
+
+    private static class ThreadLocalSecureRandomProvider
+        implements SecureRandomProvider
+    {
+        final ThreadLocal<SecureRandom> defaultRandoms = new ThreadLocal<SecureRandom>();
+
+        public SecureRandom get()
+        {
+            if (defaultRandoms.get() == null)
+            {
+                defaultRandoms.set(new SecureRandom());
+            }
+
+            return defaultRandoms.get();
+        }
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/DefaultBufferedBlockCipher.java b/bcprov/src/main/java/org/bouncycastle/crypto/DefaultBufferedBlockCipher.java
new file mode 100644
index 0000000..662a23a
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/DefaultBufferedBlockCipher.java
@@ -0,0 +1,356 @@
+package org.bouncycastle.crypto;
+
+
+/**
+ * A wrapper class that allows block ciphers to be used to process data in
+ * a piecemeal fashion. The BufferedBlockCipher outputs a block only when the
+ * buffer is full and more data is being added, or on a doFinal.
+ * <p>
+ * Note: in the case where the underlying cipher is either a CFB cipher or an
+ * OFB one the last block may not be a multiple of the block size. Use this class
+ * for construction rather than BufferedBlockCipher as BufferedBlockCipher will eventually
+ * turn into an interface.
+ */
+public class DefaultBufferedBlockCipher
+    extends BufferedBlockCipher
+{
+    protected byte[]        buf;
+    protected int           bufOff;
+
+    protected boolean          forEncryption;
+    protected BlockCipher      cipher;
+    protected MultiBlockCipher mbCipher;
+
+    protected boolean       partialBlockOkay;
+    protected boolean       pgpCFB;
+
+    /**
+     * constructor for subclasses
+     */
+    protected DefaultBufferedBlockCipher()
+    {
+    }
+
+    /**
+     * Create a buffered block cipher without padding.
+     *
+     * @param cipher the underlying block cipher this buffering object wraps.
+     */
+    public DefaultBufferedBlockCipher(
+        BlockCipher     cipher)
+    {
+        this.cipher = cipher;
+
+        if (cipher instanceof MultiBlockCipher)
+        {
+            this.mbCipher = (MultiBlockCipher)cipher;
+            buf = new byte[mbCipher.getMultiBlockSize()];
+        }
+        else
+        {
+            this.mbCipher = null;
+            buf = new byte[cipher.getBlockSize()];
+        }
+
+        bufOff = 0;
+
+        //
+        // check if we can handle partial blocks on doFinal.
+        //
+        String  name = cipher.getAlgorithmName();
+        int     idx = name.indexOf('/') + 1;
+
+        pgpCFB = (idx > 0 && name.startsWith("PGP", idx));
+
+        if (pgpCFB || cipher instanceof StreamCipher)
+        {
+            partialBlockOkay = true;
+        }
+        else
+        {
+            partialBlockOkay = (idx > 0 && (name.startsWith("OpenPGP", idx)));
+        }
+    }
+
+    /**
+     * return the cipher this object wraps.
+     *
+     * @return the cipher this object wraps.
+     */
+    public BlockCipher getUnderlyingCipher()
+    {
+        return cipher;
+    }
+
+    /**
+     * initialise the cipher.
+     *
+     * @param forEncryption if true the cipher is initialised for
+     *  encryption, if false for decryption.
+     * @param params the key and other data required by the cipher.
+     * @exception IllegalArgumentException if the params argument is
+     * inappropriate.
+     */
+    public void init(
+        boolean             forEncryption,
+        CipherParameters    params)
+        throws IllegalArgumentException
+    {
+        this.forEncryption = forEncryption;
+
+        reset();
+
+        cipher.init(forEncryption, params);
+    }
+
+    /**
+     * return the blocksize for the underlying cipher.
+     *
+     * @return the blocksize for the underlying cipher.
+     */
+    public int getBlockSize()
+    {
+        return cipher.getBlockSize();
+    }
+
+    /**
+     * return the size of the output buffer required for an update 
+     * an input of len bytes.
+     *
+     * @param len the length of the input.
+     * @return the space required to accommodate a call to update
+     * with len bytes of input.
+     */
+    public int getUpdateOutputSize(
+        int len)
+    {
+        int total       = len + bufOff;
+        int leftOver;
+
+        if (pgpCFB)
+        {
+            if (forEncryption)
+            {
+                leftOver = total % buf.length - (cipher.getBlockSize() + 2);
+            }
+            else
+            {
+                leftOver = total % buf.length;
+            }
+        }
+        else
+        {
+            leftOver    = total % buf.length;
+        }
+
+        return total - leftOver;
+    }
+
+    /**
+     * return the size of the output buffer required for an update plus a
+     * doFinal with an input of 'length' bytes.
+     *
+     * @param length the length of the input.
+     * @return the space required to accommodate a call to update and doFinal
+     * with 'length' bytes of input.
+     */
+    public int getOutputSize(
+        int length)
+    {
+        if (pgpCFB && forEncryption)
+        {
+            return length + bufOff + (cipher.getBlockSize() + 2);
+        }
+
+        // Note: Can assume partialBlockOkay is true for purposes of this calculation
+        return length + bufOff;
+    }
+
+    /**
+     * process a single byte, producing an output block if necessary.
+     *
+     * @param in the input byte.
+     * @param out the space for any output that might be produced.
+     * @param outOff the offset from which the output will be copied.
+     * @return the number of output bytes copied to out.
+     * @exception DataLengthException if there isn't enough space in out.
+     * @exception IllegalStateException if the cipher isn't initialised.
+     */
+    public int processByte(
+        byte        in,
+        byte[]      out,
+        int         outOff)
+        throws DataLengthException, IllegalStateException
+    {
+        int         resultLen = 0;
+
+        buf[bufOff++] = in;
+
+        if (bufOff == buf.length)
+        {
+            resultLen = cipher.processBlock(buf, 0, out, outOff);
+            bufOff = 0;
+        }
+
+        return resultLen;
+    }
+
+    /**
+     * process an array of bytes, producing output if necessary.
+     *
+     * @param in the input byte array.
+     * @param inOff the offset at which the input data starts.
+     * @param len the number of bytes to be copied out of the input array.
+     * @param out the space for any output that might be produced.
+     * @param outOff the offset from which the output will be copied.
+     * @return the number of output bytes copied to out.
+     * @exception DataLengthException if there isn't enough space in out.
+     * @exception IllegalStateException if the cipher isn't initialised.
+     */
+    public int processBytes(
+        byte[]      in,
+        int         inOff,
+        int         len,
+        byte[]      out,
+        int         outOff)
+        throws DataLengthException, IllegalStateException
+    {
+        if (len < 0)
+        {
+            throw new IllegalArgumentException("Can't have a negative input length!");
+        }
+
+        int blockSize   = getBlockSize();
+        int length      = getUpdateOutputSize(len);
+        
+        if (length > 0)
+        {
+            if ((outOff + length) > out.length)
+            {
+                throw new OutputLengthException("output buffer too short");
+            }
+        }
+
+        int resultLen = 0;
+        int gapLen = buf.length - bufOff;
+
+        if (len > gapLen)
+        {
+            System.arraycopy(in, inOff, buf, bufOff, gapLen);
+
+            resultLen += cipher.processBlock(buf, 0, out, outOff);
+
+            bufOff = 0;
+            len -= gapLen;
+            inOff += gapLen;
+
+            if (mbCipher != null)
+            {
+                int blockCount = len / mbCipher.getMultiBlockSize();
+
+                if (blockCount > 0)
+                {
+                    resultLen += mbCipher.processBlocks(in, inOff, blockCount, out, outOff + resultLen);
+
+                    int processed = blockCount * mbCipher.getMultiBlockSize();
+
+                    len -= processed;
+                    inOff += processed;
+                }
+            }
+            else
+            {
+                while (len > buf.length)
+                {
+                    resultLen += cipher.processBlock(in, inOff, out, outOff + resultLen);
+
+                    len -= blockSize;
+                    inOff += blockSize;
+                }
+            }
+        }
+
+        System.arraycopy(in, inOff, buf, bufOff, len);
+
+        bufOff += len;
+
+        if (bufOff == buf.length)
+        {
+            resultLen += cipher.processBlock(buf, 0, out, outOff + resultLen);
+            bufOff = 0;
+        }
+
+        return resultLen;
+    }
+
+    /**
+     * Process the last block in the buffer.
+     *
+     * @param out the array the block currently being held is copied into.
+     * @param outOff the offset at which the copying starts.
+     * @return the number of output bytes copied to out.
+     * @exception DataLengthException if there is insufficient space in out for
+     * the output, or the input is not block size aligned and should be.
+     * @exception IllegalStateException if the underlying cipher is not
+     * initialised.
+     * @exception InvalidCipherTextException if padding is expected and not found.
+     * @exception DataLengthException if the input is not block size
+     * aligned.
+     */
+    public int doFinal(
+        byte[]  out,
+        int     outOff)
+        throws DataLengthException, IllegalStateException, InvalidCipherTextException
+    {
+        try
+        {
+            int resultLen = 0;
+
+            if (outOff + bufOff > out.length)
+            {
+                throw new OutputLengthException("output buffer too short for doFinal()");
+            }
+
+            if (bufOff != 0)
+            {
+                if (!partialBlockOkay)
+                {
+                    throw new DataLengthException("data not block size aligned");
+                }
+
+                cipher.processBlock(buf, 0, buf, 0);
+                resultLen = bufOff;
+                bufOff = 0;
+                System.arraycopy(buf, 0, out, outOff, resultLen);
+            }
+
+            return resultLen;
+        }
+        finally
+        {
+            reset();
+        }
+    }
+
+    /**
+     * Reset the buffer and cipher. After resetting the object is in the same
+     * state as it was after the last init (if there was one).
+     */
+    public void reset()
+    {
+        //
+        // clean the buffer.
+        //
+        for (int i = 0; i < buf.length; i++)
+        {
+            buf[i] = 0;
+        }
+
+        bufOff = 0;
+
+        //
+        // reset the underlying cipher.
+        //
+        cipher.reset();
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/DefaultMultiBlockCipher.java b/bcprov/src/main/java/org/bouncycastle/crypto/DefaultMultiBlockCipher.java
new file mode 100644
index 0000000..3bc565c
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/DefaultMultiBlockCipher.java
@@ -0,0 +1,33 @@
+package org.bouncycastle.crypto;
+
+public abstract class DefaultMultiBlockCipher
+    implements MultiBlockCipher
+{
+    protected DefaultMultiBlockCipher()
+    {
+    }
+
+    public int getMultiBlockSize()
+    {
+        return this.getBlockSize();
+    }
+
+    public int processBlocks(byte[] in, int inOff, int blockCount, byte[] out, int outOff)
+        throws DataLengthException, IllegalStateException
+    {
+
+        // TODO check if the underlying cipher supports the multiblock interface and call it directly?
+
+        int resultLen = 0;
+        int blockSize = this.getMultiBlockSize();
+        
+        for (int i = 0; i != blockCount; i++)
+        {
+            resultLen += this.processBlock(in, inOff, out, outOff + resultLen);
+
+            inOff += blockSize;
+        }
+
+        return resultLen;
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/EncapsulatedSecretExtractor.java b/bcprov/src/main/java/org/bouncycastle/crypto/EncapsulatedSecretExtractor.java
new file mode 100644
index 0000000..2b0b15f
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/EncapsulatedSecretExtractor.java
@@ -0,0 +1,18 @@
+package org.bouncycastle.crypto;
+
+public interface EncapsulatedSecretExtractor
+{
+    /**
+     * Extract the secret based on the recipient private key.
+     *
+     * @param encapsulation the encapsulated secret.
+     */
+    byte[] extractSecret(byte[] encapsulation);
+
+    /**
+     * Return the length in bytes of the encapsulation.
+     *
+     * @return length in bytes of an encapsulation for this parameter set.
+     */
+    int getEncapsulationLength();
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/EncapsulatedSecretGenerator.java b/bcprov/src/main/java/org/bouncycastle/crypto/EncapsulatedSecretGenerator.java
new file mode 100644
index 0000000..760e3b5
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/EncapsulatedSecretGenerator.java
@@ -0,0 +1,13 @@
+package org.bouncycastle.crypto;
+
+import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
+
+public interface EncapsulatedSecretGenerator
+{
+    /**
+     * Generate secret/encapsulation based on the recipient public key.
+     *
+     * @return An SecretWithEncapsulation derived from the recipient public key.
+     */
+    SecretWithEncapsulation generateEncapsulated(AsymmetricKeyParameter recipientKey);
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/MultiBlockCipher.java b/bcprov/src/main/java/org/bouncycastle/crypto/MultiBlockCipher.java
new file mode 100644
index 0000000..dad4020
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/MultiBlockCipher.java
@@ -0,0 +1,31 @@
+package org.bouncycastle.crypto;
+
+/**
+ * Base interface for a cipher engine capable of processing multiple blocks at a time.
+ */
+public interface MultiBlockCipher
+    extends BlockCipher
+{
+    /**
+     * Return the multi-block size for this cipher (in bytes).
+     *
+     * @return the multi-block size for this cipher in bytes.
+     */
+    int getMultiBlockSize();
+
+    /**
+     * Process blockCount blocks from input in offset inOff and place the output in
+     * out from offset outOff.
+     *
+     * @param in input data array.
+     * @param inOff start of input data in in.
+     * @param blockCount number of blocks to be processed.
+     * @param out output data array.
+     * @param outOff start position for output data.
+     * @return number of bytes written to out.
+     * @throws DataLengthException
+     * @throws IllegalStateException
+     */
+    int processBlocks(byte[] in, int inOff, int blockCount, byte[] out, int outOff)
+        throws DataLengthException, IllegalStateException;
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/SavableDigest.java b/bcprov/src/main/java/org/bouncycastle/crypto/SavableDigest.java
new file mode 100644
index 0000000..fed8cd7
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/SavableDigest.java
@@ -0,0 +1,13 @@
+package org.bouncycastle.crypto;
+
+import org.bouncycastle.crypto.digests.EncodableDigest;
+import org.bouncycastle.util.Memoable;
+
+/**
+ * Extended digest which provides the ability to store state and
+ * provide an encoding.
+ */
+public interface SavableDigest
+    extends ExtendedDigest, EncodableDigest, Memoable
+{
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/SecretWithEncapsulation.java b/bcprov/src/main/java/org/bouncycastle/crypto/SecretWithEncapsulation.java
new file mode 100644
index 0000000..15dabba
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/SecretWithEncapsulation.java
@@ -0,0 +1,24 @@
+package org.bouncycastle.crypto;
+
+import javax.security.auth.Destroyable;
+
+/**
+ * Interface describing secret with encapsulation details.
+ */
+public interface SecretWithEncapsulation
+    extends Destroyable
+{
+    /**
+     * Return the secret associated with the encapsulation.
+     *
+     * @return the secret the encapsulation is for.
+     */
+    byte[] getSecret();
+
+    /**
+     * Return the data that carries the secret in its encapsulated form.
+     *
+     * @return the encapsulation of the secret.
+     */
+    byte[] getEncapsulation();
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/SecureRandomProvider.java b/bcprov/src/main/java/org/bouncycastle/crypto/SecureRandomProvider.java
new file mode 100644
index 0000000..33dea73
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/SecureRandomProvider.java
@@ -0,0 +1,15 @@
+package org.bouncycastle.crypto;
+
+import java.security.SecureRandom;
+
+/**
+ * Source provider for SecureRandom implementations.
+ */
+public interface SecureRandomProvider
+{
+    /**
+     * Return a SecureRandom instance.
+     * @return a SecureRandom
+     */
+    SecureRandom get();
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/StreamBlockCipher.java b/bcprov/src/main/java/org/bouncycastle/crypto/StreamBlockCipher.java
index 77d8ac9..b1702fe 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/StreamBlockCipher.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/StreamBlockCipher.java
@@ -5,7 +5,8 @@
  * a streaming mode.
  */
 public abstract class StreamBlockCipher
-    implements BlockCipher, StreamCipher
+    extends DefaultMultiBlockCipher
+    implements StreamCipher
 {
     private final BlockCipher cipher;
 
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/agreement/DHBasicAgreement.java b/bcprov/src/main/java/org/bouncycastle/crypto/agreement/DHBasicAgreement.java
index 3490819..61c6f39 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/agreement/DHBasicAgreement.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/agreement/DHBasicAgreement.java
@@ -4,6 +4,7 @@
 
 import org.bouncycastle.crypto.BasicAgreement;
 import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.CryptoServicesRegistrar;
 import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
 import org.bouncycastle.crypto.params.DHParameters;
 import org.bouncycastle.crypto.params.DHPrivateKeyParameters;
@@ -47,6 +48,8 @@
 
         this.key = (DHPrivateKeyParameters)kParam;
         this.dhParams = key.getParameters();
+
+        CryptoServicesRegistrar.checkConstraints(Utils.getDefaultProperties("DHB", key));
     }
 
     public int getFieldSize()
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/agreement/ECDHBasicAgreement.java b/bcprov/src/main/java/org/bouncycastle/crypto/agreement/ECDHBasicAgreement.java
index 49a79c8..e344638 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/agreement/ECDHBasicAgreement.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/agreement/ECDHBasicAgreement.java
@@ -4,6 +4,7 @@
 
 import org.bouncycastle.crypto.BasicAgreement;
 import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.CryptoServicesRegistrar;
 import org.bouncycastle.crypto.params.ECDomainParameters;
 import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
 import org.bouncycastle.crypto.params.ECPublicKeyParameters;
@@ -34,6 +35,8 @@
         CipherParameters key)
     {
         this.key = (ECPrivateKeyParameters)key;
+
+        CryptoServicesRegistrar.checkConstraints(Utils.getDefaultProperties("ECDH", this.key));
     }
 
     public int getFieldSize()
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/agreement/Utils.java b/bcprov/src/main/java/org/bouncycastle/crypto/agreement/Utils.java
new file mode 100644
index 0000000..80e779a
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/agreement/Utils.java
@@ -0,0 +1,40 @@
+package org.bouncycastle.crypto.agreement;
+
+import org.bouncycastle.crypto.CryptoServiceProperties;
+import org.bouncycastle.crypto.CryptoServicePurpose;
+import org.bouncycastle.crypto.constraints.ConstraintUtils;
+import org.bouncycastle.crypto.constraints.DefaultServiceProperties;
+import org.bouncycastle.crypto.params.DHKeyParameters;
+import org.bouncycastle.crypto.params.ECKeyParameters;
+// Android-removed: unsupported algorithms
+// import org.bouncycastle.crypto.params.X25519PrivateKeyParameters;
+// import org.bouncycastle.crypto.params.X448PrivateKeyParameters;
+
+class Utils
+{
+    static CryptoServiceProperties getDefaultProperties(String algorithm, ECKeyParameters k)
+    {
+        return new DefaultServiceProperties(algorithm, ConstraintUtils.bitsOfSecurityFor(k.getParameters().getCurve()), k, CryptoServicePurpose.AGREEMENT);
+    }
+
+    static CryptoServiceProperties getDefaultProperties(String algorithm, DHKeyParameters k)
+    {
+        return new DefaultServiceProperties(algorithm, ConstraintUtils.bitsOfSecurityFor(k.getParameters().getP()), k, CryptoServicePurpose.AGREEMENT);
+    }
+
+    // BEGIN Android-removed: unsupported algorithms
+    /*
+    static CryptoServiceProperties getDefaultProperties(String algorithm, X448PrivateKeyParameters k)
+    {
+        return new DefaultServiceProperties(algorithm, 224, k, CryptoServicePurpose.AGREEMENT);
+    }
+
+    static CryptoServiceProperties getDefaultProperties(String algorithm, X25519PrivateKeyParameters k)
+    {
+        return new DefaultServiceProperties(algorithm, 128, k, CryptoServicePurpose.AGREEMENT);
+    }
+    */
+    // END Android-removed: unsupported algorithms
+}
+
+
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/constraints/ConstraintUtils.java b/bcprov/src/main/java/org/bouncycastle/crypto/constraints/ConstraintUtils.java
new file mode 100644
index 0000000..a902a80
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/constraints/ConstraintUtils.java
@@ -0,0 +1,47 @@
+package org.bouncycastle.crypto.constraints;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.math.ec.ECCurve;
+
+public class ConstraintUtils
+{
+    /**
+     * Return the bits of security for the passed in RSA modulus or DH/DSA group value.
+     *
+     * @param p a modulus or group value
+     * @return the security strength in bits.
+     */
+    public static int bitsOfSecurityFor(BigInteger p)
+    {
+        return bitsOfSecurityForFF(p.bitLength());
+    }
+
+    /**
+     * Return the bits of security for the passed in Elliptic Curve.
+     *
+     * @param curve the ECCurve of interest.
+     * @return the security strength in bits.
+     */
+    public static int bitsOfSecurityFor(ECCurve curve)
+    {
+        int sBits = (curve.getFieldSize() + 1) / 2;
+
+        return (sBits > 256) ? 256 : sBits;
+    }
+
+    public static int bitsOfSecurityForFF(int strength)
+    {
+        if (strength >= 2048)
+        {
+            return (strength >= 3072) ?
+                        ((strength >= 7680) ?
+                            ((strength >= 15360) ? 256
+                            : 192)
+                        : 128)
+                   : 112;
+        }
+
+        return (strength >= 1024) ? 80 : 20;      // TODO: possibly a bit harsh...
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/constraints/DefaultServiceProperties.java b/bcprov/src/main/java/org/bouncycastle/crypto/constraints/DefaultServiceProperties.java
new file mode 100644
index 0000000..42642e9
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/constraints/DefaultServiceProperties.java
@@ -0,0 +1,55 @@
+package org.bouncycastle.crypto.constraints;
+
+import org.bouncycastle.crypto.CryptoServiceProperties;
+import org.bouncycastle.crypto.CryptoServicePurpose;
+
+public class DefaultServiceProperties
+    implements CryptoServiceProperties
+{
+    private final String algorithm;
+    private final int bitsOfSecurity;
+    private final Object params;
+    private final CryptoServicePurpose purpose;
+
+    public DefaultServiceProperties(String algorithm, int bitsOfSecurity)
+    {
+        this(algorithm, bitsOfSecurity, null, CryptoServicePurpose.ANY);
+    }
+
+    public DefaultServiceProperties(String algorithm, int bitsOfSecurity, Object params)
+    {
+        this(algorithm, bitsOfSecurity, params, CryptoServicePurpose.ANY);
+    }
+
+    public DefaultServiceProperties(String algorithm, int bitsOfSecurity, Object params, CryptoServicePurpose purpose)
+    {
+        this.algorithm = algorithm;
+        this.bitsOfSecurity = bitsOfSecurity;
+        this.params = params;
+        if (params instanceof CryptoServicePurpose)
+        {
+            throw new IllegalArgumentException("params should not be CryptoServicePurpose");
+        }
+        this.purpose = purpose;
+    }
+
+    public int bitsOfSecurity()
+    {
+        return bitsOfSecurity;
+    }
+
+    public String getServiceName()
+    {
+        return algorithm;
+    }
+
+    public CryptoServicePurpose getPurpose()
+    {
+        return purpose;
+    }
+
+    public Object getParams()
+    {
+        return params;
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/digests/AsconDigest.java b/bcprov/src/main/java/org/bouncycastle/crypto/digests/AsconDigest.java
new file mode 100644
index 0000000..fd47927
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/digests/AsconDigest.java
@@ -0,0 +1,215 @@
+package org.bouncycastle.crypto.digests;
+
+import java.io.ByteArrayOutputStream;
+
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.ExtendedDigest;
+import org.bouncycastle.crypto.OutputLengthException;
+
+/* ASCON v1.2 Digest, https://ascon.iaik.tugraz.at/ .
+ * <p>
+ * https://csrc.nist.gov/CSRC/media/Projects/lightweight-cryptography/documents/finalist-round/updated-spec-doc/ascon-spec-final.pdf
+ * <p>
+ * ASCON v1.2 Digest with reference to C Reference Impl from: https://github.com/ascon/ascon-c .
+ */
+public class AsconDigest
+    implements ExtendedDigest
+{
+    public enum AsconParameters
+    {
+        AsconHash,
+        AsconHashA,
+    }
+
+    AsconParameters asconParameters;
+
+    public AsconDigest(AsconParameters parameters)
+    {
+        this.asconParameters = parameters;
+        switch (parameters)
+        {
+        case AsconHash:
+            ASCON_PB_ROUNDS = 12;
+            algorithmName = "Ascon-Hash";
+            break;
+        case AsconHashA:
+            ASCON_PB_ROUNDS = 8;
+            algorithmName = "Ascon-HashA";
+            break;
+        default:
+            throw new IllegalArgumentException("Invalid parameter settings for Ascon Hash");
+        }
+        reset();
+    }
+
+    private final String algorithmName;
+    private final ByteArrayOutputStream buffer = new ByteArrayOutputStream();
+    private long x0;
+    private long x1;
+    private long x2;
+    private long x3;
+    private long x4;
+    private final int CRYPTO_BYTES = 32;
+    private final int ASCON_PB_ROUNDS;
+
+    private long ROR(long x, int n)
+    {
+        return x >>> n | x << (64 - n);
+    }
+
+    private void ROUND(long C)
+    {
+        long t0 = x0 ^ x1 ^ x2 ^ x3 ^ C ^ (x1 & (x0 ^ x2 ^ x4 ^ C));
+        long t1 = x0 ^ x2 ^ x3 ^ x4 ^ C ^ ((x1 ^ x2 ^ C) & (x1 ^ x3));
+        long t2 = x1 ^ x2 ^ x4 ^ C ^ (x3 & x4);
+        long t3 = x0 ^ x1 ^ x2 ^ C ^ ((~x0) & (x3 ^ x4));
+        long t4 = x1 ^ x3 ^ x4 ^ ((x0 ^ x4) & x1);
+        x0 = t0 ^ ROR(t0, 19) ^ ROR(t0, 28);
+        x1 = t1 ^ ROR(t1, 39) ^ ROR(t1, 61);
+        x2 = ~(t2 ^ ROR(t2, 1) ^ ROR(t2, 6));
+        x3 = t3 ^ ROR(t3, 10) ^ ROR(t3, 17);
+        x4 = t4 ^ ROR(t4, 7) ^ ROR(t4, 41);
+    }
+
+    private void P(int nr)
+    {
+        if (nr == 12)
+        {
+            ROUND(0xf0L);
+            ROUND(0xe1L);
+            ROUND(0xd2L);
+            ROUND(0xc3L);
+        }
+        if (nr >= 8)
+        {
+            ROUND(0xb4L);
+            ROUND(0xa5L);
+        }
+        ROUND(0x96L);
+        ROUND(0x87L);
+        ROUND(0x78L);
+        ROUND(0x69L);
+        ROUND(0x5aL);
+        ROUND(0x4bL);
+    }
+
+    private long PAD(int i)
+    {
+        return 0x80L << (56 - (i << 3));
+    }
+
+    private long LOADBYTES(final byte[] bytes, int inOff, int n)
+    {
+        long x = 0;
+        for (int i = 0; i < n; ++i)
+        {
+            x |= (bytes[i + inOff] & 0xFFL) << ((7 - i) << 3);
+        }
+        return x;
+    }
+
+    private void STOREBYTES(byte[] bytes, int inOff, long w, int n)
+    {
+        for (int i = 0; i < n; ++i)
+        {
+            bytes[i + inOff] = (byte)(w >>> ((7 - i) << 3));
+        }
+    }
+
+    @Override
+    public String getAlgorithmName()
+    {
+        return algorithmName;
+    }
+
+    @Override
+    public int getDigestSize()
+    {
+        return CRYPTO_BYTES;
+    }
+
+    @Override
+    public int getByteLength()
+    {
+        return 8;
+    }
+
+    @Override
+    public void update(byte in)
+    {
+        buffer.write(in);
+    }
+
+    @Override
+    public void update(byte[] input, int inOff, int len)
+    {
+        if ((inOff + len) > input.length)
+        {
+            throw new DataLengthException("input buffer too short");
+        }
+        buffer.write(input, inOff, len);
+    }
+
+    @Override
+    public int doFinal(byte[] output, int outOff)
+    {
+        if (CRYPTO_BYTES + outOff > output.length)
+        {
+            throw new OutputLengthException("output buffer is too short");
+        }
+        byte[] input = buffer.toByteArray();
+        int len = buffer.size();
+        int inOff = 0;
+        /* absorb full plaintext blocks */
+        int ASCON_HASH_RATE = 8;
+        while (len >= ASCON_HASH_RATE)
+        {
+            x0 ^= LOADBYTES(input, inOff, 8);
+            P(ASCON_PB_ROUNDS);
+            inOff += ASCON_HASH_RATE;
+            len -= ASCON_HASH_RATE;
+        }
+        /* absorb final plaintext block */
+        x0 ^= LOADBYTES(input, inOff, len);
+        x0 ^= PAD(len);
+        int ASCON_PA_ROUNDS = 12;
+        P(ASCON_PA_ROUNDS);
+        /* squeeze full output blocks */
+        len = CRYPTO_BYTES;
+        while (len > ASCON_HASH_RATE)
+        {
+            STOREBYTES(output, outOff, x0, 8);
+            P(ASCON_PB_ROUNDS);
+            outOff += ASCON_HASH_RATE;
+            len -= ASCON_HASH_RATE;
+        }
+        /* squeeze final output block */
+        STOREBYTES(output, outOff, x0, len);
+        reset();
+        return CRYPTO_BYTES;
+    }
+
+    @Override
+    public void reset()
+    {
+        buffer.reset();
+        /* initialize */
+        switch (asconParameters)
+        {
+        case AsconHashA:
+            x0 = 92044056785660070L;
+            x1 = 8326807761760157607L;
+            x2 = 3371194088139667532L;
+            x3 = -2956994353054992515L;
+            x4 = -6828509670848688761L;
+            break;
+        case AsconHash:
+            x0 = -1255492011513352131L;
+            x1 = -8380609354527731710L;
+            x2 = -5437372128236807582L;
+            x3 = 4834782570098516968L;
+            x4 = 3787428097924915520L;
+            break;
+        }
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/digests/GeneralDigest.java b/bcprov/src/main/java/org/bouncycastle/crypto/digests/GeneralDigest.java
index 011e97c..518a54b 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/digests/GeneralDigest.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/digests/GeneralDigest.java
@@ -1,3 +1,4 @@
+// BEGIN Android-changed: maintain old behaviour
 package org.bouncycastle.crypto.digests;
 
 import org.bouncycastle.crypto.ExtendedDigest;
@@ -158,3 +159,4 @@
 
     protected abstract void processBlock();
 }
+// END Android-changed: maintain old behaviour
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/digests/Haraka256Digest.java b/bcprov/src/main/java/org/bouncycastle/crypto/digests/Haraka256Digest.java
new file mode 100644
index 0000000..1e04a23
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/digests/Haraka256Digest.java
@@ -0,0 +1,154 @@
+package org.bouncycastle.crypto.digests;
+
+import org.bouncycastle.crypto.CryptoServicePurpose;
+import org.bouncycastle.crypto.CryptoServicesRegistrar;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.Bytes;
+
+/**
+ * Haraka-256 v2, https://eprint.iacr.org/2016/098.pdf
+ * <p>
+ * Haraka256-256 with reference to Python Reference Impl from: https://github.com/kste/haraka
+ * </p>
+ */
+public class Haraka256Digest
+    extends HarakaBase
+{
+    private void mix256(byte[][] s1, byte[][] s2)
+    {
+        System.arraycopy(s1[0], 0, s2[0], 0, 4);
+        System.arraycopy(s1[1], 0, s2[0], 4, 4);
+        System.arraycopy(s1[0], 4, s2[0], 8, 4);
+        System.arraycopy(s1[1], 4, s2[0], 12, 4);
+
+        System.arraycopy(s1[0], 8, s2[1], 0, 4);
+        System.arraycopy(s1[1], 8, s2[1], 4, 4);
+        System.arraycopy(s1[0], 12, s2[1], 8, 4);
+        System.arraycopy(s1[1], 12, s2[1], 12, 4);
+    }
+
+    private int haraka256256(byte[] msg, byte[] out, int outOff)
+    {
+        byte[][] s1 = new byte[2][16];
+        byte[][] s2 = new byte[2][16];
+
+        System.arraycopy(msg, 0, s1[0], 0, 16);
+        System.arraycopy(msg, 16, s1[1], 0, 16);
+
+        s1[0] = aesEnc(s1[0], RC[0]);
+        s1[1] = aesEnc(s1[1], RC[1]);
+        s1[0] = aesEnc(s1[0], RC[2]);
+        s1[1] = aesEnc(s1[1], RC[3]);
+        mix256(s1, s2);
+
+        s1[0] = aesEnc(s2[0], RC[4]);
+        s1[1] = aesEnc(s2[1], RC[5]);
+        s1[0] = aesEnc(s1[0], RC[6]);
+        s1[1] = aesEnc(s1[1], RC[7]);
+        mix256(s1, s2);
+
+        s1[0] = aesEnc(s2[0], RC[8]);
+        s1[1] = aesEnc(s2[1], RC[9]);
+        s1[0] = aesEnc(s1[0], RC[10]);
+        s1[1] = aesEnc(s1[1], RC[11]);
+        mix256(s1, s2);
+
+        s1[0] = aesEnc(s2[0], RC[12]);
+        s1[1] = aesEnc(s2[1], RC[13]);
+        s1[0] = aesEnc(s1[0], RC[14]);
+        s1[1] = aesEnc(s1[1], RC[15]);
+        mix256(s1, s2);
+
+        s1[0] = aesEnc(s2[0], RC[16]);
+        s1[1] = aesEnc(s2[1], RC[17]);
+        s1[0] = aesEnc(s1[0], RC[18]);
+        s1[1] = aesEnc(s1[1], RC[19]);
+        mix256(s1, s2);
+
+        Bytes.xor(16, s2[0], 0, msg,  0, out, outOff);
+        Bytes.xor(16, s2[1], 0, msg, 16, out, outOff + 16);
+
+        return DIGEST_SIZE;
+    }
+
+    private final byte[] buffer;
+    private int off;
+
+    private final CryptoServicePurpose purpose;
+
+
+    public Haraka256Digest()
+    {
+        this(CryptoServicePurpose.ANY);
+    }
+
+    public Haraka256Digest(CryptoServicePurpose purpose)
+    {
+        this.purpose = purpose;
+
+        this.buffer = new byte[32];
+
+        CryptoServicesRegistrar.checkConstraints(Utils.getDefaultProperties(this, getDigestSize()*4, purpose));
+    }
+
+    public Haraka256Digest(Haraka256Digest digest)
+    {
+        this.purpose = digest.purpose;
+
+        this.buffer = Arrays.clone(digest.buffer);
+        this.off = digest.off;
+
+        CryptoServicesRegistrar.checkConstraints(Utils.getDefaultProperties(this, getDigestSize()*4, purpose));
+    }
+
+    public String getAlgorithmName()
+    {
+        return "Haraka-256";
+    }
+
+    public void update(byte in)
+    {
+        if (off > 32 - 1)
+        {
+            throw new IllegalArgumentException("total input cannot be more than 32 bytes");
+        }
+
+        buffer[off++] = in;
+    }
+
+    public void update(byte[] in, int inOff, int len)
+    {
+        if (off > 32 - len)
+        {
+            throw new IllegalArgumentException("total input cannot be more than 32 bytes");
+        }
+
+        System.arraycopy(in, inOff, buffer, off, len);
+        off += len;
+    }
+
+    public int doFinal(byte[] out, int outOff)
+    {
+        if (off != 32)
+        {
+            throw new IllegalStateException("input must be exactly 32 bytes");
+        }
+
+        if (out.length - outOff < 32)
+        {
+            throw new IllegalArgumentException("output too short to receive digest");
+        }
+
+        int rv = haraka256256(buffer, out, outOff);
+
+        reset();
+
+        return rv;
+    }
+
+    public void reset()
+    {
+        off = 0;
+        Arrays.clear(buffer);
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/digests/Haraka512Digest.java b/bcprov/src/main/java/org/bouncycastle/crypto/digests/Haraka512Digest.java
new file mode 100644
index 0000000..d9e9ba6
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/digests/Haraka512Digest.java
@@ -0,0 +1,189 @@
+package org.bouncycastle.crypto.digests;
+
+import org.bouncycastle.crypto.CryptoServicePurpose;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.Bytes;
+
+/**
+ * Haraka-512 v2, https://eprint.iacr.org/2016/098.pdf
+ * <p>
+ * Haraka512-256 with reference to Python Reference Impl from: https://github.com/kste/haraka
+ * </p>
+ */
+public class Haraka512Digest
+    extends HarakaBase
+{
+    private final byte[] buffer;
+    private int off;
+
+    private final CryptoServicePurpose purpose;
+
+
+    public Haraka512Digest()
+    {
+        this(CryptoServicePurpose.ANY);
+    }
+    public Haraka512Digest(CryptoServicePurpose purpose)
+    {
+        this.purpose = purpose;
+
+        this.buffer = new byte[64];
+    }
+
+    public Haraka512Digest(Haraka512Digest digest)
+    {
+        this.purpose = digest.purpose;
+
+        this.buffer = Arrays.clone(digest.buffer);
+        this.off = digest.off;
+    }
+
+    private void mix512(byte[][] s1, byte[][] s2)
+    {
+        System.arraycopy(s1[0], 12, s2[0], 0, 4);
+        System.arraycopy(s1[2], 12, s2[0], 4, 4);
+        System.arraycopy(s1[1], 12, s2[0], 8, 4);
+        System.arraycopy(s1[3], 12, s2[0], 12, 4);
+
+        System.arraycopy(s1[2], 0, s2[1], 0, 4);
+        System.arraycopy(s1[0], 0, s2[1], 4, 4);
+        System.arraycopy(s1[3], 0, s2[1], 8, 4);
+        System.arraycopy(s1[1], 0, s2[1], 12, 4);
+
+        System.arraycopy(s1[2], 4, s2[2], 0, 4);
+        System.arraycopy(s1[0], 4, s2[2], 4, 4);
+        System.arraycopy(s1[3], 4, s2[2], 8, 4);
+        System.arraycopy(s1[1], 4, s2[2], 12, 4);
+
+        System.arraycopy(s1[0], 8, s2[3], 0, 4);
+        System.arraycopy(s1[2], 8, s2[3], 4, 4);
+        System.arraycopy(s1[1], 8, s2[3], 8, 4);
+        System.arraycopy(s1[3], 8, s2[3], 12, 4);
+    }
+
+    private int haraka512256(byte[] msg, byte[] out, int outOff)
+    {
+        byte[][] s1 = new byte[4][16];
+        byte[][] s2 = new byte[4][16];
+
+        //-- Unrolled version of above.
+
+        System.arraycopy(msg, 0, s1[0], 0, 16);
+        System.arraycopy(msg, 16, s1[1], 0, 16);
+        System.arraycopy(msg, 32, s1[2], 0, 16);
+        System.arraycopy(msg, 48, s1[3], 0, 16);
+
+        s1[0] = aesEnc(s1[0], RC[0]);
+        s1[1] = aesEnc(s1[1], RC[1]);
+        s1[2] = aesEnc(s1[2], RC[2]);
+        s1[3] = aesEnc(s1[3], RC[3]);
+        s1[0] = aesEnc(s1[0], RC[4]);
+        s1[1] = aesEnc(s1[1], RC[5]);
+        s1[2] = aesEnc(s1[2], RC[6]);
+        s1[3] = aesEnc(s1[3], RC[7]);
+        mix512(s1, s2);
+
+        s1[0] = aesEnc(s2[0], RC[8]);
+        s1[1] = aesEnc(s2[1], RC[9]);
+        s1[2] = aesEnc(s2[2], RC[10]);
+        s1[3] = aesEnc(s2[3], RC[11]);
+        s1[0] = aesEnc(s1[0], RC[12]);
+        s1[1] = aesEnc(s1[1], RC[13]);
+        s1[2] = aesEnc(s1[2], RC[14]);
+        s1[3] = aesEnc(s1[3], RC[15]);
+        mix512(s1, s2);
+
+        s1[0] = aesEnc(s2[0], RC[16]);
+        s1[1] = aesEnc(s2[1], RC[17]);
+        s1[2] = aesEnc(s2[2], RC[18]);
+        s1[3] = aesEnc(s2[3], RC[19]);
+        s1[0] = aesEnc(s1[0], RC[20]);
+        s1[1] = aesEnc(s1[1], RC[21]);
+        s1[2] = aesEnc(s1[2], RC[22]);
+        s1[3] = aesEnc(s1[3], RC[23]);
+        mix512(s1, s2);
+
+        s1[0] = aesEnc(s2[0], RC[24]);
+        s1[1] = aesEnc(s2[1], RC[25]);
+        s1[2] = aesEnc(s2[2], RC[26]);
+        s1[3] = aesEnc(s2[3], RC[27]);
+        s1[0] = aesEnc(s1[0], RC[28]);
+        s1[1] = aesEnc(s1[1], RC[29]);
+        s1[2] = aesEnc(s1[2], RC[30]);
+        s1[3] = aesEnc(s1[3], RC[31]);
+        mix512(s1, s2);
+
+        s1[0] = aesEnc(s2[0], RC[32]);
+        s1[1] = aesEnc(s2[1], RC[33]);
+        s1[2] = aesEnc(s2[2], RC[34]);
+        s1[3] = aesEnc(s2[3], RC[35]);
+        s1[0] = aesEnc(s1[0], RC[36]);
+        s1[1] = aesEnc(s1[1], RC[37]);
+        s1[2] = aesEnc(s1[2], RC[38]);
+        s1[3] = aesEnc(s1[3], RC[39]);
+        mix512(s1, s2);
+
+        Bytes.xor(16, s2[0], 0, msg,  0, s1[0], 0);
+        Bytes.xor(16, s2[1], 0, msg, 16, s1[1], 0);
+        Bytes.xor(16, s2[2], 0, msg, 32, s1[2], 0);
+        Bytes.xor(16, s2[3], 0, msg, 48, s1[3], 0);
+
+        System.arraycopy(s1[0], 8, out, outOff, 8);
+        System.arraycopy(s1[1], 8, out, outOff + 8, 8);
+        System.arraycopy(s1[2], 0, out, outOff + 16, 8);
+        System.arraycopy(s1[3], 0, out, outOff + 24, 8);
+
+        return DIGEST_SIZE;
+    }
+
+    public String getAlgorithmName()
+    {
+        return "Haraka-512";
+    }
+
+    public void update(byte in)
+    {
+        if (off > 64 - 1)
+        {
+            throw new IllegalArgumentException("total input cannot be more than 64 bytes");
+        }
+
+        buffer[off++] = in;
+    }
+
+    public void update(byte[] in, int inOff, int len)
+    {
+        if (off > 64 - len)
+        {
+            throw new IllegalArgumentException("total input cannot be more than 64 bytes");
+        }
+
+        System.arraycopy(in, inOff, buffer, off, len);
+        off += len;
+    }
+
+    public int doFinal(byte[] out, int outOff)
+    {
+        if (off != 64)
+        {
+            throw new IllegalStateException("input must be exactly 64 bytes");
+        }
+
+        if (out.length - outOff < 32)
+        {
+            throw new IllegalArgumentException("output too short to receive digest");
+        }
+
+        int rv = haraka512256(buffer, out, outOff);
+
+        reset();
+
+        return rv;
+    }
+
+    public void reset()
+    {
+        off = 0;
+        Arrays.clear(buffer);
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/digests/HarakaBase.java b/bcprov/src/main/java/org/bouncycastle/crypto/digests/HarakaBase.java
new file mode 100644
index 0000000..db9f3f2
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/digests/HarakaBase.java
@@ -0,0 +1,147 @@
+package org.bouncycastle.crypto.digests;
+
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.util.Bytes;
+
+/**
+ * Base class for Haraka v2, https://eprint.iacr.org/2016/098.pdf
+ */
+public abstract class HarakaBase
+    implements Digest
+{
+    protected static final int DIGEST_SIZE = 32;
+
+    // Haraka round constants
+    static final byte[][] RC = new byte[][]
+    {
+        new byte[]{ (byte)0x9D, (byte)0x7B, (byte)0x81, (byte)0x75, (byte)0xF0, (byte)0xFE, (byte)0xC5, (byte)0xB2, (byte)0x0A, (byte)0xC0, (byte)0x20, (byte)0xE6, (byte)0x4C, (byte)0x70, (byte)0x84, (byte)0x06 },
+        new byte[]{ (byte)0x17, (byte)0xF7, (byte)0x08, (byte)0x2F, (byte)0xA4, (byte)0x6B, (byte)0x0F, (byte)0x64, (byte)0x6B, (byte)0xA0, (byte)0xF3, (byte)0x88, (byte)0xE1, (byte)0xB4, (byte)0x66, (byte)0x8B },
+        new byte[]{ (byte)0x14, (byte)0x91, (byte)0x02, (byte)0x9F, (byte)0x60, (byte)0x9D, (byte)0x02, (byte)0xCF, (byte)0x98, (byte)0x84, (byte)0xF2, (byte)0x53, (byte)0x2D, (byte)0xDE, (byte)0x02, (byte)0x34 },
+        new byte[]{ (byte)0x79, (byte)0x4F, (byte)0x5B, (byte)0xFD, (byte)0xAF, (byte)0xBC, (byte)0xF3, (byte)0xBB, (byte)0x08, (byte)0x4F, (byte)0x7B, (byte)0x2E, (byte)0xE6, (byte)0xEA, (byte)0xD6, (byte)0x0E },
+        new byte[]{ (byte)0x44, (byte)0x70, (byte)0x39, (byte)0xBE, (byte)0x1C, (byte)0xCD, (byte)0xEE, (byte)0x79, (byte)0x8B, (byte)0x44, (byte)0x72, (byte)0x48, (byte)0xCB, (byte)0xB0, (byte)0xCF, (byte)0xCB },
+        new byte[]{ (byte)0x7B, (byte)0x05, (byte)0x8A, (byte)0x2B, (byte)0xED, (byte)0x35, (byte)0x53, (byte)0x8D, (byte)0xB7, (byte)0x32, (byte)0x90, (byte)0x6E, (byte)0xEE, (byte)0xCD, (byte)0xEA, (byte)0x7E },
+        new byte[]{ (byte)0x1B, (byte)0xEF, (byte)0x4F, (byte)0xDA, (byte)0x61, (byte)0x27, (byte)0x41, (byte)0xE2, (byte)0xD0, (byte)0x7C, (byte)0x2E, (byte)0x5E, (byte)0x43, (byte)0x8F, (byte)0xC2, (byte)0x67 },
+        new byte[]{ (byte)0x3B, (byte)0x0B, (byte)0xC7, (byte)0x1F, (byte)0xE2, (byte)0xFD, (byte)0x5F, (byte)0x67, (byte)0x07, (byte)0xCC, (byte)0xCA, (byte)0xAF, (byte)0xB0, (byte)0xD9, (byte)0x24, (byte)0x29 },
+        new byte[]{ (byte)0xEE, (byte)0x65, (byte)0xD4, (byte)0xB9, (byte)0xCA, (byte)0x8F, (byte)0xDB, (byte)0xEC, (byte)0xE9, (byte)0x7F, (byte)0x86, (byte)0xE6, (byte)0xF1, (byte)0x63, (byte)0x4D, (byte)0xAB },
+        new byte[]{ (byte)0x33, (byte)0x7E, (byte)0x03, (byte)0xAD, (byte)0x4F, (byte)0x40, (byte)0x2A, (byte)0x5B, (byte)0x64, (byte)0xCD, (byte)0xB7, (byte)0xD4, (byte)0x84, (byte)0xBF, (byte)0x30, (byte)0x1C },
+        new byte[]{ (byte)0x00, (byte)0x98, (byte)0xF6, (byte)0x8D, (byte)0x2E, (byte)0x8B, (byte)0x02, (byte)0x69, (byte)0xBF, (byte)0x23, (byte)0x17, (byte)0x94, (byte)0xB9, (byte)0x0B, (byte)0xCC, (byte)0xB2 },
+        new byte[]{ (byte)0x8A, (byte)0x2D, (byte)0x9D, (byte)0x5C, (byte)0xC8, (byte)0x9E, (byte)0xAA, (byte)0x4A, (byte)0x72, (byte)0x55, (byte)0x6F, (byte)0xDE, (byte)0xA6, (byte)0x78, (byte)0x04, (byte)0xFA },
+        new byte[]{ (byte)0xD4, (byte)0x9F, (byte)0x12, (byte)0x29, (byte)0x2E, (byte)0x4F, (byte)0xFA, (byte)0x0E, (byte)0x12, (byte)0x2A, (byte)0x77, (byte)0x6B, (byte)0x2B, (byte)0x9F, (byte)0xB4, (byte)0xDF },
+        new byte[]{ (byte)0xEE, (byte)0x12, (byte)0x6A, (byte)0xBB, (byte)0xAE, (byte)0x11, (byte)0xD6, (byte)0x32, (byte)0x36, (byte)0xA2, (byte)0x49, (byte)0xF4, (byte)0x44, (byte)0x03, (byte)0xA1, (byte)0x1E },
+        new byte[]{ (byte)0xA6, (byte)0xEC, (byte)0xA8, (byte)0x9C, (byte)0xC9, (byte)0x00, (byte)0x96, (byte)0x5F, (byte)0x84, (byte)0x00, (byte)0x05, (byte)0x4B, (byte)0x88, (byte)0x49, (byte)0x04, (byte)0xAF },
+        new byte[]{ (byte)0xEC, (byte)0x93, (byte)0xE5, (byte)0x27, (byte)0xE3, (byte)0xC7, (byte)0xA2, (byte)0x78, (byte)0x4F, (byte)0x9C, (byte)0x19, (byte)0x9D, (byte)0xD8, (byte)0x5E, (byte)0x02, (byte)0x21 },
+        new byte[]{ (byte)0x73, (byte)0x01, (byte)0xD4, (byte)0x82, (byte)0xCD, (byte)0x2E, (byte)0x28, (byte)0xB9, (byte)0xB7, (byte)0xC9, (byte)0x59, (byte)0xA7, (byte)0xF8, (byte)0xAA, (byte)0x3A, (byte)0xBF },
+        new byte[]{ (byte)0x6B, (byte)0x7D, (byte)0x30, (byte)0x10, (byte)0xD9, (byte)0xEF, (byte)0xF2, (byte)0x37, (byte)0x17, (byte)0xB0, (byte)0x86, (byte)0x61, (byte)0x0D, (byte)0x70, (byte)0x60, (byte)0x62 },
+        new byte[]{ (byte)0xC6, (byte)0x9A, (byte)0xFC, (byte)0xF6, (byte)0x53, (byte)0x91, (byte)0xC2, (byte)0x81, (byte)0x43, (byte)0x04, (byte)0x30, (byte)0x21, (byte)0xC2, (byte)0x45, (byte)0xCA, (byte)0x5A },
+        new byte[]{ (byte)0x3A, (byte)0x94, (byte)0xD1, (byte)0x36, (byte)0xE8, (byte)0x92, (byte)0xAF, (byte)0x2C, (byte)0xBB, (byte)0x68, (byte)0x6B, (byte)0x22, (byte)0x3C, (byte)0x97, (byte)0x23, (byte)0x92 },
+        new byte[]{ (byte)0xB4, (byte)0x71, (byte)0x10, (byte)0xE5, (byte)0x58, (byte)0xB9, (byte)0xBA, (byte)0x6C, (byte)0xEB, (byte)0x86, (byte)0x58, (byte)0x22, (byte)0x38, (byte)0x92, (byte)0xBF, (byte)0xD3 },
+        new byte[]{ (byte)0x8D, (byte)0x12, (byte)0xE1, (byte)0x24, (byte)0xDD, (byte)0xFD, (byte)0x3D, (byte)0x93, (byte)0x77, (byte)0xC6, (byte)0xF0, (byte)0xAE, (byte)0xE5, (byte)0x3C, (byte)0x86, (byte)0xDB },
+        new byte[]{ (byte)0xB1, (byte)0x12, (byte)0x22, (byte)0xCB, (byte)0xE3, (byte)0x8D, (byte)0xE4, (byte)0x83, (byte)0x9C, (byte)0xA0, (byte)0xEB, (byte)0xFF, (byte)0x68, (byte)0x62, (byte)0x60, (byte)0xBB },
+        new byte[]{ (byte)0x7D, (byte)0xF7, (byte)0x2B, (byte)0xC7, (byte)0x4E, (byte)0x1A, (byte)0xB9, (byte)0x2D, (byte)0x9C, (byte)0xD1, (byte)0xE4, (byte)0xE2, (byte)0xDC, (byte)0xD3, (byte)0x4B, (byte)0x73 },
+        new byte[]{ (byte)0x4E, (byte)0x92, (byte)0xB3, (byte)0x2C, (byte)0xC4, (byte)0x15, (byte)0x14, (byte)0x4B, (byte)0x43, (byte)0x1B, (byte)0x30, (byte)0x61, (byte)0xC3, (byte)0x47, (byte)0xBB, (byte)0x43 },
+        new byte[]{ (byte)0x99, (byte)0x68, (byte)0xEB, (byte)0x16, (byte)0xDD, (byte)0x31, (byte)0xB2, (byte)0x03, (byte)0xF6, (byte)0xEF, (byte)0x07, (byte)0xE7, (byte)0xA8, (byte)0x75, (byte)0xA7, (byte)0xDB },
+        new byte[]{ (byte)0x2C, (byte)0x47, (byte)0xCA, (byte)0x7E, (byte)0x02, (byte)0x23, (byte)0x5E, (byte)0x8E, (byte)0x77, (byte)0x59, (byte)0x75, (byte)0x3C, (byte)0x4B, (byte)0x61, (byte)0xF3, (byte)0x6D },
+        new byte[]{ (byte)0xF9, (byte)0x17, (byte)0x86, (byte)0xB8, (byte)0xB9, (byte)0xE5, (byte)0x1B, (byte)0x6D, (byte)0x77, (byte)0x7D, (byte)0xDE, (byte)0xD6, (byte)0x17, (byte)0x5A, (byte)0xA7, (byte)0xCD },
+        new byte[]{ (byte)0x5D, (byte)0xEE, (byte)0x46, (byte)0xA9, (byte)0x9D, (byte)0x06, (byte)0x6C, (byte)0x9D, (byte)0xAA, (byte)0xE9, (byte)0xA8, (byte)0x6B, (byte)0xF0, (byte)0x43, (byte)0x6B, (byte)0xEC },
+        new byte[]{ (byte)0xC1, (byte)0x27, (byte)0xF3, (byte)0x3B, (byte)0x59, (byte)0x11, (byte)0x53, (byte)0xA2, (byte)0x2B, (byte)0x33, (byte)0x57, (byte)0xF9, (byte)0x50, (byte)0x69, (byte)0x1E, (byte)0xCB },
+        new byte[]{ (byte)0xD9, (byte)0xD0, (byte)0x0E, (byte)0x60, (byte)0x53, (byte)0x03, (byte)0xED, (byte)0xE4, (byte)0x9C, (byte)0x61, (byte)0xDA, (byte)0x00, (byte)0x75, (byte)0x0C, (byte)0xEE, (byte)0x2C },
+        new byte[]{ (byte)0x50, (byte)0xA3, (byte)0xA4, (byte)0x63, (byte)0xBC, (byte)0xBA, (byte)0xBB, (byte)0x80, (byte)0xAB, (byte)0x0C, (byte)0xE9, (byte)0x96, (byte)0xA1, (byte)0xA5, (byte)0xB1, (byte)0xF0 },
+        new byte[]{ (byte)0x39, (byte)0xCA, (byte)0x8D, (byte)0x93, (byte)0x30, (byte)0xDE, (byte)0x0D, (byte)0xAB, (byte)0x88, (byte)0x29, (byte)0x96, (byte)0x5E, (byte)0x02, (byte)0xB1, (byte)0x3D, (byte)0xAE },
+        new byte[]{ (byte)0x42, (byte)0xB4, (byte)0x75, (byte)0x2E, (byte)0xA8, (byte)0xF3, (byte)0x14, (byte)0x88, (byte)0x0B, (byte)0xA4, (byte)0x54, (byte)0xD5, (byte)0x38, (byte)0x8F, (byte)0xBB, (byte)0x17 },
+        new byte[]{ (byte)0xF6, (byte)0x16, (byte)0x0A, (byte)0x36, (byte)0x79, (byte)0xB7, (byte)0xB6, (byte)0xAE, (byte)0xD7, (byte)0x7F, (byte)0x42, (byte)0x5F, (byte)0x5B, (byte)0x8A, (byte)0xBB, (byte)0x34 },
+        new byte[]{ (byte)0xDE, (byte)0xAF, (byte)0xBA, (byte)0xFF, (byte)0x18, (byte)0x59, (byte)0xCE, (byte)0x43, (byte)0x38, (byte)0x54, (byte)0xE5, (byte)0xCB, (byte)0x41, (byte)0x52, (byte)0xF6, (byte)0x26 },
+        new byte[]{ (byte)0x78, (byte)0xC9, (byte)0x9E, (byte)0x83, (byte)0xF7, (byte)0x9C, (byte)0xCA, (byte)0xA2, (byte)0x6A, (byte)0x02, (byte)0xF3, (byte)0xB9, (byte)0x54, (byte)0x9A, (byte)0xE9, (byte)0x4C },
+        new byte[]{ (byte)0x35, (byte)0x12, (byte)0x90, (byte)0x22, (byte)0x28, (byte)0x6E, (byte)0xC0, (byte)0x40, (byte)0xBE, (byte)0xF7, (byte)0xDF, (byte)0x1B, (byte)0x1A, (byte)0xA5, (byte)0x51, (byte)0xAE },
+        new byte[]{ (byte)0xCF, (byte)0x59, (byte)0xA6, (byte)0x48, (byte)0x0F, (byte)0xBC, (byte)0x73, (byte)0xC1, (byte)0x2B, (byte)0xD2, (byte)0x7E, (byte)0xBA, (byte)0x3C, (byte)0x61, (byte)0xC1, (byte)0xA0 },
+        new byte[]{ (byte)0xA1, (byte)0x9D, (byte)0xC5, (byte)0xE9, (byte)0xFD, (byte)0xBD, (byte)0xD6, (byte)0x4A, (byte)0x88, (byte)0x82, (byte)0x28, (byte)0x02, (byte)0x03, (byte)0xCC, (byte)0x6A, (byte)0x75 },
+    };
+
+    private static final byte[][] S = new byte[][]{
+        { (byte)0x63, (byte)0x7C, (byte)0x77, (byte)0x7B, (byte)0xF2, (byte)0x6B, (byte)0x6F, (byte)0xC5, (byte)0x30, (byte)0x01, (byte)0x67, (byte)0x2B, (byte)0xFE, (byte)0xD7, (byte)0xAB, (byte)0x76 },
+        { (byte)0xCA, (byte)0x82, (byte)0xC9, (byte)0x7D, (byte)0xFA, (byte)0x59, (byte)0x47, (byte)0xF0, (byte)0xAD, (byte)0xD4, (byte)0xA2, (byte)0xAF, (byte)0x9C, (byte)0xA4, (byte)0x72, (byte)0xC0 },
+        { (byte)0xB7, (byte)0xFD, (byte)0x93, (byte)0x26, (byte)0x36, (byte)0x3F, (byte)0xF7, (byte)0xCC, (byte)0x34, (byte)0xA5, (byte)0xE5, (byte)0xF1, (byte)0x71, (byte)0xD8, (byte)0x31, (byte)0x15 },
+        { (byte)0x04, (byte)0xC7, (byte)0x23, (byte)0xC3, (byte)0x18, (byte)0x96, (byte)0x05, (byte)0x9A, (byte)0x07, (byte)0x12, (byte)0x80, (byte)0xE2, (byte)0xEB, (byte)0x27, (byte)0xB2, (byte)0x75 },
+        { (byte)0x09, (byte)0x83, (byte)0x2C, (byte)0x1A, (byte)0x1B, (byte)0x6E, (byte)0x5A, (byte)0xA0, (byte)0x52, (byte)0x3B, (byte)0xD6, (byte)0xB3, (byte)0x29, (byte)0xE3, (byte)0x2F, (byte)0x84 },
+        { (byte)0x53, (byte)0xD1, (byte)0x00, (byte)0xED, (byte)0x20, (byte)0xFC, (byte)0xB1, (byte)0x5B, (byte)0x6A, (byte)0xCB, (byte)0xBE, (byte)0x39, (byte)0x4A, (byte)0x4C, (byte)0x58, (byte)0xCF },
+        { (byte)0xD0, (byte)0xEF, (byte)0xAA, (byte)0xFB, (byte)0x43, (byte)0x4D, (byte)0x33, (byte)0x85, (byte)0x45, (byte)0xF9, (byte)0x02, (byte)0x7F, (byte)0x50, (byte)0x3C, (byte)0x9F, (byte)0xA8 },
+        { (byte)0x51, (byte)0xA3, (byte)0x40, (byte)0x8F, (byte)0x92, (byte)0x9D, (byte)0x38, (byte)0xF5, (byte)0xBC, (byte)0xB6, (byte)0xDA, (byte)0x21, (byte)0x10, (byte)0xFF, (byte)0xF3, (byte)0xD2 },
+        { (byte)0xCD, (byte)0x0C, (byte)0x13, (byte)0xEC, (byte)0x5F, (byte)0x97, (byte)0x44, (byte)0x17, (byte)0xC4, (byte)0xA7, (byte)0x7E, (byte)0x3D, (byte)0x64, (byte)0x5D, (byte)0x19, (byte)0x73 },
+        { (byte)0x60, (byte)0x81, (byte)0x4F, (byte)0xDC, (byte)0x22, (byte)0x2A, (byte)0x90, (byte)0x88, (byte)0x46, (byte)0xEE, (byte)0xB8, (byte)0x14, (byte)0xDE, (byte)0x5E, (byte)0x0B, (byte)0xDB },
+        { (byte)0xE0, (byte)0x32, (byte)0x3A, (byte)0x0A, (byte)0x49, (byte)0x06, (byte)0x24, (byte)0x5C, (byte)0xC2, (byte)0xD3, (byte)0xAC, (byte)0x62, (byte)0x91, (byte)0x95, (byte)0xE4, (byte)0x79 },
+        { (byte)0xE7, (byte)0xC8, (byte)0x37, (byte)0x6D, (byte)0x8D, (byte)0xD5, (byte)0x4E, (byte)0xA9, (byte)0x6C, (byte)0x56, (byte)0xF4, (byte)0xEA, (byte)0x65, (byte)0x7A, (byte)0xAE, (byte)0x08 },
+        { (byte)0xBA, (byte)0x78, (byte)0x25, (byte)0x2E, (byte)0x1C, (byte)0xA6, (byte)0xB4, (byte)0xC6, (byte)0xE8, (byte)0xDD, (byte)0x74, (byte)0x1F, (byte)0x4B, (byte)0xBD, (byte)0x8B, (byte)0x8A },
+        { (byte)0x70, (byte)0x3E, (byte)0xB5, (byte)0x66, (byte)0x48, (byte)0x03, (byte)0xF6, (byte)0x0E, (byte)0x61, (byte)0x35, (byte)0x57, (byte)0xB9, (byte)0x86, (byte)0xC1, (byte)0x1D, (byte)0x9E },
+        { (byte)0xE1, (byte)0xF8, (byte)0x98, (byte)0x11, (byte)0x69, (byte)0xD9, (byte)0x8E, (byte)0x94, (byte)0x9B, (byte)0x1E, (byte)0x87, (byte)0xE9, (byte)0xCE, (byte)0x55, (byte)0x28, (byte)0xDF },
+        { (byte)0x8C, (byte)0xA1, (byte)0x89, (byte)0x0D, (byte)0xBF, (byte)0xE6, (byte)0x42, (byte)0x68, (byte)0x41, (byte)0x99, (byte)0x2D, (byte)0x0F, (byte)0xB0, (byte)0x54, (byte)0xBB, (byte)0x16 },
+    };
+
+    static byte sBox(byte x)
+    {
+        return S[(((x & 0xFF) >>> 4))][x & 0xF];
+    }
+
+    static byte[] subBytes(byte[] s)
+    {
+        byte[] out = new byte[s.length];
+        out[0] = sBox(s[0]);
+        out[1] = sBox(s[1]);
+        out[2] = sBox(s[2]);
+        out[3] = sBox(s[3]);
+        out[4] = sBox(s[4]);
+        out[5] = sBox(s[5]);
+        out[6] = sBox(s[6]);
+        out[7] = sBox(s[7]);
+        out[8] = sBox(s[8]);
+        out[9] = sBox(s[9]);
+        out[10] = sBox(s[10]);
+        out[11] = sBox(s[11]);
+        out[12] = sBox(s[12]);
+        out[13] = sBox(s[13]);
+        out[14] = sBox(s[14]);
+        out[15] = sBox(s[15]);
+        return out;
+    }
+
+    static byte[] shiftRows(byte[] s)
+    {
+        return new byte[]{
+            s[0], s[5], s[10], s[15],
+            s[4], s[9], s[14], s[3],
+            s[8], s[13], s[2], s[7],
+            s[12], s[1], s[6], s[11]
+        };
+    }
+
+    static byte[] aesEnc(byte[] s, byte[] rk)
+    {
+        s = subBytes(s);
+        s = shiftRows(s);
+        s = mixColumns(s);
+        Bytes.xorTo(16, rk, s);
+        return s;
+    }
+
+    static byte mulX(byte p)
+    {
+        return (byte)(((p & 0x7F) << 1) ^ (((p & 0x80) >> 7) * 0x1B));
+    }
+
+    private static byte[] mixColumns(byte[] s)
+    {
+        byte[] out = new byte[s.length];
+        int j = 0;
+        for (int i = 0; i < 4; i++)
+        {
+            out[j++] = (byte)(mulX(s[4 * i]) ^ mulX(s[4 * i + 1]) ^ s[4 * i + 1] ^ s[4 * i + 2] ^ s[4 * i + 3]);
+            out[j++] = (byte)(s[4 * i] ^ mulX(s[4 * i + 1]) ^ mulX(s[4 * i + 2]) ^ s[4 * i + 2] ^ s[4 * i + 3]);
+            out[j++] = (byte)(s[4 * i] ^ s[4 * i + 1] ^ mulX(s[4 * i + 2]) ^ mulX(s[4 * i + 3]) ^ s[4 * i + 3]);
+            out[j++] = (byte)(mulX(s[4 * i]) ^ s[4 * i] ^ s[4 * i + 1] ^ s[4 * i + 2] ^ mulX(s[4 * i + 3]));
+        }
+        return out;
+    }
+
+    public int getDigestSize()
+    {
+        return DIGEST_SIZE;
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/digests/ISAPDigest.java b/bcprov/src/main/java/org/bouncycastle/crypto/digests/ISAPDigest.java
new file mode 100644
index 0000000..9120e75
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/digests/ISAPDigest.java
@@ -0,0 +1,146 @@
+package org.bouncycastle.crypto.digests;
+
+import java.io.ByteArrayOutputStream;
+
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.OutputLengthException;
+import org.bouncycastle.util.Pack;
+
+/**
+ * ISAP Hash v2, https://isap.iaik.tugraz.at/
+ * https://csrc.nist.gov/CSRC/media/Projects/lightweight-cryptography/documents/finalist-round/updated-spec-doc/isap-spec-final.pdf
+ * <p>
+ * ISAP Hash v2 with reference to C Reference Impl from: https://github.com/isap-lwc/isap-code-package
+ * </p>
+ */
+
+public class ISAPDigest
+    implements Digest
+{
+    private long x0, x1, x2, x3, x4;
+    private long t0, t1, t2, t3, t4;
+    private ByteArrayOutputStream buffer = new ByteArrayOutputStream();
+
+    private void ROUND(long C)
+    {
+        t0 = x0 ^ x1 ^ x2 ^ x3 ^ C ^ (x1 & (x0 ^ x2 ^ x4 ^ C));
+        t1 = x0 ^ x2 ^ x3 ^ x4 ^ C ^ ((x1 ^ x2 ^ C) & (x1 ^ x3));
+        t2 = x1 ^ x2 ^ x4 ^ C ^ (x3 & x4);
+        t3 = x0 ^ x1 ^ x2 ^ C ^ ((~x0) & (x3 ^ x4));
+        t4 = x1 ^ x3 ^ x4 ^ ((x0 ^ x4) & x1);
+        x0 = t0 ^ ROTR(t0, 19) ^ ROTR(t0, 28);
+        x1 = t1 ^ ROTR(t1, 39) ^ ROTR(t1, 61);
+        x2 = ~(t2 ^ ROTR(t2, 1) ^ ROTR(t2, 6));
+        x3 = t3 ^ ROTR(t3, 10) ^ ROTR(t3, 17);
+        x4 = t4 ^ ROTR(t4, 7) ^ ROTR(t4, 41);
+    }
+
+    private void P12()
+    {
+        ROUND(0xf0);
+        ROUND(0xe1);
+        ROUND(0xd2);
+        ROUND(0xc3);
+        ROUND(0xb4);
+        ROUND(0xa5);
+        ROUND(0x96);
+        ROUND(0x87);
+        ROUND(0x78);
+        ROUND(0x69);
+        ROUND(0x5a);
+        ROUND(0x4b);
+    }
+
+    private long ROTR(long x, long n)
+    {
+        return (x >>> n) | (x << (64 - n));
+    }
+
+    protected long U64BIG(long x)
+    {
+        return ((ROTR(x, 8) & (0xFF000000FF000000L)) | (ROTR(x, 24) & (0x00FF000000FF0000L)) |
+            (ROTR(x, 40) & (0x0000FF000000FF00L)) | (ROTR(x, 56) & (0x000000FF000000FFL)));
+    }
+
+    @Override
+    public String getAlgorithmName()
+    {
+        return "ISAP Hash";
+    }
+
+    @Override
+    public int getDigestSize()
+    {
+        return 32;
+    }
+
+    @Override
+    public void update(byte input)
+    {
+        buffer.write(input);
+    }
+
+    @Override
+    public void update(byte[] input, int inOff, int len)
+    {
+        if ((inOff + len) > input.length)
+        {
+            throw new DataLengthException("input buffer too short");
+        }
+        buffer.write(input, inOff, len);
+    }
+
+    @Override
+    public int doFinal(byte[] out, int outOff)
+    {
+        if (32 + outOff > out.length)
+        {
+            throw new OutputLengthException("output buffer is too short");
+        }
+        t0 = t1 = t2 = t3 = t4 = 0;
+        /* init state */
+        x0 = -1255492011513352131L;
+        x1 = -8380609354527731710L;
+        x2 = -5437372128236807582L;
+        x3 = 4834782570098516968L;
+        x4 = 3787428097924915520L;
+        /* absorb */
+        byte[] input = buffer.toByteArray();
+        int len = input.length;
+        long[] in64 = new long[len >> 3];
+        Pack.littleEndianToLong(input, 0, in64, 0, in64.length);
+        int idx = 0;
+        while (len >= 8)
+        {
+            x0 ^= U64BIG(in64[idx++]);
+            P12();
+            len -= 8;
+        }
+        /* absorb final input block */
+        x0 ^= 0x80L << ((7 - len) << 3);
+        while (len > 0)
+        {
+            x0 ^= (input[(idx << 3) + --len] & 0xFFL) << ((7 - len) << 3);
+        }
+        P12();
+        // squeeze
+        long[] out64 = new long[4];
+        for (idx = 0; idx < 3; ++idx)
+        {
+            out64[idx] = U64BIG(x0);
+            P12();
+        }
+        /* squeeze final output block */
+        out64[idx] = U64BIG(x0);
+        Pack.longToLittleEndian(out64, out, outOff);
+        buffer.reset();
+        return 32;
+    }
+
+    @Override
+    public void reset()
+    {
+        buffer.reset();
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/digests/LongDigest.java b/bcprov/src/main/java/org/bouncycastle/crypto/digests/LongDigest.java
index 8ea474b..9fba7aa 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/digests/LongDigest.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/digests/LongDigest.java
@@ -1,5 +1,7 @@
 package org.bouncycastle.crypto.digests;
 
+import org.bouncycastle.crypto.CryptoServiceProperties;
+import org.bouncycastle.crypto.CryptoServicePurpose;
 import org.bouncycastle.crypto.ExtendedDigest;
 import org.bouncycastle.util.Memoable;
 import org.bouncycastle.util.Pack;
@@ -12,6 +14,8 @@
 {
     private static final int BYTE_LENGTH = 128;
 
+    protected final CryptoServicePurpose purpose;
+
     private byte[] xBuf = new byte[8];
     private int     xBufOff;
 
@@ -28,6 +32,16 @@
      */
     protected LongDigest()
     {
+        this(CryptoServicePurpose.ANY);
+    }
+
+    /**
+     * Constructor for variable length word
+     */
+    protected LongDigest(CryptoServicePurpose purpose)
+    {
+        this.purpose = purpose;
+
         xBufOff = 0;
 
         reset();
@@ -40,6 +54,8 @@
      */
     protected LongDigest(LongDigest t)
     {
+        this.purpose = t.purpose;
+
         copyIn(t);
     }
 
@@ -147,7 +163,7 @@
         //
         // process whole words.
         //
-        while (len > xBuf.length)
+        while (len >= xBuf.length)
         {
             processWord(in, inOff);
 
@@ -406,4 +422,5 @@
 0x4cc5d4becb3e42b6L, 0x597f299cfc657e2aL, 0x5fcb6fab3ad6faecL, 0x6c44198c4a475817L
     };
 
+    protected abstract CryptoServiceProperties cryptoServiceProperties();
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/digests/MD4Digest.java b/bcprov/src/main/java/org/bouncycastle/crypto/digests/MD4Digest.java
index 093e35d..89b4424 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/digests/MD4Digest.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/digests/MD4Digest.java
@@ -1,3 +1,4 @@
+// BEGIN Android-changed: maintain old behaviour
 package org.bouncycastle.crypto.digests;
 
 
@@ -289,3 +290,4 @@
         copyIn(d);
     }
 }
+// END Android-changed: maintain old behaviour
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/digests/MD5Digest.java b/bcprov/src/main/java/org/bouncycastle/crypto/digests/MD5Digest.java
index 417c4ba..6f8cbf3 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/digests/MD5Digest.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/digests/MD5Digest.java
@@ -1,3 +1,4 @@
+// BEGIN Android-changed: maintain old behaviour
 package org.bouncycastle.crypto.digests;
 
 
@@ -359,3 +360,4 @@
         return state;
     }
 }
+// END Android-changed: maintain old behaviour
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/digests/PhotonBeetleDigest.java b/bcprov/src/main/java/org/bouncycastle/crypto/digests/PhotonBeetleDigest.java
new file mode 100644
index 0000000..853a93d
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/digests/PhotonBeetleDigest.java
@@ -0,0 +1,219 @@
+package org.bouncycastle.crypto.digests;
+
+import java.io.ByteArrayOutputStream;
+
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.OutputLengthException;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.Bytes;
+
+/**
+ * Photon-Beetle, https://www.isical.ac.in/~lightweight/beetle/
+ * https://csrc.nist.gov/CSRC/media/Projects/lightweight-cryptography/documents/finalist-round/updated-spec-doc/photon-beetle-spec-final.pdf
+ * <p>
+ * Photon-Beetle with reference to C Reference Impl from: https://github.com/PHOTON-Beetle/Software
+ * </p>
+ */
+public class PhotonBeetleDigest
+    implements Digest
+{
+    private byte[] state;
+    private byte[][] state_2d;
+    private ByteArrayOutputStream buffer = new ByteArrayOutputStream();
+    private final int INITIAL_RATE_INBYTES = 16;
+    private int RATE_INBYTES = 4;
+    private int SQUEEZE_RATE_INBYTES = 16;
+    private int STATE_INBYTES = 32;
+    private int TAG_INBYTES = 32;
+    private int LAST_THREE_BITS_OFFSET = 5;
+    private int ROUND = 12;
+    private int D = 8;
+    private int Dq = 3;
+    private int Dr = 7;
+    private int DSquare = 64;
+    private int S = 4;
+    private int S_1 = 3;
+    private byte[][] RC = {//[D][12]
+        {1, 3, 7, 14, 13, 11, 6, 12, 9, 2, 5, 10},
+        {0, 2, 6, 15, 12, 10, 7, 13, 8, 3, 4, 11},
+        {2, 0, 4, 13, 14, 8, 5, 15, 10, 1, 6, 9},
+        {6, 4, 0, 9, 10, 12, 1, 11, 14, 5, 2, 13},
+        {14, 12, 8, 1, 2, 4, 9, 3, 6, 13, 10, 5},
+        {15, 13, 9, 0, 3, 5, 8, 2, 7, 12, 11, 4},
+        {13, 15, 11, 2, 1, 7, 10, 0, 5, 14, 9, 6},
+        {9, 11, 15, 6, 5, 3, 14, 4, 1, 10, 13, 2}
+    };
+    private byte[][] MixColMatrix = { //[D][D]
+        {2, 4, 2, 11, 2, 8, 5, 6},
+        {12, 9, 8, 13, 7, 7, 5, 2},
+        {4, 4, 13, 13, 9, 4, 13, 9},
+        {1, 6, 5, 1, 12, 13, 15, 14},
+        {15, 12, 9, 13, 14, 5, 14, 13},
+        {9, 14, 5, 15, 4, 12, 9, 6},
+        {12, 2, 2, 10, 3, 1, 1, 14},
+        {15, 1, 13, 10, 5, 10, 2, 3}
+    };
+
+    private byte[] sbox = {12, 5, 6, 11, 9, 0, 10, 13, 3, 14, 15, 8, 4, 7, 1, 2};
+
+    public PhotonBeetleDigest()
+    {
+        state = new byte[STATE_INBYTES];
+        state_2d = new byte[D][D];
+    }
+
+    @Override
+    public String getAlgorithmName()
+    {
+        return "Photon-Beetle Hash";
+    }
+
+    @Override
+    public int getDigestSize()
+    {
+        return TAG_INBYTES;
+    }
+
+    @Override
+    public void update(byte input)
+    {
+        buffer.write(input);
+    }
+
+    @Override
+    public void update(byte[] input, int inOff, int len)
+    {
+        if ((inOff + len) > input.length)
+        {
+            throw new DataLengthException("input buffer too short");
+        }
+        buffer.write(input, inOff, len);
+    }
+
+    @Override
+    public int doFinal(byte[] output, int outOff)
+    {
+        if (32 + outOff > output.length)
+        {
+            throw new OutputLengthException("output buffer is too short");
+        }
+        byte[] input = buffer.toByteArray();
+        int inlen = input.length;
+        if (inlen == 0)
+        {
+            state[STATE_INBYTES - 1] ^= 1 << LAST_THREE_BITS_OFFSET;
+        }
+        else if (inlen <= INITIAL_RATE_INBYTES)
+        {
+            System.arraycopy(input, 0, state, 0, inlen);
+            if (inlen < INITIAL_RATE_INBYTES)
+            {
+                state[inlen] ^= 0x01; // ozs
+            }
+            state[STATE_INBYTES - 1] ^= (inlen < INITIAL_RATE_INBYTES ? (byte)1 : (byte)2) << LAST_THREE_BITS_OFFSET;
+        }
+        else
+        {
+            System.arraycopy(input, 0, state, 0, INITIAL_RATE_INBYTES);
+            inlen -= INITIAL_RATE_INBYTES;
+            int Dlen_inblocks = (inlen + RATE_INBYTES - 1) / RATE_INBYTES;
+            int i, LastDBlocklen;
+            for (i = 0; i < Dlen_inblocks - 1; i++)
+            {
+                PHOTON_Permutation();
+                Bytes.xorTo(RATE_INBYTES, input, INITIAL_RATE_INBYTES + i * RATE_INBYTES, state, 0);
+            }
+            PHOTON_Permutation();
+            LastDBlocklen = inlen - i * RATE_INBYTES;
+            Bytes.xorTo(LastDBlocklen, input, INITIAL_RATE_INBYTES + i * RATE_INBYTES, state, 0);
+            if (LastDBlocklen < RATE_INBYTES)
+            {
+                state[LastDBlocklen] ^= 0x01; // ozs
+            }
+            state[STATE_INBYTES - 1] ^= (inlen % RATE_INBYTES == 0 ? (byte)1 : (byte)2) << LAST_THREE_BITS_OFFSET;
+        }
+        PHOTON_Permutation();
+        System.arraycopy(state, 0, output, outOff, SQUEEZE_RATE_INBYTES);
+        PHOTON_Permutation();
+        System.arraycopy(state, 0, output, outOff + SQUEEZE_RATE_INBYTES, TAG_INBYTES - SQUEEZE_RATE_INBYTES);
+        return TAG_INBYTES;
+    }
+
+    @Override
+    public void reset()
+    {
+        buffer.reset();
+        Arrays.fill(state, (byte)0);
+    }
+
+    void PHOTON_Permutation()
+    {
+        int i, j, k, l;
+        for (i = 0; i < DSquare; i++)
+        {
+            state_2d[i >>> Dq][i & Dr] = (byte)(((state[i >> 1] & 0xFF) >>> (4 * (i & 1))) & 0xf);
+        }
+        for (int round = 0; round < ROUND; round++)
+        {
+            //AddKey
+            for (i = 0; i < D; i++)
+            {
+                state_2d[i][0] ^= RC[i][round];
+            }
+            //SubCell
+            for (i = 0; i < D; i++)
+            {
+                for (j = 0; j < D; j++)
+                {
+                    state_2d[i][j] = sbox[state_2d[i][j]];
+                }
+            }
+            //ShiftRow
+            for (i = 1; i < D; i++)
+            {
+                System.arraycopy(state_2d[i], 0, state, 0, D);
+                System.arraycopy(state, i, state_2d[i], 0, D - i);
+                System.arraycopy(state, 0, state_2d[i], D - i, i);
+            }
+            //MixColumn
+            for (j = 0; j < D; j++)
+            {
+                for (i = 0; i < D; i++)
+                {
+                    byte sum = 0;
+                    for (k = 0; k < D; k++)
+                    {
+                        int x = MixColMatrix[i][k], ret = 0, b = state_2d[k][j];
+                        for (l = 0; l < S; l++)
+                        {
+                            if (((b >>> l) & 1) != 0)
+                            {
+                                ret ^= x;
+                            }
+                            if (((x >>> S_1) & 1) != 0)
+                            {
+                                x <<= 1;
+                                x ^= 0x3;
+                            }
+                            else
+                            {
+                                x <<= 1;
+                            }
+                        }
+                        sum ^= ret & 15;
+                    }
+                    state[i] = sum;
+                }
+                for (i = 0; i < D; i++)
+                {
+                    state_2d[i][j] = state[i];
+                }
+            }
+        }
+        for (i = 0; i < DSquare; i += 2)
+        {
+            state[i >>> 1] = (byte)(((state_2d[i >>> Dq][i & Dr] & 0xf)) | ((state_2d[i >>> Dq][(i + 1) & Dr] & 0xf) << 4));
+        }
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/digests/SHA1Digest.java b/bcprov/src/main/java/org/bouncycastle/crypto/digests/SHA1Digest.java
index 4290d20..25df8ca 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/digests/SHA1Digest.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/digests/SHA1Digest.java
@@ -1,3 +1,4 @@
+// BEGIN Android-changed: maintain old behaviour
 package org.bouncycastle.crypto.digests;
 
 import org.bouncycastle.util.Memoable;
@@ -348,6 +349,4 @@
     }
 }
 
-
-
-
+// END Android-changed: maintain old behaviour
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/digests/SHA224Digest.java b/bcprov/src/main/java/org/bouncycastle/crypto/digests/SHA224Digest.java
index c0fbd45..0127986 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/digests/SHA224Digest.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/digests/SHA224Digest.java
@@ -1,3 +1,4 @@
+// BEGIN Android-changed: maintain old behaviour
 package org.bouncycastle.crypto.digests;
 
 
@@ -358,4 +359,4 @@
         return state;
     }
 }
-
+// END Android-changed: maintain old behaviour
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/digests/SHA256Digest.java b/bcprov/src/main/java/org/bouncycastle/crypto/digests/SHA256Digest.java
index 0fb1050..84cf094 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/digests/SHA256Digest.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/digests/SHA256Digest.java
@@ -1,6 +1,12 @@
+// BEGIN Android-changed: adapt to old version of GeneralDigest
 package org.bouncycastle.crypto.digests;
 
 
+import org.bouncycastle.crypto.CryptoServiceProperties;
+import org.bouncycastle.crypto.CryptoServicePurpose;
+import org.bouncycastle.crypto.CryptoServicesRegistrar;
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.SavableDigest;
 import org.bouncycastle.util.Memoable;
 import org.bouncycastle.util.Pack;
 
@@ -18,20 +24,60 @@
  */
 public class SHA256Digest
     extends GeneralDigest
-    implements EncodableDigest
+    implements SavableDigest
 {
     private static final int    DIGEST_LENGTH = 32;
+    protected final CryptoServicePurpose purpose;
 
     private int     H1, H2, H3, H4, H5, H6, H7, H8;
 
     private int[]   X = new int[64];
     private int     xOff;
 
+    public static SavableDigest newInstance()
+    {
+        return new SHA256Digest();
+    }
+
+    public static SavableDigest newInstance(CryptoServicePurpose purpose)
+    {
+        return new SHA256Digest(purpose);
+    }
+
+    public static SavableDigest newInstance(Digest digest)
+    {
+        if (digest instanceof SHA256Digest)
+        {
+            return new SHA256Digest((SHA256Digest) digest);
+        }
+
+        throw new IllegalArgumentException("receiver digest not available for input type " + (digest != null ? digest.getClass().getName() : "null"));
+    }
+
+    public static SavableDigest newInstance(byte[] encoded)
+    {
+        return new SHA256Digest(encoded);
+    }
+
     /**
      * Standard constructor
      */
     public SHA256Digest()
     {
+        this(CryptoServicePurpose.ANY);
+    }
+
+    /**
+     * Standard constructor, with purpose
+     */
+    public SHA256Digest(CryptoServicePurpose purpose)
+    {
+        super();
+
+        this.purpose = purpose;
+
+        CryptoServicesRegistrar.checkConstraints(cryptoServiceProperties());
+
         reset();
     }
 
@@ -43,6 +89,8 @@
     {
         super(t);
 
+        this.purpose = CryptoServicePurpose.ANY;
+
         copyIn(t);
     }
 
@@ -72,6 +120,8 @@
     {
         super(encodedState);
 
+        this.purpose = CryptoServicePurpose.ANY;
+
         H1 = Pack.bigEndianToInt(encodedState, 16);
         H2 = Pack.bigEndianToInt(encodedState, 20);
         H3 = Pack.bigEndianToInt(encodedState, 24);
@@ -103,13 +153,7 @@
         byte[]  in,
         int     inOff)
     {
-        // Note: Inlined for performance
-//        X[xOff] = Pack.bigEndianToInt(in, inOff);
-        int n = in[inOff] << 24;
-        n |= (in[++inOff] & 0xff) << 16;
-        n |= (in[++inOff] & 0xff) << 8;
-        n |= (in[++inOff] & 0xff);
-        X[xOff] = n;
+        X[xOff] = Pack.bigEndianToInt(in, inOff);
 
         if (++xOff == 16)
         {
@@ -129,9 +173,7 @@
         X[15] = (int)(bitLength & 0xffffffff);
     }
 
-    public int doFinal(
-        byte[]  out,
-        int     outOff)
+    public int doFinal(byte[] out, int outOff)
     {
         finish();
 
@@ -199,7 +241,7 @@
         int     g = H7;
         int     h = H8;
 
-        int t = 0;     
+        int t = 0;
         for(int i = 0; i < 8; i ++)
         {
             // t = 8 * i
@@ -332,7 +374,7 @@
 
     public byte[] getEncodedState()
     {
-        byte[] state = new byte[52 + xOff * 4];
+        byte[] state = new byte[52 + xOff * 4 + 1];
 
         super.populateState(state);
 
@@ -351,7 +393,14 @@
             Pack.intToBigEndian(X[i], state, 52 + (i * 4));
         }
 
+        state[state.length - 1] = (byte)purpose.ordinal();
+
         return state;
     }
-}
 
+    protected CryptoServiceProperties cryptoServiceProperties()
+    {
+        return Utils.getDefaultProperties(this, 256, purpose);
+    }
+}
+// END Android-changed: adapt to old version of GeneralDigest
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/digests/SHA384Digest.java b/bcprov/src/main/java/org/bouncycastle/crypto/digests/SHA384Digest.java
index 0c814b1..c69b578 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/digests/SHA384Digest.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/digests/SHA384Digest.java
@@ -1,5 +1,8 @@
 package org.bouncycastle.crypto.digests;
 
+import org.bouncycastle.crypto.CryptoServiceProperties;
+import org.bouncycastle.crypto.CryptoServicePurpose;
+import org.bouncycastle.crypto.CryptoServicesRegistrar;
 import org.bouncycastle.util.Memoable;
 import org.bouncycastle.util.Pack;
 
@@ -25,6 +28,19 @@
      */
     public SHA384Digest()
     {
+        this(CryptoServicePurpose.ANY);
+    }
+
+    /**
+     * Standard constructor, with purpose
+     */
+    public SHA384Digest(CryptoServicePurpose purpose)
+    {
+        super(purpose);
+
+        CryptoServicesRegistrar.checkConstraints(cryptoServiceProperties());
+
+        reset();
     }
 
     /**
@@ -34,6 +50,8 @@
     public SHA384Digest(SHA384Digest t)
     {
         super(t);
+
+        CryptoServicesRegistrar.checkConstraints(cryptoServiceProperties());
     }
 
     /**
@@ -43,7 +61,11 @@
      */
     public SHA384Digest(byte[] encodedState)
     {
+        super(CryptoServicePurpose.values()[encodedState[encodedState.length - 1]]);
+
         restoreState(encodedState);
+
+        CryptoServicesRegistrar.checkConstraints(cryptoServiceProperties());
     }
 
     public String getAlgorithmName()
@@ -109,8 +131,16 @@
 
     public byte[] getEncodedState()
     {
-        byte[] encoded = new byte[getEncodedStateSize()];
+        byte[] encoded = new byte[getEncodedStateSize() + 1];
         super.populateState(encoded);
+
+        encoded[encoded.length - 1] = (byte)purpose.ordinal();
+
         return encoded;
     }
+
+    protected CryptoServiceProperties cryptoServiceProperties()
+    {
+        return Utils.getDefaultProperties(this, 256, purpose);
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/digests/SHA512Digest.java b/bcprov/src/main/java/org/bouncycastle/crypto/digests/SHA512Digest.java
index dea3d88..0fab188 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/digests/SHA512Digest.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/digests/SHA512Digest.java
@@ -1,5 +1,8 @@
 package org.bouncycastle.crypto.digests;
 
+import org.bouncycastle.crypto.CryptoServiceProperties;
+import org.bouncycastle.crypto.CryptoServicePurpose;
+import org.bouncycastle.crypto.CryptoServicesRegistrar;
 import org.bouncycastle.util.Memoable;
 import org.bouncycastle.util.Pack;
 
@@ -25,6 +28,19 @@
      */
     public SHA512Digest()
     {
+        this(CryptoServicePurpose.ANY);
+    }
+
+    /**
+     * Standard constructor, with purpose
+     */
+    public SHA512Digest(CryptoServicePurpose purpose)
+    {
+        super(purpose);
+
+        CryptoServicesRegistrar.checkConstraints(cryptoServiceProperties());
+
+        reset();
     }
 
     /**
@@ -34,6 +50,8 @@
     public SHA512Digest(SHA512Digest t)
     {
         super(t);
+
+        CryptoServicesRegistrar.checkConstraints(cryptoServiceProperties());
     }
 
     /**
@@ -43,7 +61,11 @@
      */
     public SHA512Digest(byte[] encodedState)
     {
+        super(CryptoServicePurpose.values()[encodedState[encodedState.length - 1]]);
+
         restoreState(encodedState);
+
+        CryptoServicesRegistrar.checkConstraints(cryptoServiceProperties());
     }
 
     public String getAlgorithmName()
@@ -111,9 +133,17 @@
 
     public byte[] getEncodedState()
     {
-        byte[] encoded = new byte[getEncodedStateSize()];
+        byte[] encoded = new byte[getEncodedStateSize() + 1];
         super.populateState(encoded);
+
+        encoded[encoded.length - 1] = (byte)purpose.ordinal();
+
         return encoded;
     }
+
+    protected CryptoServiceProperties cryptoServiceProperties()
+    {
+        return Utils.getDefaultProperties(this, 256, purpose);
+    }
 }
 
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/digests/Utils.java b/bcprov/src/main/java/org/bouncycastle/crypto/digests/Utils.java
new file mode 100644
index 0000000..cad69c3
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/digests/Utils.java
@@ -0,0 +1,97 @@
+package org.bouncycastle.crypto.digests;
+
+import org.bouncycastle.crypto.CryptoServiceProperties;
+import org.bouncycastle.crypto.CryptoServicePurpose;
+import org.bouncycastle.crypto.Digest;
+
+class Utils
+{
+    static CryptoServiceProperties getDefaultProperties(Digest digest, CryptoServicePurpose purpose)
+    {
+        return new DefaultProperties(digest.getDigestSize() * 4, digest.getAlgorithmName(), purpose);
+    }
+
+    static CryptoServiceProperties getDefaultProperties(Digest digest, int prfBitsOfSecurity, CryptoServicePurpose purpose)
+    {
+        return new DefaultPropertiesWithPRF(digest.getDigestSize() * 4, prfBitsOfSecurity, digest.getAlgorithmName(), purpose);
+    }
+
+    // Service Definitions
+    private static class DefaultPropertiesWithPRF
+        implements CryptoServiceProperties
+    {
+        private final int bitsOfSecurity;
+        private final int prfBitsOfSecurity;
+        private final String algorithmName;
+        private final CryptoServicePurpose purpose;
+
+        public DefaultPropertiesWithPRF(int bitsOfSecurity, int prfBitsOfSecurity, String algorithmName, CryptoServicePurpose purpose)
+        {
+            this.bitsOfSecurity = bitsOfSecurity;
+            this.prfBitsOfSecurity = prfBitsOfSecurity;
+            this.algorithmName = algorithmName;
+            this.purpose = purpose;
+        }
+
+        public int bitsOfSecurity()
+        {
+            if (purpose == CryptoServicePurpose.PRF)
+            {
+                return prfBitsOfSecurity;
+            }
+
+            return bitsOfSecurity;
+        }
+
+        public String getServiceName()
+        {
+            return algorithmName;
+        }
+
+        public CryptoServicePurpose getPurpose()
+        {
+            return purpose;
+        }
+
+        public Object getParams()
+        {
+            return null;
+        }
+    }
+
+    // Service Definitions
+    private static class DefaultProperties
+        implements CryptoServiceProperties
+    {
+        private final int bitsOfSecurity;
+        private final String algorithmName;
+        private final CryptoServicePurpose purpose;
+
+        public DefaultProperties(int bitsOfSecurity, String algorithmName, CryptoServicePurpose purpose)
+        {
+            this.bitsOfSecurity = bitsOfSecurity;
+            this.algorithmName = algorithmName;
+            this.purpose = purpose;
+        }
+
+        public int bitsOfSecurity()
+        {
+            return bitsOfSecurity;
+        }
+
+        public String getServiceName()
+        {
+            return algorithmName;
+        }
+
+        public CryptoServicePurpose getPurpose()
+        {
+            return purpose;
+        }
+
+        public Object getParams()
+        {
+            return null;
+        }
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/digests/XofUtils.java b/bcprov/src/main/java/org/bouncycastle/crypto/digests/XofUtils.java
index 6e06a47..d9dbab9 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/digests/XofUtils.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/digests/XofUtils.java
@@ -1,27 +1,29 @@
 package org.bouncycastle.crypto.digests;
 
+import org.bouncycastle.util.Arrays;
+
 public class XofUtils
 {
     public static byte[] leftEncode(long strLen)
     {
-    	byte n = 1;
+        byte n = 1;
 
         long v = strLen;
-    	while ((v >>= 8) != 0)
+        while ((v >>= 8) != 0)
         {
-    		n++;
-    	}
+            n++;
+        }
 
         byte[] b = new byte[n + 1];
 
-    	b[0] = n;
+        b[0] = n;
 
-    	for (int i = 1; i <= n; i++)
-    	{
-    		b[i] = (byte)(strLen >> (8 * (n - i)));
-    	}
+        for (int i = 1; i <= n; i++)
+        {
+            b[i] = (byte)(strLen >> (8 * (n - i)));
+        }
 
-    	return b;
+        return b;
     }
 
     public static byte[] rightEncode(long strLen)
@@ -45,4 +47,18 @@
 
         return b;
     }
+
+    static byte[] encode(byte X)
+    {
+        return Arrays.concatenate(XofUtils.leftEncode(8), new byte[] { X });
+    }
+
+    static byte[] encode(byte[] in, int inOff, int len)
+    {
+        if (in.length == len)
+        {
+            return Arrays.concatenate(XofUtils.leftEncode(len * 8), in);
+        }
+        return Arrays.concatenate(XofUtils.leftEncode(len * 8), Arrays.copyOfRange(in, inOff, inOff + len));
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/digests/XoodyakDigest.java b/bcprov/src/main/java/org/bouncycastle/crypto/digests/XoodyakDigest.java
new file mode 100644
index 0000000..476dee3
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/digests/XoodyakDigest.java
@@ -0,0 +1,206 @@
+package org.bouncycastle.crypto.digests;
+
+import java.io.ByteArrayOutputStream;
+
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.OutputLengthException;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.Pack;
+
+/**
+ * Xoodyak v1, https://csrc.nist.gov/CSRC/media/Projects/lightweight-cryptography/documents/finalist-round/updated-spec-doc/xoodyak-spec-final.pdf
+ * <p>
+ * Xoodyak with reference to C Reference Impl from: https://github.com/XKCP/XKCP
+ * </p>
+ */
+
+public class XoodyakDigest
+    implements Digest
+{
+    private byte[] state;
+    private int phase;
+    private MODE mode;
+    private int Rabsorb;
+    private final int f_bPrime = 48;
+    private final int Rhash = 16;
+    private final int PhaseDown = 1;
+    private final int PhaseUp = 2;
+    private final int NLANES = 12;
+    private final int NROWS = 3;
+    private final int NCOLUMS = 4;
+    private final int MAXROUNDS = 12;
+    private final int TAGLEN = 16;
+    private final int[] RC = {0x00000058, 0x00000038, 0x000003C0, 0x000000D0, 0x00000120, 0x00000014, 0x00000060,
+        0x0000002C, 0x00000380, 0x000000F0, 0x000001A0, 0x00000012};
+    private final ByteArrayOutputStream buffer = new ByteArrayOutputStream();
+
+    enum MODE
+    {
+        ModeHash,
+        ModeKeyed
+    }
+
+    public XoodyakDigest()
+    {
+        state = new byte[48];
+        reset();
+    }
+
+    @Override
+    public String getAlgorithmName()
+    {
+        return "Xoodyak Hash";
+    }
+
+    @Override
+    public int getDigestSize()
+    {
+        return 32;
+    }
+
+    @Override
+    public void update(byte input)
+    {
+        buffer.write(input);
+    }
+
+    @Override
+    public void update(byte[] input, int inOff, int len)
+    {
+        if ((inOff + len) > input.length)
+        {
+            throw new DataLengthException("input buffer too short");
+        }
+        buffer.write(input, inOff, len);
+
+    }
+
+    @Override
+    public int doFinal(byte[] output, int outOff)
+    {
+        if (32 + outOff > output.length)
+        {
+            throw new OutputLengthException("output buffer is too short");
+        }
+        byte[] input = buffer.toByteArray();
+        int inOff = 0;
+        int len = buffer.size();
+        int Cd = 0x03;
+        int splitLen;
+        do
+        {
+            if (phase != PhaseUp)
+            {
+                Up(null, 0, 0, 0);
+            }
+            splitLen = Math.min(len, Rabsorb);
+            Down(input, inOff, splitLen, Cd);
+            Cd = 0;
+            inOff += splitLen;
+            len -= splitLen;
+        }
+        while (len != 0);
+        Up(output, outOff, TAGLEN, 0x40);
+        Down(null, 0, 0, 0);
+        Up(output, outOff + TAGLEN, TAGLEN, 0);
+        return 32;
+    }
+
+    @Override
+    public void reset()
+    {
+        Arrays.fill(state, (byte)0);
+        phase = PhaseUp;
+        mode = MODE.ModeHash;
+        Rabsorb = Rhash;
+        buffer.reset();
+    }
+
+    private void Up(byte[] Yi, int YiOff, int YiLen, int Cu)
+    {
+        if (mode != MODE.ModeHash)
+        {
+            state[f_bPrime - 1] ^= Cu;
+        }
+        int[] a = new int[NLANES];
+        Pack.littleEndianToInt(state, 0, a, 0, a.length);
+        int x, y;
+        int[] b = new int[NLANES];
+        int[] p = new int[NCOLUMS];
+        int[] e = new int[NCOLUMS];
+        for (int i = 0; i < MAXROUNDS; ++i)
+        {
+            /* Theta: Column Parity Mixer */
+            for (x = 0; x < NCOLUMS; ++x)
+            {
+                p[x] = a[index(x, 0)] ^ a[index(x, 1)] ^ a[index(x, 2)];
+            }
+            for (x = 0; x < NCOLUMS; ++x)
+            {
+                y = p[(x + 3) & 3];
+                e[x] = ROTL32(y, 5) ^ ROTL32(y, 14);
+            }
+            for (x = 0; x < NCOLUMS; ++x)
+            {
+                for (y = 0; y < NROWS; ++y)
+                {
+                    a[index(x, y)] ^= e[x];
+                }
+            }
+            /* Rho-west: plane shift */
+            for (x = 0; x < NCOLUMS; ++x)
+            {
+                b[index(x, 0)] = a[index(x, 0)];
+                b[index(x, 1)] = a[index(x + 3, 1)];
+                b[index(x, 2)] = ROTL32(a[index(x, 2)], 11);
+            }
+            /* Iota: round ant */
+            b[0] ^= RC[i];
+            /* Chi: non linear layer */
+            for (x = 0; x < NCOLUMS; ++x)
+            {
+                for (y = 0; y < NROWS; ++y)
+                {
+                    a[index(x, y)] = b[index(x, y)] ^ (~b[index(x, y + 1)] & b[index(x, y + 2)]);
+                }
+            }
+            /* Rho-east: plane shift */
+            for (x = 0; x < NCOLUMS; ++x)
+            {
+                b[index(x, 0)] = a[index(x, 0)];
+                b[index(x, 1)] = ROTL32(a[index(x, 1)], 1);
+                b[index(x, 2)] = ROTL32(a[index(x + 2, 2)], 8);
+            }
+            System.arraycopy(b, 0, a, 0, NLANES);
+        }
+        Pack.intToLittleEndian(a, 0, a.length, state, 0);
+        phase = PhaseUp;
+        if (Yi != null)
+        {
+            System.arraycopy(state, 0, Yi, YiOff, YiLen);
+        }
+    }
+
+    void Down(byte[] Xi, int XiOff, int XiLen, int Cd)
+    {
+        for (int i = 0; i < XiLen; i++)
+        {
+            state[i] ^= Xi[XiOff++];
+        }
+        state[XiLen] ^= 0x01;
+        state[f_bPrime - 1] ^= (mode == MODE.ModeHash) ? (Cd & 0x01) : Cd;
+        phase = PhaseDown;
+    }
+
+    private int index(int x, int y)
+    {
+        return (((y % NROWS) * NCOLUMS) + ((x) % NCOLUMS));
+    }
+
+    private int ROTL32(int a, int offset)
+    {
+        return (a << (offset & 31)) ^ (a >>> ((32 - (offset)) & 31));
+    }
+
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/ec/CustomNamedCurves.java b/bcprov/src/main/java/org/bouncycastle/crypto/ec/CustomNamedCurves.java
index 43ea0a7..910240b 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/ec/CustomNamedCurves.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/ec/CustomNamedCurves.java
@@ -83,10 +83,15 @@
      *
     static X9ECParametersHolder curve25519 = new X9ECParametersHolder()
     {
+        protected ECCurve createCurve()
+        {
+            return configureCurve(new Curve25519());
+        }
+
         protected X9ECParameters createParameters()
         {
             byte[] S = null;
-            ECCurve curve = configureCurve(new Curve25519());
+            ECCurve curve = getCurve();
 
             /*
              * NOTE: Curve25519 was specified in Montgomery form. Rewriting in Weierstrass form
@@ -109,10 +114,15 @@
      *
     static X9ECParametersHolder secp128r1 = new X9ECParametersHolder()
     {
+        protected ECCurve createCurve()
+        {
+            return configureCurve(new SecP128R1Curve());
+        }
+
         protected X9ECParameters createParameters()
         {
             byte[] S = Hex.decodeStrict("000E0D4D696E6768756151750CC03A4473D03679");
-            ECCurve curve = configureCurve(new SecP128R1Curve());
+            ECCurve curve = getCurve();
             X9ECPoint G = configureBasepoint(curve,
                 "04161FF7528B899B2D0C28607CA52C5B86CF5AC8395BAFEB13C02DA292DDED7A83");
             return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
@@ -124,9 +134,8 @@
      *
     static X9ECParametersHolder secp160k1 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
-            byte[] S = null;
             GLVTypeBParameters glv = new GLVTypeBParameters(
                 new BigInteger("9ba48cba5ebcb9b6bd33b92830b2a2e0e192f10a", 16),
                 new BigInteger("c39c6c3b3a36d7701b9c71a1f5804ae5d0003f4", 16),
@@ -140,7 +149,13 @@
                     new BigInteger("9162fbe73984472a0a9d0590", 16),
                     new BigInteger("96341f1138933bc2f503fd44", 16),
                     176));
-            ECCurve curve = configureCurveGLV(new SecP160K1Curve(), glv);
+            return configureCurveGLV(new SecP160K1Curve(), glv);
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = null;
+            ECCurve curve = getCurve();
             X9ECPoint G = configureBasepoint(curve,
                 "043B4C382CE37AA192A4019E763036F4F5DD4D7EBB938CF935318FDCED6BC28286531733C3F03C4FEE");
             return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
@@ -152,10 +167,15 @@
      *
     static X9ECParametersHolder secp160r1 = new X9ECParametersHolder()
     {
+        protected ECCurve createCurve()
+        {
+            return configureCurve(new SecP160R1Curve());
+        }
+
         protected X9ECParameters createParameters()
         {
             byte[] S = Hex.decodeStrict("1053CDE42C14D696E67687561517533BF3F83345");
-            ECCurve curve = configureCurve(new SecP160R1Curve());
+            ECCurve curve = getCurve();
             X9ECPoint G = configureBasepoint(curve,
                 "044A96B5688EF573284664698968C38BB913CBFC8223A628553168947D59DCC912042351377AC5FB32");
             return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
@@ -167,10 +187,15 @@
      *
     static X9ECParametersHolder secp160r2 = new X9ECParametersHolder()
     {
+        protected ECCurve createCurve()
+        {
+            return configureCurve(new SecP160R2Curve());
+        }
+
         protected X9ECParameters createParameters()
         {
             byte[] S = Hex.decodeStrict("B99B99B099B323E02709A4D696E6768756151751");
-            ECCurve curve = configureCurve(new SecP160R2Curve());
+            ECCurve curve = getCurve();
             X9ECPoint G = configureBasepoint(curve,
                 "0452DCB034293A117E1F4FF11B30F7199D3144CE6DFEAFFEF2E331F296E071FA0DF9982CFEA7D43F2E");
             return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
@@ -184,9 +209,8 @@
      */
     static X9ECParametersHolder secp192k1 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
-            byte[] S = null;
             GLVTypeBParameters glv = new GLVTypeBParameters(
                 new BigInteger("bb85691939b869c1d087f601554b96b80cb4f55b35f433c2", 16),
                 new BigInteger("3d84f26c12238d7b4f3d516613c1759033b1a5800175d0b1", 16),
@@ -200,7 +224,13 @@
                     new BigInteger("71169be7330b3038edb025f1d0f9", 16),
                     new BigInteger("b3fb3400dec5c4adceb8655d4c94", 16),
                     208));
-            ECCurve curve = configureCurveGLV(new SecP192K1Curve(), glv);
+            return configureCurveGLV(new SecP192K1Curve(), glv);
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = null;
+            ECCurve curve = getCurve();
             X9ECPoint G = configureBasepoint(curve,
                 "04DB4FF10EC057E9AE26B07D0280B7F4341DA5D1B1EAE06C7D9B2F2F6D9C5628A7844163D015BE86344082AA88D95E2F9D");
             return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
@@ -212,10 +242,15 @@
      */
     static X9ECParametersHolder secp192r1 = new X9ECParametersHolder()
     {
+        protected ECCurve createCurve()
+        {
+            return configureCurve(new SecP192R1Curve());
+        }
+
         protected X9ECParameters createParameters()
         {
             byte[] S = Hex.decodeStrict("3045AE6FC8422F64ED579528D38120EAE12196D5");
-            ECCurve curve = configureCurve(new SecP192R1Curve());
+            ECCurve curve = getCurve();
             X9ECPoint G = configureBasepoint(curve,
                 "04188DA80EB03090F67CBF20EB43A18800F4FF0AFD82FF101207192B95FFC8DA78631011ED6B24CDD573F977A11E794811");
             return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
@@ -227,9 +262,8 @@
      */
     static X9ECParametersHolder secp224k1 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
-            byte[] S = null;
             GLVTypeBParameters glv = new GLVTypeBParameters(
                 new BigInteger("fe0e87005b4e83761908c5131d552a850b3f58b749c37cf5b84d6768", 16),
                 new BigInteger("60dcd2104c4cbc0be6eeefc2bdd610739ec34e317f9b33046c9e4788", 16),
@@ -243,7 +277,13 @@
                     new BigInteger("6b8cf07d4ca75c88957d9d67059037a4", 16),
                     new BigInteger("b8adf1378a6eb73409fa6c9c637ba7f5", 16),
                     240));
-            ECCurve curve = configureCurveGLV(new SecP224K1Curve(), glv);
+            return configureCurveGLV(new SecP224K1Curve(), glv);
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = null;
+            ECCurve curve = getCurve();
             X9ECPoint G = configureBasepoint(curve,
                 "04A1455B334DF099DF30FC28A169A467E9E47075A90F7E650EB6B7A45C7E089FED7FBA344282CAFBD6F7E319F7C0B0BD59E2CA4BDB556D61A5");
             return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
@@ -255,10 +295,15 @@
      */
     static X9ECParametersHolder secp224r1 = new X9ECParametersHolder()
     {
+        protected ECCurve createCurve()
+        {
+            return configureCurve(new SecP224R1Curve());
+        }
+
         protected X9ECParameters createParameters()
         {
             byte[] S = Hex.decodeStrict("BD71344799D5C7FCDC45B59FA3B9AB8F6A948BC5");
-            ECCurve curve = configureCurve(new SecP224R1Curve());
+            ECCurve curve = getCurve();
             X9ECPoint G = configureBasepoint(curve,
                 "04B70E0CBD6BB4BF7F321390B94A03C1D356C21122343280D6115C1D21BD376388B5F723FB4C22DFE6CD4375A05A07476444D5819985007E34");
             return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
@@ -270,9 +315,8 @@
      */
     static X9ECParametersHolder secp256k1 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
-            byte[] S = null;
             GLVTypeBParameters glv = new GLVTypeBParameters(
                 new BigInteger("7ae96a2b657c07106e64479eac3434e99cf0497512f58995c1396c28719501ee", 16),
                 new BigInteger("5363ad4cc05c30e0a5261c028812645a122e22ea20816678df02967c1b23bd72", 16),
@@ -286,7 +330,13 @@
                     new BigInteger("3086d221a7d46bcde86c90e49284eb153dab", 16),
                     new BigInteger("e4437ed6010e88286f547fa90abfe4c42212", 16),
                     272));
-            ECCurve curve = configureCurveGLV(new SecP256K1Curve(), glv);
+            return configureCurveGLV(new SecP256K1Curve(), glv);
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = null;
+            ECCurve curve = getCurve();
             X9ECPoint G = configureBasepoint(curve,
                 "0479BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8");
             return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
@@ -298,10 +348,15 @@
      */
     static X9ECParametersHolder secp256r1 = new X9ECParametersHolder()
     {
+        protected ECCurve createCurve()
+        {
+            return configureCurve(new SecP256R1Curve());
+        }
+
         protected X9ECParameters createParameters()
         {
             byte[] S = Hex.decodeStrict("C49D360886E704936A6678E1139D26B7819F7E90");
-            ECCurve curve = configureCurve(new SecP256R1Curve());
+            ECCurve curve = getCurve();
             X9ECPoint G = configureBasepoint(curve,
                 "046B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C2964FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5");
             return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
@@ -313,10 +368,15 @@
      */
     static X9ECParametersHolder secp384r1 = new X9ECParametersHolder()
     {
+        protected ECCurve createCurve()
+        {
+            return configureCurve(new SecP384R1Curve());
+        }
+
         protected X9ECParameters createParameters()
         {
             byte[] S = Hex.decodeStrict("A335926AA319A27A1D00896A6773A4827ACDAC73");
-            ECCurve curve = configureCurve(new SecP384R1Curve());
+            ECCurve curve = getCurve();
             X9ECPoint G = configureBasepoint(curve, "04"
                 + "AA87CA22BE8B05378EB1C71EF320AD746E1D3B628BA79B9859F741E082542A385502F25DBF55296C3A545E3872760AB7"
                 + "3617DE4A96262C6F5D9E98BF9292DC29F8F41DBD289A147CE9DA3113B5F0B8C00A60B1CE1D7E819D7A431D7C90EA0E5F");
@@ -329,10 +389,15 @@
      */
     static X9ECParametersHolder secp521r1 = new X9ECParametersHolder()
     {
+        protected ECCurve createCurve()
+        {
+            return configureCurve(new SecP521R1Curve());
+        }
+
         protected X9ECParameters createParameters()
         {
             byte[] S = Hex.decodeStrict("D09E8800291CB85396CC6717393284AAA0DA64BA");
-            ECCurve curve = configureCurve(new SecP521R1Curve());
+            ECCurve curve = getCurve();
             X9ECPoint G = configureBasepoint(curve, "04"
                 + "00C6858E06B70404E9CD9E3ECB662395B4429C648139053FB521F828AF606B4D3DBAA14B5E77EFE75928FE1DC127A2FFA8DE3348B3C1856A429BF97E7E31C2E5BD66"
                 + "011839296A789A3BC0045C8A5FB42C7D1BD998F54449579B446817AFBD17273E662C97EE72995EF42640C550B9013FAD0761353C7086A272C24088BE94769FD16650");
@@ -347,10 +412,15 @@
      *
     static X9ECParametersHolder sect113r1 = new X9ECParametersHolder()
     {
+        protected ECCurve createCurve()
+        {
+            return configureCurve(new SecT113R1Curve());
+        }
+
         protected X9ECParameters createParameters()
         {
             byte[] S = Hex.decodeStrict("10E723AB14D696E6768756151756FEBF8FCB49A9");
-            ECCurve curve = configureCurve(new SecT113R1Curve());
+            ECCurve curve = getCurve();
             X9ECPoint G = configureBasepoint(curve,
                 "04009D73616F35F4AB1407D73562C10F00A52830277958EE84D1315ED31886");
             return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
@@ -362,10 +432,15 @@
      *
     static X9ECParametersHolder sect113r2 = new X9ECParametersHolder()
     {
+        protected ECCurve createCurve()
+        {
+            return configureCurve(new SecT113R2Curve());
+        }
+
         protected X9ECParameters createParameters()
         {
             byte[] S = Hex.decodeStrict("10C0FB15760860DEF1EEF4D696E676875615175D");
-            ECCurve curve = configureCurve(new SecT113R2Curve());
+            ECCurve curve = getCurve();
             X9ECPoint G = configureBasepoint(curve,
                 "0401A57A6A7B26CA5EF52FCDB816479700B3ADC94ED1FE674C06E695BABA1D");
             return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
@@ -377,10 +452,15 @@
      *
     static X9ECParametersHolder sect131r1 = new X9ECParametersHolder()
     {
+        protected ECCurve createCurve()
+        {
+            return configureCurve(new SecT131R1Curve());
+        }
+
         protected X9ECParameters createParameters()
         {
             byte[] S = Hex.decodeStrict("4D696E676875615175985BD3ADBADA21B43A97E2");
-            ECCurve curve = configureCurve(new SecT131R1Curve());
+            ECCurve curve = getCurve();
             X9ECPoint G = configureBasepoint(curve,
                 "040081BAF91FDF9833C40F9C181343638399078C6E7EA38C001F73C8134B1B4EF9E150");
             return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
@@ -392,10 +472,15 @@
      *
     static X9ECParametersHolder sect131r2 = new X9ECParametersHolder()
     {
+        protected ECCurve createCurve()
+        {
+            return configureCurve(new SecT131R2Curve());
+        }
+
         protected X9ECParameters createParameters()
         {
             byte[] S = Hex.decodeStrict("985BD3ADBAD4D696E676875615175A21B43A97E3");
-            ECCurve curve = configureCurve(new SecT131R2Curve());
+            ECCurve curve = getCurve();
             X9ECPoint G = configureBasepoint(curve,
                 "040356DCD8F2F95031AD652D23951BB366A80648F06D867940A5366D9E265DE9EB240F");
             return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
@@ -407,10 +492,15 @@
      *
     static X9ECParametersHolder sect163k1 = new X9ECParametersHolder()
     {
+        protected ECCurve createCurve()
+        {
+            return configureCurve(new SecT163K1Curve());
+        }
+
         protected X9ECParameters createParameters()
         {
             byte[] S = null;
-            ECCurve curve = configureCurve(new SecT163K1Curve());
+            ECCurve curve = getCurve();
             X9ECPoint G = configureBasepoint(curve,
                 "0402FE13C0537BBC11ACAA07D793DE4E6D5E5C94EEE80289070FB05D38FF58321F2E800536D538CCDAA3D9");
             return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
@@ -422,10 +512,15 @@
      *
     static X9ECParametersHolder sect163r1 = new X9ECParametersHolder()
     {
+        protected ECCurve createCurve()
+        {
+            return configureCurve(new SecT163R1Curve());
+        }
+
         protected X9ECParameters createParameters()
         {
             byte[] S = Hex.decodeStrict("24B7B137C8A14D696E6768756151756FD0DA2E5C");
-            ECCurve curve = configureCurve(new SecT163R1Curve());
+            ECCurve curve = getCurve();
             X9ECPoint G = configureBasepoint(curve,
                 "040369979697AB43897789566789567F787A7876A65400435EDB42EFAFB2989D51FEFCE3C80988F41FF883");
             return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
@@ -437,10 +532,15 @@
      *
     static X9ECParametersHolder sect163r2 = new X9ECParametersHolder()
     {
+        protected ECCurve createCurve()
+        {
+            return configureCurve(new SecT163R2Curve());
+        }
+
         protected X9ECParameters createParameters()
         {
             byte[] S = Hex.decodeStrict("85E25BFE5C86226CDB12016F7553F9D0E693A268");
-            ECCurve curve = configureCurve(new SecT163R2Curve());
+            ECCurve curve = getCurve();
             X9ECPoint G = configureBasepoint(curve,
                 "0403F0EBA16286A2D57EA0991168D4994637E8343E3600D51FBC6C71A0094FA2CDD545B11C5C0C797324F1");
             return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
@@ -452,10 +552,15 @@
      *
     static X9ECParametersHolder sect193r1 = new X9ECParametersHolder()
     {
+        protected ECCurve createCurve()
+        {
+            return configureCurve(new SecT193R1Curve());
+        }
+
         protected X9ECParameters createParameters()
         {
             byte[] S = Hex.decodeStrict("103FAEC74D696E676875615175777FC5B191EF30");
-            ECCurve curve = configureCurve(new SecT193R1Curve());
+            ECCurve curve = getCurve();
             X9ECPoint G = configureBasepoint(curve,
                 "0401F481BC5F0FF84A74AD6CDF6FDEF4BF6179625372D8C0C5E10025E399F2903712CCF3EA9E3A1AD17FB0B3201B6AF7CE1B05");
             return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
@@ -467,10 +572,15 @@
      *
     static X9ECParametersHolder sect193r2 = new X9ECParametersHolder()
     {
+        protected ECCurve createCurve()
+        {
+            return configureCurve(new SecT193R2Curve());
+        }
+
         protected X9ECParameters createParameters()
         {
             byte[] S = Hex.decodeStrict("10B7B4D696E676875615175137C8A16FD0DA2211");
-            ECCurve curve = configureCurve(new SecT193R2Curve());
+            ECCurve curve = getCurve();
             X9ECPoint G = configureBasepoint(curve,
                 "0400D9B67D192E0367C803F39E1A7E82CA14A651350AAE617E8F01CE94335607C304AC29E7DEFBD9CA01F596F927224CDECF6C");
             return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
@@ -482,10 +592,15 @@
      *
     static X9ECParametersHolder sect233k1 = new X9ECParametersHolder()
     {
+        protected ECCurve createCurve()
+        {
+            return configureCurve(new SecT233K1Curve());
+        }
+
         protected X9ECParameters createParameters()
         {
             byte[] S = null;
-            ECCurve curve = configureCurve(new SecT233K1Curve());
+            ECCurve curve = getCurve();
             X9ECPoint G = configureBasepoint(curve,
                 "04017232BA853A7E731AF129F22FF4149563A419C26BF50A4C9D6EEFAD612601DB537DECE819B7F70F555A67C427A8CD9BF18AEB9B56E0C11056FAE6A3");
             return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
@@ -497,10 +612,15 @@
      *
     static X9ECParametersHolder sect233r1 = new X9ECParametersHolder()
     {
+        protected ECCurve createCurve()
+        {
+            return configureCurve(new SecT233R1Curve());
+        }
+
         protected X9ECParameters createParameters()
         {
             byte[] S = Hex.decodeStrict("74D59FF07F6B413D0EA14B344B20A2DB049B50C3");
-            ECCurve curve = configureCurve(new SecT233R1Curve());
+            ECCurve curve = getCurve();
             X9ECPoint G = configureBasepoint(curve,
                 "0400FAC9DFCBAC8313BB2139F1BB755FEF65BC391F8B36F8F8EB7371FD558B01006A08A41903350678E58528BEBF8A0BEFF867A7CA36716F7E01F81052");
             return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
@@ -512,10 +632,15 @@
      *
     static X9ECParametersHolder sect239k1 = new X9ECParametersHolder()
     {
+        protected ECCurve createCurve()
+        {
+            return configureCurve(new SecT239K1Curve());
+        }
+
         protected X9ECParameters createParameters()
         {
             byte[] S = null;
-            ECCurve curve = configureCurve(new SecT239K1Curve());
+            ECCurve curve = getCurve();
             X9ECPoint G = configureBasepoint(curve,
                 "0429A0B6A887A983E9730988A68727A8B2D126C44CC2CC7B2A6555193035DC76310804F12E549BDB011C103089E73510ACB275FC312A5DC6B76553F0CA");
             return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
@@ -527,10 +652,15 @@
      *
     static X9ECParametersHolder sect283k1 = new X9ECParametersHolder()
     {
+        protected ECCurve createCurve()
+        {
+            return configureCurve(new SecT283K1Curve());
+        }
+
         protected X9ECParameters createParameters()
         {
             byte[] S = null;
-            ECCurve curve = configureCurve(new SecT283K1Curve());
+            ECCurve curve = getCurve();
             X9ECPoint G = configureBasepoint(curve, "04"
                 + "0503213F78CA44883F1A3B8162F188E553CD265F23C1567A16876913B0C2AC2458492836"
                 + "01CCDA380F1C9E318D90F95D07E5426FE87E45C0E8184698E45962364E34116177DD2259");
@@ -543,10 +673,15 @@
      *
     static X9ECParametersHolder sect283r1 = new X9ECParametersHolder()
     {
+        protected ECCurve createCurve()
+        {
+            return configureCurve(new SecT283R1Curve());
+        }
+
         protected X9ECParameters createParameters()
         {
             byte[] S = Hex.decodeStrict("77E2B07370EB0F832A6DD5B62DFC88CD06BB84BE");
-            ECCurve curve = configureCurve(new SecT283R1Curve());
+            ECCurve curve = getCurve();
             X9ECPoint G = configureBasepoint(curve, "04"
                 + "05F939258DB7DD90E1934F8C70B0DFEC2EED25B8557EAC9C80E2E198F8CDBECD86B12053"
                 + "03676854FE24141CB98FE6D4B20D02B4516FF702350EDDB0826779C813F0DF45BE8112F4");
@@ -559,10 +694,15 @@
      *
     static X9ECParametersHolder sect409k1 = new X9ECParametersHolder()
     {
+        protected ECCurve createCurve()
+        {
+            return configureCurve(new SecT409K1Curve());
+        }
+
         protected X9ECParameters createParameters()
         {
             byte[] S = null;
-            ECCurve curve = configureCurve(new SecT409K1Curve());
+            ECCurve curve = getCurve();
             X9ECPoint G = configureBasepoint(curve, "04"
                 + "0060F05F658F49C1AD3AB1890F7184210EFD0987E307C84C27ACCFB8F9F67CC2C460189EB5AAAA62EE222EB1B35540CFE9023746"
                 + "01E369050B7C4E42ACBA1DACBF04299C3460782F918EA427E6325165E9EA10E3DA5F6C42E9C55215AA9CA27A5863EC48D8E0286B");
@@ -575,10 +715,15 @@
      *
     static X9ECParametersHolder sect409r1 = new X9ECParametersHolder()
     {
+        protected ECCurve createCurve()
+        {
+            return configureCurve(new SecT409R1Curve());
+        }
+
         protected X9ECParameters createParameters()
         {
             byte[] S = Hex.decodeStrict("4099B5A457F9D69F79213D094C4BCD4D4262210B");
-            ECCurve curve = configureCurve(new SecT409R1Curve());
+            ECCurve curve = getCurve();
             X9ECPoint G = configureBasepoint(curve, "04"
                 + "015D4860D088DDB3496B0C6064756260441CDE4AF1771D4DB01FFE5B34E59703DC255A868A1180515603AEAB60794E54BB7996A7"
                 + "0061B1CFAB6BE5F32BBFA78324ED106A7636B9C5A7BD198D0158AA4F5488D08F38514F1FDF4B4F40D2181B3681C364BA0273C706");
@@ -591,10 +736,15 @@
      *
     static X9ECParametersHolder sect571k1 = new X9ECParametersHolder()
     {
+        protected ECCurve createCurve()
+        {
+            return configureCurve(new SecT571K1Curve());
+        }
+
         protected X9ECParameters createParameters()
         {
             byte[] S = null;
-            ECCurve curve = configureCurve(new SecT571K1Curve());
+            ECCurve curve = getCurve();
             X9ECPoint G = configureBasepoint(curve, "04"
                 + "026EB7A859923FBC82189631F8103FE4AC9CA2970012D5D46024804801841CA44370958493B205E647DA304DB4CEB08CBBD1BA39494776FB988B47174DCA88C7E2945283A01C8972"
                 + "0349DC807F4FBF374F4AEADE3BCA95314DD58CEC9F307A54FFC61EFC006D8A2C9D4979C0AC44AEA74FBEBBB9F772AEDCB620B01A7BA7AF1B320430C8591984F601CD4C143EF1C7A3");
@@ -607,10 +757,15 @@
      *
     static X9ECParametersHolder sect571r1 = new X9ECParametersHolder()
     {
+        protected ECCurve createCurve()
+        {
+            return configureCurve(new SecT571R1Curve());
+        }
+
         protected X9ECParameters createParameters()
         {
             byte[] S = Hex.decodeStrict("2AA058F73A0E33AB486B0F610410C53A7F132310");
-            ECCurve curve = configureCurve(new SecT571R1Curve());
+            ECCurve curve = getCurve();
             X9ECPoint G = configureBasepoint(curve, "04"
                 + "0303001D34B856296C16C0D40D3CD7750A93D1D2955FA80AA5F40FC8DB7B2ABDBDE53950F4C0D293CDD711A35B67FB1499AE60038614F1394ABFA3B4C850D927E1E7769C8EEC2D19"
                 + "037BF27342DA639B6DCCFFFEB73D69D78C6C27A6009CBBCA1980F8533921E8A684423E43BAB08A576291AF8F461BB2A8B3531D2F0485C19B16E2F1516E23DD3C1A4827AF1B8AC15B");
@@ -623,10 +778,15 @@
      *
     static X9ECParametersHolder sm2p256v1 = new X9ECParametersHolder()
     {
+        protected ECCurve createCurve()
+        {
+            return configureCurve(new SM2P256V1Curve());
+        }
+
         protected X9ECParameters createParameters()
         {
             byte[] S = null;
-            ECCurve curve = configureCurve(new SM2P256V1Curve());
+            ECCurve curve = getCurve();
             X9ECPoint G = configureBasepoint(curve,
                 "0432C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7BC3736A2F4F6779C59BDCEE36B692153D0A9877CC62A474002DF32E52139F0A0");
             return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
@@ -636,6 +796,7 @@
     // END Android-removed: Unsupported curves
 
 
+
     static final Hashtable nameToCurve = new Hashtable();
     static final Hashtable nameToOID = new Hashtable();
     static final Hashtable oidToCurve = new Hashtable();
@@ -742,10 +903,15 @@
 
     public static X9ECParameters getByName(String name)
     {
-        X9ECParametersHolder holder = (X9ECParametersHolder)nameToCurve.get(Strings.toLowerCase(name));
+        X9ECParametersHolder holder = getByNameLazy(name);
         return holder == null ? null : holder.getParameters();
     }
 
+    public static X9ECParametersHolder getByNameLazy(String name)
+    {
+        return (X9ECParametersHolder)nameToCurve.get(Strings.toLowerCase(name));
+    }
+
     /**
      * return the X9ECParameters object for the named curve represented by the passed in object
      * identifier. Null if the curve isn't present.
@@ -755,10 +921,15 @@
      */
     public static X9ECParameters getByOID(ASN1ObjectIdentifier oid)
     {
-        X9ECParametersHolder holder = (X9ECParametersHolder)oidToCurve.get(oid);
+        X9ECParametersHolder holder = getByOIDLazy(oid);
         return holder == null ? null : holder.getParameters();
     }
 
+    public static X9ECParametersHolder getByOIDLazy(ASN1ObjectIdentifier oid)
+    {
+        return (X9ECParametersHolder)oidToCurve.get(oid);
+    }
+
     /**
      * return the object identifier signified by the passed in name. Null if there is no object
      * identifier associated with name.
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/encodings/OAEPEncoding.java b/bcprov/src/main/java/org/bouncycastle/crypto/encodings/OAEPEncoding.java
index a38da9a..2e32b25 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/encodings/OAEPEncoding.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/encodings/OAEPEncoding.java
@@ -13,6 +13,7 @@
 // import org.bouncycastle.crypto.util.DigestFactory;
 import org.bouncycastle.crypto.digests.AndroidDigestFactory;
 import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.Pack;
 
 /**
  * Optimal Asymmetric Encryption Padding (OAEP) - see PKCS 1 V 2.
@@ -225,7 +226,9 @@
         // on encryption, we need to make sure our decrypted block comes back
         // the same size.
         //
-        boolean wrongData = (block.length < (2 * defHash.length) + 1);
+
+        // i.e. wrong when block.length < (2 * defHash.length) + 1
+        int wrongMask = (block.length - ((2 * defHash.length) + 1)) >> 31;
 
         if (data.length <= block.length)
         {
@@ -234,7 +237,7 @@
         else
         {
             System.arraycopy(data, 0, block, 0, block.length);
-            wrongData = true;
+            wrongMask |= 1;
         }
 
         //
@@ -262,39 +265,38 @@
         // check the hash of the encoding params.
         // long check to try to avoid this been a source of a timing attack.
         //
-        boolean defHashWrong = false;
-
         for (int i = 0; i != defHash.length; i++)
         {
-            if (defHash[i] != block[defHash.length + i])
-            {
-                defHashWrong = true;
-            }
+            wrongMask |= defHash[i] ^ block[defHash.length + i];
         }
 
         //
         // find the data block
         //
-        int start = block.length;
+        int start = -1;
 
         for (int index = 2 * defHash.length; index != block.length; index++)
         {
-            if (block[index] != 0 & start == block.length)
-            {
-                start = index;
-            }
+            int octet = block[index] & 0xFF;
+
+            // i.e. mask will be 0xFFFFFFFF if octet is non-zero and start is (still) negative, else 0.
+            int shouldSetMask = (-octet & start) >> 31;
+
+            start += index & shouldSetMask;
         }
 
-        boolean dataStartWrong = (start > (block.length - 1) | block[start] != 1);
+        wrongMask |= start >> 31;
+        ++start;
+        wrongMask |= block[start] ^ 1;
 
-        start++;
-
-        if (defHashWrong | wrongData | dataStartWrong)
+        if (wrongMask != 0)
         {
             Arrays.fill(block, (byte)0);
             throw new InvalidCipherTextException("data wrong");
         }
 
+        ++start;
+
         //
         // extract the data block
         //
@@ -307,19 +309,6 @@
     }
 
     /**
-     * int to octet string.
-     */
-    private void ItoOSP(
-        int     i,
-        byte[]  sp)
-    {
-        sp[0] = (byte)(i >>> 24);
-        sp[1] = (byte)(i >>> 16);
-        sp[2] = (byte)(i >>> 8);
-        sp[3] = (byte)(i >>> 0);
-    }
-
-    /**
      * mask generator function, as described in PKCS1v2.
      */
     private byte[] maskGeneratorFunction1(
@@ -337,7 +326,7 @@
 
         while (counter < (length / hashBuf.length))
         {
-            ItoOSP(counter, C);
+            Pack.intToBigEndian(counter, C, 0);
 
             mgf1Hash.update(Z, zOff, zLen);
             mgf1Hash.update(C, 0, C.length);
@@ -350,7 +339,7 @@
 
         if ((counter * hashBuf.length) < length)
         {
-            ItoOSP(counter, C);
+            Pack.intToBigEndian(counter, C, 0);
 
             mgf1Hash.update(Z, zOff, zLen);
             mgf1Hash.update(C, 0, C.length);
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/encodings/PKCS1Encoding.java b/bcprov/src/main/java/org/bouncycastle/crypto/encodings/PKCS1Encoding.java
index 02a1ff1..470f81c 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/encodings/PKCS1Encoding.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/encodings/PKCS1Encoding.java
@@ -233,52 +233,86 @@
     }
 
     /**
-     * Checks if the argument is a correctly PKCS#1.5 encoded Plaintext
-     * for encryption.
-     *
-     * @param encoded The Plaintext.
-     * @param pLen    Expected length of the plaintext.
-     * @return Either 0, if the encoding is correct, or -1, if it is incorrect.
+     * Check the argument is a valid encoding with type 1. Returns the plaintext length if valid, or -1 if invalid.
      */
-    private static int checkPkcs1Encoding(byte[] encoded, int pLen)
+    private static int checkPkcs1Encoding1(byte[] buf)
     {
-        int correct = 0;
-        /*
-		 * Check if the first two bytes are 0 2
-		 */
-        correct |= (encoded[0] ^ 2);
+        int foundZeroMask = 0;
+        int lastPadPos = 0;
 
-		/*
-		 * Now the padding check, check for no 0 byte in the padding
-		 */
-        int plen = encoded.length - (
-            pLen /* Length of the PMS */
-                + 1 /* Final 0-byte before PMS */
-        );
+        // The first byte should be 0x01
+        int badPadSign = -((buf[0] & 0xFF) ^ 0x01);
 
-        for (int i = 1; i < plen; i++)
+        // There must be a zero terminator for the padding somewhere
+        for (int i = 1; i < buf.length; ++i)
         {
-            int tmp = encoded[i];
-            tmp |= tmp >> 1;
-            tmp |= tmp >> 2;
-            tmp |= tmp >> 4;
-            correct |= (tmp & 1) - 1;
+            int padByte = buf[i] & 0xFF;
+            int is0x00Mask = ((padByte ^ 0x00) - 1) >> 31;
+            int is0xFFMask = ((padByte ^ 0xFF) - 1) >> 31;
+            lastPadPos ^= i & ~foundZeroMask & is0x00Mask;
+            foundZeroMask |= is0x00Mask;
+            badPadSign |= ~(foundZeroMask | is0xFFMask);
         }
 
-		/*
-		 * Make sure the padding ends with a 0 byte.
-		 */
-        correct |= encoded[encoded.length - (pLen + 1)];
+        // The header should be at least 10 bytes
+        badPadSign |= lastPadPos - 9;
 
-		/*
-		 * Return 0 or 1, depending on the result.
-		 */
-        correct |= correct >> 1;
-        correct |= correct >> 2;
-        correct |= correct >> 4;
-        return ~((correct & 1) - 1);
+        int plaintextLength = buf.length - 1 - lastPadPos;
+        return plaintextLength | badPadSign >> 31;
     }
 
+    /**
+     * Check the argument is a valid encoding with type 2. Returns the plaintext length if valid, or -1 if invalid.
+     */
+    private static int checkPkcs1Encoding2(byte[] buf)
+    {
+        int foundZeroMask = 0;
+        int lastPadPos = 0;
+
+        // The first byte should be 0x02
+        int badPadSign = -((buf[0] & 0xFF) ^ 0x02);
+
+        // There must be a zero terminator for the padding somewhere
+        for (int i = 1; i < buf.length; ++i)
+        {
+            int padByte = buf[i] & 0xFF;
+            int is0x00Mask = ((padByte ^ 0x00) - 1) >> 31;
+            lastPadPos ^= i & ~foundZeroMask & is0x00Mask;
+            foundZeroMask |= is0x00Mask;
+        }
+
+        // The header should be at least 10 bytes
+        badPadSign |= lastPadPos - 9;
+
+        int plaintextLength = buf.length - 1 - lastPadPos;
+        return plaintextLength | badPadSign >> 31;
+    }
+
+    /**
+     * Check the argument is a valid encoding with type 2 of a plaintext with the given length. Returns 0 if
+     * valid, or -1 if invalid.
+     */
+    private static int checkPkcs1Encoding2(byte[] buf, int plaintextLength)
+    {
+        // The first byte should be 0x02
+        int badPadSign = -((buf[0] & 0xFF) ^ 0x02);
+
+        int lastPadPos = buf.length - 1 - plaintextLength;
+
+        // The header should be at least 10 bytes
+        badPadSign |= lastPadPos - 9;
+
+        // All pad bytes before the last one should be non-zero
+        for (int i = 1; i < lastPadPos; ++i)
+        {
+            badPadSign |= (buf[i] & 0xFF) - 1;
+        }
+
+        // Last pad byte should be zero
+        badPadSign |= -(buf[lastPadPos] & 0xFF);
+
+        return badPadSign >> 31;
+    }
 
     /**
      * Decode PKCS#1.5 encoding, and return a random value if the padding is not correct.
@@ -298,36 +332,43 @@
             throw new InvalidCipherTextException("sorry, this method is only for decryption, not for signing");
         }
 
-        byte[] block = engine.processBlock(in, inOff, inLen);
-        byte[] random;
-        if (this.fallback == null)
+        int plaintextLength = this.pLen;
+
+        byte[] random = fallback;
+        if (fallback == null)
         {
-            random = new byte[this.pLen];
+            random = new byte[plaintextLength];
             this.random.nextBytes(random);
         }
-        else
+
+        int badPadMask = 0;
+        int strictBlockSize = engine.getOutputBlockSize();
+        byte[] block = engine.processBlock(in, inOff, inLen);
+
+        byte[] data = block;
+        if (block.length != strictBlockSize)
         {
-            random = fallback;
+            if (useStrictLength || block.length < strictBlockSize)
+            {
+                data = blockBuffer;
+            }
         }
 
-        byte[] data = (useStrictLength & (block.length != engine.getOutputBlockSize())) ? blockBuffer : block;
+        badPadMask |= checkPkcs1Encoding2(data, plaintextLength);
 
-		/*
-		 * Check the padding.
-		 */
-        int correct = PKCS1Encoding.checkPkcs1Encoding(data, this.pLen);
-		
-		/*
-		 * Now, to a constant time constant memory copy of the decrypted value
-		 * or the random value, depending on the validity of the padding.
-		 */
-        byte[] result = new byte[this.pLen];
-        for (int i = 0; i < this.pLen; i++)
+        /*
+         * Now, to a constant time constant memory copy of the decrypted value
+         * or the random value, depending on the validity of the padding.
+         */
+        int dataOff = data.length - plaintextLength; 
+        byte[] result = new byte[plaintextLength];
+        for (int i = 0; i < plaintextLength; ++i)
         {
-            result[i] = (byte)((data[i + (data.length - pLen)] & (~correct)) | (random[i] & correct));
+            result[i] = (byte)((data[dataOff + i] & ~badPadMask) | (random[i] & badPadMask));
         }
 
-        Arrays.fill(data, (byte)0);
+        Arrays.fill(block, (byte)0);
+        Arrays.fill(blockBuffer, 0, Math.max(0, blockBuffer.length - block.length), (byte)0);
 
         return result;
     }
@@ -335,95 +376,50 @@
     /**
      * @throws InvalidCipherTextException if the decrypted block is not in PKCS1 format.
      */
-    private byte[] decodeBlock(
-        byte[] in,
-        int inOff,
-        int inLen)
+    private byte[] decodeBlock(byte[] in, int inOff, int inLen)
         throws InvalidCipherTextException
     {
         /*
          * If the length of the expected plaintext is known, we use a constant-time decryption.
          * If the decryption fails, we return a random value.
          */
-        if (this.pLen != -1)
+        if (forPrivateKey && this.pLen != -1)
         {
             return this.decodeBlockOrRandom(in, inOff, inLen);
         }
 
+        int strictBlockSize = engine.getOutputBlockSize();
         byte[] block = engine.processBlock(in, inOff, inLen);
-        boolean incorrectLength = (useStrictLength & (block.length != engine.getOutputBlockSize()));
 
-        byte[] data;
-        if (block.length < getOutputBlockSize())
+        boolean incorrectLength = useStrictLength & (block.length != strictBlockSize);
+
+        byte[] data = block;
+        if (block.length < strictBlockSize)
         {
             data = blockBuffer;
         }
-        else
+
+        int plaintextLength = forPrivateKey ? checkPkcs1Encoding2(data) : checkPkcs1Encoding1(data);
+
+        try
         {
-            data = block;
-        }
-
-        byte type = data[0];
-
-        boolean badType;
-        if (forPrivateKey)
-        {
-            badType = (type != 2);
-        }
-        else
-        {
-            badType = (type != 1);
-        }
-
-        //
-        // find and extract the message block.
-        //
-        int start = findStart(type, data);
-
-        start++;           // data should start at the next byte
-
-        if (badType | start < HEADER_LENGTH)
-        {
-            Arrays.fill(data, (byte)0);
-            throw new InvalidCipherTextException("block incorrect");
-        }
-
-        // if we get this far, it's likely to be a genuine encoding error
-        if (incorrectLength)
-        {
-            Arrays.fill(data, (byte)0);
-            throw new InvalidCipherTextException("block incorrect size");
-        }
-
-        byte[] result = new byte[data.length - start];
-
-        System.arraycopy(data, start, result, 0, result.length);
-
-        return result;
-    }
-
-    private int findStart(byte type, byte[] block)
-        throws InvalidCipherTextException
-    {
-        int start = -1;
-        boolean padErr = false;
-
-        for (int i = 1; i != block.length; i++)
-        {
-            byte pad = block[i];
-
-            if (pad == 0 & start < 0)
+            if (plaintextLength < 0)
             {
-                start = i;
+                throw new InvalidCipherTextException("block incorrect");
             }
-            padErr |= (type == 1 & start < 0 & pad != (byte)0xff);
-        }
+            if (incorrectLength)
+            {
+                throw new InvalidCipherTextException("block incorrect size");
+            }
 
-        if (padErr)
+            byte[] result = new byte[plaintextLength];
+            System.arraycopy(data, data.length - plaintextLength, result, 0, plaintextLength);
+            return result;
+        }
+        finally
         {
-            return -1;
+            Arrays.fill(block, (byte)0);
+            Arrays.fill(blockBuffer, 0, Math.max(0, blockBuffer.length - block.length), (byte)0);
         }
-
-        return start;
     }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/engines/AESEngine.java b/bcprov/src/main/java/org/bouncycastle/crypto/engines/AESEngine.java
index 0b491ab..ac2eb9a 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/engines/AESEngine.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/engines/AESEngine.java
@@ -1,9 +1,12 @@
 package org.bouncycastle.crypto.engines;
 
-import org.bouncycastle.crypto.BlockCipher;
 import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.CryptoServicesRegistrar;
 import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.DefaultMultiBlockCipher;
+import org.bouncycastle.crypto.MultiBlockCipher;
 import org.bouncycastle.crypto.OutputLengthException;
+import org.bouncycastle.crypto.constraints.DefaultServiceProperties;
 import org.bouncycastle.crypto.params.KeyParameter;
 import org.bouncycastle.util.Arrays;
 import org.bouncycastle.util.Pack;
@@ -14,7 +17,7 @@
  * For further details see: <a href="https://csrc.nist.gov/encryption/aes/">https://csrc.nist.gov/encryption/aes/</a>.
  *
  * This implementation is based on optimizations from Dr. Brian Gladman's paper and C code at
- * <a href="http://fp.gladman.plus.com/cryptography_technology/rijndael/">http://fp.gladman.plus.com/cryptography_technology/rijndael/</a>
+ * <a href="https://fp.gladman.plus.com/cryptography_technology/rijndael/">https://fp.gladman.plus.com/cryptography_technology/rijndael/</a>
  *
  * There are three levels of tradeoff of speed vs memory
  * Because java has no preprocessor, they are written as three separate classes from which to choose
@@ -32,7 +35,7 @@
  *
  */
 public class AESEngine
-    implements BlockCipher
+    extends DefaultMultiBlockCipher
 {
     // The S box
     private static final byte[] S = {
@@ -412,7 +415,6 @@
 
     private int         ROUNDS;
     private int[][]     WorkingKey = null;
-    private int         C0, C1, C2, C3;
     private boolean     forEncryption;
 
     private byte[]      s;
@@ -420,10 +422,22 @@
     private static final int BLOCK_SIZE = 16;
 
     /**
+      * Return an AESEngine.
+      *
+      * @return an AES ECB mode cipher.
+      */
+     public static MultiBlockCipher newInstance()
+     {
+         return new AESEngine();
+     }
+
+    /**
      * default constructor - 128 bit block size.
+     * @deprecated use AESEngine.newInstance()
      */
     public AESEngine()
     {
+        CryptoServicesRegistrar.checkConstraints(new DefaultServiceProperties(getAlgorithmName(), 256));
     }
 
     /**
@@ -450,6 +464,9 @@
             {
                 s = Arrays.clone(Si);
             }
+
+            CryptoServicesRegistrar.checkConstraints(new DefaultServiceProperties(getAlgorithmName(), bitsOfSecurity(), params, Utils.getPurpose(forEncryption)));
+
             return;
         }
 
@@ -466,38 +483,30 @@
         return BLOCK_SIZE;
     }
 
-    public int processBlock(
-        byte[] in,
-        int inOff,
-        byte[] out,
-        int outOff)
+    public int processBlock(byte[] in, int inOff, byte[] out, int outOff)
     {
         if (WorkingKey == null)
         {
             throw new IllegalStateException("AES engine not initialised");
         }
 
-        if ((inOff + (32 / 2)) > in.length)
+        if (inOff > (in.length - BLOCK_SIZE))
         {
             throw new DataLengthException("input buffer too short");
         }
 
-        if ((outOff + (32 / 2)) > out.length)
+        if (outOff > (out.length - BLOCK_SIZE))
         {
             throw new OutputLengthException("output buffer too short");
         }
 
         if (forEncryption)
         {
-            unpackBlock(in, inOff);
-            encryptBlock(WorkingKey);
-            packBlock(out, outOff);
+            encryptBlock(in, inOff, out, outOff, WorkingKey);
         }
         else
         {
-            unpackBlock(in, inOff);
-            decryptBlock(WorkingKey);
-            packBlock(out, outOff);
+            decryptBlock(in, inOff, out, outOff, WorkingKey);
         }
 
         return BLOCK_SIZE;
@@ -507,68 +516,18 @@
     {
     }
 
-    private void unpackBlock(
-        byte[]      bytes,
-        int         off)
+    private void encryptBlock(byte[] in, int inOff, byte[] out, int outOff, int[][] KW)
     {
-        int     index = off;
+        int C0 = Pack.littleEndianToInt(in, inOff +  0);
+        int C1 = Pack.littleEndianToInt(in, inOff +  4);
+        int C2 = Pack.littleEndianToInt(in, inOff +  8);
+        int C3 = Pack.littleEndianToInt(in, inOff + 12);
 
-        C0 = (bytes[index++] & 0xff);
-        C0 |= (bytes[index++] & 0xff) << 8;
-        C0 |= (bytes[index++] & 0xff) << 16;
-        C0 |= bytes[index++] << 24;
+        int t0 = C0 ^ KW[0][0];
+        int t1 = C1 ^ KW[0][1];
+        int t2 = C2 ^ KW[0][2];
 
-        C1 = (bytes[index++] & 0xff);
-        C1 |= (bytes[index++] & 0xff) << 8;
-        C1 |= (bytes[index++] & 0xff) << 16;
-        C1 |= bytes[index++] << 24;
-
-        C2 = (bytes[index++] & 0xff);
-        C2 |= (bytes[index++] & 0xff) << 8;
-        C2 |= (bytes[index++] & 0xff) << 16;
-        C2 |= bytes[index++] << 24;
-
-        C3 = (bytes[index++] & 0xff);
-        C3 |= (bytes[index++] & 0xff) << 8;
-        C3 |= (bytes[index++] & 0xff) << 16;
-        C3 |= bytes[index++] << 24;
-    }
-
-    private void packBlock(
-        byte[]      bytes,
-        int         off)
-    {
-        int     index = off;
-
-        bytes[index++] = (byte)C0;
-        bytes[index++] = (byte)(C0 >> 8);
-        bytes[index++] = (byte)(C0 >> 16);
-        bytes[index++] = (byte)(C0 >> 24);
-
-        bytes[index++] = (byte)C1;
-        bytes[index++] = (byte)(C1 >> 8);
-        bytes[index++] = (byte)(C1 >> 16);
-        bytes[index++] = (byte)(C1 >> 24);
-
-        bytes[index++] = (byte)C2;
-        bytes[index++] = (byte)(C2 >> 8);
-        bytes[index++] = (byte)(C2 >> 16);
-        bytes[index++] = (byte)(C2 >> 24);
-
-        bytes[index++] = (byte)C3;
-        bytes[index++] = (byte)(C3 >> 8);
-        bytes[index++] = (byte)(C3 >> 16);
-        bytes[index++] = (byte)(C3 >> 24);
-    }
-
-
-    private void encryptBlock(int[][] KW)
-    {
-        int t0 = this.C0 ^ KW[0][0];
-        int t1 = this.C1 ^ KW[0][1];
-        int t2 = this.C2 ^ KW[0][2];
-
-        int r = 1, r0, r1, r2, r3 = this.C3 ^ KW[0][3];
+        int r = 1, r0, r1, r2, r3 = C3 ^ KW[0][3];
         while (r < ROUNDS - 1)
         {
             r0 = T0[t0&255] ^ shift(T0[(t1>>8)&255], 24) ^ shift(T0[(t2>>16)&255], 16) ^ shift(T0[(r3>>24)&255], 8) ^ KW[r][0];
@@ -588,19 +547,29 @@
 
         // the final round's table is a simple function of S so we don't use a whole other four tables for it
 
-        this.C0 = (S[r0&255]&255) ^ ((S[(r1>>8)&255]&255)<<8) ^ ((s[(r2>>16)&255]&255)<<16) ^ (s[(r3>>24)&255]<<24) ^ KW[r][0];
-        this.C1 = (s[r1&255]&255) ^ ((S[(r2>>8)&255]&255)<<8) ^ ((S[(r3>>16)&255]&255)<<16) ^ (s[(r0>>24)&255]<<24) ^ KW[r][1];
-        this.C2 = (s[r2&255]&255) ^ ((S[(r3>>8)&255]&255)<<8) ^ ((S[(r0>>16)&255]&255)<<16) ^ (S[(r1>>24)&255]<<24) ^ KW[r][2];
-        this.C3 = (s[r3&255]&255) ^ ((s[(r0>>8)&255]&255)<<8) ^ ((s[(r1>>16)&255]&255)<<16) ^ (S[(r2>>24)&255]<<24) ^ KW[r][3];
+        C0 = (S[r0&255]&255) ^ ((S[(r1>>8)&255]&255)<<8) ^ ((s[(r2>>16)&255]&255)<<16) ^ (s[(r3>>24)&255]<<24) ^ KW[r][0];
+        C1 = (s[r1&255]&255) ^ ((S[(r2>>8)&255]&255)<<8) ^ ((S[(r3>>16)&255]&255)<<16) ^ (s[(r0>>24)&255]<<24) ^ KW[r][1];
+        C2 = (s[r2&255]&255) ^ ((S[(r3>>8)&255]&255)<<8) ^ ((S[(r0>>16)&255]&255)<<16) ^ (S[(r1>>24)&255]<<24) ^ KW[r][2];
+        C3 = (s[r3&255]&255) ^ ((s[(r0>>8)&255]&255)<<8) ^ ((s[(r1>>16)&255]&255)<<16) ^ (S[(r2>>24)&255]<<24) ^ KW[r][3];
+
+        Pack.intToLittleEndian(C0, out, outOff +  0);
+        Pack.intToLittleEndian(C1, out, outOff +  4);
+        Pack.intToLittleEndian(C2, out, outOff +  8);
+        Pack.intToLittleEndian(C3, out, outOff + 12);
     }
 
-    private void decryptBlock(int[][] KW)
+    private void decryptBlock(byte[] in, int inOff, byte[] out, int outOff, int[][] KW)
     {
-        int t0 = this.C0 ^ KW[ROUNDS][0];
-        int t1 = this.C1 ^ KW[ROUNDS][1];
-        int t2 = this.C2 ^ KW[ROUNDS][2];
+        int C0 = Pack.littleEndianToInt(in, inOff +  0);
+        int C1 = Pack.littleEndianToInt(in, inOff +  4);
+        int C2 = Pack.littleEndianToInt(in, inOff +  8);
+        int C3 = Pack.littleEndianToInt(in, inOff + 12);
 
-        int r = ROUNDS - 1, r0, r1, r2, r3 = this.C3 ^ KW[ROUNDS][3];
+        int t0 = C0 ^ KW[ROUNDS][0];
+        int t1 = C1 ^ KW[ROUNDS][1];
+        int t2 = C2 ^ KW[ROUNDS][2];
+
+        int r = ROUNDS - 1, r0, r1, r2, r3 = C3 ^ KW[ROUNDS][3];
         while (r > 1)
         {
             r0 = Tinv0[t0&255] ^ shift(Tinv0[(r3>>8)&255], 24) ^ shift(Tinv0[(t2>>16)&255], 16) ^ shift(Tinv0[(t1>>24)&255], 8) ^ KW[r][0];
@@ -620,9 +589,23 @@
         
         // the final round's table is a simple function of Si so we don't use a whole other four tables for it
 
-        this.C0 = (Si[r0&255]&255) ^ ((s[(r3>>8)&255]&255)<<8) ^ ((s[(r2>>16)&255]&255)<<16) ^ (Si[(r1>>24)&255]<<24) ^ KW[0][0];
-        this.C1 = (s[r1&255]&255) ^ ((s[(r0>>8)&255]&255)<<8) ^ ((Si[(r3>>16)&255]&255)<<16) ^ (s[(r2>>24)&255]<<24) ^ KW[0][1];
-        this.C2 = (s[r2&255]&255) ^ ((Si[(r1>>8)&255]&255)<<8) ^ ((Si[(r0>>16)&255]&255)<<16) ^ (s[(r3>>24)&255]<<24) ^ KW[0][2];
-        this.C3 = (Si[r3&255]&255) ^ ((s[(r2>>8)&255]&255)<<8) ^ ((s[(r1>>16)&255]&255)<<16) ^ (s[(r0>>24)&255]<<24) ^ KW[0][3];
+        C0 = (Si[r0&255]&255) ^ ((s[(r3>>8)&255]&255)<<8) ^ ((s[(r2>>16)&255]&255)<<16) ^ (Si[(r1>>24)&255]<<24) ^ KW[0][0];
+        C1 = (s[r1&255]&255) ^ ((s[(r0>>8)&255]&255)<<8) ^ ((Si[(r3>>16)&255]&255)<<16) ^ (s[(r2>>24)&255]<<24) ^ KW[0][1];
+        C2 = (s[r2&255]&255) ^ ((Si[(r1>>8)&255]&255)<<8) ^ ((Si[(r0>>16)&255]&255)<<16) ^ (s[(r3>>24)&255]<<24) ^ KW[0][2];
+        C3 = (Si[r3&255]&255) ^ ((s[(r2>>8)&255]&255)<<8) ^ ((s[(r1>>16)&255]&255)<<16) ^ (s[(r0>>24)&255]<<24) ^ KW[0][3];
+
+        Pack.intToLittleEndian(C0, out, outOff +  0);
+        Pack.intToLittleEndian(C1, out, outOff +  4);
+        Pack.intToLittleEndian(C2, out, outOff +  8);
+        Pack.intToLittleEndian(C3, out, outOff + 12);
+    }
+
+    private int bitsOfSecurity()
+    {
+        if (WorkingKey == null)
+        {
+            return 256;
+        }
+        return (WorkingKey.length - 7) << 5;
     }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/engines/AESFastEngine.java b/bcprov/src/main/java/org/bouncycastle/crypto/engines/AESFastEngine.java
index b73e0a5..1d41763 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/engines/AESFastEngine.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/engines/AESFastEngine.java
@@ -2,8 +2,10 @@
 
 import org.bouncycastle.crypto.BlockCipher;
 import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.CryptoServicesRegistrar;
 import org.bouncycastle.crypto.DataLengthException;
 import org.bouncycastle.crypto.OutputLengthException;
+import org.bouncycastle.crypto.constraints.DefaultServiceProperties;
 import org.bouncycastle.crypto.params.KeyParameter;
 import org.bouncycastle.util.Pack;
 
@@ -13,7 +15,7 @@
  * For further details see: <a href="https://csrc.nist.gov/encryption/aes/">https://csrc.nist.gov/encryption/aes/</a>.
  *
  * This implementation is based on optimizations from Dr. Brian Gladman's paper and C code at
- * <a href="http://fp.gladman.plus.com/cryptography_technology/rijndael/">http://fp.gladman.plus.com/cryptography_technology/rijndael/</a>
+ * <a href="https://fp.gladman.plus.com/cryptography_technology/rijndael/">https://fp.gladman.plus.com/cryptography_technology/rijndael/</a>
  *
  * There are three levels of tradeoff of speed vs memory
  * Because java has no preprocessor, they are written as three separate classes from which to choose
@@ -741,7 +743,6 @@
 
     private int         ROUNDS;
     private int[][]     WorkingKey = null;
-    private int         C0, C1, C2, C3;
     private boolean     forEncryption;
 
     private static final int BLOCK_SIZE = 16;
@@ -751,6 +752,7 @@
      */
     public AESFastEngine()
     {
+        CryptoServicesRegistrar.checkConstraints(new DefaultServiceProperties(getAlgorithmName(), 256));
     }
 
     /**
@@ -769,6 +771,7 @@
         {
             WorkingKey = generateWorkingKey(((KeyParameter)params).getKey(), forEncryption);
             this.forEncryption = forEncryption;
+            CryptoServicesRegistrar.checkConstraints(new DefaultServiceProperties(getAlgorithmName(), bitsOfSecurity(), params, Utils.getPurpose(forEncryption)));
             return;
         }
 
@@ -785,40 +788,32 @@
         return BLOCK_SIZE;
     }
 
-    public int processBlock(
-        byte[] in,
-        int inOff,
-        byte[] out,
-        int outOff)
+    public int processBlock(byte[] in, int inOff, byte[] out, int outOff)
     {
         if (WorkingKey == null)
         {
             throw new IllegalStateException("AES engine not initialised");
         }
 
-        if ((inOff + (32 / 2)) > in.length)
+        if (inOff > (in.length - BLOCK_SIZE))
         {
             throw new DataLengthException("input buffer too short");
         }
 
-        if ((outOff + (32 / 2)) > out.length)
+        if (outOff > (out.length - BLOCK_SIZE))
         {
             throw new OutputLengthException("output buffer too short");
         }
 
-        unpackBlock(in, inOff);
-
         if (forEncryption)
         {
-            encryptBlock(WorkingKey);
+            encryptBlock(in, inOff, out, outOff, WorkingKey);
         }
         else
         {
-            decryptBlock(WorkingKey);
+            decryptBlock(in, inOff, out, outOff, WorkingKey);
         }
 
-        packBlock(out, outOff);
-
         return BLOCK_SIZE;
     }
 
@@ -826,27 +821,16 @@
     {
     }
 
-    private void unpackBlock(byte[] bytes, int off)
+    private void encryptBlock(byte[] in, int inOff, byte[] out, int outOff, int[][] KW)
     {
-        this.C0 = Pack.littleEndianToInt(bytes, off);
-        this.C1 = Pack.littleEndianToInt(bytes, off + 4);
-        this.C2 = Pack.littleEndianToInt(bytes, off + 8);
-        this.C3 = Pack.littleEndianToInt(bytes, off + 12);
-    }
+        int C0 = Pack.littleEndianToInt(in, inOff +  0);
+        int C1 = Pack.littleEndianToInt(in, inOff +  4);
+        int C2 = Pack.littleEndianToInt(in, inOff +  8);
+        int C3 = Pack.littleEndianToInt(in, inOff + 12);
 
-    private void packBlock(byte[] bytes, int off)
-    {
-        Pack.intToLittleEndian(this.C0, bytes, off);
-        Pack.intToLittleEndian(this.C1, bytes, off + 4);
-        Pack.intToLittleEndian(this.C2, bytes, off + 8);
-        Pack.intToLittleEndian(this.C3, bytes, off + 12);
-    }
-
-    private void encryptBlock(int[][] KW)
-    {
-        int t0 = this.C0 ^ KW[0][0];
-        int t1 = this.C1 ^ KW[0][1];
-        int t2 = this.C2 ^ KW[0][2];
+        int t0 = C0 ^ KW[0][0];
+        int t1 = C1 ^ KW[0][1];
+        int t2 = C2 ^ KW[0][2];
 
         /*
          * Fast engine has precomputed rotr(T0, 8/16/24) tables T1/T2/T3.
@@ -855,7 +839,7 @@
          * avoids additional array range checks on 3 more arrays (which on HotSpot are more
          * expensive than the offset additions).
          */
-        int r = 1, r0, r1, r2, r3 = this.C3 ^ KW[0][3];
+        int r = 1, r0, r1, r2, r3 = C3 ^ KW[0][3];
         int i0, i1, i2, i3;
 
         while (r < ROUNDS - 1)
@@ -913,28 +897,38 @@
 
         i0 = r0; i1 = r1 >>> 8; i2 = r2 >>> 16; i3 = r3 >>> 24;
         i0 = S[i0 & 255] & 255; i1 = S[i1 & 255] & 255; i2 = S[i2 & 255] & 255; i3 = S[i3 & 255] & 255;
-        this.C0 = i0 ^ i1 << 8 ^ i2 << 16 ^ i3 << 24 ^ KW[r][0];
+        C0 = i0 ^ i1 << 8 ^ i2 << 16 ^ i3 << 24 ^ KW[r][0];
 
         i0 = r1; i1 = r2 >>> 8; i2 = r3 >>> 16; i3 = r0 >>> 24;
         i0 = S[i0 & 255] & 255; i1 = S[i1 & 255] & 255; i2 = S[i2 & 255] & 255; i3 = S[i3 & 255] & 255;
-        this.C1 = i0 ^ i1 << 8 ^ i2 << 16 ^ i3 << 24 ^ KW[r][1];
+        C1 = i0 ^ i1 << 8 ^ i2 << 16 ^ i3 << 24 ^ KW[r][1];
 
         i0 = r2; i1 = r3 >>> 8; i2 = r0 >>> 16; i3 = r1 >>> 24;
         i0 = S[i0 & 255] & 255; i1 = S[i1 & 255] & 255; i2 = S[i2 & 255] & 255; i3 = S[i3 & 255] & 255;
-        this.C2 = i0 ^ i1 << 8 ^ i2 << 16 ^ i3 << 24 ^ KW[r][2];
+        C2 = i0 ^ i1 << 8 ^ i2 << 16 ^ i3 << 24 ^ KW[r][2];
 
         i0 = r3; i1 = r0 >>> 8; i2 = r1 >>> 16; i3 = r2 >>> 24;
         i0 = S[i0 & 255] & 255; i1 = S[i1 & 255] & 255; i2 = S[i2 & 255] & 255; i3 = S[i3 & 255] & 255;
-        this.C3 = i0 ^ i1 << 8 ^ i2 << 16 ^ i3 << 24 ^ KW[r][3];
+        C3 = i0 ^ i1 << 8 ^ i2 << 16 ^ i3 << 24 ^ KW[r][3];
+
+        Pack.intToLittleEndian(C0, out, outOff +  0);
+        Pack.intToLittleEndian(C1, out, outOff +  4);
+        Pack.intToLittleEndian(C2, out, outOff +  8);
+        Pack.intToLittleEndian(C3, out, outOff + 12);
     }
 
-    private void decryptBlock(int[][] KW)
+    private void decryptBlock(byte[] in, int inOff, byte[] out, int outOff, int[][] KW)
     {
-        int t0 = this.C0 ^ KW[ROUNDS][0];
-        int t1 = this.C1 ^ KW[ROUNDS][1];
-        int t2 = this.C2 ^ KW[ROUNDS][2];
+        int C0 = Pack.littleEndianToInt(in, inOff +  0);
+        int C1 = Pack.littleEndianToInt(in, inOff +  4);
+        int C2 = Pack.littleEndianToInt(in, inOff +  8);
+        int C3 = Pack.littleEndianToInt(in, inOff + 12);
 
-        int r = ROUNDS - 1, r0, r1, r2, r3 = this.C3 ^ KW[ROUNDS][3];
+        int t0 = C0 ^ KW[ROUNDS][0];
+        int t1 = C1 ^ KW[ROUNDS][1];
+        int t2 = C2 ^ KW[ROUNDS][2];
+
+        int r = ROUNDS - 1, r0, r1, r2, r3 = C3 ^ KW[ROUNDS][3];
         int i0, i1, i2, i3;
 
         while (r > 1)
@@ -992,18 +986,33 @@
 
         i0 = r0; i1 = r3 >>> 8; i2 = r2 >>> 16; i3 = r1 >>> 24;
         i0 = Si[i0 & 255] & 255; i1 = Si[i1 & 255] & 255; i2 = Si[i2 & 255] & 255; i3 = Si[i3 & 255] & 255;
-        this.C0 = i0 ^ i1 << 8 ^ i2 << 16 ^ i3 << 24 ^ KW[0][0];
+        C0 = i0 ^ i1 << 8 ^ i2 << 16 ^ i3 << 24 ^ KW[0][0];
 
         i0 = r1; i1 = r0 >>> 8; i2 = r3 >>> 16; i3 = r2 >>> 24;
         i0 = Si[i0 & 255] & 255; i1 = Si[i1 & 255] & 255; i2 = Si[i2 & 255] & 255; i3 = Si[i3 & 255] & 255;
-        this.C1 = i0 ^ i1 << 8 ^ i2 << 16 ^ i3 << 24 ^ KW[0][1];
+        C1 = i0 ^ i1 << 8 ^ i2 << 16 ^ i3 << 24 ^ KW[0][1];
 
         i0 = r2; i1 = r1 >>> 8; i2 = r0 >>> 16; i3 = r3 >>> 24;
         i0 = Si[i0 & 255] & 255; i1 = Si[i1 & 255] & 255; i2 = Si[i2 & 255] & 255; i3 = Si[i3 & 255] & 255;
-        this.C2 = i0 ^ i1 << 8 ^ i2 << 16 ^ i3 << 24 ^ KW[0][2];
+        C2 = i0 ^ i1 << 8 ^ i2 << 16 ^ i3 << 24 ^ KW[0][2];
 
         i0 = r3; i1 = r2 >>> 8; i2 = r1 >>> 16; i3 = r0 >>> 24;
         i0 = Si[i0 & 255] & 255; i1 = Si[i1 & 255] & 255; i2 = Si[i2 & 255] & 255; i3 = Si[i3 & 255] & 255;
-        this.C3 = i0 ^ i1 << 8 ^ i2 << 16 ^ i3 << 24 ^ KW[0][3];
+        C3 = i0 ^ i1 << 8 ^ i2 << 16 ^ i3 << 24 ^ KW[0][3];
+
+        Pack.intToLittleEndian(C0, out, outOff +  0);
+        Pack.intToLittleEndian(C1, out, outOff +  4);
+        Pack.intToLittleEndian(C2, out, outOff +  8);
+        Pack.intToLittleEndian(C3, out, outOff + 12);
+    }
+
+    // Service Definitions
+    private int bitsOfSecurity()
+    {
+        if (WorkingKey == null)
+        {
+            return 256;
+        }
+        return (WorkingKey.length - 7) << 5;
     }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/engines/AESWrapEngine.java b/bcprov/src/main/java/org/bouncycastle/crypto/engines/AESWrapEngine.java
index b80d653..cf70e87 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/engines/AESWrapEngine.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/engines/AESWrapEngine.java
@@ -14,16 +14,16 @@
      */
     public AESWrapEngine()
     {
-        super(new AESEngine());
+        super(AESEngine.newInstance());
     }
 
     /**
-     * Create an AESWrapEngine where the underlying cipher is set to decrypt for wrapping, encrypt for unwrapping.
+     * Create an AESWrapEngine where the underlying cipher is (optionally) set to decrypt for wrapping, encrypt for unwrapping.
      *
      * @param useReverseDirection true if underlying cipher should be used in decryption mode, false otherwise.
      */
     public AESWrapEngine(boolean useReverseDirection)
     {
-        super(new AESEngine(), useReverseDirection);
+        super(AESEngine.newInstance(), useReverseDirection);
     }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/engines/AsconEngine.java b/bcprov/src/main/java/org/bouncycastle/crypto/engines/AsconEngine.java
new file mode 100644
index 0000000..e4d705f
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/engines/AsconEngine.java
@@ -0,0 +1,738 @@
+package org.bouncycastle.crypto.engines;
+
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.CryptoServicesRegistrar;
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.InvalidCipherTextException;
+import org.bouncycastle.crypto.OutputLengthException;
+import org.bouncycastle.crypto.constraints.DefaultServiceProperties;
+import org.bouncycastle.crypto.modes.AEADCipher;
+import org.bouncycastle.crypto.params.AEADParameters;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.crypto.params.ParametersWithIV;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.Longs;
+import org.bouncycastle.util.Pack;
+
+/**
+ * ASCON AEAD v1.2, https://ascon.iaik.tugraz.at/
+ * https://csrc.nist.gov/CSRC/media/Projects/lightweight-cryptography/documents/finalist-round/updated-spec-doc/ascon-spec-final.pdf
+ * <p>
+ * ASCON AEAD v1.2 with reference to C Reference Impl from: https://github.com/ascon/ascon-c
+ * </p>
+ */
+public class AsconEngine
+    implements AEADCipher
+{
+    public enum AsconParameters
+    {
+        ascon80pq,
+        ascon128a,
+        ascon128
+    }
+
+    private enum State
+    {
+        Uninitialized,
+        EncInit,
+        EncAad,
+        EncData,
+        EncFinal,
+        DecInit,
+        DecAad,
+        DecData,
+        DecFinal,
+    }
+
+    private final AsconParameters asconParameters;
+    private State m_state = State.Uninitialized;
+    private byte[] mac;
+    private byte[] initialAssociatedText;
+    private final String algorithmName;
+    private final int CRYPTO_KEYBYTES;
+    private final int CRYPTO_ABYTES;
+    private final int ASCON_AEAD_RATE;
+    private final int nr;
+    private long K0;
+    private long K1;
+    private long K2;
+    private long N0;
+    private long N1;
+    private final long ASCON_IV;
+    private long x0;
+    private long x1;
+    private long x2;
+    private long x3;
+    private long x4;
+    private final int m_bufferSizeDecrypt;
+    private final byte[] m_buf;
+    private int m_bufPos = 0;
+
+    public AsconEngine(AsconParameters asconParameters)
+    {
+        this.asconParameters = asconParameters;
+        switch (asconParameters)
+        {
+        case ascon80pq:
+            CRYPTO_KEYBYTES = 20;
+            CRYPTO_ABYTES = 16;
+            ASCON_AEAD_RATE = 8;
+            ASCON_IV = 0xa0400c0600000000L;
+            algorithmName = "Ascon-80pq AEAD";
+            break;
+        case ascon128a:
+            CRYPTO_KEYBYTES = 16;
+            CRYPTO_ABYTES = 16;
+            ASCON_AEAD_RATE = 16;
+            ASCON_IV = 0x80800c0800000000L;
+            algorithmName = "Ascon-128a AEAD";
+            break;
+        case ascon128:
+            CRYPTO_KEYBYTES = 16;
+            CRYPTO_ABYTES = 16;
+            ASCON_AEAD_RATE = 8;
+            ASCON_IV = 0x80400c0600000000L;
+            algorithmName = "Ascon-128 AEAD";
+            break;
+        default:
+            throw new IllegalArgumentException("invalid parameter setting for ASCON AEAD");
+        }
+        nr = (ASCON_AEAD_RATE == 8) ? 6 : 8;
+        m_bufferSizeDecrypt = ASCON_AEAD_RATE + CRYPTO_ABYTES;
+        m_buf = new byte[m_bufferSizeDecrypt];
+    }
+
+    private long PAD(int i)
+    {
+        return 0x80L << (56 - (i << 3));
+    }
+
+    private void ROUND(long C)
+    {
+        long t0 = x0 ^ x1 ^ x2 ^ x3 ^ C ^ (x1 & (x0 ^ x2 ^ x4 ^ C));
+        long t1 = x0 ^ x2 ^ x3 ^ x4 ^ C ^ ((x1 ^ x2 ^ C) & (x1 ^ x3));
+        long t2 = x1 ^ x2 ^ x4 ^ C ^ (x3 & x4);
+        long t3 = x0 ^ x1 ^ x2 ^ C ^ ((~x0) & (x3 ^ x4));
+        long t4 = x1 ^ x3 ^ x4 ^ ((x0 ^ x4) & x1);
+        x0 = t0 ^ Longs.rotateRight(t0, 19) ^ Longs.rotateRight(t0, 28);
+        x1 = t1 ^ Longs.rotateRight(t1, 39) ^ Longs.rotateRight(t1, 61);
+        x2 = ~(t2 ^ Longs.rotateRight(t2, 1) ^ Longs.rotateRight(t2, 6));
+        x3 = t3 ^ Longs.rotateRight(t3, 10) ^ Longs.rotateRight(t3, 17);
+        x4 = t4 ^ Longs.rotateRight(t4, 7) ^ Longs.rotateRight(t4, 41);
+    }
+
+    private void P(int nr)
+    {
+        if (nr >= 8)
+        {
+            if (nr == 12)
+            {
+                ROUND(0xf0L);
+                ROUND(0xe1L);
+                ROUND(0xd2L);
+                ROUND(0xc3L);
+            }
+            ROUND(0xb4L);
+            ROUND(0xa5L);
+        }
+        ROUND(0x96L);
+        ROUND(0x87L);
+        ROUND(0x78L);
+        ROUND(0x69L);
+        ROUND(0x5aL);
+        ROUND(0x4bL);
+    }
+
+    private void ascon_aeadinit()
+    {
+        /* initialize */
+        x0 = ASCON_IV;
+        if (CRYPTO_KEYBYTES == 20)
+        {
+            x0 ^= K0;
+        }
+        x1 = K1;
+        x2 = K2;
+        x3 = N0;
+        x4 = N1;
+        P(12);
+        if (CRYPTO_KEYBYTES == 20)
+        {
+            x2 ^= K0;
+        }
+        x3 ^= K1;
+        x4 ^= K2;
+    }
+
+    private void checkAAD()
+    {
+        switch (m_state)
+        {
+        case DecInit:
+            m_state = State.DecAad;
+            break;
+        case EncInit:
+            m_state = State.EncAad;
+            break;
+        case DecAad:
+        case EncAad:
+            break;
+        case EncFinal:
+            throw new IllegalStateException(getAlgorithmName() + " cannot be reused for encryption");
+        default:
+            throw new IllegalStateException(getAlgorithmName() + " needs to be initialized");
+        }
+    }
+
+    private boolean checkData()
+    {
+        switch (m_state)
+        {
+        case DecInit:
+        case DecAad:
+            finishAAD(State.DecData);
+            return false;
+        case EncInit:
+        case EncAad:
+            finishAAD(State.EncData);
+            return true;
+        case DecData:
+            return false;
+        case EncData:
+            return true;
+        case EncFinal:
+            throw new IllegalStateException(getAlgorithmName() + " cannot be reused for encryption");
+        default:
+            throw new IllegalStateException(getAlgorithmName() + " needs to be initialized");
+        }
+    }
+
+    private void processBufferAAD(byte[] buffer, int inOff)
+    {
+        x0 ^= Pack.bigEndianToLong(buffer, inOff);
+        if (ASCON_AEAD_RATE == 16)
+        {
+            x1 ^= Pack.bigEndianToLong(buffer, 8 + inOff);
+        }
+        P(nr);
+    }
+
+    private void finishAAD(State nextState)
+    {
+        // State indicates whether we ever received AAD
+        switch (m_state)
+        {
+        case DecAad:
+        case EncAad:
+            m_buf[m_bufPos] = (byte)0x80;
+            if (m_bufPos >= 8) // ASCON_AEAD_RATE == 16 is implied
+            {
+                x0 ^= Pack.bigEndianToLong(m_buf, 0);
+                x1 ^= Pack.bigEndianToLong(m_buf, 8) & (-1L << (56 - ((m_bufPos - 8) << 3)));
+            }
+            else
+            {
+                x0 ^= Pack.bigEndianToLong(m_buf, 0) & (-1L << (56 - (m_bufPos << 3)));
+            }
+            P(nr);
+            break;
+        default:
+            break;
+        }
+        // domain separation
+        x4 ^= 1L;
+        m_bufPos = 0;
+        m_state = nextState;
+    }
+
+    private void processBufferDecrypt(byte[] buffer, int bufOff, byte[] output, int outOff)
+    {
+        if (outOff + ASCON_AEAD_RATE > output.length)
+        {
+            throw new OutputLengthException("output buffer too short");
+        }
+        long t0 = Pack.bigEndianToLong(buffer, bufOff);
+        Pack.longToBigEndian(x0 ^ t0, output, outOff);
+        x0 = t0;
+
+        if (ASCON_AEAD_RATE == 16)
+        {
+            long t1 = Pack.bigEndianToLong(buffer, bufOff + 8);
+            Pack.longToBigEndian(x1 ^ t1, output, outOff + 8);
+            x1 = t1;
+        }
+        P(nr);
+    }
+
+    private void processBufferEncrypt(byte[] buffer, int bufOff, byte[] output, int outOff)
+    {
+        if (outOff + ASCON_AEAD_RATE > output.length)
+        {
+            throw new OutputLengthException("output buffer too short");
+        }
+        x0 ^= Pack.bigEndianToLong(buffer, bufOff);
+        Pack.longToBigEndian(x0, output, outOff);
+
+        if (ASCON_AEAD_RATE == 16)
+        {
+            x1 ^= Pack.bigEndianToLong(buffer, bufOff + 8);
+            Pack.longToBigEndian(x1, output, outOff + 8);
+        }
+
+        P(nr);
+    }
+
+    private void processFinalDecrypt(byte[] input, int inOff, int inLen, byte[] output, int outOff)
+    {
+        if (inLen >= 8) // ASCON_AEAD_RATE == 16 is implied
+        {
+            long c0 = Pack.bigEndianToLong(input, inOff);
+            x0 ^= c0;
+            Pack.longToBigEndian(x0, output, outOff);
+            x0 = c0;
+            inOff += 8;
+            outOff += 8;
+            inLen -= 8;
+            x1 ^= PAD(inLen);
+            if (inLen != 0)
+            {
+                long c1 = Pack.littleEndianToLong_High(input, inOff, inLen);
+                x1 ^= c1;
+                Pack.longToLittleEndian_High(x1, output, outOff, inLen);
+                x1 &= -1L >>> (inLen << 3);
+                x1 ^= c1;
+            }
+        }
+        else
+        {
+            x0 ^= PAD(inLen);
+            if (inLen != 0)
+            {
+                long c0 = Pack.littleEndianToLong_High(input, inOff, inLen);
+                x0 ^= c0;
+                Pack.longToLittleEndian_High(x0, output, outOff, inLen);
+                x0 &= -1L >>> (inLen << 3);
+                x0 ^= c0;
+            }
+        }
+
+        finishData(State.DecFinal);
+    }
+
+    private void processFinalEncrypt(byte[] input, int inOff, int inLen, byte[] output, int outOff)
+    {
+        if (inLen >= 8) // ASCON_AEAD_RATE == 16 is implied
+        {
+            x0 ^= Pack.bigEndianToLong(input, inOff);
+            Pack.longToBigEndian(x0, output, outOff);
+            inOff += 8;
+            outOff += 8;
+            inLen -= 8;
+            x1 ^= PAD(inLen);
+            if (inLen != 0)
+            {
+                x1 ^= Pack.littleEndianToLong_High(input, inOff, inLen);
+                Pack.longToLittleEndian_High(x1, output, outOff, inLen);
+            }
+        }
+        else
+        {
+            x0 ^= PAD(inLen);
+            if (inLen != 0)
+            {
+                x0 ^= Pack.littleEndianToLong_High(input, inOff, inLen);
+                Pack.longToLittleEndian_High(x0, output, outOff, inLen);
+            }
+        }
+        finishData(State.EncFinal);
+    }
+
+    private void finishData(State nextState)
+    {
+        switch (asconParameters)
+        {
+        case ascon128:
+            x1 ^= K1;
+            x2 ^= K2;
+            break;
+        case ascon128a:
+            x2 ^= K1;
+            x3 ^= K2;
+            break;
+        case ascon80pq:
+            x1 ^= (K0 << 32 | K1 >> 32);
+            x2 ^= (K1 << 32 | K2 >> 32);
+            x3 ^= K2 << 32;
+            break;
+        default:
+            throw new IllegalStateException();
+        }
+        P(12);
+        x3 ^= K1;
+        x4 ^= K2;
+
+        m_state = nextState;
+    }
+
+    public void init(boolean forEncryption, CipherParameters params)
+        throws IllegalArgumentException
+    {
+        KeyParameter key;
+        byte[] npub;
+        if (params instanceof AEADParameters)
+        {
+            AEADParameters aeadParameters = (AEADParameters)params;
+            key = aeadParameters.getKey();
+            npub = aeadParameters.getNonce();
+            initialAssociatedText = aeadParameters.getAssociatedText();
+
+            int macSizeBits = aeadParameters.getMacSize();
+            if (macSizeBits != CRYPTO_ABYTES * 8)
+            {
+                throw new IllegalArgumentException("Invalid value for MAC size: " + macSizeBits);
+            }
+        }
+        else if (params instanceof ParametersWithIV)
+        {
+            ParametersWithIV withIV = (ParametersWithIV)params;
+            key = (KeyParameter)withIV.getParameters();
+            npub = withIV.getIV();
+            initialAssociatedText = null;
+        }
+        else
+        {
+            throw new IllegalArgumentException("invalid parameters passed to Ascon");
+        }
+
+        if (key == null)
+        {
+            throw new IllegalArgumentException("Ascon Init parameters must include a key");
+        }
+        if (npub == null || npub.length != CRYPTO_ABYTES)
+        {
+            throw new IllegalArgumentException(asconParameters + " requires exactly " + CRYPTO_ABYTES + " bytes of IV");
+        }
+
+        byte[] k = key.getKey();
+        if (k.length != CRYPTO_KEYBYTES)
+        {
+            throw new IllegalArgumentException(asconParameters + " key must be " + CRYPTO_KEYBYTES + " bytes long");
+        }
+
+        CryptoServicesRegistrar.checkConstraints(new DefaultServiceProperties(
+            this.getAlgorithmName(), 128, params, Utils.getPurpose(forEncryption)));
+        N0 = Pack.bigEndianToLong(npub, 0);
+        N1 = Pack.bigEndianToLong(npub, 8);
+        if (CRYPTO_KEYBYTES == 16)
+        {
+            K1 = Pack.bigEndianToLong(k, 0);
+            K2 = Pack.bigEndianToLong(k, 8);
+        }
+        else if (CRYPTO_KEYBYTES == 20)
+        {
+            K0 = Pack.bigEndianToInt(k, 0);
+            K1 = Pack.bigEndianToLong(k, 4);
+            K2 = Pack.bigEndianToLong(k, 12);
+        }
+        else
+        {
+            throw new IllegalStateException();
+        }
+
+        m_state = forEncryption ? State.EncInit : State.DecInit;
+
+        reset(true);
+    }
+
+    public String getAlgorithmName()
+    {
+        return algorithmName;
+    }
+
+    public String getAlgorithmVersion()
+    {
+        return "v1.2";
+    }
+
+    public void processAADByte(byte in)
+    {
+        checkAAD();
+        m_buf[m_bufPos] = in;
+        if (++m_bufPos == ASCON_AEAD_RATE)
+        {
+            processBufferAAD(m_buf, 0);
+        }
+    }
+
+    public void processAADBytes(byte[] inBytes, int inOff, int len)
+    {
+        if ((inOff + len) > inBytes.length)
+        {
+            throw new DataLengthException("input buffer too short");
+        }
+        // Don't enter AAD state until we actually get input
+        if (len <= 0)
+        {
+            return;
+        }
+        checkAAD();
+        if (m_bufPos > 0)
+        {
+            int available = ASCON_AEAD_RATE - m_bufPos;
+            if (len < available)
+            {
+                System.arraycopy(inBytes, inOff, m_buf, m_bufPos, len);
+                m_bufPos += len;
+                return;
+            }
+            System.arraycopy(inBytes, inOff, m_buf, m_bufPos, available);
+            inOff += available;
+            len -= available;
+            processBufferAAD(m_buf, 0);
+            //m_bufPos = 0;
+        }
+        while (len >= ASCON_AEAD_RATE)
+        {
+            processBufferAAD(inBytes, inOff);
+            inOff += ASCON_AEAD_RATE;
+            len -= ASCON_AEAD_RATE;
+        }
+        System.arraycopy(inBytes, inOff, m_buf, 0, len);
+        m_bufPos = len;
+    }
+
+    public int processByte(byte in, byte[] out, int outOff)
+        throws DataLengthException
+    {
+        return processBytes(new byte[]{in}, 0, 1, out, outOff);
+    }
+
+    public int processBytes(byte[] inBytes, int inOff, int len, byte[] outBytes, int outOff)
+        throws DataLengthException
+    {
+        if ((inOff + len) > inBytes.length)
+        {
+            throw new DataLengthException("input buffer too short");
+        }
+        boolean forEncryption = checkData();
+        int resultLength = 0;
+
+        if (forEncryption)
+        {
+            if (m_bufPos > 0)
+            {
+                int available = ASCON_AEAD_RATE - m_bufPos;
+                if (len < available)
+                {
+                    System.arraycopy(inBytes, inOff, m_buf, m_bufPos, len);
+                    m_bufPos += len;
+                    return 0;
+                }
+
+                System.arraycopy(inBytes, inOff, m_buf, m_bufPos, available);
+                inOff += available;
+                len -= available;
+
+                processBufferEncrypt(m_buf, 0, outBytes, outOff);
+                resultLength = ASCON_AEAD_RATE;
+                //m_bufPos = 0;
+            }
+
+            while (len >= ASCON_AEAD_RATE)
+            {
+                processBufferEncrypt(inBytes, inOff, outBytes, outOff + resultLength);
+                inOff += ASCON_AEAD_RATE;
+                len -= ASCON_AEAD_RATE;
+                resultLength += ASCON_AEAD_RATE;
+            }
+        }
+        else
+        {
+            int available = m_bufferSizeDecrypt - m_bufPos;
+            if (len < available)
+            {
+                System.arraycopy(inBytes, inOff, m_buf, m_bufPos, len);
+                m_bufPos += len;
+                return 0;
+            }
+
+            // NOTE: Need 'while' here because ASCON_AEAD_RATE < CRYPTO_ABYTES in some parameter sets
+            while (m_bufPos >= ASCON_AEAD_RATE)
+            {
+                processBufferDecrypt(m_buf, 0, outBytes, outOff + resultLength);
+                m_bufPos -= ASCON_AEAD_RATE;
+                System.arraycopy(m_buf, ASCON_AEAD_RATE, m_buf, 0, m_bufPos);
+                resultLength += ASCON_AEAD_RATE;
+
+                available += ASCON_AEAD_RATE;
+                if (len < available)
+                {
+                    System.arraycopy(inBytes, inOff, m_buf, m_bufPos, len);
+                    m_bufPos += len;
+                    return resultLength;
+                }
+            }
+
+            available = ASCON_AEAD_RATE - m_bufPos;
+            System.arraycopy(inBytes, inOff, m_buf, m_bufPos, available);
+            inOff += available;
+            len -= available;
+            processBufferDecrypt(m_buf, 0, outBytes, outOff + resultLength);
+            resultLength += ASCON_AEAD_RATE;
+            //m_bufPos = 0;
+
+            while (len >= m_bufferSizeDecrypt)
+            {
+                processBufferDecrypt(inBytes, inOff, outBytes, outOff + resultLength);
+                inOff += ASCON_AEAD_RATE;
+                len -= ASCON_AEAD_RATE;
+                resultLength += ASCON_AEAD_RATE;
+            }
+        }
+
+        System.arraycopy(inBytes, inOff, m_buf, 0, len);
+        m_bufPos = len;
+
+        return resultLength;
+    }
+
+    public int doFinal(byte[] outBytes, int outOff)
+        throws IllegalStateException, InvalidCipherTextException, DataLengthException
+    {
+        boolean forEncryption = checkData();
+        int resultLength;
+        if (forEncryption)
+        {
+            resultLength = m_bufPos + CRYPTO_ABYTES;
+            if (outOff + resultLength > outBytes.length)
+            {
+                throw new OutputLengthException("output buffer too short");
+            }
+            processFinalEncrypt(m_buf, 0, m_bufPos, outBytes, outOff);
+            mac = new byte[CRYPTO_ABYTES];
+            Pack.longToBigEndian(x3, mac, 0);
+            Pack.longToBigEndian(x4, mac, 8);
+            System.arraycopy(mac, 0, outBytes, outOff + m_bufPos, CRYPTO_ABYTES);
+            reset(false);
+        }
+        else
+        {
+            if (m_bufPos < CRYPTO_ABYTES)
+            {
+                throw new InvalidCipherTextException("data too short");
+            }
+            m_bufPos -= CRYPTO_ABYTES;
+            resultLength = m_bufPos;
+            if (outOff + resultLength > outBytes.length)
+            {
+                throw new OutputLengthException("output buffer too short");
+            }
+            processFinalDecrypt(m_buf, 0, m_bufPos, outBytes, outOff);
+            x3 ^= Pack.bigEndianToLong(m_buf, m_bufPos);
+            x4 ^= Pack.bigEndianToLong(m_buf, m_bufPos + 8);
+            if ((x3 | x4) != 0L)
+            {
+                throw new InvalidCipherTextException("mac check in " + getAlgorithmName() + " failed");
+            }
+            reset(true);
+        }
+        return resultLength;
+    }
+
+    public byte[] getMac()
+    {
+        return mac;
+    }
+
+    public int getUpdateOutputSize(int len)
+    {
+        int total = Math.max(0, len);
+        switch (m_state)
+        {
+        case DecInit:
+        case DecAad:
+            total = Math.max(0, total - CRYPTO_ABYTES);
+            break;
+        case DecData:
+        case DecFinal:
+            total = Math.max(0, total + m_bufPos - CRYPTO_ABYTES);
+            break;
+        case EncData:
+        case EncFinal:
+            total += m_bufPos;
+            break;
+        default:
+            break;
+        }
+        return total - total % ASCON_AEAD_RATE;
+    }
+
+    public int getOutputSize(int len)
+    {
+        int total = Math.max(0, len);
+
+        switch (m_state)
+        {
+        case DecInit:
+        case DecAad:
+            return Math.max(0, total - CRYPTO_ABYTES);
+        case DecData:
+        case DecFinal:
+            return Math.max(0, total + m_bufPos - CRYPTO_ABYTES);
+        case EncData:
+        case EncFinal:
+            return total + m_bufPos + CRYPTO_ABYTES;
+        default:
+            return total + CRYPTO_ABYTES;
+        }
+    }
+
+    public void reset()
+    {
+        reset(true);
+    }
+
+    private void reset(boolean clearMac)
+    {
+        if (clearMac)
+        {
+            mac = null;
+        }
+        Arrays.clear(m_buf);
+        m_bufPos = 0;
+
+        switch (m_state)
+        {
+        case DecInit:
+        case EncInit:
+            break;
+        case DecAad:
+        case DecData:
+        case DecFinal:
+            m_state = State.DecInit;
+            break;
+        case EncAad:
+        case EncData:
+        case EncFinal:
+            m_state = State.EncFinal;
+            return;
+        default:
+            throw new IllegalStateException(getAlgorithmName() + " needs to be initialized");
+        }
+        ascon_aeadinit();
+        if (initialAssociatedText != null)
+        {
+            processAADBytes(initialAssociatedText, 0, initialAssociatedText.length);
+        }
+    }
+
+    public int getKeyBytesSize()
+    {
+        return CRYPTO_KEYBYTES;
+    }
+
+    public int getIVBytesSize()
+    {
+        return CRYPTO_ABYTES;
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/engines/BlowfishEngine.java b/bcprov/src/main/java/org/bouncycastle/crypto/engines/BlowfishEngine.java
index 33918c2..c44c57f 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/engines/BlowfishEngine.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/engines/BlowfishEngine.java
@@ -2,8 +2,11 @@
 
 import org.bouncycastle.crypto.BlockCipher;
 import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.CryptoServicePurpose;
+import org.bouncycastle.crypto.CryptoServicesRegistrar;
 import org.bouncycastle.crypto.DataLengthException;
 import org.bouncycastle.crypto.OutputLengthException;
+import org.bouncycastle.crypto.constraints.DefaultServiceProperties;
 import org.bouncycastle.crypto.params.KeyParameter;
 
 /**
@@ -315,6 +318,7 @@
         S2 = new int[SBOX_SK];
         S3 = new int[SBOX_SK];
         P = new int[P_SZ];
+        CryptoServicesRegistrar.checkConstraints(new DefaultServiceProperties(getAlgorithmName(), bitsOfSecurity()));
     }
 
     /**
@@ -335,6 +339,7 @@
             this.workingKey = ((KeyParameter)params).getKey();
             setKey(this.workingKey);
 
+            CryptoServicesRegistrar.checkConstraints(new DefaultServiceProperties(getAlgorithmName(), bitsOfSecurity(), params, getPurpose()));
             return;
         }
 
@@ -431,6 +436,11 @@
 
     private void setKey(byte[] key)
     {
+        if (key.length < 4 || key.length > 56)
+        {
+            throw new IllegalArgumentException("key length must be in range 32 to 448 bits");
+        }
+
         /*
          * - comments are from _Applied Crypto_, Schneier, p338
          * please be careful comparing the two, AC numbers the
@@ -575,4 +585,22 @@
         b[offset + 1] = (byte)(in >> 16);
         b[offset]     = (byte)(in >> 24);
     }
+
+    private int bitsOfSecurity()
+    {
+        if (workingKey == null)
+        {
+            return 256;
+        }
+        return (workingKey.length > 32) ? 256 : workingKey.length * 8;
+    }
+
+    private CryptoServicePurpose getPurpose()
+    {
+        if (workingKey == null)
+        {
+            return CryptoServicePurpose.ANY;
+        }
+        return encrypting ? CryptoServicePurpose.ENCRYPTION : CryptoServicePurpose.DECRYPTION;
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/engines/DESBase.java b/bcprov/src/main/java/org/bouncycastle/crypto/engines/DESBase.java
new file mode 100644
index 0000000..c7f05e2
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/engines/DESBase.java
@@ -0,0 +1,407 @@
+package org.bouncycastle.crypto.engines;
+
+import org.bouncycastle.util.Pack;
+
+/**
+ * a class that provides a basic DES engine.
+ */
+class DESBase
+{
+    protected static final int  BLOCK_SIZE = 8;
+
+    /**
+     * standard constructor.
+     */
+    public DESBase()
+    {
+    }
+
+    /**
+     * what follows is mainly taken from "Applied Cryptography", by
+     * Bruce Schneier, however it also bears great resemblance to Richard
+     * Outerbridge's D3DES...
+     */
+
+//    private static final short[]    Df_Key =
+//        {
+//            0x01,0x23,0x45,0x67,0x89,0xab,0xcd,0xef,
+//            0xfe,0xdc,0xba,0x98,0x76,0x54,0x32,0x10,
+//            0x89,0xab,0xcd,0xef,0x01,0x23,0x45,0x67
+//        };
+
+    private static final short[]    bytebit =
+        {
+            0200, 0100, 040, 020, 010, 04, 02, 01
+        };
+
+    private static final int[]    bigbyte =
+        {
+            0x800000, 0x400000, 0x200000, 0x100000,
+            0x80000,  0x40000,  0x20000,  0x10000,
+            0x8000,      0x4000,   0x2000,   0x1000,
+            0x800,    0x400,    0x200,    0x100,
+            0x80,      0x40,        0x20,     0x10,
+            0x8,      0x4,      0x2,      0x1
+        };
+
+    /*
+     * Use the key schedule specified in the Standard (ANSI X3.92-1981).
+     */
+
+    private static final byte[]    pc1 =
+        {
+            56, 48, 40, 32, 24, 16,  8,   0, 57, 49, 41, 33, 25, 17,
+             9,  1, 58, 50, 42, 34, 26,  18, 10,  2, 59, 51, 43, 35,
+            62, 54, 46, 38, 30, 22, 14,   6, 61, 53, 45, 37, 29, 21,
+            13,  5, 60, 52, 44, 36, 28,  20, 12,  4, 27, 19, 11,  3
+        };
+
+    private static final byte[] totrot =
+        {
+            1, 2, 4, 6, 8, 10, 12, 14,
+            15, 17, 19, 21, 23, 25, 27, 28
+        };
+
+    private static final byte[] pc2 =
+        {
+            13, 16, 10, 23,  0,  4,  2, 27, 14,  5, 20,  9,
+            22, 18, 11,  3, 25,  7, 15,  6, 26, 19, 12,  1,
+            40, 51, 30, 36, 46, 54, 29, 39, 50, 44, 32, 47,
+            43, 48, 38, 55, 33, 52, 45, 41, 49, 35, 28, 31
+        };
+
+    private static final int[] SP1 = {
+        0x01010400, 0x00000000, 0x00010000, 0x01010404,
+        0x01010004, 0x00010404, 0x00000004, 0x00010000,
+        0x00000400, 0x01010400, 0x01010404, 0x00000400,
+        0x01000404, 0x01010004, 0x01000000, 0x00000004,
+        0x00000404, 0x01000400, 0x01000400, 0x00010400,
+        0x00010400, 0x01010000, 0x01010000, 0x01000404,
+        0x00010004, 0x01000004, 0x01000004, 0x00010004,
+        0x00000000, 0x00000404, 0x00010404, 0x01000000,
+        0x00010000, 0x01010404, 0x00000004, 0x01010000,
+        0x01010400, 0x01000000, 0x01000000, 0x00000400,
+        0x01010004, 0x00010000, 0x00010400, 0x01000004,
+        0x00000400, 0x00000004, 0x01000404, 0x00010404,
+        0x01010404, 0x00010004, 0x01010000, 0x01000404,
+        0x01000004, 0x00000404, 0x00010404, 0x01010400,
+        0x00000404, 0x01000400, 0x01000400, 0x00000000,
+        0x00010004, 0x00010400, 0x00000000, 0x01010004
+    };
+
+    private static final int[] SP2 = {
+        0x80108020, 0x80008000, 0x00008000, 0x00108020,
+        0x00100000, 0x00000020, 0x80100020, 0x80008020,
+        0x80000020, 0x80108020, 0x80108000, 0x80000000,
+        0x80008000, 0x00100000, 0x00000020, 0x80100020,
+        0x00108000, 0x00100020, 0x80008020, 0x00000000,
+        0x80000000, 0x00008000, 0x00108020, 0x80100000,
+        0x00100020, 0x80000020, 0x00000000, 0x00108000,
+        0x00008020, 0x80108000, 0x80100000, 0x00008020,
+        0x00000000, 0x00108020, 0x80100020, 0x00100000,
+        0x80008020, 0x80100000, 0x80108000, 0x00008000,
+        0x80100000, 0x80008000, 0x00000020, 0x80108020,
+        0x00108020, 0x00000020, 0x00008000, 0x80000000,
+        0x00008020, 0x80108000, 0x00100000, 0x80000020,
+        0x00100020, 0x80008020, 0x80000020, 0x00100020,
+        0x00108000, 0x00000000, 0x80008000, 0x00008020,
+        0x80000000, 0x80100020, 0x80108020, 0x00108000
+    };
+
+    private static final int[] SP3 = {
+        0x00000208, 0x08020200, 0x00000000, 0x08020008,
+        0x08000200, 0x00000000, 0x00020208, 0x08000200,
+        0x00020008, 0x08000008, 0x08000008, 0x00020000,
+        0x08020208, 0x00020008, 0x08020000, 0x00000208,
+        0x08000000, 0x00000008, 0x08020200, 0x00000200,
+        0x00020200, 0x08020000, 0x08020008, 0x00020208,
+        0x08000208, 0x00020200, 0x00020000, 0x08000208,
+        0x00000008, 0x08020208, 0x00000200, 0x08000000,
+        0x08020200, 0x08000000, 0x00020008, 0x00000208,
+        0x00020000, 0x08020200, 0x08000200, 0x00000000,
+        0x00000200, 0x00020008, 0x08020208, 0x08000200,
+        0x08000008, 0x00000200, 0x00000000, 0x08020008,
+        0x08000208, 0x00020000, 0x08000000, 0x08020208,
+        0x00000008, 0x00020208, 0x00020200, 0x08000008,
+        0x08020000, 0x08000208, 0x00000208, 0x08020000,
+        0x00020208, 0x00000008, 0x08020008, 0x00020200
+    };
+
+    private static final int[] SP4 = {
+        0x00802001, 0x00002081, 0x00002081, 0x00000080,
+        0x00802080, 0x00800081, 0x00800001, 0x00002001,
+        0x00000000, 0x00802000, 0x00802000, 0x00802081,
+        0x00000081, 0x00000000, 0x00800080, 0x00800001,
+        0x00000001, 0x00002000, 0x00800000, 0x00802001,
+        0x00000080, 0x00800000, 0x00002001, 0x00002080,
+        0x00800081, 0x00000001, 0x00002080, 0x00800080,
+        0x00002000, 0x00802080, 0x00802081, 0x00000081,
+        0x00800080, 0x00800001, 0x00802000, 0x00802081,
+        0x00000081, 0x00000000, 0x00000000, 0x00802000,
+        0x00002080, 0x00800080, 0x00800081, 0x00000001,
+        0x00802001, 0x00002081, 0x00002081, 0x00000080,
+        0x00802081, 0x00000081, 0x00000001, 0x00002000,
+        0x00800001, 0x00002001, 0x00802080, 0x00800081,
+        0x00002001, 0x00002080, 0x00800000, 0x00802001,
+        0x00000080, 0x00800000, 0x00002000, 0x00802080
+    };
+
+    private static final int[] SP5 = {
+        0x00000100, 0x02080100, 0x02080000, 0x42000100,
+        0x00080000, 0x00000100, 0x40000000, 0x02080000,
+        0x40080100, 0x00080000, 0x02000100, 0x40080100,
+        0x42000100, 0x42080000, 0x00080100, 0x40000000,
+        0x02000000, 0x40080000, 0x40080000, 0x00000000,
+        0x40000100, 0x42080100, 0x42080100, 0x02000100,
+        0x42080000, 0x40000100, 0x00000000, 0x42000000,
+        0x02080100, 0x02000000, 0x42000000, 0x00080100,
+        0x00080000, 0x42000100, 0x00000100, 0x02000000,
+        0x40000000, 0x02080000, 0x42000100, 0x40080100,
+        0x02000100, 0x40000000, 0x42080000, 0x02080100,
+        0x40080100, 0x00000100, 0x02000000, 0x42080000,
+        0x42080100, 0x00080100, 0x42000000, 0x42080100,
+        0x02080000, 0x00000000, 0x40080000, 0x42000000,
+        0x00080100, 0x02000100, 0x40000100, 0x00080000,
+        0x00000000, 0x40080000, 0x02080100, 0x40000100
+    };
+
+    private static final int[] SP6 = {
+        0x20000010, 0x20400000, 0x00004000, 0x20404010,
+        0x20400000, 0x00000010, 0x20404010, 0x00400000,
+        0x20004000, 0x00404010, 0x00400000, 0x20000010,
+        0x00400010, 0x20004000, 0x20000000, 0x00004010,
+        0x00000000, 0x00400010, 0x20004010, 0x00004000,
+        0x00404000, 0x20004010, 0x00000010, 0x20400010,
+        0x20400010, 0x00000000, 0x00404010, 0x20404000,
+        0x00004010, 0x00404000, 0x20404000, 0x20000000,
+        0x20004000, 0x00000010, 0x20400010, 0x00404000,
+        0x20404010, 0x00400000, 0x00004010, 0x20000010,
+        0x00400000, 0x20004000, 0x20000000, 0x00004010,
+        0x20000010, 0x20404010, 0x00404000, 0x20400000,
+        0x00404010, 0x20404000, 0x00000000, 0x20400010,
+        0x00000010, 0x00004000, 0x20400000, 0x00404010,
+        0x00004000, 0x00400010, 0x20004010, 0x00000000,
+        0x20404000, 0x20000000, 0x00400010, 0x20004010
+    };
+
+    private static final int[] SP7 = {
+        0x00200000, 0x04200002, 0x04000802, 0x00000000,
+        0x00000800, 0x04000802, 0x00200802, 0x04200800,
+        0x04200802, 0x00200000, 0x00000000, 0x04000002,
+        0x00000002, 0x04000000, 0x04200002, 0x00000802,
+        0x04000800, 0x00200802, 0x00200002, 0x04000800,
+        0x04000002, 0x04200000, 0x04200800, 0x00200002,
+        0x04200000, 0x00000800, 0x00000802, 0x04200802,
+        0x00200800, 0x00000002, 0x04000000, 0x00200800,
+        0x04000000, 0x00200800, 0x00200000, 0x04000802,
+        0x04000802, 0x04200002, 0x04200002, 0x00000002,
+        0x00200002, 0x04000000, 0x04000800, 0x00200000,
+        0x04200800, 0x00000802, 0x00200802, 0x04200800,
+        0x00000802, 0x04000002, 0x04200802, 0x04200000,
+        0x00200800, 0x00000000, 0x00000002, 0x04200802,
+        0x00000000, 0x00200802, 0x04200000, 0x00000800,
+        0x04000002, 0x04000800, 0x00000800, 0x00200002
+    };
+
+    private static final int[] SP8 = {
+        0x10001040, 0x00001000, 0x00040000, 0x10041040,
+        0x10000000, 0x10001040, 0x00000040, 0x10000000,
+        0x00040040, 0x10040000, 0x10041040, 0x00041000,
+        0x10041000, 0x00041040, 0x00001000, 0x00000040,
+        0x10040000, 0x10000040, 0x10001000, 0x00001040,
+        0x00041000, 0x00040040, 0x10040040, 0x10041000,
+        0x00001040, 0x00000000, 0x00000000, 0x10040040,
+        0x10000040, 0x10001000, 0x00041040, 0x00040000,
+        0x00041040, 0x00040000, 0x10041000, 0x00001000,
+        0x00000040, 0x10040040, 0x00001000, 0x00041040,
+        0x10001000, 0x00000040, 0x10000040, 0x10040000,
+        0x10040040, 0x10000000, 0x00040000, 0x10001040,
+        0x00000000, 0x10041040, 0x00040040, 0x10000040,
+        0x10040000, 0x10001000, 0x10001040, 0x00000000,
+        0x10041040, 0x00041000, 0x00041000, 0x00001040,
+        0x00001040, 0x00040040, 0x10000000, 0x10041000
+    };
+
+    /**
+     * generate an integer based working key based on our secret key
+     * and what we processing we are planning to do.
+     *
+     * Acknowledgements for this routine go to James Gillogly &amp; Phil Karn.
+     *         (whoever, and wherever they are!).
+     */
+    protected int[] generateWorkingKey(
+        boolean encrypting,
+        byte[]  key)
+    {
+        int[]       newKey = new int[32];
+        boolean[]   pc1m = new boolean[56],
+                    pcr = new boolean[56];
+
+        for (int j = 0; j < 56; j++)
+        {
+            int    l = pc1[j];
+
+            pc1m[j] = ((key[l >>> 3] & bytebit[l & 07]) != 0);
+        }
+
+        for (int i = 0; i < 16; i++)
+        {
+            int    l, m, n;
+
+            if (encrypting)
+            {
+                m = i << 1;
+            }
+            else
+            {
+                m = (15 - i) << 1;
+            }
+
+            n = m + 1;
+            newKey[m] = newKey[n] = 0;
+
+            for (int j = 0; j < 28; j++)
+            {
+                l = j + totrot[i];
+                if (l < 28)
+                {
+                    pcr[j] = pc1m[l];
+                }
+                else
+                {
+                    pcr[j] = pc1m[l - 28];
+                }
+            }
+
+            for (int j = 28; j < 56; j++)
+            {
+                l = j + totrot[i];
+                if (l < 56)
+                {
+                    pcr[j] = pc1m[l];
+                }
+                else
+                {
+                    pcr[j] = pc1m[l - 28];
+                }
+            }
+
+            for (int j = 0; j < 24; j++)
+            {
+                if (pcr[pc2[j]])
+                {
+                    newKey[m] |= bigbyte[j];
+                }
+
+                if (pcr[pc2[j + 24]])
+                {
+                    newKey[n] |= bigbyte[j];
+                }
+            }
+        }
+
+        //
+        // store the processed key
+        //
+        for (int i = 0; i != 32; i += 2)
+        {
+            int    i1, i2;
+
+            i1 = newKey[i];
+            i2 = newKey[i + 1];
+
+            newKey[i] = ((i1 & 0x00fc0000) << 6) | ((i1 & 0x00000fc0) << 10)
+                                   | ((i2 & 0x00fc0000) >>> 10) | ((i2 & 0x00000fc0) >>> 6);
+
+            newKey[i + 1] = ((i1 & 0x0003f000) << 12) | ((i1 & 0x0000003f) << 16)
+                                   | ((i2 & 0x0003f000) >>> 4) | (i2 & 0x0000003f);
+        }
+
+        return newKey;
+    }
+
+    /**
+     * the DES engine.
+     */
+    protected void desFunc(
+        int[]   wKey,
+        byte[]  in,
+        int     inOff,
+        byte[]  out,
+        int     outOff)
+    {
+        int     work, right, left;
+
+        left = Pack.bigEndianToInt(in, inOff);
+        right = Pack.bigEndianToInt(in, inOff + 4);
+
+        work = ((left >>> 4) ^ right) & 0x0f0f0f0f;
+        right ^= work;
+        left ^= (work << 4);
+        work = ((left >>> 16) ^ right) & 0x0000ffff;
+        right ^= work;
+        left ^= (work << 16);
+        work = ((right >>> 2) ^ left) & 0x33333333;
+        left ^= work;
+        right ^= (work << 2);
+        work = ((right >>> 8) ^ left) & 0x00ff00ff;
+        left ^= work;
+        right ^= (work << 8);
+        right = (right << 1) | (right >>> 31);
+        work = (left ^ right) & 0xaaaaaaaa;
+        left ^= work;
+        right ^= work;
+        left = (left << 1) | (left >>> 31);
+
+        for (int round = 0; round < 8; round++)
+        {
+            int     fval;
+
+            work  = (right << 28) | (right >>> 4);
+            work ^= wKey[round * 4 + 0];
+            fval  = SP7[ work      & 0x3f];
+            fval |= SP5[(work >>>  8) & 0x3f];
+            fval |= SP3[(work >>> 16) & 0x3f];
+            fval |= SP1[(work >>> 24) & 0x3f];
+            work  = right ^ wKey[round * 4 + 1];
+            fval |= SP8[ work      & 0x3f];
+            fval |= SP6[(work >>>  8) & 0x3f];
+            fval |= SP4[(work >>> 16) & 0x3f];
+            fval |= SP2[(work >>> 24) & 0x3f];
+            left ^= fval;
+            work  = (left << 28) | (left >>> 4);
+            work ^= wKey[round * 4 + 2];
+            fval  = SP7[ work      & 0x3f];
+            fval |= SP5[(work >>>  8) & 0x3f];
+            fval |= SP3[(work >>> 16) & 0x3f];
+            fval |= SP1[(work >>> 24) & 0x3f];
+            work  = left ^ wKey[round * 4 + 3];
+            fval |= SP8[ work      & 0x3f];
+            fval |= SP6[(work >>>  8) & 0x3f];
+            fval |= SP4[(work >>> 16) & 0x3f];
+            fval |= SP2[(work >>> 24) & 0x3f];
+            right ^= fval;
+        }
+
+        right = (right << 31) | (right >>> 1);
+        work = (left ^ right) & 0xaaaaaaaa;
+        left ^= work;
+        right ^= work;
+        left = (left << 31) | (left >>> 1);
+        work = ((left >>> 8) ^ right) & 0x00ff00ff;
+        right ^= work;
+        left ^= (work << 8);
+        work = ((left >>> 2) ^ right) & 0x33333333;
+        right ^= work;
+        left ^= (work << 2);
+        work = ((right >>> 16) ^ left) & 0x0000ffff;
+        left ^= work;
+        right ^= (work << 16);
+        work = ((right >>> 4) ^ left) & 0x0f0f0f0f;
+        left ^= work;
+        right ^= (work << 4);
+
+        Pack.intToBigEndian(right, out, outOff);
+        Pack.intToBigEndian(left, out, outOff + 4);
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/engines/DESEngine.java b/bcprov/src/main/java/org/bouncycastle/crypto/engines/DESEngine.java
index 58ea618..51ba9bd 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/engines/DESEngine.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/engines/DESEngine.java
@@ -2,8 +2,10 @@
 
 import org.bouncycastle.crypto.BlockCipher;
 import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.CryptoServicesRegistrar;
 import org.bouncycastle.crypto.DataLengthException;
 import org.bouncycastle.crypto.OutputLengthException;
+import org.bouncycastle.crypto.constraints.DefaultServiceProperties;
 import org.bouncycastle.crypto.params.KeyParameter;
 import org.bouncycastle.util.Pack;
 
@@ -11,10 +13,12 @@
  * a class that provides a basic DES engine.
  */
 public class DESEngine
+    extends DESBase
     implements BlockCipher
 {
     protected static final int  BLOCK_SIZE = 8;
 
+    private boolean             forEncryption;
     private int[]               workingKey = null;
 
     /**
@@ -22,6 +26,7 @@
      */
     public DESEngine()
     {
+        CryptoServicesRegistrar.checkConstraints(new DefaultServiceProperties(getAlgorithmName(), 56));
     }
 
     /**
@@ -38,14 +43,17 @@
     {
         if (params instanceof KeyParameter)
         {
-            if (((KeyParameter)params).getKey().length > 8)
+            if (((KeyParameter)params).getKeyLength() > 8)
             {
                 throw new IllegalArgumentException("DES key too long - should be 8 bytes");
             }
-            
+
+            forEncryption = encrypting;
             workingKey = generateWorkingKey(encrypting,
                                   ((KeyParameter)params).getKey());
 
+            CryptoServicesRegistrar.checkConstraints(new DefaultServiceProperties(getAlgorithmName(), 56, params, Utils.getPurpose(forEncryption)));
+
             return;
         }
 
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/engines/DESedeEngine.java b/bcprov/src/main/java/org/bouncycastle/crypto/engines/DESedeEngine.java
index 513eccd..09cab6b 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/engines/DESedeEngine.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/engines/DESedeEngine.java
@@ -1,42 +1,47 @@
 package org.bouncycastle.crypto.engines;
 
+import org.bouncycastle.crypto.BlockCipher;
 import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.CryptoServicesRegistrar;
 import org.bouncycastle.crypto.DataLengthException;
 import org.bouncycastle.crypto.OutputLengthException;
+import org.bouncycastle.crypto.constraints.DefaultServiceProperties;
 import org.bouncycastle.crypto.params.KeyParameter;
 
 /**
  * a class that provides a basic DESede (or Triple DES) engine.
  */
 public class DESedeEngine
-    extends DESEngine
+    extends DESBase
+    implements BlockCipher
 {
-    protected static final int  BLOCK_SIZE = 8;
+    protected static final int BLOCK_SIZE = 8;
 
-    private int[]               workingKey1 = null;
-    private int[]               workingKey2 = null;
-    private int[]               workingKey3 = null;
+    private int[] workingKey1 = null;
+    private int[] workingKey2 = null;
+    private int[] workingKey3 = null;
 
-    private boolean             forEncryption;
+    private boolean forEncryption;
 
     /**
      * standard constructor.
      */
     public DESedeEngine()
     {
+        CryptoServicesRegistrar.checkConstraints(new DefaultServiceProperties(getAlgorithmName(), bitsOfSecurity()));
     }
 
     /**
      * initialise a DESede cipher.
      *
      * @param encrypting whether or not we are for encryption.
-     * @param params the parameters required to set up the cipher.
-     * @exception IllegalArgumentException if the params argument is
-     * inappropriate.
+     * @param params     the parameters required to set up the cipher.
+     * @throws IllegalArgumentException if the params argument is
+     *                                  inappropriate.
      */
     public void init(
-        boolean           encrypting,
-        CipherParameters  params)
+        boolean encrypting,
+        CipherParameters params)
     {
         if (!(params instanceof KeyParameter))
         {
@@ -70,6 +75,8 @@
         {
             workingKey3 = workingKey1;
         }
+
+        CryptoServicesRegistrar.checkConstraints(new DefaultServiceProperties(getAlgorithmName(), bitsOfSecurity(), params, Utils.getPurpose(forEncryption)));
     }
 
     public String getAlgorithmName()
@@ -124,4 +131,14 @@
     public void reset()
     {
     }
+
+    // Service Definitions
+    private int bitsOfSecurity()
+    {
+        if (workingKey1 != null && workingKey1 == workingKey3)
+        {
+            return 80;
+        }
+        return 112;
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/engines/DESedeWrapEngine.java b/bcprov/src/main/java/org/bouncycastle/crypto/engines/DESedeWrapEngine.java
index 9978d35..c4ee7b3 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/engines/DESedeWrapEngine.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/engines/DESedeWrapEngine.java
@@ -182,8 +182,8 @@
       System.arraycopy(this.iv, 0, TEMP2, 0, this.iv.length);
       System.arraycopy(TEMP1, 0, TEMP2, this.iv.length, TEMP1.length);
 
-      // Reverse the order of the octets in TEMP2 and call the result TEMP3.
-      byte[] TEMP3 = reverse(TEMP2);
+      // Reverse the order of the octets in TEMP2.
+      Arrays.reverseInPlace(TEMP2);
 
       // Encrypt TEMP3 in CBC mode using the KEK and an initialization vector
       // of 0x 4a dd a2 2c 79 e8 21 05. The resulting cipher text is the desired
@@ -192,12 +192,12 @@
 
       this.engine.init(true, param2);
 
-      for (int currentBytePos = 0; currentBytePos != TEMP3.length; currentBytePos += blockSize) 
+      for (int currentBytePos = 0; currentBytePos != TEMP2.length; currentBytePos += blockSize) 
       {
-         engine.processBlock(TEMP3, currentBytePos, TEMP3, currentBytePos);
+         engine.processBlock(TEMP2, currentBytePos, TEMP2, currentBytePos);
       }
 
-      return TEMP3;
+      return TEMP2;
    }
 
    /**
@@ -250,15 +250,15 @@
 
       this.engine.init(false, param2);
 
-      byte TEMP3[] = new byte[inLen];
+      byte TEMP2[] = new byte[inLen];
 
       for (int currentBytePos = 0; currentBytePos != inLen; currentBytePos += blockSize) 
       {
-         engine.processBlock(in, inOff + currentBytePos, TEMP3, currentBytePos);
+         engine.processBlock(in, inOff + currentBytePos, TEMP2, currentBytePos);
       }
 
-      // Reverse the order of the octets in TEMP3 and call the result TEMP2.
-      byte[] TEMP2 = reverse(TEMP3);
+      // Reverse the order of the octets in TEMP2.
+      Arrays.reverseInPlace(TEMP2);
 
       // Decompose TEMP2 into IV, the first 8 octets, and TEMP1, the remaining octets.
       this.iv = new byte[8];
@@ -341,14 +341,4 @@
     {
         return Arrays.constantTimeAreEqual(calculateCMSKeyChecksum(key), checksum);
     }
-
-    private static byte[] reverse(byte[] bs)
-    {
-        byte[] result = new byte[bs.length];
-        for (int i = 0; i < bs.length; i++) 
-        {
-           result[i] = bs[bs.length - (i + 1)];
-        }
-        return result;
-    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/engines/RC2Engine.java b/bcprov/src/main/java/org/bouncycastle/crypto/engines/RC2Engine.java
index 02cb881..bd63b4d 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/engines/RC2Engine.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/engines/RC2Engine.java
@@ -1,9 +1,7 @@
 package org.bouncycastle.crypto.engines;
 
-import org.bouncycastle.crypto.BlockCipher;
-import org.bouncycastle.crypto.CipherParameters;
-import org.bouncycastle.crypto.DataLengthException;
-import org.bouncycastle.crypto.OutputLengthException;
+import org.bouncycastle.crypto.*;
+import org.bouncycastle.crypto.constraints.DefaultServiceProperties;
 import org.bouncycastle.crypto.params.KeyParameter;
 import org.bouncycastle.crypto.params.RC2Parameters;
 
@@ -122,17 +120,18 @@
         CipherParameters  params)
     {
         this.encrypting = encrypting;
-
+        byte[] key;
         if (params instanceof RC2Parameters)
         {
             RC2Parameters   param = (RC2Parameters)params;
 
             workingKey = generateWorkingKey(param.getKey(),
                                             param.getEffectiveKeyBits());
+            key = param.getKey();
         }
         else if (params instanceof KeyParameter)
         {
-            byte[]    key = ((KeyParameter)params).getKey();
+            key = ((KeyParameter)params).getKey();
 
             workingKey = generateWorkingKey(key, key.length * 8);
         }
@@ -141,6 +140,7 @@
             throw new IllegalArgumentException("invalid parameter passed to RC2 init - " + params.getClass().getName());
         }
 
+        CryptoServicesRegistrar.checkConstraints(new DefaultServiceProperties(getAlgorithmName(), key.length * 8, params, Utils.getPurpose(encrypting)));
     }
 
     public void reset()
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/engines/RC4Engine.java b/bcprov/src/main/java/org/bouncycastle/crypto/engines/RC4Engine.java
index c1ceaa4..cbdaa31 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/engines/RC4Engine.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/engines/RC4Engine.java
@@ -1,12 +1,15 @@
 package org.bouncycastle.crypto.engines;
 
 import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.CryptoServicesRegistrar;
 import org.bouncycastle.crypto.DataLengthException;
 import org.bouncycastle.crypto.OutputLengthException;
 import org.bouncycastle.crypto.StreamCipher;
+import org.bouncycastle.crypto.constraints.DefaultServiceProperties;
 import org.bouncycastle.crypto.params.KeyParameter;
 
-public class RC4Engine implements StreamCipher
+public class RC4Engine
+    implements StreamCipher
 {
     private final static int STATE_LENGTH = 256;
 
@@ -19,6 +22,12 @@
     private int         x = 0;
     private int         y = 0;
     private byte[]      workingKey = null;
+    private boolean     forEncryption;
+
+    public RC4Engine()
+    {
+        CryptoServicesRegistrar.checkConstraints(new DefaultServiceProperties(getAlgorithmName(), 20));
+    }
 
     /**
      * initialise a RC4 cipher.
@@ -30,8 +39,7 @@
      */
     public void init(
         boolean             forEncryption, 
-        CipherParameters     params
-   )
+        CipherParameters     params)
     {
         if (params instanceof KeyParameter)
         {
@@ -40,9 +48,12 @@
              * symmetrical, so the 'forEncryption' is 
              * irrelevant.
              */
-            workingKey = ((KeyParameter)params).getKey();
+            this.workingKey = ((KeyParameter)params).getKey();
+            this.forEncryption = forEncryption;
             setKey(workingKey);
 
+            CryptoServicesRegistrar.checkConstraints(new DefaultServiceProperties(getAlgorithmName(), 20, params, Utils.getPurpose(forEncryption)));
+
             return;
         }
 
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/engines/RFC3394WrapEngine.java b/bcprov/src/main/java/org/bouncycastle/crypto/engines/RFC3394WrapEngine.java
index 46e5cbe..955c4cc 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/engines/RFC3394WrapEngine.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/engines/RFC3394WrapEngine.java
@@ -20,14 +20,15 @@
 public class RFC3394WrapEngine
     implements Wrapper
 {
-    private BlockCipher     engine;
-    private boolean         wrapCipherMode;
-    private KeyParameter    param;
-    private boolean         forWrapping;
+    private static final byte[] DEFAULT_IV = { (byte)0xa6, (byte)0xa6, (byte)0xa6, (byte)0xa6, (byte)0xa6, (byte)0xa6,
+        (byte)0xa6, (byte)0xa6 };
 
-    private byte[]          iv = {
-                              (byte)0xa6, (byte)0xa6, (byte)0xa6, (byte)0xa6,
-                              (byte)0xa6, (byte)0xa6, (byte)0xa6, (byte)0xa6 };
+    private final BlockCipher engine;
+    private final boolean wrapCipherMode;
+    private final byte[] iv = new byte[8];
+
+    private KeyParameter param = null;
+    private boolean forWrapping = true;
 
     /**
      * Create a RFC 3394 WrapEngine specifying the encrypt for wrapping, decrypt for unwrapping.
@@ -48,7 +49,7 @@
     public RFC3394WrapEngine(BlockCipher engine, boolean useReverseDirection)
     {
         this.engine = engine;
-        this.wrapCipherMode = (useReverseDirection) ? false : true;
+        this.wrapCipherMode = !useReverseDirection;
     }
 
     public void init(
@@ -65,15 +66,24 @@
         if (param instanceof KeyParameter)
         {
             this.param = (KeyParameter)param;
+            System.arraycopy(DEFAULT_IV, 0, iv, 0, 8);
         }
         else if (param instanceof ParametersWithIV)
         {
-            this.iv = ((ParametersWithIV)param).getIV();
-            this.param = (KeyParameter)((ParametersWithIV) param).getParameters();
-            if (this.iv.length != 8)
+            ParametersWithIV withIV = (ParametersWithIV)param;
+
+            byte[] iv = withIV.getIV();
+            if (iv.length != 8)
             {
                throw new IllegalArgumentException("IV not equal to 8");
             }
+
+            this.param = (KeyParameter)withIV.getParameters();
+            System.arraycopy(iv, 0, this.iv, 0, 8);
+        }
+        else
+        {
+            // TODO Throw an exception for bad parameters?
         }
     }
 
@@ -91,6 +101,10 @@
         {
             throw new IllegalStateException("not set for wrapping");
         }
+        if (inLen < 8)
+        {
+            throw new DataLengthException("wrap data must be at least 8 bytes");
+        }
 
         int     n = inLen / 8;
 
@@ -99,34 +113,41 @@
             throw new DataLengthException("wrap data must be a multiple of 8 bytes");
         }
 
-        byte[]  block = new byte[inLen + iv.length];
-        byte[]  buf = new byte[8 + iv.length];
+        engine.init(wrapCipherMode, param);
 
+        byte[] block = new byte[inLen + iv.length];
         System.arraycopy(iv, 0, block, 0, iv.length);
         System.arraycopy(in, inOff, block, iv.length, inLen);
 
-        engine.init(wrapCipherMode, param);
-
-        for (int j = 0; j != 6; j++)
+        if (n == 1)
         {
-            for (int i = 1; i <= n; i++)
+            engine.processBlock(block, 0, block, 0);
+        }
+        else
+        {
+            byte[] buf = new byte[8 + iv.length];
+
+            for (int j = 0; j != 6; j++)
             {
-                System.arraycopy(block, 0, buf, 0, iv.length);
-                System.arraycopy(block, 8 * i, buf, iv.length, 8);
-                engine.processBlock(buf, 0, buf, 0);
-
-                int t = n * j + i;
-                for (int k = 1; t != 0; k++)
+                for (int i = 1; i <= n; i++)
                 {
-                    byte    v = (byte)t;
+                    System.arraycopy(block, 0, buf, 0, iv.length);
+                    System.arraycopy(block, 8 * i, buf, iv.length, 8);
+                    engine.processBlock(buf, 0, buf, 0);
 
-                    buf[iv.length - k] ^= v;
+                    int t = n * j + i;
+                    for (int k = 1; t != 0; k++)
+                    {
+                        byte    v = (byte)t;
 
-                    t >>>= 8;
+                        buf[iv.length - k] ^= v;
+
+                        t >>>= 8;
+                    }
+
+                    System.arraycopy(buf, 0, block, 0, 8);
+                    System.arraycopy(buf, 8, block, 8 * i, 8);
                 }
-
-                System.arraycopy(buf, 0, block, 0, 8);
-                System.arraycopy(buf, 8, block, 8 * i, 8);
             }
         }
 
@@ -143,6 +164,10 @@
         {
             throw new IllegalStateException("not set for unwrapping");
         }
+        if (inLen < 16)
+        {
+            throw new InvalidCipherTextException("unwrap data too short");
+        }
 
         int     n = inLen / 8;
 
@@ -151,43 +176,89 @@
             throw new InvalidCipherTextException("unwrap data must be a multiple of 8 bytes");
         }
 
-        byte[]  block = new byte[inLen - iv.length];
-        byte[]  a = new byte[iv.length];
-        byte[]  buf = new byte[8 + iv.length];
-
-        System.arraycopy(in, inOff, a, 0, iv.length);
-        System.arraycopy(in, inOff + iv.length, block, 0, inLen - iv.length);
-
         engine.init(!wrapCipherMode, param);
 
+        byte[] block = new byte[inLen - iv.length];
+        byte[] a = new byte[iv.length];
+        byte[] buf = new byte[8 + iv.length];
+
         n = n - 1;
 
-        for (int j = 5; j >= 0; j--)
+        if (n == 1)
         {
-            for (int i = n; i >= 1; i--)
+            engine.processBlock(in, inOff, buf, 0);
+            System.arraycopy(buf, 0, a, 0, iv.length);
+            System.arraycopy(buf, iv.length, block, 0, 8);
+        }
+        else
+        {
+            System.arraycopy(in, inOff, a, 0, iv.length);
+            System.arraycopy(in, inOff + iv.length, block, 0, inLen - iv.length);
+
+            for (int j = 5; j >= 0; j--)
             {
-                System.arraycopy(a, 0, buf, 0, iv.length);
-                System.arraycopy(block, 8 * (i - 1), buf, iv.length, 8);
-
-                int t = n * j + i;
-                for (int k = 1; t != 0; k++)
+                for (int i = n; i >= 1; i--)
                 {
-                    byte    v = (byte)t;
-
-                    buf[iv.length - k] ^= v;
-
-                    t >>>= 8;
+                    System.arraycopy(a, 0, buf, 0, iv.length);
+                    System.arraycopy(block, 8 * (i - 1), buf, iv.length, 8);
+    
+                    int t = n * j + i;
+                    for (int k = 1; t != 0; k++)
+                    {
+                        byte    v = (byte)t;
+    
+                        buf[iv.length - k] ^= v;
+    
+                        t >>>= 8;
+                    }
+    
+                    engine.processBlock(buf, 0, buf, 0);
+                    System.arraycopy(buf, 0, a, 0, 8);
+                    System.arraycopy(buf, 8, block, 8 * (i - 1), 8);
                 }
-
-                engine.processBlock(buf, 0, buf, 0);
-                System.arraycopy(buf, 0, a, 0, 8);
-                System.arraycopy(buf, 8, block, 8 * (i - 1), 8);
             }
         }
 
-        if (!Arrays.constantTimeAreEqual(a, iv))
+        if (n != 1)
         {
-            throw new InvalidCipherTextException("checksum failed");
+            if (!Arrays.constantTimeAreEqual(a, iv))
+            {
+                throw new InvalidCipherTextException("checksum failed");
+            }
+        }
+        else
+        {
+            // TODO: old (incorrect) backwards compatible unwrap - will be removed.
+            if (!Arrays.constantTimeAreEqual(a, iv))
+            {
+                System.arraycopy(in, inOff, a, 0, iv.length);
+                System.arraycopy(in, inOff + iv.length, block, 0, inLen - iv.length);
+
+                for (int j = 5; j >= 0; j--)
+                {
+                    System.arraycopy(a, 0, buf, 0, iv.length);
+                    System.arraycopy(block, 0, buf, iv.length, 8);
+
+                    int t = n * j + 1;
+                    for (int k = 1; t != 0; k++)
+                    {
+                        byte v = (byte)t;
+
+                        buf[iv.length - k] ^= v;
+
+                        t >>>= 8;
+                    }
+
+                    engine.processBlock(buf, 0, buf, 0);
+                    System.arraycopy(buf, 0, a, 0, 8);
+                    System.arraycopy(buf, 8, block, 0, 8);
+                }
+                
+                if (!Arrays.constantTimeAreEqual(a, iv))
+                {
+                    throw new InvalidCipherTextException("checksum failed");
+                }
+            }
         }
 
         return block;
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/engines/RSABlindedEngine.java b/bcprov/src/main/java/org/bouncycastle/crypto/engines/RSABlindedEngine.java
index cc42899..3b9edb9 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/engines/RSABlindedEngine.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/engines/RSABlindedEngine.java
@@ -110,39 +110,31 @@
         }
 
         BigInteger input = core.convertInput(in, inOff, inLen);
+        BigInteger result = processInput(input);
+        return core.convertOutput(result);
+    }
 
-        BigInteger result;
+    private BigInteger processInput(BigInteger input)
+    {
         if (key instanceof RSAPrivateCrtKeyParameters)
         {
-            RSAPrivateCrtKeyParameters k = (RSAPrivateCrtKeyParameters)key;
+            RSAPrivateCrtKeyParameters crtKey = (RSAPrivateCrtKeyParameters)key;
 
-            BigInteger e = k.getPublicExponent();
+            BigInteger e = crtKey.getPublicExponent();
             if (e != null)   // can't do blinding without a public exponent
             {
-                BigInteger m = k.getModulus();
+                BigInteger m = crtKey.getModulus();
+
                 BigInteger r = BigIntegers.createRandomInRange(ONE, m.subtract(ONE), random);
+                BigInteger blind = r.modPow(e, m);
+                BigInteger unblind = BigIntegers.modOddInverse(m, r);
 
-                BigInteger blindedInput = r.modPow(e, m).multiply(input).mod(m);
+                BigInteger blindedInput = blind.multiply(input).mod(m);
                 BigInteger blindedResult = core.processBlock(blindedInput);
-
-                BigInteger rInv = BigIntegers.modOddInverse(m, r);
-                result = blindedResult.multiply(rInv).mod(m);
-                // defence against Arjen Lenstra’s CRT attack
-                if (!input.equals(result.modPow(e, m)))
-                {
-                    throw new IllegalStateException("RSA engine faulty decryption/signing detected");
-                }
-            }
-            else
-            {
-                result = core.processBlock(input);
+                return unblind.multiply(blindedResult).mod(m);
             }
         }
-        else
-        {
-            result = core.processBlock(input);
-        }
 
-        return core.convertOutput(result);
+        return core.processBlock(input);
     }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/engines/RSACoreEngine.java b/bcprov/src/main/java/org/bouncycastle/crypto/engines/RSACoreEngine.java
index ca482d9..0525948 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/engines/RSACoreEngine.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/engines/RSACoreEngine.java
@@ -3,7 +3,11 @@
 import java.math.BigInteger;
 
 import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.CryptoServicePurpose;
+import org.bouncycastle.crypto.CryptoServicesRegistrar;
 import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.constraints.ConstraintUtils;
+import org.bouncycastle.crypto.constraints.DefaultServiceProperties;
 import org.bouncycastle.crypto.params.ParametersWithRandom;
 import org.bouncycastle.crypto.params.RSAKeyParameters;
 import org.bouncycastle.crypto.params.RSAPrivateCrtKeyParameters;
@@ -15,21 +19,21 @@
 class RSACoreEngine
 {
     private RSAKeyParameters key;
-    private boolean          forEncryption;
+    private boolean forEncryption;
 
     /**
      * initialise the RSA engine.
      *
      * @param forEncryption true if we are encrypting, false otherwise.
-     * @param param the necessary RSA key parameters.
+     * @param param         the necessary RSA key parameters.
      */
     public void init(
-        boolean          forEncryption,
+        boolean forEncryption,
         CipherParameters param)
     {
         if (param instanceof ParametersWithRandom)
         {
-            ParametersWithRandom    rParam = (ParametersWithRandom)param;
+            ParametersWithRandom rParam = (ParametersWithRandom)param;
 
             key = (RSAKeyParameters)rParam.getParameters();
         }
@@ -39,6 +43,8 @@
         }
 
         this.forEncryption = forEncryption;
+
+        CryptoServicesRegistrar.checkConstraints(new DefaultServiceProperties("RSA", ConstraintUtils.bitsOfSecurityFor(key.getModulus()), key, getPurpose(key.isPrivate(), forEncryption)));
     }
 
     /**
@@ -50,7 +56,7 @@
      */
     public int getInputBlockSize()
     {
-        int     bitSize = key.getModulus().bitLength();
+        int bitSize = key.getModulus().bitLength();
 
         if (forEncryption)
         {
@@ -71,7 +77,7 @@
      */
     public int getOutputBlockSize()
     {
-        int     bitSize = key.getModulus().bitLength();
+        int bitSize = key.getModulus().bitLength();
 
         if (forEncryption)
         {
@@ -84,9 +90,9 @@
     }
 
     public BigInteger convertInput(
-        byte[]  in,
-        int     inOff,
-        int     inLen)
+        byte[] in,
+        int inOff,
+        int inLen)
     {
         if (inLen > (getInputBlockSize() + 1))
         {
@@ -97,7 +103,7 @@
             throw new DataLengthException("input too large for RSA cipher.");
         }
 
-        byte[]  block;
+        byte[] block;
 
         if (inOff != 0 || inLen != in.length)
         {
@@ -122,13 +128,13 @@
     public byte[] convertOutput(
         BigInteger result)
     {
-        byte[]      output = result.toByteArray();
+        byte[] output = result.toByteArray();
 
         if (forEncryption)
         {
             if (output[0] == 0 && output.length > getOutputBlockSize())        // have ended up with an extra zero byte, copy down.
             {
-                byte[]  tmp = new byte[output.length - 1];
+                byte[] tmp = new byte[output.length - 1];
 
                 System.arraycopy(output, 1, tmp, 0, tmp.length);
 
@@ -137,7 +143,7 @@
 
             if (output.length < getOutputBlockSize())     // have ended up with less bytes than normal, lengthen
             {
-                byte[]  tmp = new byte[getOutputBlockSize()];
+                byte[] tmp = new byte[getOutputBlockSize()];
 
                 System.arraycopy(output, 0, tmp, tmp.length - output.length, output.length);
 
@@ -148,7 +154,7 @@
         }
         else
         {
-            byte[]  rv;
+            byte[] rv;
             if (output[0] == 0)        // have ended up with an extra zero byte, copy down.
             {
                 rv = new byte[output.length - 1];
@@ -179,35 +185,64 @@
             //
             RSAPrivateCrtKeyParameters crtKey = (RSAPrivateCrtKeyParameters)key;
 
-            BigInteger p = crtKey.getP();
-            BigInteger q = crtKey.getQ();
-            BigInteger dP = crtKey.getDP();
-            BigInteger dQ = crtKey.getDQ();
-            BigInteger qInv = crtKey.getQInv();
+            BigInteger e = crtKey.getPublicExponent();
+            if (e != null)   // can't apply fault-attack countermeasure without public exponent
+            {
+                BigInteger p = crtKey.getP();
+                BigInteger q = crtKey.getQ();
+                BigInteger dP = crtKey.getDP();
+                BigInteger dQ = crtKey.getDQ();
+                BigInteger qInv = crtKey.getQInv();
 
-            BigInteger mP, mQ, h, m;
+                BigInteger mP, mQ, h, m;
 
-            // mP = ((input mod p) ^ dP)) mod p
-            mP = (input.remainder(p)).modPow(dP, p);
+                // mP = ((input mod p) ^ dP)) mod p
+                mP = (input.remainder(p)).modPow(dP, p);
 
-            // mQ = ((input mod q) ^ dQ)) mod q
-            mQ = (input.remainder(q)).modPow(dQ, q);
+                // mQ = ((input mod q) ^ dQ)) mod q
+                mQ = (input.remainder(q)).modPow(dQ, q);
 
-            // h = qInv * (mP - mQ) mod p
-            h = mP.subtract(mQ);
-            h = h.multiply(qInv);
-            h = h.mod(p);               // mod (in Java) returns the positive residual
+                // h = qInv * (mP - mQ) mod p
+                h = mP.subtract(mQ);
+                h = h.multiply(qInv);
+                h = h.mod(p);               // mod (in Java) returns the positive residual
 
-            // m = h * q + mQ
-            m = h.multiply(q);
-            m = m.add(mQ);
+                // m = h * q + mQ
+                m = h.multiply(q).add(mQ);
 
-            return m;
+                // defence against Arjen Lenstra’s CRT attack
+                BigInteger check = m.modPow(e, crtKey.getModulus()); 
+                if (!check.equals(input))
+                {
+                    throw new IllegalStateException("RSA engine faulty decryption/signing detected");
+                }
+
+                return m;
+            }
         }
-        else
+
+        return input.modPow(key.getExponent(), key.getModulus());
+    }
+
+    private CryptoServicePurpose getPurpose(boolean isPrivate, boolean forEncryption)
+    {
+        boolean isSigning = isPrivate && forEncryption;
+        boolean isEncryption = !isPrivate && forEncryption;
+        boolean isVerifying = !isPrivate && !forEncryption;
+
+        if (isSigning)
         {
-            return input.modPow(
-                        key.getExponent(), key.getModulus());
+            return CryptoServicePurpose.SIGNING;
         }
+        if (isEncryption)
+        {
+            return CryptoServicePurpose.ENCRYPTION;
+        }
+        if (isVerifying)
+        {
+            return CryptoServicePurpose.VERIFYING;
+        }
+
+        return CryptoServicePurpose.DECRYPTION;
     }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/engines/TwofishEngine.java b/bcprov/src/main/java/org/bouncycastle/crypto/engines/TwofishEngine.java
index 31ac087..97b4321 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/engines/TwofishEngine.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/engines/TwofishEngine.java
@@ -2,9 +2,13 @@
 
 import org.bouncycastle.crypto.BlockCipher;
 import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.CryptoServicesRegistrar;
 import org.bouncycastle.crypto.DataLengthException;
 import org.bouncycastle.crypto.OutputLengthException;
+import org.bouncycastle.crypto.constraints.DefaultServiceProperties;
 import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.util.Integers;
+import org.bouncycastle.util.Pack;
 
 /**
  * A class that provides Twofish encryption operations.
@@ -224,6 +228,8 @@
 
     public TwofishEngine()
     {
+        CryptoServicesRegistrar.checkConstraints(new DefaultServiceProperties(getAlgorithmName(), 256));
+
         // calculate the MDS matrix
         int[] m1 = new int[2];
         int[] mX = new int[2];
@@ -272,7 +278,21 @@
         {
             this.encrypting = encrypting;
             this.workingKey = ((KeyParameter)params).getKey();
-            this.k64Cnt = (this.workingKey.length / 8); // pre-padded ?
+
+            int keyBits = this.workingKey.length * 8;
+            switch (keyBits)
+            {
+            case 128:
+            case 192:
+            case 256:
+                break;
+            default:
+                throw new IllegalArgumentException("Key length not 128/192/256 bits.");
+            }
+
+            CryptoServicesRegistrar.checkConstraints(new DefaultServiceProperties(getAlgorithmName(), keyBits, params, Utils.getPurpose(encrypting)));
+
+            this.k64Cnt = this.workingKey.length / 8;
             setKey(this.workingKey);
 
             return;
@@ -344,28 +364,16 @@
         int[] sBoxKeys = new int[MAX_KEY_BITS/64]; // 4 
         gSubKeys = new int[TOTAL_SUBKEYS];
 
-        if (k64Cnt < 1) 
-        {
-            throw new IllegalArgumentException("Key size less than 64 bits");
-        }
-        
-        if (k64Cnt > 4)
-        {
-            throw new IllegalArgumentException("Key size larger than 256 bits");
-        }
-
         /*
-         * k64Cnt is the number of 8 byte blocks (64 chunks)
-         * that are in the input key.  The input key is a
-         * maximum of 32 bytes (256 bits), so the range
-         * for k64Cnt is 1..4
+         * k64Cnt is the number of 8 byte blocks (64 chunks) that are in the input key.
+         * The input key is 16, 24 or 32 bytes, so the range for k64Cnt is 2..4
          */
         for (int i=0; i<k64Cnt ; i++)
         {
             int p = i* 8;
 
-            k32e[i] = BytesTo32Bits(key, p);
-            k32o[i] = BytesTo32Bits(key, p+4);
+            k32e[i] = Pack.littleEndianToInt(key, p);
+            k32o[i] = Pack.littleEndianToInt(key, p + 4);
 
             sBoxKeys[k64Cnt-1-i] = RS_MDS_Encode(k32e[i], k32o[i]);
         }
@@ -376,7 +384,7 @@
             q = i*SK_STEP;
             A = F32(q,         k32e);
             B = F32(q+SK_BUMP, k32o);
-            B = B << 8 | B >>> 24;
+            B = Integers.rotateLeft(B, 8);
             A += B;
             gSubKeys[i*2] = A;
             A += B;
@@ -448,10 +456,10 @@
         byte[] dst,
         int dstIndex)
     {
-        int x0 = BytesTo32Bits(src, srcIndex) ^ gSubKeys[INPUT_WHITEN];
-        int x1 = BytesTo32Bits(src, srcIndex + 4) ^ gSubKeys[INPUT_WHITEN + 1];
-        int x2 = BytesTo32Bits(src, srcIndex + 8) ^ gSubKeys[INPUT_WHITEN + 2];
-        int x3 = BytesTo32Bits(src, srcIndex + 12) ^ gSubKeys[INPUT_WHITEN + 3];
+        int x0 = Pack.littleEndianToInt(src, srcIndex) ^ gSubKeys[INPUT_WHITEN];
+        int x1 = Pack.littleEndianToInt(src, srcIndex + 4) ^ gSubKeys[INPUT_WHITEN + 1];
+        int x2 = Pack.littleEndianToInt(src, srcIndex + 8) ^ gSubKeys[INPUT_WHITEN + 2];
+        int x3 = Pack.littleEndianToInt(src, srcIndex + 12) ^ gSubKeys[INPUT_WHITEN + 3];
 
         int k = ROUND_SUBKEYS;
         int t0, t1;
@@ -460,20 +468,20 @@
             t0 = Fe32_0(x0);
             t1 = Fe32_3(x1);
             x2 ^= t0 + t1 + gSubKeys[k++];
-            x2 = x2 >>>1 | x2 << 31;
-            x3 = (x3 << 1 | x3 >>> 31) ^ (t0 + 2*t1 + gSubKeys[k++]);
+            x2 = Integers.rotateRight(x2, 1);
+            x3 = Integers.rotateLeft(x3, 1) ^ (t0 + 2*t1 + gSubKeys[k++]);
 
             t0 = Fe32_0(x2);
             t1 = Fe32_3(x3);
             x0 ^= t0 + t1 + gSubKeys[k++];
-            x0 = x0 >>>1 | x0 << 31;
-            x1 = (x1 << 1 | x1 >>> 31) ^ (t0 + 2*t1 + gSubKeys[k++]);
+            x0 = Integers.rotateRight(x0, 1);
+            x1 = Integers.rotateLeft(x1, 1) ^ (t0 + 2*t1 + gSubKeys[k++]);
         }
 
-        Bits32ToBytes(x2 ^ gSubKeys[OUTPUT_WHITEN], dst, dstIndex);
-        Bits32ToBytes(x3 ^ gSubKeys[OUTPUT_WHITEN + 1], dst, dstIndex + 4);
-        Bits32ToBytes(x0 ^ gSubKeys[OUTPUT_WHITEN + 2], dst, dstIndex + 8);
-        Bits32ToBytes(x1 ^ gSubKeys[OUTPUT_WHITEN + 3], dst, dstIndex + 12);
+        Pack.intToLittleEndian(x2 ^ gSubKeys[OUTPUT_WHITEN], dst, dstIndex);
+        Pack.intToLittleEndian(x3 ^ gSubKeys[OUTPUT_WHITEN + 1], dst, dstIndex + 4);
+        Pack.intToLittleEndian(x0 ^ gSubKeys[OUTPUT_WHITEN + 2], dst, dstIndex + 8);
+        Pack.intToLittleEndian(x1 ^ gSubKeys[OUTPUT_WHITEN + 3], dst, dstIndex + 12);
     }
 
     /**
@@ -487,10 +495,10 @@
         byte[] dst,
         int dstIndex)
     {
-        int x2 = BytesTo32Bits(src, srcIndex) ^ gSubKeys[OUTPUT_WHITEN];
-        int x3 = BytesTo32Bits(src, srcIndex+4) ^ gSubKeys[OUTPUT_WHITEN + 1];
-        int x0 = BytesTo32Bits(src, srcIndex+8) ^ gSubKeys[OUTPUT_WHITEN + 2];
-        int x1 = BytesTo32Bits(src, srcIndex+12) ^ gSubKeys[OUTPUT_WHITEN + 3];
+        int x2 = Pack.littleEndianToInt(src, srcIndex) ^ gSubKeys[OUTPUT_WHITEN];
+        int x3 = Pack.littleEndianToInt(src, srcIndex + 4) ^ gSubKeys[OUTPUT_WHITEN + 1];
+        int x0 = Pack.littleEndianToInt(src, srcIndex + 8) ^ gSubKeys[OUTPUT_WHITEN + 2];
+        int x1 = Pack.littleEndianToInt(src, srcIndex + 12) ^ gSubKeys[OUTPUT_WHITEN + 3];
 
         int k = ROUND_SUBKEYS + 2 * ROUNDS -1 ;
         int t0, t1;
@@ -499,20 +507,20 @@
             t0 = Fe32_0(x2);
             t1 = Fe32_3(x3);
             x1 ^= t0 + 2*t1 + gSubKeys[k--];
-            x0 = (x0 << 1 | x0 >>> 31) ^ (t0 + t1 + gSubKeys[k--]);
-            x1 = x1 >>>1 | x1 << 31;
+            x0 = Integers.rotateLeft(x0, 1) ^ (t0 + t1 + gSubKeys[k--]);
+            x1 = Integers.rotateRight(x1, 1);
 
             t0 = Fe32_0(x0);
             t1 = Fe32_3(x1);
             x3 ^= t0 + 2*t1 + gSubKeys[k--];
-            x2 = (x2 << 1 | x2 >>> 31) ^ (t0 + t1 + gSubKeys[k--]);
-            x3 = x3 >>>1 | x3 << 31;
+            x2 = Integers.rotateLeft(x2, 1) ^ (t0 + t1 + gSubKeys[k--]);
+            x3 = Integers.rotateRight(x3, 1);
         }
 
-        Bits32ToBytes(x0 ^ gSubKeys[INPUT_WHITEN], dst, dstIndex);
-        Bits32ToBytes(x1 ^ gSubKeys[INPUT_WHITEN + 1], dst, dstIndex + 4);
-        Bits32ToBytes(x2 ^ gSubKeys[INPUT_WHITEN + 2], dst, dstIndex + 8);
-        Bits32ToBytes(x3 ^ gSubKeys[INPUT_WHITEN + 3], dst, dstIndex + 12);
+        Pack.intToLittleEndian(x0 ^ gSubKeys[INPUT_WHITEN], dst, dstIndex);
+        Pack.intToLittleEndian(x1 ^ gSubKeys[INPUT_WHITEN + 1], dst, dstIndex + 4);
+        Pack.intToLittleEndian(x2 ^ gSubKeys[INPUT_WHITEN + 2], dst, dstIndex + 8);
+        Pack.intToLittleEndian(x3 ^ gSubKeys[INPUT_WHITEN + 3], dst, dstIndex + 12);
     }
 
     /* 
@@ -661,20 +669,4 @@
                gSBox[ 0x200 + 2*((x >>> 8) & 0xff) ] ^
                gSBox[ 0x201 + 2*((x >>> 16) & 0xff) ];
     }
-    
-    private int BytesTo32Bits(byte[] b, int p)
-    {
-        return ((b[p] & 0xff)) | 
-             ((b[p+1] & 0xff) << 8) |
-             ((b[p+2] & 0xff) << 16) |
-             ((b[p+3] & 0xff) << 24);
-    }
-
-    private void Bits32ToBytes(int in,  byte[] b, int offset)
-    {
-        b[offset] = (byte)in;
-        b[offset + 1] = (byte)(in >> 8);
-        b[offset + 2] = (byte)(in >> 16);
-        b[offset + 3] = (byte)(in >> 24);
-    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/engines/Utils.java b/bcprov/src/main/java/org/bouncycastle/crypto/engines/Utils.java
new file mode 100644
index 0000000..4d50f5d
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/engines/Utils.java
@@ -0,0 +1,11 @@
+package org.bouncycastle.crypto.engines;
+
+import org.bouncycastle.crypto.CryptoServicePurpose;
+
+class Utils
+{
+    static CryptoServicePurpose getPurpose(boolean forEncryption)
+    {
+        return forEncryption ? CryptoServicePurpose.ENCRYPTION : CryptoServicePurpose.DECRYPTION;
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/engines/Zuc128CoreEngine.java b/bcprov/src/main/java/org/bouncycastle/crypto/engines/Zuc128CoreEngine.java
new file mode 100644
index 0000000..7e2eeca
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/engines/Zuc128CoreEngine.java
@@ -0,0 +1,575 @@
+package org.bouncycastle.crypto.engines;
+
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.CryptoServicePurpose;
+import org.bouncycastle.crypto.CryptoServicesRegistrar;
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.OutputLengthException;
+import org.bouncycastle.crypto.StreamCipher;
+import org.bouncycastle.crypto.constraints.DefaultServiceProperties;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.crypto.params.ParametersWithIV;
+import org.bouncycastle.util.Memoable;
+
+/**
+ * Zuc128Engine implementation.
+ * Based on https://www.gsma.com/aboutus/wp-content/uploads/2014/12/eea3eia3zucv16.pdf
+ */
+public class Zuc128CoreEngine
+    implements StreamCipher, Memoable
+{
+    /* the s-boxes */
+    private static final byte[] S0 = new byte[]{
+        (byte)0x3e, (byte)0x72, (byte)0x5b, (byte)0x47, (byte)0xca, (byte)0xe0, (byte)0x00, (byte)0x33, (byte)0x04, (byte)0xd1, (byte)0x54, (byte)0x98, (byte)0x09, (byte)0xb9, (byte)0x6d, (byte)0xcb,
+        (byte)0x7b, (byte)0x1b, (byte)0xf9, (byte)0x32, (byte)0xaf, (byte)0x9d, (byte)0x6a, (byte)0xa5, (byte)0xb8, (byte)0x2d, (byte)0xfc, (byte)0x1d, (byte)0x08, (byte)0x53, (byte)0x03, (byte)0x90,
+        (byte)0x4d, (byte)0x4e, (byte)0x84, (byte)0x99, (byte)0xe4, (byte)0xce, (byte)0xd9, (byte)0x91, (byte)0xdd, (byte)0xb6, (byte)0x85, (byte)0x48, (byte)0x8b, (byte)0x29, (byte)0x6e, (byte)0xac,
+        (byte)0xcd, (byte)0xc1, (byte)0xf8, (byte)0x1e, (byte)0x73, (byte)0x43, (byte)0x69, (byte)0xc6, (byte)0xb5, (byte)0xbd, (byte)0xfd, (byte)0x39, (byte)0x63, (byte)0x20, (byte)0xd4, (byte)0x38,
+        (byte)0x76, (byte)0x7d, (byte)0xb2, (byte)0xa7, (byte)0xcf, (byte)0xed, (byte)0x57, (byte)0xc5, (byte)0xf3, (byte)0x2c, (byte)0xbb, (byte)0x14, (byte)0x21, (byte)0x06, (byte)0x55, (byte)0x9b,
+        (byte)0xe3, (byte)0xef, (byte)0x5e, (byte)0x31, (byte)0x4f, (byte)0x7f, (byte)0x5a, (byte)0xa4, (byte)0x0d, (byte)0x82, (byte)0x51, (byte)0x49, (byte)0x5f, (byte)0xba, (byte)0x58, (byte)0x1c,
+        (byte)0x4a, (byte)0x16, (byte)0xd5, (byte)0x17, (byte)0xa8, (byte)0x92, (byte)0x24, (byte)0x1f, (byte)0x8c, (byte)0xff, (byte)0xd8, (byte)0xae, (byte)0x2e, (byte)0x01, (byte)0xd3, (byte)0xad,
+        (byte)0x3b, (byte)0x4b, (byte)0xda, (byte)0x46, (byte)0xeb, (byte)0xc9, (byte)0xde, (byte)0x9a, (byte)0x8f, (byte)0x87, (byte)0xd7, (byte)0x3a, (byte)0x80, (byte)0x6f, (byte)0x2f, (byte)0xc8,
+        (byte)0xb1, (byte)0xb4, (byte)0x37, (byte)0xf7, (byte)0x0a, (byte)0x22, (byte)0x13, (byte)0x28, (byte)0x7c, (byte)0xcc, (byte)0x3c, (byte)0x89, (byte)0xc7, (byte)0xc3, (byte)0x96, (byte)0x56,
+        (byte)0x07, (byte)0xbf, (byte)0x7e, (byte)0xf0, (byte)0x0b, (byte)0x2b, (byte)0x97, (byte)0x52, (byte)0x35, (byte)0x41, (byte)0x79, (byte)0x61, (byte)0xa6, (byte)0x4c, (byte)0x10, (byte)0xfe,
+        (byte)0xbc, (byte)0x26, (byte)0x95, (byte)0x88, (byte)0x8a, (byte)0xb0, (byte)0xa3, (byte)0xfb, (byte)0xc0, (byte)0x18, (byte)0x94, (byte)0xf2, (byte)0xe1, (byte)0xe5, (byte)0xe9, (byte)0x5d,
+        (byte)0xd0, (byte)0xdc, (byte)0x11, (byte)0x66, (byte)0x64, (byte)0x5c, (byte)0xec, (byte)0x59, (byte)0x42, (byte)0x75, (byte)0x12, (byte)0xf5, (byte)0x74, (byte)0x9c, (byte)0xaa, (byte)0x23,
+        (byte)0x0e, (byte)0x86, (byte)0xab, (byte)0xbe, (byte)0x2a, (byte)0x02, (byte)0xe7, (byte)0x67, (byte)0xe6, (byte)0x44, (byte)0xa2, (byte)0x6c, (byte)0xc2, (byte)0x93, (byte)0x9f, (byte)0xf1,
+        (byte)0xf6, (byte)0xfa, (byte)0x36, (byte)0xd2, (byte)0x50, (byte)0x68, (byte)0x9e, (byte)0x62, (byte)0x71, (byte)0x15, (byte)0x3d, (byte)0xd6, (byte)0x40, (byte)0xc4, (byte)0xe2, (byte)0x0f,
+        (byte)0x8e, (byte)0x83, (byte)0x77, (byte)0x6b, (byte)0x25, (byte)0x05, (byte)0x3f, (byte)0x0c, (byte)0x30, (byte)0xea, (byte)0x70, (byte)0xb7, (byte)0xa1, (byte)0xe8, (byte)0xa9, (byte)0x65,
+        (byte)0x8d, (byte)0x27, (byte)0x1a, (byte)0xdb, (byte)0x81, (byte)0xb3, (byte)0xa0, (byte)0xf4, (byte)0x45, (byte)0x7a, (byte)0x19, (byte)0xdf, (byte)0xee, (byte)0x78, (byte)0x34, (byte)0x60
+    };
+
+    private static final byte[] S1 = new byte[]{
+        (byte)0x55, (byte)0xc2, (byte)0x63, (byte)0x71, (byte)0x3b, (byte)0xc8, (byte)0x47, (byte)0x86, (byte)0x9f, (byte)0x3c, (byte)0xda, (byte)0x5b, (byte)0x29, (byte)0xaa, (byte)0xfd, (byte)0x77,
+        (byte)0x8c, (byte)0xc5, (byte)0x94, (byte)0x0c, (byte)0xa6, (byte)0x1a, (byte)0x13, (byte)0x00, (byte)0xe3, (byte)0xa8, (byte)0x16, (byte)0x72, (byte)0x40, (byte)0xf9, (byte)0xf8, (byte)0x42,
+        (byte)0x44, (byte)0x26, (byte)0x68, (byte)0x96, (byte)0x81, (byte)0xd9, (byte)0x45, (byte)0x3e, (byte)0x10, (byte)0x76, (byte)0xc6, (byte)0xa7, (byte)0x8b, (byte)0x39, (byte)0x43, (byte)0xe1,
+        (byte)0x3a, (byte)0xb5, (byte)0x56, (byte)0x2a, (byte)0xc0, (byte)0x6d, (byte)0xb3, (byte)0x05, (byte)0x22, (byte)0x66, (byte)0xbf, (byte)0xdc, (byte)0x0b, (byte)0xfa, (byte)0x62, (byte)0x48,
+        (byte)0xdd, (byte)0x20, (byte)0x11, (byte)0x06, (byte)0x36, (byte)0xc9, (byte)0xc1, (byte)0xcf, (byte)0xf6, (byte)0x27, (byte)0x52, (byte)0xbb, (byte)0x69, (byte)0xf5, (byte)0xd4, (byte)0x87,
+        (byte)0x7f, (byte)0x84, (byte)0x4c, (byte)0xd2, (byte)0x9c, (byte)0x57, (byte)0xa4, (byte)0xbc, (byte)0x4f, (byte)0x9a, (byte)0xdf, (byte)0xfe, (byte)0xd6, (byte)0x8d, (byte)0x7a, (byte)0xeb,
+        (byte)0x2b, (byte)0x53, (byte)0xd8, (byte)0x5c, (byte)0xa1, (byte)0x14, (byte)0x17, (byte)0xfb, (byte)0x23, (byte)0xd5, (byte)0x7d, (byte)0x30, (byte)0x67, (byte)0x73, (byte)0x08, (byte)0x09,
+        (byte)0xee, (byte)0xb7, (byte)0x70, (byte)0x3f, (byte)0x61, (byte)0xb2, (byte)0x19, (byte)0x8e, (byte)0x4e, (byte)0xe5, (byte)0x4b, (byte)0x93, (byte)0x8f, (byte)0x5d, (byte)0xdb, (byte)0xa9,
+        (byte)0xad, (byte)0xf1, (byte)0xae, (byte)0x2e, (byte)0xcb, (byte)0x0d, (byte)0xfc, (byte)0xf4, (byte)0x2d, (byte)0x46, (byte)0x6e, (byte)0x1d, (byte)0x97, (byte)0xe8, (byte)0xd1, (byte)0xe9,
+        (byte)0x4d, (byte)0x37, (byte)0xa5, (byte)0x75, (byte)0x5e, (byte)0x83, (byte)0x9e, (byte)0xab, (byte)0x82, (byte)0x9d, (byte)0xb9, (byte)0x1c, (byte)0xe0, (byte)0xcd, (byte)0x49, (byte)0x89,
+        (byte)0x01, (byte)0xb6, (byte)0xbd, (byte)0x58, (byte)0x24, (byte)0xa2, (byte)0x5f, (byte)0x38, (byte)0x78, (byte)0x99, (byte)0x15, (byte)0x90, (byte)0x50, (byte)0xb8, (byte)0x95, (byte)0xe4,
+        (byte)0xd0, (byte)0x91, (byte)0xc7, (byte)0xce, (byte)0xed, (byte)0x0f, (byte)0xb4, (byte)0x6f, (byte)0xa0, (byte)0xcc, (byte)0xf0, (byte)0x02, (byte)0x4a, (byte)0x79, (byte)0xc3, (byte)0xde,
+        (byte)0xa3, (byte)0xef, (byte)0xea, (byte)0x51, (byte)0xe6, (byte)0x6b, (byte)0x18, (byte)0xec, (byte)0x1b, (byte)0x2c, (byte)0x80, (byte)0xf7, (byte)0x74, (byte)0xe7, (byte)0xff, (byte)0x21,
+        (byte)0x5a, (byte)0x6a, (byte)0x54, (byte)0x1e, (byte)0x41, (byte)0x31, (byte)0x92, (byte)0x35, (byte)0xc4, (byte)0x33, (byte)0x07, (byte)0x0a, (byte)0xba, (byte)0x7e, (byte)0x0e, (byte)0x34,
+        (byte)0x88, (byte)0xb1, (byte)0x98, (byte)0x7c, (byte)0xf3, (byte)0x3d, (byte)0x60, (byte)0x6c, (byte)0x7b, (byte)0xca, (byte)0xd3, (byte)0x1f, (byte)0x32, (byte)0x65, (byte)0x04, (byte)0x28,
+        (byte)0x64, (byte)0xbe, (byte)0x85, (byte)0x9b, (byte)0x2f, (byte)0x59, (byte)0x8a, (byte)0xd7, (byte)0xb0, (byte)0x25, (byte)0xac, (byte)0xaf, (byte)0x12, (byte)0x03, (byte)0xe2, (byte)0xf2
+    };
+
+    /* the constants D */
+    private static final short[] EK_d = new short[]{
+        0x44D7, 0x26BC, 0x626B, 0x135E, 0x5789, 0x35E2, 0x7135, 0x09AF,
+        0x4D78, 0x2F13, 0x6BC4, 0x1AF1, 0x5E26, 0x3C4D, 0x789A, 0x47AC
+    };
+
+    /**
+     * State.
+     */
+    private final int[] LFSR = new int[16];
+    private final int[] F = new int[2];
+    private final int[] BRC = new int[4];
+
+    /**
+     * index of next byte in keyStream.
+     */
+    private int theIndex;
+
+    /**
+     * Advanced stream.
+     */
+    private final byte[] keyStream = new byte[4];   // Integer.BYTES
+
+    /**
+     * The iterations.
+     */
+    private int theIterations;
+
+    /**
+     * Reset state.
+     */
+    private Zuc128CoreEngine theResetState;
+
+    /**
+     * Constructor.
+     */
+    protected Zuc128CoreEngine()
+    {
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param pSource the source engine
+     */
+    protected Zuc128CoreEngine(final Zuc128CoreEngine pSource)
+    {
+        reset(pSource);
+    }
+
+    /**
+     * initialise a Snow3G cipher.
+     *
+     * @param forEncryption whether or not we are for encryption.
+     * @param params        the parameters required to set up the cipher.
+     * @throws IllegalArgumentException if the params argument is inappropriate.
+     */
+    public void init(final boolean forEncryption,
+                     final CipherParameters params)
+    {
+        /*
+         * encryption and decryption is completely symmetrical.
+         */
+
+        /* Determine parameters */
+        CipherParameters myParams = params;
+        byte[] newKey = null;
+        byte[] newIV = null;
+        if ((myParams instanceof ParametersWithIV))
+        {
+            final ParametersWithIV ivParams = (ParametersWithIV)myParams;
+            newIV = ivParams.getIV();
+            myParams = ivParams.getParameters();
+        }
+        if (myParams instanceof KeyParameter)
+        {
+            final KeyParameter keyParam = (KeyParameter)myParams;
+            newKey = keyParam.getKey();
+        }
+
+        /* Initialise engine and mark as initialised */
+        theIndex = 0;
+        theIterations = 0;
+        setKeyAndIV(newKey, newIV);
+
+        CryptoServicesRegistrar.checkConstraints(new DefaultServiceProperties(getAlgorithmName(), newKey.length * 8,
+            params, forEncryption ? CryptoServicePurpose.ENCRYPTION : CryptoServicePurpose.DECRYPTION));
+
+        /* Save reset state */
+        theResetState = (Zuc128CoreEngine)copy();
+    }
+
+    /**
+     * Obtain Max iterations.
+     *
+     * @return the maximum iterations
+     */
+    protected int getMaxIterations()
+    {
+        return 2047;
+    }
+
+    /**
+     * Obtain Algorithm Name.
+     *
+     * @return the name
+     */
+    public String getAlgorithmName()
+    {
+        return "Zuc-128";
+    }
+
+    /**
+     * Process bytes.
+     *
+     * @param in     the input buffer
+     * @param inOff  the starting offset in the input buffer
+     * @param len    the length of data in the input buffer
+     * @param out    the output buffer
+     * @param outOff the starting offset in the output buffer
+     * @return the number of bytes returned in the output buffer
+     */
+    public int processBytes(final byte[] in,
+                            final int inOff,
+                            final int len,
+                            final byte[] out,
+                            final int outOff)
+    {
+        /* Check for errors */
+        if (theResetState == null)
+        {
+            throw new IllegalStateException(getAlgorithmName() + " not initialised");
+        }
+        if ((inOff + len) > in.length)
+        {
+            throw new DataLengthException("input buffer too short");
+        }
+        if ((outOff + len) > out.length)
+        {
+            throw new OutputLengthException("output buffer too short");
+        }
+
+        /* Loop through the input bytes */
+        for (int i = 0; i < len; i++)
+        {
+            out[i + outOff] = returnByte(in[i + inOff]);
+        }
+        return len;
+    }
+
+    /**
+     * Reset the engine.
+     */
+    public void reset()
+    {
+        if (theResetState != null)
+        {
+            reset(theResetState);
+        }
+    }
+
+    /**
+     * Process single byte.
+     *
+     * @param in the input byte
+     * @return the output byte
+     */
+    public byte returnByte(final byte in)
+    {
+        /* Make the keyStream if required */
+        if (theIndex == 0)
+        {
+            makeKeyStream();
+        }
+
+        /* Map the next byte and adjust index */
+        final byte out = (byte)(keyStream[theIndex] ^ in);
+        theIndex = (theIndex + 1) % 4; // Integer.BYTES
+
+        /* Return the mapped character */
+        return out;
+    }
+
+    /**
+     * Encode a 32-bit value into a buffer (little-endian).
+     *
+     * @param val the value to encode
+     * @param buf the output buffer
+     * @param off the output offset
+     */
+    public static void encode32be(int val, byte[] buf, int off)
+    {
+        buf[off] = (byte)(val >> 24);
+        buf[off + 1] = (byte)(val >> 16);
+        buf[off + 2] = (byte)(val >> 8);
+        buf[off + 3] = (byte)val;
+    }
+
+    /* ����������������������- */
+
+    /**
+     * Modular add c = a + b mod (2^31 � 1).
+     *
+     * @param a value A
+     * @param b value B
+     * @return the result
+     */
+    private int AddM(final int a, final int b)
+    {
+        final int c = a + b;
+        return (c & 0x7FFFFFFF) + (c >>> 31);
+    }
+
+    /**
+     * Multiply by power of two.
+     *
+     * @param x input value
+     * @param k the power of two
+     * @return the result
+     */
+    private static int MulByPow2(final int x, final int k)
+    {
+        return ((((x) << k) | ((x) >>> (31 - k))) & 0x7FFFFFFF);
+    }
+
+    /**
+     * LFSR with initialisation mode.
+     *
+     * @param u
+     */
+    private void LFSRWithInitialisationMode(final int u)
+    {
+        int f = LFSR[0];
+        int v = MulByPow2(LFSR[0], 8);
+        f = AddM(f, v);
+        v = MulByPow2(LFSR[4], 20);
+        f = AddM(f, v);
+        v = MulByPow2(LFSR[10], 21);
+        f = AddM(f, v);
+        v = MulByPow2(LFSR[13], 17);
+        f = AddM(f, v);
+        v = MulByPow2(LFSR[15], 15);
+        f = AddM(f, v);
+        f = AddM(f, u);
+
+        /* update the state */
+        LFSR[0] = LFSR[1];
+        LFSR[1] = LFSR[2];
+        LFSR[2] = LFSR[3];
+        LFSR[3] = LFSR[4];
+        LFSR[4] = LFSR[5];
+        LFSR[5] = LFSR[6];
+        LFSR[6] = LFSR[7];
+        LFSR[7] = LFSR[8];
+        LFSR[8] = LFSR[9];
+        LFSR[9] = LFSR[10];
+        LFSR[10] = LFSR[11];
+        LFSR[11] = LFSR[12];
+        LFSR[12] = LFSR[13];
+        LFSR[13] = LFSR[14];
+        LFSR[14] = LFSR[15];
+        LFSR[15] = f;
+    }
+
+    /**
+     * LFSR with work mode.
+     */
+    private void LFSRWithWorkMode()
+    {
+        int f, v;
+        f = LFSR[0];
+        v = MulByPow2(LFSR[0], 8);
+        f = AddM(f, v);
+        v = MulByPow2(LFSR[4], 20);
+        f = AddM(f, v);
+        v = MulByPow2(LFSR[10], 21);
+        f = AddM(f, v);
+        v = MulByPow2(LFSR[13], 17);
+        f = AddM(f, v);
+        v = MulByPow2(LFSR[15], 15);
+        f = AddM(f, v);
+
+        /* update the state */
+        LFSR[0] = LFSR[1];
+        LFSR[1] = LFSR[2];
+        LFSR[2] = LFSR[3];
+        LFSR[3] = LFSR[4];
+        LFSR[4] = LFSR[5];
+        LFSR[5] = LFSR[6];
+        LFSR[6] = LFSR[7];
+        LFSR[7] = LFSR[8];
+        LFSR[8] = LFSR[9];
+        LFSR[9] = LFSR[10];
+        LFSR[10] = LFSR[11];
+        LFSR[11] = LFSR[12];
+        LFSR[12] = LFSR[13];
+        LFSR[13] = LFSR[14];
+        LFSR[14] = LFSR[15];
+        LFSR[15] = f;
+    }
+
+    /**
+     * BitReorganization.
+     */
+    private void BitReorganization()
+    {
+        BRC[0] = ((LFSR[15] & 0x7FFF8000) << 1) | (LFSR[14] & 0xFFFF);
+        BRC[1] = ((LFSR[11] & 0xFFFF) << 16) | (LFSR[9] >>> 15);
+        BRC[2] = ((LFSR[7] & 0xFFFF) << 16) | (LFSR[5] >>> 15);
+        BRC[3] = ((LFSR[2] & 0xFFFF) << 16) | (LFSR[0] >>> 15);
+    }
+
+    /**
+     * Rotate integer.
+     *
+     * @param a the integer
+     * @param k the shift
+     * @return the result
+     */
+    static int ROT(int a, int k)
+    {
+        return (((a) << k) | ((a) >>> (32 - k)));
+    }
+
+    /**
+     * L1.
+     *
+     * @param X the input integer.
+     * @return the result
+     */
+    private static int L1(final int X)
+    {
+        return (X ^ ROT(X, 2) ^ ROT(X, 10) ^ ROT(X, 18) ^ ROT(X, 24));
+    }
+
+    /**
+     * L2.
+     *
+     * @param X the input integer.
+     * @return the result
+     */
+    private static int L2(final int X)
+    {
+        return (X ^ ROT(X, 8) ^ ROT(X, 14) ^ ROT(X, 22) ^ ROT(X, 30));
+    }
+
+    /**
+     * Build a 32-bit integer from constituent parts.
+     *
+     * @param a part A
+     * @param b part B
+     * @param c part C
+     * @param d part D
+     * @return the built integer
+     */
+    private static int MAKEU32(final byte a,
+                               final byte b,
+                               final byte c,
+                               final byte d)
+    {
+        return (((a & 0xFF) << 24) | ((b & 0xFF) << 16) | ((c & 0xFF) << 8) | ((d & 0xFF)));
+    }
+
+    /**
+     * F.
+     */
+    int F()
+    {
+        int W, W1, W2, u, v;
+        W = (BRC[0] ^ F[0]) + F[1];
+        W1 = F[0] + BRC[1];
+        W2 = F[1] ^ BRC[2];
+        u = L1((W1 << 16) | (W2 >>> 16));
+        v = L2((W2 << 16) | (W1 >>> 16));
+        F[0] = MAKEU32(S0[u >>> 24], S1[(u >>> 16) & 0xFF],
+            S0[(u >>> 8) & 0xFF], S1[u & 0xFF]);
+        F[1] = MAKEU32(S0[v >>> 24], S1[(v >>> 16) & 0xFF],
+            S0[(v >>> 8) & 0xFF], S1[v & 0xFF]);
+        return W;
+    }
+
+    /**
+     * Build a 31-bit integer from constituent parts.
+     *
+     * @param a part A
+     * @param b part B
+     * @param c part C
+     * @return the built integer
+     */
+    private static int MAKEU31(final byte a,
+                               final short b,
+                               final byte c)
+    {
+        return (((a & 0xFF) << 23) | ((b & 0xFFFF) << 8) | (c & 0xFF));
+    }
+
+    /**
+     * Process key and IV into LFSR.
+     *
+     * @param pLFSR the LFSR
+     * @param k     the key
+     * @param iv    the iv
+     */
+    protected void setKeyAndIV(final int[] pLFSR,
+                               final byte[] k,
+                               final byte[] iv)
+    {
+        /* Check lengths */
+        if (k == null || k.length != 16)
+        {
+            throw new IllegalArgumentException("A key of 16 bytes is needed");
+        }
+        if (iv == null || iv.length != 16)
+        {
+            throw new IllegalArgumentException("An IV of 16 bytes is needed");
+        }
+
+        /* expand key */
+        LFSR[0] = MAKEU31(k[0], EK_d[0], iv[0]);
+        LFSR[1] = MAKEU31(k[1], EK_d[1], iv[1]);
+        LFSR[2] = MAKEU31(k[2], EK_d[2], iv[2]);
+        LFSR[3] = MAKEU31(k[3], EK_d[3], iv[3]);
+        LFSR[4] = MAKEU31(k[4], EK_d[4], iv[4]);
+        LFSR[5] = MAKEU31(k[5], EK_d[5], iv[5]);
+        LFSR[6] = MAKEU31(k[6], EK_d[6], iv[6]);
+        LFSR[7] = MAKEU31(k[7], EK_d[7], iv[7]);
+        LFSR[8] = MAKEU31(k[8], EK_d[8], iv[8]);
+        LFSR[9] = MAKEU31(k[9], EK_d[9], iv[9]);
+        LFSR[10] = MAKEU31(k[10], EK_d[10], iv[10]);
+        LFSR[11] = MAKEU31(k[11], EK_d[11], iv[11]);
+        LFSR[12] = MAKEU31(k[12], EK_d[12], iv[12]);
+        LFSR[13] = MAKEU31(k[13], EK_d[13], iv[13]);
+        LFSR[14] = MAKEU31(k[14], EK_d[14], iv[14]);
+        LFSR[15] = MAKEU31(k[15], EK_d[15], iv[15]);
+    }
+
+    /**
+     * Process key and IV.
+     *
+     * @param k  the key
+     * @param iv the IV
+     */
+    private void setKeyAndIV(final byte[] k,
+                             final byte[] iv)
+    {
+        /* Initialise LFSR */
+        setKeyAndIV(LFSR, k, iv);
+
+        /* set F_R1 and F_R2 to zero */
+        F[0] = 0;
+        F[1] = 0;
+        int nCount = 32;
+        while (nCount > 0)
+        {
+            BitReorganization();
+            final int w = F();
+            LFSRWithInitialisationMode(w >>> 1);
+            nCount--;
+        }
+        BitReorganization();
+        F(); /* discard the output of F */
+        LFSRWithWorkMode();
+    }
+
+    /**
+     * Create the next byte keyStream.
+     */
+    private void makeKeyStream()
+    {
+        encode32be(makeKeyStreamWord(), keyStream, 0);
+    }
+
+    /**
+     * Create the next keyStream word.
+     *
+     * @return the next word
+     */
+    protected int makeKeyStreamWord()
+    {
+        if (theIterations++ >= getMaxIterations())
+        {
+            throw new IllegalStateException("Too much data processed by singleKey/IV");
+        }
+        BitReorganization();
+        final int result = F() ^ BRC[3];
+        LFSRWithWorkMode();
+        return result;
+    }
+
+    /**
+     * Create a copy of the engine.
+     *
+     * @return the copy
+     */
+    public Memoable copy()
+    {
+        return new Zuc128CoreEngine(this);
+    }
+
+    /**
+     * Reset from saved engine state.
+     *
+     * @param pState the state to restore
+     */
+    public void reset(final Memoable pState)
+    {
+        final Zuc128CoreEngine e = (Zuc128CoreEngine)pState;
+        System.arraycopy(e.LFSR, 0, LFSR, 0, LFSR.length);
+        System.arraycopy(e.F, 0, F, 0, F.length);
+        System.arraycopy(e.BRC, 0, BRC, 0, BRC.length);
+        System.arraycopy(e.keyStream, 0, keyStream, 0, keyStream.length);
+        theIndex = e.theIndex;
+        theIterations = e.theIterations;
+        theResetState = e;
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/engines/Zuc256CoreEngine.java b/bcprov/src/main/java/org/bouncycastle/crypto/engines/Zuc256CoreEngine.java
new file mode 100644
index 0000000..c42a72e
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/engines/Zuc256CoreEngine.java
@@ -0,0 +1,181 @@
+package org.bouncycastle.crypto.engines;
+
+import org.bouncycastle.util.Memoable;
+
+/**
+ * Zuc256 implementation.
+ * Based on https://www.is.cas.cn/ztzl2016/zouchongzhi/201801/W020180126529970733243.pdf
+ */
+public class Zuc256CoreEngine
+    extends Zuc128CoreEngine
+{
+    /* the constants D */
+    private static final byte[] EK_d = new byte[]{
+        0x22, 0x2f, 0x24, 0x2a, 0x6d, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x52, 0x10, 0x30
+//        0b0100010, 0b0101111, 0b0100100, 0b0101010, 0b1101101, 0b1000000, 0b1000000, 0b1000000,
+//        0b1000000, 0b1000000, 0b1000000, 0b1000000, 0b1000000, 0b1010010, 0b0010000, 0b0110000
+    };
+
+    /* the constants D for 32 bit Mac*/
+    private static final byte[] EK_d32 = new byte[]{
+         0x22, 0x2f, 0x25, 0x2a, 0x6d, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x52, 0x10, 0x30
+//        0b0100010, 0b0101111, 0b0100101, 0b0101010, 0b1101101, 0b1000000, 0b1000000, 0b1000000,
+//        0b1000000, 0b1000000, 0b1000000, 0b1000000, 0b1000000, 0b1010010, 0b0010000, 0b0110000
+    };
+
+    /* the constants D for 64 bit Mac */
+    private static final byte[] EK_d64 = new byte[]{
+        0x23, 0x2f, 0x24, 0x2a, 0x6d, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x52, 0x10, 0x30
+//        0b0100011, 0b0101111, 0b0100100, 0b0101010, 0b1101101, 0b1000000, 0b1000000, 0b1000000,
+//        0b1000000, 0b1000000, 0b1000000, 0b1000000, 0b1000000, 0b1010010, 0b0010000, 0b0110000
+    };
+
+    /* the constants D for 128 bit Mac */
+    private static final byte[] EK_d128 = new byte[]{
+        0x23, 0x2f, 0x25, 0x2a, 0x6d, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x52, 0x10, 0x30
+//        0b0100011, 0b0101111, 0b0100101, 0b0101010, 0b1101101, 0b1000000, 0b1000000, 0b1000000,
+//        0b1000000, 0b1000000, 0b1000000, 0b1000000, 0b1000000, 0b1010010, 0b0010000, 0b0110000
+    };
+
+    /**
+     * The selected D constants.
+     */
+    private byte[] theD;
+
+    /**
+     * Constructor for streamCipher.
+     */
+    protected Zuc256CoreEngine()
+    {
+        theD = EK_d;
+    }
+
+    /**
+     * Constructor for Mac.
+     *
+     * @param pLength the Mac length
+     */
+    protected Zuc256CoreEngine(final int pLength)
+    {
+        switch (pLength)
+        {
+        case 32:
+            theD = EK_d32;
+            break;
+        case 64:
+            theD = EK_d64;
+            break;
+        case 128:
+            theD = EK_d128;
+            break;
+        default:
+            throw new IllegalArgumentException("Unsupported length: " + pLength);
+        }
+    }
+
+    /**
+     * Constructor for Memoable.
+     *
+     * @param pSource the source engine
+     */
+    protected Zuc256CoreEngine(final Zuc256CoreEngine pSource)
+    {
+        super(pSource);
+    }
+
+    /**
+     * Obtain Max iterations.
+     *
+     * @return the maximum iterations
+     */
+    protected int getMaxIterations()
+    {
+        return 625;
+    }
+
+    /**
+     * Obtain Algorithm Name.
+     *
+     * @return the name
+     */
+    public String getAlgorithmName()
+    {
+        return "Zuc-256";
+    }
+
+    /**
+     * Build a 31-bit integer from constituent parts.
+     *
+     * @param a part A
+     * @param b part B
+     * @param c part C
+     * @param d part D
+     * @return the built integer
+     */
+    private static int MAKEU31(byte a, byte b, byte c, byte d)
+    {
+        return (((a & 0xFF) << 23) | ((b & 0xFF) << 16) | ((c & 0xFF) << 8) | (d & 0xFF));
+    }
+
+    /**
+     * Process key and IV into LFSR.
+     *
+     * @param pLFSR the LFSR
+     * @param k     the key
+     * @param iv    the iv
+     */
+    protected void setKeyAndIV(final int[] pLFSR,
+                               final byte[] k,
+                               final byte[] iv)
+    {
+        /* Check lengths */
+        if (k == null || k.length != 32)
+        {
+            throw new IllegalArgumentException("A key of 32 bytes is needed");
+        }
+        if (iv == null || iv.length != 25)
+        {
+            throw new IllegalArgumentException("An IV of 25 bytes is needed");
+        }
+
+        /* expand key and IV */
+        pLFSR[0] = MAKEU31(k[0], theD[0], k[21], k[16]);
+        pLFSR[1] = MAKEU31(k[1], theD[1], k[22], k[17]);
+        pLFSR[2] = MAKEU31(k[2], theD[2], k[23], k[18]);
+        pLFSR[3] = MAKEU31(k[3], theD[3], k[24], k[19]);
+        pLFSR[4] = MAKEU31(k[4], theD[4], k[25], k[20]);
+        pLFSR[5] = MAKEU31(iv[0], (byte)(theD[5] | (iv[17] & 0x3F)), k[5], k[26]);
+        pLFSR[6] = MAKEU31(iv[1], (byte)(theD[6] | (iv[18] & 0x3F)), k[6], k[27]);
+        pLFSR[7] = MAKEU31(iv[10], (byte)(theD[7] | (iv[19] & 0x3F)), k[7], iv[2]);
+        pLFSR[8] = MAKEU31(k[8], (byte)(theD[8] | (iv[20] & 0x3F)), iv[3], iv[11]);
+        pLFSR[9] = MAKEU31(k[9], (byte)(theD[9] | (iv[21] & 0x3F)), iv[12], iv[4]);
+        pLFSR[10] = MAKEU31(iv[5], (byte)(theD[10] | (iv[22] & 0x3F)), k[10], k[28]);
+        pLFSR[11] = MAKEU31(k[11], (byte)(theD[11] | (iv[23] & 0x3F)), iv[6], iv[13]);
+        pLFSR[12] = MAKEU31(k[12], (byte)(theD[12] | (iv[24] & 0x3F)), iv[7], iv[14]);
+        pLFSR[13] = MAKEU31(k[13], theD[13], iv[15], iv[8]);
+        pLFSR[14] = MAKEU31(k[14], (byte)(theD[14] | ((k[31] >>> 4) & 0xF)), iv[16], iv[9]);
+        pLFSR[15] = MAKEU31(k[15], (byte)(theD[15] | (k[31] & 0xF)), k[30], k[29]);
+    }
+
+    /**
+     * Create a copy of the engine.
+     *
+     * @return the copy
+     */
+    public Memoable copy()
+    {
+        return new Zuc256CoreEngine(this);
+    }
+
+    /**
+     * Reset from saved engine state.
+     *
+     * @param pState the state to restore
+     */
+    public void reset(final Memoable pState)
+    {
+        final Zuc256CoreEngine e = (Zuc256CoreEngine)pState;
+        super.reset(pState);
+        theD = e.theD;
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/engines/Zuc256Engine.java b/bcprov/src/main/java/org/bouncycastle/crypto/engines/Zuc256Engine.java
new file mode 100644
index 0000000..c80610c
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/engines/Zuc256Engine.java
@@ -0,0 +1,49 @@
+package org.bouncycastle.crypto.engines;
+
+import org.bouncycastle.util.Memoable;
+
+/**
+ * Zuc256 implementation.
+ * Based on https://www.is.cas.cn/ztzl2016/zouchongzhi/201801/W020180126529970733243.pdf
+ */
+public final class Zuc256Engine
+    extends Zuc256CoreEngine
+{
+    /**
+     * Constructor for streamCipher.
+     */
+    public Zuc256Engine()
+    {
+        super();
+    }
+
+    /**
+     * Constructor for Mac.
+     *
+     * @param pLength the Mac length
+     */
+    public Zuc256Engine(final int pLength)
+    {
+        super(pLength);
+    }
+
+    /**
+     * Constructor for Memoable.
+     *
+     * @param pSource the source engine
+     */
+    private Zuc256Engine(final Zuc256Engine pSource)
+    {
+        super(pSource);
+    }
+
+    /**
+     * Create a copy of the engine.
+     *
+     * @return the copy
+     */
+    public Memoable copy()
+    {
+        return new Zuc256Engine(this);
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/generators/DESKeyGenerator.java b/bcprov/src/main/java/org/bouncycastle/crypto/generators/DESKeyGenerator.java
index 7111118..ba68ef7 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/generators/DESKeyGenerator.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/generators/DESKeyGenerator.java
@@ -1,7 +1,10 @@
 package org.bouncycastle.crypto.generators;
 
 import org.bouncycastle.crypto.CipherKeyGenerator;
+import org.bouncycastle.crypto.CryptoServicePurpose;
+import org.bouncycastle.crypto.CryptoServicesRegistrar;
 import org.bouncycastle.crypto.KeyGenerationParameters;
+import org.bouncycastle.crypto.constraints.DefaultServiceProperties;
 import org.bouncycastle.crypto.params.DESParameters;
 
 public class DESKeyGenerator
@@ -29,6 +32,8 @@
                     + (DESParameters.DES_KEY_LENGTH * 8)
                     + " bits long.");
         }
+
+        CryptoServicesRegistrar.checkConstraints(new DefaultServiceProperties("DESKeyGen", 56, null, CryptoServicePurpose.KEYGEN));
     }
 
     public byte[] generateKey()
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/generators/DESedeKeyGenerator.java b/bcprov/src/main/java/org/bouncycastle/crypto/generators/DESedeKeyGenerator.java
index 19a2ecf..9ba8a55 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/generators/DESedeKeyGenerator.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/generators/DESedeKeyGenerator.java
@@ -1,6 +1,9 @@
 package org.bouncycastle.crypto.generators;
 
+import org.bouncycastle.crypto.CryptoServicePurpose;
+import org.bouncycastle.crypto.CryptoServicesRegistrar;
 import org.bouncycastle.crypto.KeyGenerationParameters;
+import org.bouncycastle.crypto.constraints.DefaultServiceProperties;
 import org.bouncycastle.crypto.params.DESedeParameters;
 
 public class DESedeKeyGenerator
@@ -39,6 +42,8 @@
                 + (2 * 8 * DESedeParameters.DES_KEY_LENGTH)
                 + " bits long.");
         }
+
+        CryptoServicesRegistrar.checkConstraints(new DefaultServiceProperties("DESedeKeyGen", 112, null, CryptoServicePurpose.KEYGEN));
     }
 
     public byte[] generateKey()
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/generators/DHBasicKeyPairGenerator.java b/bcprov/src/main/java/org/bouncycastle/crypto/generators/DHBasicKeyPairGenerator.java
index f93428e..ca7a712 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/generators/DHBasicKeyPairGenerator.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/generators/DHBasicKeyPairGenerator.java
@@ -1,15 +1,19 @@
 package org.bouncycastle.crypto.generators;
 
+import java.math.BigInteger;
+
 import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
 import org.bouncycastle.crypto.AsymmetricCipherKeyPairGenerator;
+import org.bouncycastle.crypto.CryptoServicePurpose;
+import org.bouncycastle.crypto.CryptoServicesRegistrar;
 import org.bouncycastle.crypto.KeyGenerationParameters;
+import org.bouncycastle.crypto.constraints.ConstraintUtils;
+import org.bouncycastle.crypto.constraints.DefaultServiceProperties;
 import org.bouncycastle.crypto.params.DHKeyGenerationParameters;
 import org.bouncycastle.crypto.params.DHParameters;
 import org.bouncycastle.crypto.params.DHPrivateKeyParameters;
 import org.bouncycastle.crypto.params.DHPublicKeyParameters;
 
-import java.math.BigInteger;
-
 /**
  * a basic Diffie-Hellman key pair generator.
  *
@@ -25,6 +29,8 @@
         KeyGenerationParameters param)
     {
         this.param = (DHKeyGenerationParameters)param;
+
+        CryptoServicesRegistrar.checkConstraints(new DefaultServiceProperties("DHBasicKeyGen", ConstraintUtils.bitsOfSecurityFor(this.param.getParameters().getP()), this.param.getParameters(), CryptoServicePurpose.KEYGEN));
     }
 
     public AsymmetricCipherKeyPair generateKeyPair()
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/generators/DSAKeyPairGenerator.java b/bcprov/src/main/java/org/bouncycastle/crypto/generators/DSAKeyPairGenerator.java
index ff3df35..d0a36e6 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/generators/DSAKeyPairGenerator.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/generators/DSAKeyPairGenerator.java
@@ -5,7 +5,11 @@
 
 import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
 import org.bouncycastle.crypto.AsymmetricCipherKeyPairGenerator;
+import org.bouncycastle.crypto.CryptoServicePurpose;
+import org.bouncycastle.crypto.CryptoServicesRegistrar;
 import org.bouncycastle.crypto.KeyGenerationParameters;
+import org.bouncycastle.crypto.constraints.ConstraintUtils;
+import org.bouncycastle.crypto.constraints.DefaultServiceProperties;
 import org.bouncycastle.crypto.params.DSAKeyGenerationParameters;
 import org.bouncycastle.crypto.params.DSAParameters;
 import org.bouncycastle.crypto.params.DSAPrivateKeyParameters;
@@ -30,6 +34,8 @@
         KeyGenerationParameters param)
     {
         this.param = (DSAKeyGenerationParameters)param;
+
+        CryptoServicesRegistrar.checkConstraints(new DefaultServiceProperties("DSAKeyGen", ConstraintUtils.bitsOfSecurityFor(this.param.getParameters().getP()), this.param.getParameters(), CryptoServicePurpose.KEYGEN));
     }
 
     public AsymmetricCipherKeyPair generateKeyPair()
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/generators/ECKeyPairGenerator.java b/bcprov/src/main/java/org/bouncycastle/crypto/generators/ECKeyPairGenerator.java
index 1712db7..f622687 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/generators/ECKeyPairGenerator.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/generators/ECKeyPairGenerator.java
@@ -5,7 +5,11 @@
 
 import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
 import org.bouncycastle.crypto.AsymmetricCipherKeyPairGenerator;
+import org.bouncycastle.crypto.CryptoServicePurpose;
+import org.bouncycastle.crypto.CryptoServicesRegistrar;
 import org.bouncycastle.crypto.KeyGenerationParameters;
+import org.bouncycastle.crypto.constraints.ConstraintUtils;
+import org.bouncycastle.crypto.constraints.DefaultServiceProperties;
 import org.bouncycastle.crypto.params.ECDomainParameters;
 import org.bouncycastle.crypto.params.ECKeyGenerationParameters;
 import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
@@ -20,9 +24,20 @@
 public class ECKeyPairGenerator
     implements AsymmetricCipherKeyPairGenerator, ECConstants
 {
+    private final String name;
     ECDomainParameters  params;
     SecureRandom        random;
 
+    public ECKeyPairGenerator()
+    {
+        this("ECKeyGen");
+    }
+
+    protected ECKeyPairGenerator(String name)
+    {
+        this.name = name;
+    }
+
     public void init(
         KeyGenerationParameters param)
     {
@@ -30,6 +45,8 @@
 
         this.random = ecP.getRandom();
         this.params = ecP.getDomainParameters();
+
+        CryptoServicesRegistrar.checkConstraints(new DefaultServiceProperties(name, ConstraintUtils.bitsOfSecurityFor(this.params.getCurve()), ecP.getDomainParameters(), CryptoServicePurpose.KEYGEN));
     }
 
     /**
@@ -47,7 +64,7 @@
         {
             d = BigIntegers.createRandomBigInteger(nBitLength, random);
 
-            if (d.compareTo(ONE) < 0  || (d.compareTo(n) >= 0))
+            if (isOutOfRangeD(d, n))
             {
                 continue;
             }
@@ -67,6 +84,11 @@
             new ECPrivateKeyParameters(d, params));
     }
 
+    protected boolean isOutOfRangeD(BigInteger d, BigInteger n)
+    {
+        return d.compareTo(ONE) < 0 || (d.compareTo(n) >= 0);
+    }
+
     protected ECMultiplier createBasePointMultiplier()
     {
         return new FixedPointCombMultiplier();
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/generators/OpenSSLPBEParametersGenerator.java b/bcprov/src/main/java/org/bouncycastle/crypto/generators/OpenSSLPBEParametersGenerator.java
index 3e850c1..e891b3d 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/generators/OpenSSLPBEParametersGenerator.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/generators/OpenSSLPBEParametersGenerator.java
@@ -12,8 +12,9 @@
 /**
  * Generator for PBE derived keys and ivs as usd by OpenSSL.
  * <p>
- * The scheme is a simple extension of PKCS 5 V2.0 Scheme 1 using MD5 with an
- * iteration count of 1.
+ * Originally this scheme was a simple extension of PKCS 5 V2.0 Scheme 1 using MD5 with an
+ * iteration count of 1. The default digest was changed to SHA-256 with OpenSSL 1.1.0. This
+ * implementation still defaults to MD5, but the digest can now be set.
  * <p>
  */
 public class OpenSSLPBEParametersGenerator
@@ -24,7 +25,7 @@
     private Digest  digest = AndroidDigestFactory.getMD5();
 
     /**
-     * Construct a OpenSSL Parameters generator. 
+     * Construct a OpenSSL Parameters generator.
      */
     public OpenSSLPBEParametersGenerator()
     {
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/generators/RSAKeyPairGenerator.java b/bcprov/src/main/java/org/bouncycastle/crypto/generators/RSAKeyPairGenerator.java
index 6d5fc97..55a03e9 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/generators/RSAKeyPairGenerator.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/generators/RSAKeyPairGenerator.java
@@ -4,7 +4,11 @@
 
 import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
 import org.bouncycastle.crypto.AsymmetricCipherKeyPairGenerator;
+import org.bouncycastle.crypto.CryptoServicePurpose;
+import org.bouncycastle.crypto.CryptoServicesRegistrar;
 import org.bouncycastle.crypto.KeyGenerationParameters;
+import org.bouncycastle.crypto.constraints.ConstraintUtils;
+import org.bouncycastle.crypto.constraints.DefaultServiceProperties;
 import org.bouncycastle.crypto.params.RSAKeyGenerationParameters;
 import org.bouncycastle.crypto.params.RSAKeyParameters;
 import org.bouncycastle.crypto.params.RSAPrivateCrtKeyParameters;
@@ -25,6 +29,8 @@
     public void init(KeyGenerationParameters param)
     {
         this.param = (RSAKeyGenerationParameters)param;
+
+        CryptoServicesRegistrar.checkConstraints(new DefaultServiceProperties("RSAKeyGen", ConstraintUtils.bitsOfSecurityForFF(param.getStrength()), null, CryptoServicePurpose.KEYGEN));
     }
 
     public AsymmetricCipherKeyPair generateKeyPair()
@@ -91,12 +97,12 @@
                     continue;
                 }
 
-	            /*
+                /*
                  * Require a minimum weight of the NAF representation, since low-weight composites may
-	             * be weak against a version of the number-field-sieve for factoring.
-	             *
-	             * See "The number field sieve for integers of low weight", Oliver Schirokauer.
-	             */
+                 * be weak against a version of the number-field-sieve for factoring.
+                 *
+                 * See "The number field sieve for integers of low weight", Oliver Schirokauer.
+                 */
                 if (WNafUtil.getNafWeight(n) < minWeight)
                 {
                     p = chooseRandomPrime(pbitlength, e, squaredBound);
@@ -142,8 +148,8 @@
             qInv = BigIntegers.modOddInverse(p, q);
 
             result = new AsymmetricCipherKeyPair(
-                new RSAKeyParameters(false, n, e),
-                new RSAPrivateCrtKeyParameters(n, e, d, p, q, dP, dQ, qInv));
+                new RSAKeyParameters(false, n, e, true),
+                new RSAPrivateCrtKeyParameters(n, e, d, p, q, dP, dQ, qInv, true));
         }
 
         return result;
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/generators/SM2KeyPairGenerator.java b/bcprov/src/main/java/org/bouncycastle/crypto/generators/SM2KeyPairGenerator.java
new file mode 100644
index 0000000..31c922f
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/generators/SM2KeyPairGenerator.java
@@ -0,0 +1,36 @@
+package org.bouncycastle.crypto.generators;
+
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
+import org.bouncycastle.crypto.AsymmetricCipherKeyPairGenerator;
+import org.bouncycastle.crypto.CryptoServicePurpose;
+import org.bouncycastle.crypto.CryptoServicesRegistrar;
+import org.bouncycastle.crypto.KeyGenerationParameters;
+import org.bouncycastle.crypto.constraints.ConstraintUtils;
+import org.bouncycastle.crypto.constraints.DefaultServiceProperties;
+import org.bouncycastle.crypto.params.ECDomainParameters;
+import org.bouncycastle.crypto.params.ECKeyGenerationParameters;
+import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
+import org.bouncycastle.crypto.params.ECPublicKeyParameters;
+import org.bouncycastle.math.ec.ECConstants;
+import org.bouncycastle.math.ec.ECMultiplier;
+import org.bouncycastle.math.ec.ECPoint;
+import org.bouncycastle.math.ec.FixedPointCombMultiplier;
+import org.bouncycastle.math.ec.WNafUtil;
+import org.bouncycastle.util.BigIntegers;
+
+public class SM2KeyPairGenerator
+    extends ECKeyPairGenerator
+{
+    public SM2KeyPairGenerator()
+    {
+        super("SM2KeyGen");
+    }
+
+    protected boolean isOutOfRangeD(BigInteger d, BigInteger n)
+    {
+        return d.compareTo(ONE) < 0 || (d.compareTo(n.subtract(BigIntegers.ONE)) >= 0);
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/macs/CBCBlockCipherMac.java b/bcprov/src/main/java/org/bouncycastle/crypto/macs/CBCBlockCipherMac.java
index 9bf6cb0..9087807 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/macs/CBCBlockCipherMac.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/macs/CBCBlockCipherMac.java
@@ -92,7 +92,7 @@
             throw new IllegalArgumentException("MAC size must be multiple of 8");
         }
 
-        this.cipher = new CBCBlockCipher(cipher);
+        this.cipher = CBCBlockCipher.newInstance(cipher);
         this.padding = padding;
         this.macSize = macSizeInBits / 8;
 
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/macs/HMac.java b/bcprov/src/main/java/org/bouncycastle/crypto/macs/HMac.java
index 5868262..c17c8f2 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/macs/HMac.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/macs/HMac.java
@@ -216,15 +216,15 @@
      */
     public void reset()
     {
-        /*
-         * reset the underlying digest.
-         */
-        digest.reset();
-
-        /*
-         * reinitialize the digest.
-         */
-        digest.update(inputPad, 0, inputPad.length);
+        if (ipadState != null)
+        {
+            ((Memoable)digest).reset(ipadState);
+        }
+        else
+        {
+            digest.reset();
+            digest.update(inputPad, 0, inputPad.length);
+        }
     }
 
     private static void xorPad(byte[] pad, int len, byte n)
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/macs/Zuc128Mac.java b/bcprov/src/main/java/org/bouncycastle/crypto/macs/Zuc128Mac.java
new file mode 100644
index 0000000..199ac23
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/macs/Zuc128Mac.java
@@ -0,0 +1,245 @@
+package org.bouncycastle.crypto.macs;
+
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.Mac;
+import org.bouncycastle.crypto.engines.Zuc128CoreEngine;
+
+/**
+ * Zuc128 Mac implementation.
+ * Based on https://www.qtc.jp/3GPP/Specs/eea3eia3specificationv16.pdf
+ */
+public final class Zuc128Mac
+    implements Mac
+{
+    /**
+     * The Maximum Bit Mask.
+     */
+    private static final int TOPBIT = 0x80;
+
+    /**
+     * The Zuc128 Engine.
+     */
+    private final InternalZuc128Engine theEngine;
+
+    /**
+     * The calculated Mac in words.
+     */
+    private int theMac;
+
+    /**
+     * The active keyStream.
+     */
+    private final int[] theKeyStream;
+
+    /**
+     * The initialised state.
+     */
+    private Zuc128CoreEngine theState;
+
+    /**
+     * The current word index.
+     */
+    private int theWordIndex;
+
+    /**
+     * The current byte index.
+     */
+    private int theByteIndex;
+
+    /**
+     * Constructor.
+     */
+    public Zuc128Mac()
+    {
+        theEngine = new InternalZuc128Engine();
+        theKeyStream = new int[2];
+    }
+
+    /**
+     * Obtain Algorithm Name.
+     *
+     * @return the name
+     */
+    public String getAlgorithmName()
+    {
+        return "Zuc128Mac";
+    }
+
+    /**
+     * Obtain Mac Size.
+     *
+     * @return the size in Bytes
+     */
+    public int getMacSize()
+    {
+        return 4; // Integer.Bytes
+    }
+
+    /**
+     * Initialise the Mac.
+     *
+     * @param pParams the parameters
+     */
+    public void init(final CipherParameters pParams)
+    {
+        /* Initialise the engine */
+        theEngine.init(true, pParams);
+        theState = (Zuc128CoreEngine)theEngine.copy();
+        initKeyStream();
+    }
+
+    /**
+     * Initialise the keyStream.
+     */
+    private void initKeyStream()
+    {
+        /* Initialise the Mac */
+        theMac = 0;
+
+        /* Initialise the KeyStream */
+        for (int i = 0; i < theKeyStream.length - 1; i++)
+        {
+            theKeyStream[i] = theEngine.createKeyStreamWord();
+        }
+        theWordIndex = theKeyStream.length - 1;
+        theByteIndex = 3; //Integer.BYTES - 1;
+    }
+
+    /**
+     * Update the mac with a single byte.
+     *
+     * @param in the byte to update with
+     */
+    public void update(final byte in)
+    {
+        /* shift for next byte */
+        shift4NextByte();
+
+        /* Loop through the bits */
+        final int bitBase = theByteIndex * 8; //Byte.SIZE;
+        for (int bitMask = TOPBIT, bitNo = 0; bitMask > 0; bitMask >>= 1, bitNo++)
+        {
+            /* If the bit is set */
+            if ((in & bitMask) != 0)
+            {
+                /* update theMac */
+                updateMac(bitBase + bitNo);
+            }
+        }
+    }
+
+    /**
+     * Shift for next byte.
+     */
+    private void shift4NextByte()
+    {
+        /* Adjust the byte index */
+        theByteIndex = (theByteIndex + 1) % 4; //Integer.BYTES;
+
+        /* Adjust keyStream if required */
+        if (theByteIndex == 0)
+        {
+            theKeyStream[theWordIndex] = theEngine.createKeyStreamWord();
+            theWordIndex = (theWordIndex + 1) % theKeyStream.length;
+        }
+    }
+
+    /**
+     * Update the Mac.
+     *
+     * @param bitNo the bit number
+     */
+    private void updateMac(final int bitNo)
+    {
+        /* Update the Mac */
+        theMac ^= getKeyStreamWord(bitNo);
+    }
+
+    /**
+     * Obtain the keyStreamWord.
+     *
+     * @param bitNo the bitNumber
+     * @return the word
+     */
+    private int getKeyStreamWord(final int bitNo)
+    {
+        /* Access the first word and return it if this is bit 0 */
+        final int myFirst = theKeyStream[theWordIndex];
+        if (bitNo == 0)
+        {
+            return myFirst;
+        }
+
+        /* Access the second word */
+        final int mySecond = theKeyStream[(theWordIndex + 1) % theKeyStream.length];
+        return (myFirst << bitNo) | (mySecond >>> (32 - bitNo)); // Integer.SIZE - bitNo
+    }
+
+    /**
+     * Update the mac.
+     *
+     * @param in    the input buffer
+     * @param inOff the starting offset in the input buffer
+     * @param len   the length of data to process
+     */
+    public void update(final byte[] in, final int inOff, final int len)
+    {
+        for (int byteNo = 0; byteNo < len; byteNo++)
+        {
+            update(in[inOff + byteNo]);
+        }
+    }
+
+    /**
+     * Obtain the final word.
+     *
+     * @return the final word
+     */
+    private int getFinalWord()
+    {
+        if (theByteIndex != 0)
+        {
+            return theEngine.createKeyStreamWord();
+        }
+        theWordIndex = (theWordIndex + 1) % theKeyStream.length;
+        return theKeyStream[theWordIndex];
+    }
+
+    /**
+     * Finalize the mac.
+     *
+     * @param out    the output buffer
+     * @param outOff the starting offset in the input buffer
+     * @return the size of the mac
+     */
+    public int doFinal(final byte[] out, final int outOff)
+    {
+        /* Finish the Mac and output it */
+        shift4NextByte();
+        theMac ^= getKeyStreamWord(theByteIndex * 8); //Byte.SIZE
+        theMac ^= getFinalWord();
+        Zuc128CoreEngine.encode32be(theMac, out, outOff);
+
+        /* Reset the Mac */
+        reset();
+        return getMacSize();
+    }
+
+    public void reset()
+    {
+        if (theState != null)
+        {
+            theEngine.reset(theState);
+        }
+        initKeyStream();
+    }
+
+    private static class InternalZuc128Engine
+        extends Zuc128CoreEngine
+    {
+        int createKeyStreamWord()
+        {
+            return super.makeKeyStreamWord();
+        }
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/macs/Zuc256Mac.java b/bcprov/src/main/java/org/bouncycastle/crypto/macs/Zuc256Mac.java
new file mode 100644
index 0000000..26313d5
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/macs/Zuc256Mac.java
@@ -0,0 +1,274 @@
+package org.bouncycastle.crypto.macs;
+
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.Mac;
+import org.bouncycastle.crypto.engines.Zuc256CoreEngine;
+
+/**
+ * Zuc256 Mac implementation.
+ * Based on https://www.is.cas.cn/ztzl2016/zouchongzhi/201801/W020180126529970733243.pdf
+ */
+public final class Zuc256Mac
+    implements Mac
+{
+    /**
+     * The Maximum Bit Mask.
+     */
+    private static final int TOPBIT = 0x80;
+
+    /**
+     * The Zuc256 Engine.
+     */
+    private final InternalZuc256Engine theEngine;
+
+    /**
+     * The mac length.
+     */
+    private final int theMacLength;
+
+    /**
+     * The calculated Mac in words.
+     */
+    private final int[] theMac;
+
+    /**
+     * The active keyStream.
+     */
+    private final int[] theKeyStream;
+
+    /**
+     * The initialised state.
+     */
+    private Zuc256CoreEngine theState;
+
+    /**
+     * The current word index.
+     */
+    private int theWordIndex;
+
+    /**
+     * The current byte index.
+     */
+    private int theByteIndex;
+
+    /**
+     * Constructor.
+     *
+     * @param pLength the bit length of the Mac
+     */
+    public Zuc256Mac(final int pLength)
+    {
+        theEngine = new InternalZuc256Engine(pLength);
+        theMacLength = pLength;
+        final int numWords = pLength / 32; // Integer.SIZE
+        theMac = new int[numWords];
+        theKeyStream = new int[numWords + 1];
+    }
+
+    /**
+     * Obtain Algorithm Name.
+     *
+     * @return the name
+     */
+    public String getAlgorithmName()
+    {
+        return "Zuc256Mac-" + theMacLength;
+    }
+
+    /**
+     * Obtain Mac Size.
+     *
+     * @return the size in Bytes
+     */
+    public int getMacSize()
+    {
+        return theMacLength / 8; //Byte.SIZE
+    }
+
+    /**
+     * Initialise the Mac.
+     *
+     * @param pParams the parameters
+     */
+    public void init(final CipherParameters pParams)
+    {
+        /* Initialise the engine */
+        theEngine.init(true, pParams);
+        theState = (Zuc256CoreEngine)theEngine.copy();
+        initKeyStream();
+    }
+
+    /**
+     * Initialise the keyStream.
+     */
+    private void initKeyStream()
+    {
+        /* Initialise the Mac */
+        for (int i = 0; i < theMac.length; i++)
+        {
+            theMac[i] = theEngine.createKeyStreamWord();
+        }
+
+        /* Initialise the KeyStream */
+        for (int i = 0; i < theKeyStream.length - 1; i++)
+        {
+            theKeyStream[i] = theEngine.createKeyStreamWord();
+        }
+        theWordIndex = theKeyStream.length - 1;
+        theByteIndex = 4 - 1;    // Integer.SIZE
+    }
+
+    /**
+     * Update the mac with a single byte.
+     *
+     * @param in the byte to update with
+     */
+    public void update(final byte in)
+    {
+        /* shift for next byte */
+        shift4NextByte();
+
+        /* Loop through the bits */
+        final int bitBase = theByteIndex * 8; //Byte.SIZE;
+        for (int bitMask = TOPBIT, bitNo = 0; bitMask > 0; bitMask >>= 1, bitNo++)
+        {
+            /* If the bit is set */
+            if ((in & bitMask) != 0)
+            {
+                /* update theMac */
+                updateMac(bitBase + bitNo);
+            }
+        }
+    }
+
+    /**
+     * Shift for next byte.
+     */
+    private void shift4NextByte()
+    {
+        /* Adjust the byte index */
+        theByteIndex = (theByteIndex + 1) % 4; //Integer.BYTES
+
+        /* Adjust keyStream if required */
+        if (theByteIndex == 0)
+        {
+            theKeyStream[theWordIndex] = theEngine.createKeyStreamWord();
+            theWordIndex = (theWordIndex + 1) % theKeyStream.length;
+        }
+    }
+
+    /**
+     * Shift for final update.
+     */
+    private void shift4Final()
+    {
+        /* Adjust the byte index */
+        theByteIndex = (theByteIndex + 1) % 4; //Integer.BYTES
+
+        /* No need to read another word to the keyStream */
+        if (theByteIndex == 0)
+        {
+            theWordIndex = (theWordIndex + 1) % theKeyStream.length;
+        }
+    }
+
+    /**
+     * Update the Mac.
+     *
+     * @param bitNo the bit number
+     */
+    private void updateMac(final int bitNo)
+    {
+        /* Loop through the Mac */
+        for (int wordNo = 0; wordNo < theMac.length; wordNo++)
+        {
+            theMac[wordNo] ^= getKeyStreamWord(wordNo, bitNo);
+        }
+    }
+
+    /**
+     * Obtain the keyStreamWord.
+     *
+     * @param wordNo the wordNumber
+     * @param bitNo  the bitNumber
+     * @return the word
+     */
+    private int getKeyStreamWord(final int wordNo, final int bitNo)
+    {
+        /* Access the first word and return it if this is bit 0 */
+        final int myFirst = theKeyStream[(theWordIndex + wordNo) % theKeyStream.length];
+        if (bitNo == 0)
+        {
+            return myFirst;
+        }
+
+        /* Access the second word */
+        final int mySecond = theKeyStream[(theWordIndex + wordNo + 1) % theKeyStream.length];
+        return (myFirst << bitNo) | (mySecond >>> (32 - bitNo)); //Integer.SIZE - bitNo
+    }
+
+    /**
+     * Update the mac.
+     *
+     * @param in    the input buffer
+     * @param inOff the starting offset in the input buffer
+     * @param len   the length of data to process
+     */
+    public void update(final byte[] in, final int inOff, final int len)
+    {
+        for (int byteNo = 0; byteNo < len; byteNo++)
+        {
+            update(in[inOff + byteNo]);
+        }
+    }
+
+    /**
+     * Finalize the mac.
+     *
+     * @param out    the output buffer
+     * @param outOff the starting offset in the output buffer
+     * @return the size of the mac
+     */
+    public int doFinal(final byte[] out, final int outOff)
+    {
+        /* shift for final update */
+        shift4Final();
+
+        /* Finish the Mac and output it */
+        updateMac(theByteIndex * 8); //Byte.SIZE)
+        for (int i = 0; i < theMac.length; i++)
+        {
+            Zuc256CoreEngine.encode32be(theMac[i], out, outOff + i * 4); //Integer.BYTES)
+        }
+
+        /* Reset the Mac */
+        reset();
+        return getMacSize();
+    }
+
+    /**
+     * Reset the Mac.
+     */
+    public void reset()
+    {
+        if (theState != null)
+        {
+            theEngine.reset(theState);
+        }
+        initKeyStream();
+    }
+
+    private static class InternalZuc256Engine
+        extends Zuc256CoreEngine
+    {
+        public InternalZuc256Engine(int pLength)
+        {
+            super(pLength);
+        }
+
+        int createKeyStreamWord()
+        {
+            return super.makeKeyStreamWord();
+        }
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/modes/CBCBlockCipher.java b/bcprov/src/main/java/org/bouncycastle/crypto/modes/CBCBlockCipher.java
index d4800e6..130ff78 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/modes/CBCBlockCipher.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/modes/CBCBlockCipher.java
@@ -3,6 +3,7 @@
 import org.bouncycastle.crypto.BlockCipher;
 import org.bouncycastle.crypto.CipherParameters;
 import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.DefaultMultiBlockCipher;
 import org.bouncycastle.crypto.params.ParametersWithIV;
 import org.bouncycastle.util.Arrays;
 
@@ -10,7 +11,8 @@
  * implements Cipher-Block-Chaining (CBC) mode on top of a simple cipher.
  */
 public class CBCBlockCipher
-    implements BlockCipher
+    extends DefaultMultiBlockCipher
+    implements CBCModeCipher
 {
     private byte[]          IV;
     private byte[]          cbcV;
@@ -21,9 +23,20 @@
     private boolean         encrypting;
 
     /**
+     * Return a new CBC mode cipher based on the passed in base cipher
+     *
+     * @param cipher the base cipher for the CBC mode.
+     */
+    public static CBCModeCipher newInstance(BlockCipher cipher)
+    {
+        return new CBCBlockCipher(cipher);
+    }
+
+    /**
      * Basic constructor.
      *
      * @param cipher the block cipher to be used as the basis of chaining.
+     * @deprecated use the CBCBlockCipher.newInstance() static method.
      */
     public CBCBlockCipher(
         BlockCipher cipher)
@@ -77,31 +90,23 @@
 
             System.arraycopy(iv, 0, IV, 0, iv.length);
 
-            reset();
-
-            // if null it's an IV changed only.
-            if (ivParam.getParameters() != null)
-            {
-                cipher.init(encrypting, ivParam.getParameters());
-            }
-            else if (oldEncrypting != encrypting)
-            {
-                throw new IllegalArgumentException("cannot change encrypting state without providing key.");
-            }
+            params = ivParam.getParameters();
         }
         else
         {
-            reset();
+            Arrays.fill(IV, (byte)0);
+        }
 
-            // if it's null, key is to be reused.
-            if (params != null)
-            {
-                cipher.init(encrypting, params);
-            }
-            else if (oldEncrypting != encrypting)
-            {
-                throw new IllegalArgumentException("cannot change encrypting state without providing key.");
-            }
+        reset();
+
+        // if null it's an IV changed only (key is to be reused).
+        if (params != null)
+        {
+            cipher.init(encrypting, params);
+        }
+        else if (oldEncrypting != encrypting)
+        {
+            throw new IllegalArgumentException("cannot change encrypting state without providing key.");
         }
     }
 
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/modes/CBCModeCipher.java b/bcprov/src/main/java/org/bouncycastle/crypto/modes/CBCModeCipher.java
new file mode 100644
index 0000000..682b580
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/modes/CBCModeCipher.java
@@ -0,0 +1,15 @@
+package org.bouncycastle.crypto.modes;
+
+import org.bouncycastle.crypto.BlockCipher;
+import org.bouncycastle.crypto.MultiBlockCipher;
+
+public interface CBCModeCipher
+    extends MultiBlockCipher
+{
+    /**
+     * return the underlying block cipher that we are wrapping.
+     *
+     * @return the underlying block cipher that we are wrapping.
+     */
+    BlockCipher getUnderlyingCipher();
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/modes/CCMBlockCipher.java b/bcprov/src/main/java/org/bouncycastle/crypto/modes/CCMBlockCipher.java
index 00e1a78..1ac255b 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/modes/CCMBlockCipher.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/modes/CCMBlockCipher.java
@@ -20,7 +20,7 @@
  * <b>Note</b>: this mode is a packet mode - it needs all the data up front.
  */
 public class CCMBlockCipher
-    implements AEADBlockCipher
+    implements CCMModeCipher
 {
     private BlockCipher           cipher;
     private int                   blockSize;
@@ -34,9 +34,20 @@
     private ExposedByteArrayOutputStream data = new ExposedByteArrayOutputStream();
 
     /**
+     * Return a new CCM mode cipher based on the passed in base cipher
+     *
+     * @param cipher the base cipher for the CCM mode.
+     */
+    public static CCMModeCipher newInstance(BlockCipher cipher)
+    {
+        return new CCMBlockCipher(cipher);
+    }
+
+    /**
      * Basic constructor.
      *
      * @param c the block cipher to be used.
+     * @deprecated use the CCMBlockCipher.newInstance() static method.
      */
     public CCMBlockCipher(BlockCipher c)
     {
@@ -260,7 +271,7 @@
         iv[0] = (byte)((q - 1) & 0x7);
         System.arraycopy(nonce, 0, iv, 1, nonce.length);
 
-        BlockCipher ctrCipher = new SICBlockCipher(cipher);
+        BlockCipher ctrCipher = SICBlockCipher.newInstance(cipher);
         ctrCipher.init(forEncryption, new ParametersWithIV(keyParam, iv));
 
         int outputLen;
@@ -454,7 +465,7 @@
         return getAssociatedTextLength() > 0;
     }
 
-    private class ExposedByteArrayOutputStream
+    private static class ExposedByteArrayOutputStream
         extends ByteArrayOutputStream
     {
         public ExposedByteArrayOutputStream()
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/modes/CCMModeCipher.java b/bcprov/src/main/java/org/bouncycastle/crypto/modes/CCMModeCipher.java
new file mode 100644
index 0000000..d96ac05
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/modes/CCMModeCipher.java
@@ -0,0 +1,6 @@
+package org.bouncycastle.crypto.modes;
+
+public interface CCMModeCipher
+    extends AEADBlockCipher
+{
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/modes/CFBBlockCipher.java b/bcprov/src/main/java/org/bouncycastle/crypto/modes/CFBBlockCipher.java
index b11716f..1bfaef9 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/modes/CFBBlockCipher.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/modes/CFBBlockCipher.java
@@ -12,6 +12,7 @@
  */
 public class CFBBlockCipher
     extends StreamBlockCipher
+    implements CFBModeCipher
 {
     private byte[]          IV;
     private byte[]          cfbV;
@@ -24,11 +25,23 @@
     private int             byteCount;
 
     /**
+     * Return a new CFB mode cipher based on the passed in base cipher
+     *
+     * @param cipher the base cipher for the CFB mode.
+     * @param blockSize the block size (in bits) used for the CFB mode.
+     */
+    public static CFBModeCipher newInstance(BlockCipher cipher, int blockSize)
+    {
+        return new CFBBlockCipher(cipher, blockSize);
+    }
+
+    /**
      * Basic constructor.
      *
      * @param cipher the block cipher to be used as the basis of the
      * feedback mode.
      * @param bitBlockSize the block size in bits (note: a multiple of 8)
+     * @deprecated use the equivalent CFBBlockCipher.newInstance() static method.
      */
     public CFBBlockCipher(
         BlockCipher cipher,
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/modes/CFBModeCipher.java b/bcprov/src/main/java/org/bouncycastle/crypto/modes/CFBModeCipher.java
new file mode 100644
index 0000000..4ef78ce
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/modes/CFBModeCipher.java
@@ -0,0 +1,9 @@
+package org.bouncycastle.crypto.modes;
+
+import org.bouncycastle.crypto.MultiBlockCipher;
+import org.bouncycastle.crypto.StreamCipher;
+
+public interface CFBModeCipher
+    extends MultiBlockCipher, StreamCipher
+{
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/modes/CTRModeCipher.java b/bcprov/src/main/java/org/bouncycastle/crypto/modes/CTRModeCipher.java
new file mode 100644
index 0000000..13fd97e
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/modes/CTRModeCipher.java
@@ -0,0 +1,16 @@
+package org.bouncycastle.crypto.modes;
+
+import org.bouncycastle.crypto.BlockCipher;
+import org.bouncycastle.crypto.MultiBlockCipher;
+import org.bouncycastle.crypto.SkippingStreamCipher;
+
+public interface CTRModeCipher
+    extends MultiBlockCipher, SkippingStreamCipher
+{
+    /**
+     * return the underlying block cipher that we are wrapping.
+     *
+     * @return the underlying block cipher that we are wrapping.
+     */
+    BlockCipher getUnderlyingCipher();
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/modes/CTSBlockCipher.java b/bcprov/src/main/java/org/bouncycastle/crypto/modes/CTSBlockCipher.java
index ea80706..af1fa46 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/modes/CTSBlockCipher.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/modes/CTSBlockCipher.java
@@ -1,8 +1,8 @@
 package org.bouncycastle.crypto.modes;
 
 import org.bouncycastle.crypto.BlockCipher;
-import org.bouncycastle.crypto.BufferedBlockCipher;
 import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.DefaultBufferedBlockCipher;
 import org.bouncycastle.crypto.InvalidCipherTextException;
 import org.bouncycastle.crypto.OutputLengthException;
 import org.bouncycastle.crypto.StreamBlockCipher;
@@ -12,7 +12,7 @@
  * be used to produce cipher text which is the same length as the plain text.
  */
 public class CTSBlockCipher
-    extends BufferedBlockCipher
+    extends DefaultBufferedBlockCipher
 {
     private int     blockSize;
 
@@ -221,9 +221,9 @@
                     buf[i] ^= block[i - blockSize];
                 }
 
-                if (cipher instanceof CBCBlockCipher)
+                if (cipher instanceof CBCModeCipher)
                 {
-                    BlockCipher c = ((CBCBlockCipher)cipher).getUnderlyingCipher();
+                    BlockCipher c = ((CBCModeCipher)cipher).getUnderlyingCipher();
 
                     c.processBlock(buf, blockSize, out, outOff);
                 }
@@ -250,9 +250,9 @@
 
             if (bufOff > blockSize)
             {
-                if (cipher instanceof CBCBlockCipher)
+                if (cipher instanceof CBCModeCipher)
                 {
-                    BlockCipher c = ((CBCBlockCipher)cipher).getUnderlyingCipher();
+                    BlockCipher c = ((CBCModeCipher)cipher).getUnderlyingCipher();
 
                     c.processBlock(buf, 0, block, 0);
                 }
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/modes/GCMBlockCipher.java b/bcprov/src/main/java/org/bouncycastle/crypto/modes/GCMBlockCipher.java
index 75343d8..9f39995 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/modes/GCMBlockCipher.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/modes/GCMBlockCipher.java
@@ -21,7 +21,7 @@
  * NIST Special Publication 800-38D.
  */
 public class GCMBlockCipher
-    implements AEADBlockCipher
+    implements GCMModeCipher
 {
     private static final int BLOCK_SIZE = 16;
     // BEGIN Android-added: Max input size limitation from NIST.
@@ -58,11 +58,45 @@
     private long        atLength;
     private long        atLengthPre;
 
+    /**
+     * Return a new GCM mode cipher based on the passed in base cipher
+     *
+     * @param cipher the base cipher for the GCM mode.
+     */
+    public static GCMModeCipher newInstance(BlockCipher cipher)
+    {
+        return new GCMBlockCipher(cipher);
+    }
+
+    /**
+     * Return a new GCM mode cipher based on the passed in base cipher and multiplier.
+     *
+     * @param cipher the base cipher for the GCM mode.
+     * @param m the GCM multiplier to use.
+     */
+    public static GCMModeCipher newInstance(BlockCipher cipher, GCMMultiplier m)
+    {
+        return new GCMBlockCipher(cipher, m);
+    }
+
+    /**
+     * Base constructor - GCM mode over base cipher c.
+     *
+     * @param c the base cipher.
+     * @deprecated use the GCMBlockCipher.newInstance() static method.
+     */
     public GCMBlockCipher(BlockCipher c)
     {
         this(c, null);
     }
 
+    /**
+     * Base constructor - GCM mode over base cipher c over base multiplier m.
+     *
+     * @param c the base cipher.
+     * @param m the GCM multiplier to use.
+     * @deprecated use the CBCBlockCipher.newInstance() static method.
+     */
     public GCMBlockCipher(BlockCipher c, GCMMultiplier m)
     {
         if (c.getBlockSize() != BLOCK_SIZE)
@@ -287,17 +321,35 @@
         }
         // END Android-added: Max input size limitation from NIST.
 
-        for (int i = 0; i < len; ++i)
+        if (atBlockPos > 0)
         {
-            atBlock[atBlockPos] = in[inOff + i];
-            if (++atBlockPos == BLOCK_SIZE)
+            int available = BLOCK_SIZE - atBlockPos;
+            if (len < available)
             {
-                // Hash each block as it fills
-                gHASHBlock(S_at, atBlock);
-                atBlockPos = 0;
-                atLength += BLOCK_SIZE;
+                System.arraycopy(in, inOff, atBlock, atBlockPos, len);
+                atBlockPos += len;
+                return;
             }
+
+            System.arraycopy(in, inOff, atBlock, atBlockPos, available);
+            gHASHBlock(S_at, atBlock);
+            atLength += BLOCK_SIZE;
+            inOff += available;
+            len -= available;
+            //atBlockPos = 0;
         }
+
+        int inLimit = inOff + len - BLOCK_SIZE;
+
+        while (inOff <= inLimit)
+        {
+            gHASHBlock(S_at, in, inOff);
+            atLength += BLOCK_SIZE;
+            inOff += BLOCK_SIZE;
+        }
+
+        atBlockPos = BLOCK_SIZE + inLimit - inOff;
+        System.arraycopy(in, inOff, atBlock, 0, atBlockPos);
     }
 
     private void initCipher()
@@ -334,13 +386,14 @@
         bufBlock[bufOff] = in;
         if (++bufOff == bufBlock.length)
         {
-            processBlock(bufBlock, 0, out, outOff);
             if (forEncryption)
             {
+                encryptBlock(bufBlock, 0, out, outOff);
                 bufOff = 0;
             }
             else
             {
+                decryptBlock(bufBlock, 0, out, outOff);
                 System.arraycopy(bufBlock, BLOCK_SIZE, bufBlock, 0, macSize);
                 bufOff = macSize;
             }
@@ -368,49 +421,79 @@
 
         if (forEncryption)
         {
-            if (bufOff != 0)
+            if (bufOff > 0)
             {
-                while (len > 0)
+                int available = BLOCK_SIZE - bufOff;
+                if (len < available)
                 {
-                    --len;
-                    bufBlock[bufOff] = in[inOff++];
-                    if (++bufOff == BLOCK_SIZE)
-                    {
-                        processBlock(bufBlock, 0, out, outOff);
-                        bufOff = 0;
-                        resultLen += BLOCK_SIZE;
-                        break;
-                    }
+                    System.arraycopy(in, inOff, bufBlock, bufOff, len);
+                    bufOff += len;
+                    return 0;
                 }
+
+                System.arraycopy(in, inOff, bufBlock, bufOff, available);
+                encryptBlock(bufBlock, 0, out, outOff);
+                inOff += available;
+                len -= available;
+                resultLen = BLOCK_SIZE;
+                //bufOff = 0;
             }
 
-            while (len >= BLOCK_SIZE)
+            int inLimit = inOff + len - BLOCK_SIZE;
+
+            while (inOff <= inLimit)
             {
-                processBlock(in, inOff, out, outOff + resultLen);
+                encryptBlock(in, inOff, out, outOff + resultLen);
                 inOff += BLOCK_SIZE;
-                len -= BLOCK_SIZE;
                 resultLen += BLOCK_SIZE;
             }
 
-            if (len > 0)
-            {
-                System.arraycopy(in, inOff, bufBlock, 0, len);
-                bufOff = len;
-            }
+            bufOff = BLOCK_SIZE + inLimit - inOff;
+            System.arraycopy(in, inOff, bufBlock, 0, bufOff);
         }
         else
         {
-            for (int i = 0; i < len; ++i)
+            int available = bufBlock.length - bufOff;
+            if (len < available)
             {
-                bufBlock[bufOff] = in[inOff + i];
-                if (++bufOff == bufBlock.length)
+                System.arraycopy(in, inOff, bufBlock, bufOff, len);
+                bufOff += len;
+                return 0;
+            }
+
+            if (bufOff >= BLOCK_SIZE)
+            {
+                decryptBlock(bufBlock, 0, out, outOff);
+                System.arraycopy(bufBlock, BLOCK_SIZE, bufBlock, 0, bufOff -= BLOCK_SIZE);
+                resultLen = BLOCK_SIZE;
+
+                available += BLOCK_SIZE;
+                if (len < available)
                 {
-                    processBlock(bufBlock, 0, out, outOff + resultLen);
-                    System.arraycopy(bufBlock, BLOCK_SIZE, bufBlock, 0, macSize);
-                    bufOff = macSize;
-                    resultLen += BLOCK_SIZE;
+                    System.arraycopy(in, inOff, bufBlock, bufOff, len);
+                    bufOff += len;
+                    return resultLen;
                 }
             }
+
+            int inLimit = inOff + len - bufBlock.length;
+
+            available = BLOCK_SIZE - bufOff;
+            System.arraycopy(in, inOff, bufBlock, bufOff, available);
+            decryptBlock(bufBlock, 0, out, outOff + resultLen);
+            inOff += available;
+            resultLen += BLOCK_SIZE;
+            //bufOff = 0;
+
+            while (inOff <= inLimit)
+            {
+                decryptBlock(in, inOff, out, outOff + resultLen);
+                inOff += BLOCK_SIZE;
+                resultLen += BLOCK_SIZE;
+            }
+
+            bufOff = bufBlock.length + inLimit - inOff;
+            System.arraycopy(in, inOff, bufBlock, 0, bufOff);
         }
 
         return resultLen;
@@ -583,7 +666,7 @@
         }
     }
 
-    private void processBlock(byte[] buf, int bufOff, byte[] out, int outOff)
+    private void decryptBlock(byte[] buf, int bufOff, byte[] out, int outOff)
     {
         if ((out.length - outOff) < BLOCK_SIZE)
         {
@@ -597,18 +680,30 @@
         byte[] ctrBlock = new byte[BLOCK_SIZE];
         getNextCTRBlock(ctrBlock);
 
-        if (forEncryption)
+        gHASHBlock(S, buf, bufOff);
+        GCMUtil.xor(ctrBlock, 0, buf, bufOff, out, outOff);
+
+        totalLength += BLOCK_SIZE;
+    }
+
+    private void encryptBlock(byte[] buf, int bufOff, byte[] out, int outOff)
+    {
+        if ((out.length - outOff) < BLOCK_SIZE)
         {
-            GCMUtil.xor(ctrBlock, buf, bufOff);
-            gHASHBlock(S, ctrBlock);
-            System.arraycopy(ctrBlock, 0, out, outOff, BLOCK_SIZE);
+            throw new OutputLengthException("Output buffer too short");
         }
-        else
+        if (totalLength == 0)
         {
-            gHASHBlock(S, buf, bufOff);
-            GCMUtil.xor(ctrBlock, 0, buf, bufOff, out, outOff);
+            initCipher();
         }
 
+        byte[] ctrBlock = new byte[BLOCK_SIZE];
+
+        getNextCTRBlock(ctrBlock);
+        GCMUtil.xor(ctrBlock, buf, bufOff);
+        gHASHBlock(S, ctrBlock);
+        System.arraycopy(ctrBlock, 0, out, outOff, BLOCK_SIZE);
+
         totalLength += BLOCK_SIZE;
     }
 
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/modes/GCMModeCipher.java b/bcprov/src/main/java/org/bouncycastle/crypto/modes/GCMModeCipher.java
new file mode 100644
index 0000000..b45459a
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/modes/GCMModeCipher.java
@@ -0,0 +1,6 @@
+package org.bouncycastle.crypto.modes;
+
+public interface GCMModeCipher
+    extends AEADBlockCipher
+{
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/modes/GCMSIVBlockCipher.java b/bcprov/src/main/java/org/bouncycastle/crypto/modes/GCMSIVBlockCipher.java
new file mode 100644
index 0000000..404629b
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/modes/GCMSIVBlockCipher.java
@@ -0,0 +1,971 @@
+package org.bouncycastle.crypto.modes;
+
+import java.io.ByteArrayOutputStream;
+
+import org.bouncycastle.crypto.BlockCipher;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.InvalidCipherTextException;
+import org.bouncycastle.crypto.OutputLengthException;
+import org.bouncycastle.crypto.engines.AESEngine;
+import org.bouncycastle.crypto.modes.gcm.GCMMultiplier;
+import org.bouncycastle.crypto.modes.gcm.Tables4kGCMMultiplier;
+import org.bouncycastle.crypto.params.AEADParameters;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.crypto.params.ParametersWithIV;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.Bytes;
+import org.bouncycastle.util.Integers;
+import org.bouncycastle.util.Longs;
+import org.bouncycastle.util.Pack;
+
+/**
+ * GCM-SIV Mode.
+ * <p>It should be noted that the specified limit of 2<sup>36</sup> bytes is not supported. This is because all bytes are
+ * cached in a <b>ByteArrayOutputStream</b> object (which has a limit of a little less than 2<sup>31</sup> bytes),
+ * and are output on the <b>doFinal</b>() call (which can only process a maximum of 2<sup>31</sup> bytes).</p>
+ * <p>The practical limit of 2<sup>31</sup> - 24 bytes is policed, and attempts to breach the limit will be rejected</p>
+ * <p>In order to properly support the higher limit, an extended form of <b>ByteArrayOutputStream</b> would be needed
+ * which would use multiple arrays to store the data. In addition, a new <b>doOutput</b> method would be required (similar
+ * to that in <b>XOF</b> digests), which would allow the data to be output over multiple calls. Alternatively an extended
+ * form of <b>ByteArrayInputStream</b> could be used to deliver the data.</p>
+ */
+public class GCMSIVBlockCipher
+         implements AEADBlockCipher
+{
+     /**
+      * The buffer length.
+      */
+     private static final int BUFLEN = 16;
+
+     /**
+      * The halfBuffer length.
+      */
+     private static final int HALFBUFLEN = BUFLEN >> 1;
+
+     /**
+      * The nonce length.
+      */
+     private static final int NONCELEN = 12;
+
+     /**
+      * The maximum data length (AEAD/PlainText). Due to implementation constraints this is restricted to the maximum
+      * array length (https://programming.guide/java/array-maximum-length.html) minus the BUFLEN to allow for the MAC
+      */
+     private static final int MAX_DATALEN = Integer.MAX_VALUE - 8 - BUFLEN;
+
+     /**
+      * The top bit mask.
+      */
+     private static final byte MASK = (byte) 0x80;
+
+     /**
+      * The addition constant.
+      */
+     private static final byte ADD = (byte) 0xE1;
+
+     /**
+      * The initialisation flag.
+      */
+     private static final int INIT = 1;
+
+     /**
+      * The aeadComplete flag.
+      */
+     private static final int AEAD_COMPLETE = 2;
+
+     /**
+      * The cipher.
+      */
+     private final BlockCipher theCipher;
+
+     /**
+      * The multiplier.
+      */
+     private final GCMMultiplier theMultiplier;
+
+     /**
+      * The gHash buffer.
+      */
+     private final byte[] theGHash = new byte[BUFLEN];
+
+     /**
+      * The reverse buffer.
+      */
+     private final byte[] theReverse = new byte[BUFLEN];
+
+     /**
+      * The aeadHasher.
+      */
+     private final GCMSIVHasher theAEADHasher;
+
+     /**
+      * The dataHasher.
+      */
+     private final GCMSIVHasher theDataHasher;
+
+     /**
+      * The plainDataStream.
+      */
+     private GCMSIVCache thePlain;
+
+     /**
+      * The encryptedDataStream (decryption only).
+      */
+     private GCMSIVCache theEncData;
+
+     /**
+      * Are we encrypting?
+      */
+     private boolean forEncryption;
+
+     /**
+      * The initialAEAD.
+      */
+     private byte[] theInitialAEAD;
+
+     /**
+      * The nonce.
+      */
+     private byte[] theNonce;
+
+     /**
+      * The flags.
+      */
+     private int theFlags;
+
+     // defined fixed
+     private byte[]      macBlock = new byte[16];
+
+     /**
+      * Constructor.
+      */
+     public GCMSIVBlockCipher()
+     {
+         this(AESEngine.newInstance());
+     }
+
+     /**
+      * Constructor.
+      * @param pCipher the underlying cipher
+      */
+     public GCMSIVBlockCipher(final BlockCipher pCipher)
+     {
+         this(pCipher, new Tables4kGCMMultiplier());
+     }
+
+     /**
+      * Constructor.
+      * @param pCipher the underlying cipher
+      * @param pMultiplier the multiplier
+      */
+     public GCMSIVBlockCipher(final BlockCipher pCipher,
+                              final GCMMultiplier pMultiplier)
+     {
+         /* Ensure that the cipher is the correct size */
+         if (pCipher.getBlockSize() != BUFLEN)
+         {
+             throw new IllegalArgumentException("Cipher required with a block size of " + BUFLEN + ".");
+         }
+
+         /* Store parameters */
+         theCipher = pCipher;
+         theMultiplier = pMultiplier;
+
+         /* Create the hashers */
+         theAEADHasher = new GCMSIVHasher();
+         theDataHasher = new GCMSIVHasher();
+     }
+
+     public BlockCipher getUnderlyingCipher()
+     {
+         return theCipher;
+     }
+
+     public void init(final boolean pEncrypt,
+                      final CipherParameters cipherParameters) throws IllegalArgumentException
+     {
+         /* Set defaults */
+         byte[] myInitialAEAD = null;
+         byte[] myNonce = null;
+         KeyParameter myKey = null;
+
+         /* Access parameters */
+         if (cipherParameters instanceof AEADParameters)
+         {
+             final AEADParameters myAEAD = (AEADParameters) cipherParameters;
+             myInitialAEAD = myAEAD.getAssociatedText();
+             myNonce = myAEAD.getNonce();
+             myKey = myAEAD.getKey();
+         }
+         else if (cipherParameters instanceof ParametersWithIV)
+         {
+             final ParametersWithIV myParms = (ParametersWithIV) cipherParameters;
+             myNonce = myParms.getIV();
+             myKey = (KeyParameter) myParms.getParameters();
+         }
+         else
+         {
+             throw new IllegalArgumentException("invalid parameters passed to GCM-SIV");
+         }
+
+         /* Check nonceSize */
+         if (myNonce == null || myNonce.length != NONCELEN)
+         {
+             throw new IllegalArgumentException("Invalid nonce");
+         }
+
+         /* Check keysize */
+         if (myKey == null
+             || (myKey.getKeyLength() != BUFLEN
+                 && myKey.getKeyLength() != (BUFLEN << 1)))
+         {
+             throw new IllegalArgumentException("Invalid key");
+         }
+
+         /* Reset details */
+         forEncryption = pEncrypt;
+         theInitialAEAD = myInitialAEAD;
+         theNonce = myNonce;
+
+         /* Initialise the keys */
+         deriveKeys(myKey);
+         resetStreams();
+     }
+
+     public String getAlgorithmName()
+     {
+         return theCipher.getAlgorithmName() + "-GCM-SIV";
+     }
+
+     /**
+      * check AEAD status.
+      * @param pLen the aeadLength
+      */
+     private void checkAEADStatus(final int pLen)
+     {
+         /* Check we are initialised */
+         if ((theFlags & INIT) == 0)
+         {
+             throw new IllegalStateException("Cipher is not initialised");
+         }
+
+         /* Check AAD is allowed */
+         if ((theFlags & AEAD_COMPLETE) != 0)
+         {
+             throw new IllegalStateException("AEAD data cannot be processed after ordinary data");
+         }
+
+         /* Make sure that we haven't breached AEAD data limit */
+         if (theAEADHasher.getBytesProcessed() + Long.MIN_VALUE
+              > (MAX_DATALEN - pLen) + Long.MIN_VALUE)
+         {
+             throw new IllegalStateException("AEAD byte count exceeded");
+         }
+     }
+
+     /**
+      * check status.
+      * @param pLen the dataLength
+      */
+     private void checkStatus(final int pLen)
+     {
+         /* Check we are initialised */
+         if ((theFlags & INIT) == 0)
+         {
+             throw new IllegalStateException("Cipher is not initialised");
+         }
+
+         /* Complete the AEAD section if this is the first data */
+         if ((theFlags & AEAD_COMPLETE) == 0)
+         {
+             theAEADHasher.completeHash();
+             theFlags |= AEAD_COMPLETE;
+         }
+
+         /* Make sure that we haven't breached data limit */
+         long dataLimit = MAX_DATALEN;
+         long currBytes = thePlain.size();
+         if (!forEncryption)
+         {
+             dataLimit += BUFLEN;
+             currBytes = theEncData.size();
+         }
+         if (currBytes + Long.MIN_VALUE
+               > (dataLimit - pLen) + Long.MIN_VALUE)
+         {
+             throw new IllegalStateException("byte count exceeded");
+         }
+     }
+
+     public void processAADByte(final byte pByte)
+     {
+         /* Check that we can supply AEAD */
+         checkAEADStatus(1);
+
+         /* Process the aead */
+         theAEADHasher.updateHash(pByte);
+     }
+
+     public void processAADBytes(final byte[] pData,
+                                 final int pOffset,
+                                 final int pLen)
+     {
+         /* Check that we can supply AEAD */
+         checkAEADStatus(pLen);
+
+         /* Check input buffer */
+         checkBuffer(pData, pOffset, pLen, false);
+
+         /* Process the aead */
+         theAEADHasher.updateHash(pData, pOffset, pLen);
+     }
+
+     public int processByte(final byte pByte,
+                            final byte[] pOutput,
+                            final int pOutOffset) throws DataLengthException
+     {
+         /* Check that we have initialised */
+         checkStatus(1);
+
+         /* Store the data */
+         if (forEncryption)
+         {
+             thePlain.write(pByte);
+             theDataHasher.updateHash(pByte);
+         }
+         else
+         {
+             theEncData.write(pByte);
+         }
+
+         /* No data returned */
+         return 0;
+     }
+
+     public int processBytes(final byte[] pData,
+                             final int pOffset,
+                             final int pLen,
+                             final byte[] pOutput,
+                             final int pOutOffset) throws DataLengthException
+     {
+         /* Check that we have initialised */
+         checkStatus(pLen);
+
+         /* Check input buffer */
+         checkBuffer(pData, pOffset, pLen, false);
+
+         /* Store the data */
+         if (forEncryption)
+         {
+             thePlain.write(pData, pOffset, pLen);
+             theDataHasher.updateHash(pData, pOffset, pLen);
+         }
+         else
+         {
+             theEncData.write(pData, pOffset, pLen);
+         }
+
+         /* No data returned */
+         return 0;
+     }
+
+     public int doFinal(final byte[] pOutput,
+                        final int pOffset) throws IllegalStateException, InvalidCipherTextException
+     {
+         /* Check that we have initialised */
+         checkStatus(0);
+
+         /* Check output buffer */
+         checkBuffer(pOutput, pOffset, getOutputSize(0), true);
+
+         /* If we are encrypting */
+         if (forEncryption)
+         {
+             /* Derive the tag */
+             final byte[] myTag = calculateTag();
+
+             /* encrypt the plain text */
+             final int myDataLen = BUFLEN + encryptPlain(myTag, pOutput, pOffset);
+
+             /* Add the tag to the output */
+             System.arraycopy(myTag, 0, pOutput, pOffset + thePlain.size(), BUFLEN);
+
+             System.arraycopy(myTag, 0, macBlock, 0, macBlock.length);
+
+             /* Reset the streams */
+             resetStreams();
+             return myDataLen;
+
+             /* else we are decrypting */
+         }
+         else
+         {
+             /* decrypt to plain text */
+             decryptPlain();
+
+             /* Release plain text */
+             final int myDataLen = thePlain.size();
+             final byte[] mySrc = thePlain.getBuffer();
+             System.arraycopy(mySrc, 0, pOutput, pOffset, myDataLen);
+
+             /* Reset the streams */
+             resetStreams();
+             return myDataLen;
+         }
+     }
+
+     public byte[] getMac()
+     {
+         return Arrays.clone(macBlock);
+     }
+
+     public int getUpdateOutputSize(final int pLen)
+     {
+         return 0;
+     }
+
+     public int getOutputSize(final int pLen)
+     {
+         if (forEncryption)
+         {
+             return pLen + thePlain.size() + BUFLEN;
+         }
+         final int myCurr = pLen + theEncData.size();
+         return myCurr > BUFLEN ? myCurr - BUFLEN : 0;
+     }
+
+     public void reset()
+     {
+         resetStreams();
+     }
+
+     /**
+      * Reset Streams.
+      */
+     private void resetStreams()
+     {
+         /* Clear the plainText buffer */
+         if (thePlain != null)
+         {
+             thePlain.clearBuffer();
+         }
+
+         /* Reset hashers */
+         theAEADHasher.reset();
+         theDataHasher.reset();
+
+         /* Recreate streams (to release memory) */
+         thePlain = new GCMSIVCache();
+         theEncData = forEncryption ? null : new GCMSIVCache();
+
+         /* Initialise AEAD if required */
+         theFlags &= ~AEAD_COMPLETE;
+         Arrays.fill(theGHash, (byte) 0);
+         if (theInitialAEAD != null)
+         {
+             theAEADHasher.updateHash(theInitialAEAD, 0, theInitialAEAD.length);
+         }
+      }
+
+     /**
+      * Obtain buffer length (allowing for null).
+      * @param pBuffer the buffere
+      * @return the length
+      */
+     private static int bufLength(final byte[] pBuffer)
+     {
+         return pBuffer == null ? 0 : pBuffer.length;
+     }
+
+     /**
+      * Check buffer.
+      * @param pBuffer the buffer
+      * @param pOffset the offset
+      * @param pLen the length
+      * @param pOutput is this an output buffer?
+      */
+     private static void checkBuffer(final byte[] pBuffer,
+                                     final int pOffset,
+                                     final int pLen,
+                                     final boolean pOutput)
+     {
+         /* Access lengths */
+         final int myBufLen = bufLength(pBuffer);
+         final int myLast = pOffset + pLen;
+
+         /* Check for negative values and buffer overflow */
+         final boolean badLen = pLen < 0 || pOffset < 0 || myLast < 0;
+         if (badLen || myLast > myBufLen)
+         {
+             throw pOutput
+                     ? new OutputLengthException("Output buffer too short.")
+                     : new DataLengthException("Input buffer too short.");
+         }
+     }
+
+     /**
+      * encrypt data stream.
+      * @param pCounter the counter
+      * @param pTarget the target buffer
+      * @param pOffset the target offset
+      * @return the length of data encrypted
+      */
+     private int encryptPlain(final byte[] pCounter,
+                              final byte[] pTarget,
+                              final int pOffset)
+     {
+         /* Access buffer and length */
+         final byte[] mySrc = thePlain.getBuffer();
+         final byte[] myCounter = Arrays.clone(pCounter);
+         myCounter[BUFLEN - 1] |= MASK;
+         final byte[] myMask = new byte[BUFLEN];
+         int myRemaining = thePlain.size();
+         int myOff = 0;
+
+         /* While we have data to process */
+         while (myRemaining > 0)
+         {
+             /* Generate the next mask */
+             theCipher.processBlock(myCounter, 0, myMask, 0);
+
+             /* Xor data into mask */
+             final int myLen = Math.min(BUFLEN, myRemaining);
+             xorBlock(myMask, mySrc, myOff, myLen);
+
+             /* Copy encrypted data to output */
+             System.arraycopy(myMask, 0, pTarget, pOffset + myOff, myLen);
+
+             /* Adjust counters */
+             myRemaining -= myLen;
+             myOff += myLen;
+             incrementCounter(myCounter);
+         }
+
+         /* Return the amount of data processed */
+         return thePlain.size();
+     }
+
+     /**
+      * decrypt data stream.
+      * @throws InvalidCipherTextException on data too short or mac check failed
+      */
+     private void decryptPlain() throws InvalidCipherTextException
+     {
+         /* Access buffer and length */
+         final byte[] mySrc = theEncData.getBuffer();
+         int myRemaining = theEncData.size() - BUFLEN;
+
+         /* Check for insufficient data */
+         if (myRemaining < 0)
+         {
+             throw new InvalidCipherTextException("Data too short");
+         }
+
+         /* Access counter */
+         final byte[] myExpected = Arrays.copyOfRange(mySrc, myRemaining, myRemaining + BUFLEN);
+         final byte[] myCounter = Arrays.clone(myExpected);
+         myCounter[BUFLEN - 1] |= MASK;
+         final byte[] myMask = new byte[BUFLEN];
+         int myOff = 0;
+
+         /* While we have data to process */
+         while (myRemaining > 0)
+         {
+             /* Generate the next mask */
+             theCipher.processBlock(myCounter, 0, myMask, 0);
+
+             /* Xor data into mask */
+             final int myLen = Math.min(BUFLEN, myRemaining);
+             xorBlock(myMask, mySrc, myOff, myLen);
+
+             /* Write data to plain dataStream */
+             thePlain.write(myMask, 0, myLen);
+             theDataHasher.updateHash(myMask, 0, myLen);
+
+             /* Adjust counters */
+             myRemaining -= myLen;
+             myOff += myLen;
+             incrementCounter(myCounter);
+         }
+
+         /* Derive and check the tag */
+         final byte[] myTag = calculateTag();
+         if (!Arrays.constantTimeAreEqual(myTag, myExpected))
+         {
+             reset();
+             throw new InvalidCipherTextException("mac check failed");
+         }
+
+         System.arraycopy(myTag, 0, macBlock, 0, macBlock.length);
+     }
+
+     /**
+      * calculate tag.
+      * @return the calculated tag
+      */
+     private byte[] calculateTag()
+     {
+         /* Complete the hash */
+         theDataHasher.completeHash();
+         final byte[] myPolyVal = completePolyVal();
+
+         /* calculate polyVal */
+         final byte[] myResult = new byte[BUFLEN];
+
+         /* Fold in the nonce */
+         for (int i = 0; i < NONCELEN; i++)
+         {
+             myPolyVal[i] ^= theNonce[i];
+         }
+
+         /* Clear top bit */
+         myPolyVal[BUFLEN - 1] &= (MASK - 1);
+
+         /* Calculate tag and return it */
+         theCipher.processBlock(myPolyVal, 0, myResult, 0);
+         return myResult;
+     }
+
+     /**
+      * complete polyVAL.
+      * @return the calculated value
+      */
+     private byte[] completePolyVal()
+     {
+         /* Build the polyVal result */
+         final byte[] myResult = new byte[BUFLEN];
+         gHashLengths();
+         fillReverse(theGHash, 0, BUFLEN, myResult);
+         return myResult;
+     }
+
+     /**
+      * process lengths.
+      */
+     private void gHashLengths()
+     {
+         /* Create reversed bigEndian buffer to keep it simple */
+         final byte[] myIn = new byte[BUFLEN];
+         Pack.longToBigEndian(Bytes.SIZE * theDataHasher.getBytesProcessed(), myIn, 0);
+         Pack.longToBigEndian(Bytes.SIZE * theAEADHasher.getBytesProcessed(), myIn, Longs.BYTES);
+
+         /* hash value */
+         gHASH(myIn);
+     }
+
+     /**
+      * perform the next GHASH step.
+      * @param pNext the next value
+      */
+     private void gHASH(final byte[] pNext)
+     {
+         xorBlock(theGHash, pNext);
+         theMultiplier.multiplyH(theGHash);
+     }
+
+     /**
+      * Byte reverse a buffer.
+      * @param pInput the input buffer
+      * @param pOffset the offset
+      * @param pLength the length of data (<= BUFLEN)
+      * @param pOutput the output buffer
+      */
+     private static void fillReverse(final byte[] pInput,
+                                     final int pOffset,
+                                     final int pLength,
+                                     final byte[] pOutput)
+     {
+         /* Loop through the buffer */
+         for (int i = 0, j = BUFLEN - 1; i < pLength; i++, j--)
+         {
+             /* Copy byte */
+             pOutput[j] = pInput[pOffset + i];
+         }
+     }
+
+     /**
+      * xor a full block buffer.
+      * @param pLeft the left operand and result
+      * @param pRight the right operand
+      */
+     private static void xorBlock(final byte[] pLeft,
+                                  final byte[] pRight)
+     {
+         /* Loop through the bytes */
+         for (int i = 0; i < BUFLEN; i++)
+         {
+             pLeft[i] ^= pRight[i];
+         }
+     }
+
+     /**
+      * xor a partial block buffer.
+      * @param pLeft the left operand and result
+      * @param pRight the right operand
+      * @param pOffset the offset in the right operand
+      * @param pLength the length of data in the right operand
+      */
+     private static void xorBlock(final byte[] pLeft,
+                                  final byte[] pRight,
+                                  final int pOffset,
+                                  final int pLength)
+                                  {
+         /* Loop through the bytes */
+         for (int i = 0; i < pLength; i++)
+         {
+             pLeft[i] ^= pRight[i + pOffset];
+         }
+     }
+
+     /**
+      * increment the counter.
+      * @param pCounter the counter to increment
+      */
+     private static void incrementCounter(final byte[] pCounter)
+     {
+         /* Loop through the bytes incrementing counter */
+         for (int i = 0; i < Integers.BYTES; i++)
+         {
+             if (++pCounter[i] != 0)
+             {
+                 break;
+             }
+         }
+     }
+
+     /**
+      * multiply by X.
+      * @param pValue the value to adjust
+      */
+     private static void mulX(final byte[] pValue)
+     {
+         /* Loop through the bytes */
+         byte myMask = (byte) 0;
+         for (int i = 0; i < BUFLEN; i++)
+         {
+             final byte myValue = pValue[i];
+             pValue[i] = (byte) (((myValue >> 1) & ~MASK) | myMask);
+             myMask = (myValue & 1) == 0 ? 0 : MASK;
+         }
+
+         /* Xor in addition if last bit was set */
+         if (myMask != 0)
+         {
+             pValue[0] ^= ADD;
+         }
+     }
+
+     /**
+      * Derive Keys.
+      * @param pKey the keyGeneration key
+      */
+     private void deriveKeys(final KeyParameter pKey)
+     {
+         /* Create the buffers */
+         final byte[] myIn = new byte[BUFLEN];
+         final byte[] myOut = new byte[BUFLEN];
+         final byte[] myResult = new byte[BUFLEN];
+         final byte[] myEncKey = new byte[pKey.getKeyLength()];
+
+         /* Prepare for encryption */
+         System.arraycopy(theNonce, 0, myIn, BUFLEN - NONCELEN, NONCELEN);
+         theCipher.init(true, pKey);
+
+         /* Derive authentication key */
+         int myOff = 0;
+         theCipher.processBlock(myIn, 0, myOut, 0);
+         System.arraycopy(myOut, 0, myResult, myOff, HALFBUFLEN);
+         myIn[0]++;
+         myOff += HALFBUFLEN;
+         theCipher.processBlock(myIn, 0, myOut, 0);
+         System.arraycopy(myOut, 0, myResult, myOff, HALFBUFLEN);
+
+         /* Derive encryption key */
+         myIn[0]++;
+         myOff = 0;
+         theCipher.processBlock(myIn, 0, myOut, 0);
+         System.arraycopy(myOut, 0, myEncKey, myOff, HALFBUFLEN);
+         myIn[0]++;
+         myOff += HALFBUFLEN;
+         theCipher.processBlock(myIn, 0, myOut, 0);
+         System.arraycopy(myOut, 0, myEncKey, myOff, HALFBUFLEN);
+
+         /* If we have a 32byte key */
+         if (myEncKey.length == BUFLEN << 1)
+         {
+             /* Derive remainder of encryption key */
+             myIn[0]++;
+             myOff += HALFBUFLEN;
+             theCipher.processBlock(myIn, 0, myOut, 0);
+             System.arraycopy(myOut, 0, myEncKey, myOff, HALFBUFLEN);
+             myIn[0]++;
+             myOff += HALFBUFLEN;
+             theCipher.processBlock(myIn, 0, myOut, 0);
+             System.arraycopy(myOut, 0, myEncKey, myOff, HALFBUFLEN);
+         }
+
+         /* Initialise the Cipher */
+         theCipher.init(true, new KeyParameter(myEncKey));
+
+         /* Initialise the multiplier */
+         fillReverse(myResult, 0, BUFLEN, myOut);
+         mulX(myOut);
+         theMultiplier.init(myOut);
+         theFlags |= INIT;
+     }
+
+     /**
+      * GCMSIVCache.
+      */
+     private static class GCMSIVCache
+             extends ByteArrayOutputStream
+     {
+         /**
+          * Constructor.
+          */
+         GCMSIVCache()
+         {
+         }
+
+         /**
+          * Obtain the buffer.
+          * @return the buffer
+          */
+         byte[] getBuffer()
+         {
+             return this.buf;
+         }
+
+         /**
+          * Clear the buffer.
+          */
+         void clearBuffer()
+         {
+             Arrays.fill(getBuffer(), (byte) 0);
+         }
+     }
+
+     /**
+      * Hash Control.
+      */
+     private class GCMSIVHasher
+     {
+         /**
+          * Cache.
+          */
+         private final byte[] theBuffer = new byte[BUFLEN];
+
+         /**
+          * Single byte cache.
+          */
+         private final byte[] theByte = new byte[1];
+
+         /**
+          * Count of active bytes in cache.
+          */
+         private int numActive;
+
+         /**
+          * Count of hashed bytes.
+          */
+         private long numHashed;
+
+         /**
+          * Obtain the count of bytes hashed.
+          * @return the count
+          */
+         long getBytesProcessed()
+         {
+             return numHashed;
+         }
+
+         /**
+          * Reset the hasher.
+          */
+         void reset()
+         {
+             numActive = 0;
+             numHashed = 0;
+         }
+
+         /**
+          * update hash.
+          * @param pByte the byte
+          */
+         void updateHash(final byte pByte)
+         {
+             theByte[0] = pByte;
+             updateHash(theByte, 0, 1);
+         }
+
+         /**
+          * update hash.
+          * @param pBuffer the buffer
+          * @param pOffset the offset within the buffer
+          * @param pLen the length of data
+          */
+         void updateHash(final byte[] pBuffer,
+                         final int pOffset,
+                         final int pLen)
+         {
+             /* If we should process the cache */
+             final int mySpace = BUFLEN - numActive;
+             int numProcessed = 0;
+             int myRemaining = pLen;
+             if (numActive > 0
+                     && pLen >= mySpace)
+             {
+                 /* Copy data into the cache and hash it */
+                 System.arraycopy(pBuffer, pOffset, theBuffer, numActive, mySpace);
+                 fillReverse(theBuffer, 0, BUFLEN, theReverse);
+                 gHASH(theReverse);
+
+                 /* Adjust counters */
+                 numProcessed += mySpace;
+                 myRemaining -= mySpace;
+                 numActive = 0;
+             }
+
+             /* While we have full blocks */
+             while (myRemaining >= BUFLEN)
+             {
+                 /* Access the next data */
+                 fillReverse(pBuffer, pOffset + numProcessed, BUFLEN, theReverse);
+                 gHASH(theReverse);
+
+                 /* Adjust counters */
+                 numProcessed += BUFLEN;
+                 myRemaining -= BUFLEN;
+             }
+
+             /* If we have remaining data */
+             if (myRemaining > 0)
+             {
+                 /* Copy data into the cache */
+                 System.arraycopy(pBuffer, pOffset + numProcessed, theBuffer, numActive, myRemaining);
+                 numActive += myRemaining;
+             }
+
+             /* Adjust the number of bytes processed */
+             numHashed += pLen;
+         }
+
+         /**
+          * complete hash.
+          */
+         void completeHash()
+         {
+             /* If we have remaining data */
+             if (numActive > 0)
+             {
+                 /* Access the next data */
+                 Arrays.fill(theReverse, (byte) 0);
+                 fillReverse(theBuffer, 0, numActive, theReverse);
+
+                 /* hash value */
+                 gHASH(theReverse);
+             }
+         }
+     }
+ }
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/modes/SICBlockCipher.java b/bcprov/src/main/java/org/bouncycastle/crypto/modes/SICBlockCipher.java
index ae4256f..8b20136 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/modes/SICBlockCipher.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/modes/SICBlockCipher.java
@@ -3,7 +3,7 @@
 import org.bouncycastle.crypto.BlockCipher;
 import org.bouncycastle.crypto.CipherParameters;
 import org.bouncycastle.crypto.DataLengthException;
-import org.bouncycastle.crypto.SkippingStreamCipher;
+import org.bouncycastle.crypto.OutputLengthException;
 import org.bouncycastle.crypto.StreamBlockCipher;
 import org.bouncycastle.crypto.params.ParametersWithIV;
 import org.bouncycastle.util.Arrays;
@@ -15,7 +15,7 @@
  */
 public class SICBlockCipher
     extends StreamBlockCipher
-    implements SkippingStreamCipher
+    implements CTRModeCipher
 {
     private final BlockCipher     cipher;
     private final int             blockSize;
@@ -26,9 +26,20 @@
     private int             byteCount;
 
     /**
+     * Return a new SIC/CTR mode cipher based on the passed in base cipher
+     *
+     * @param cipher the base cipher for the SIC/CTR mode.
+     */
+    public static CTRModeCipher newInstance(BlockCipher cipher)
+    {
+        return new SICBlockCipher(cipher);
+    }
+
+    /**
      * Basic constructor.
      *
      * @param c the block cipher to be used.
+     * @deprecated use newInstance() method.
      */
     public SICBlockCipher(BlockCipher c)
     {
@@ -91,16 +102,75 @@
     public int processBlock(byte[] in, int inOff, byte[] out, int outOff)
           throws DataLengthException, IllegalStateException
     {
-        processBytes(in, inOff, blockSize, out, outOff);
+        if (byteCount != 0)
+        {
+            processBytes(in, inOff, blockSize, out, outOff);
+            return blockSize;
+        }
 
+        if (inOff + blockSize > in.length)
+        {
+            throw new DataLengthException("input buffer too small");
+        }
+        if (outOff + blockSize > out.length)
+        {
+            throw new OutputLengthException("output buffer too short");
+        }
+
+        cipher.processBlock(counter, 0, counterOut, 0);
+        for (int i = 0; i < blockSize; ++i)
+        {
+            out[outOff + i] = (byte)(in[inOff + i] ^ counterOut[i]);
+        }
+        incrementCounter();
         return blockSize;
     }
 
+    public int processBytes(byte[] in, int inOff, int len, byte[] out, int outOff)
+        throws DataLengthException
+    {
+        if (inOff + len > in.length)
+        {
+            throw new DataLengthException("input buffer too small");
+        }
+        if (outOff + len > out.length)
+        {
+            throw new OutputLengthException("output buffer too short");
+        }
+
+        for (int i = 0; i < len; ++i)
+        {
+            byte next;
+
+            if (byteCount == 0)
+            {
+                checkLastIncrement();
+
+                cipher.processBlock(counter, 0, counterOut, 0);
+                next = (byte)(in[inOff + i] ^ counterOut[byteCount++]);
+            }
+            else
+            {
+                next = (byte)(in[inOff + i] ^ counterOut[byteCount++]);
+                if (byteCount == counter.length)
+                {
+                    byteCount = 0;
+                    incrementCounter();
+                }
+            }
+            out[outOff + i] = next;
+        }
+
+        return len;
+    }
+
     protected byte calculateByte(byte in)
           throws DataLengthException, IllegalStateException
     {
         if (byteCount == 0)
         {
+            checkLastIncrement();
+
             cipher.processBlock(counter, 0, counterOut, 0);
 
             return (byte)(counterOut[byteCount++] ^ in);
@@ -111,10 +181,7 @@
         if (byteCount == counter.length)
         {
             byteCount = 0;
-
-            incrementCounterAt(0);
-
-            checkCounter();
+            incrementCounter();
         }
 
         return rv;
@@ -125,7 +192,7 @@
         // if the IV is the same as the blocksize we assume the user knows what they are doing
         if (IV.length < blockSize)
         {
-            for (int i = 0; i != IV.length; i++)
+            for (int i = IV.length - 1; i >= 0; i--)
             {
                 if (counter[i] != IV[i])
                 {
@@ -135,6 +202,30 @@
         }
     }
 
+    private void checkLastIncrement()
+    {
+        // if the IV is the same as the blocksize we assume the user knows what they are doing
+        if (IV.length < blockSize)
+        {
+            if (counter[IV.length - 1] != IV[IV.length - 1])
+            {
+                throw new IllegalStateException("Counter in CTR/SIC mode out of range.");
+            }
+        }
+    }
+
+    private void incrementCounter()
+    {
+        int i = counter.length;
+        while (--i >= 0)
+        {
+            if (++counter[i] != 0)
+            {
+                break;
+            }
+        }
+    }
+
     private void incrementCounterAt(int pos)
     {
         int i = counter.length - pos;
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/modes/gcm/BasicGCMExponentiator.java b/bcprov/src/main/java/org/bouncycastle/crypto/modes/gcm/BasicGCMExponentiator.java
index 7316a04..5fdeded 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/modes/gcm/BasicGCMExponentiator.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/modes/gcm/BasicGCMExponentiator.java
@@ -1,7 +1,5 @@
 package org.bouncycastle.crypto.modes.gcm;
 
-import org.bouncycastle.util.Arrays;
-
 public class BasicGCMExponentiator
     implements GCMExponentiator
 {
@@ -19,7 +17,9 @@
 
         if (pow > 0)
         {
-            long[] powX = Arrays.clone(x);
+            long[] powX = new long[GCMUtil.SIZE_LONGS];
+            GCMUtil.copy(x, powX);
+
             do
             {
                 if ((pow & 1L) != 0)
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/modes/gcm/GCMUtil.java b/bcprov/src/main/java/org/bouncycastle/crypto/modes/gcm/GCMUtil.java
index 4d13820..d7c4ef8 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/modes/gcm/GCMUtil.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/modes/gcm/GCMUtil.java
@@ -6,76 +6,119 @@
 
 public abstract class GCMUtil
 {
+    public static final int SIZE_BYTES = 16;
+    public static final int SIZE_INTS = 4;
+    public static final int SIZE_LONGS = 2;
+
     private static final int E1 = 0xe1000000;
     private static final long E1L = (E1 & 0xFFFFFFFFL) << 32;
 
     public static byte[] oneAsBytes()
     {
-        byte[] tmp = new byte[16];
+        byte[] tmp = new byte[SIZE_BYTES];
         tmp[0] = (byte)0x80;
         return tmp;
     }
 
     public static int[] oneAsInts()
     {
-        int[] tmp = new int[4];
+        int[] tmp = new int[SIZE_INTS];
         tmp[0] = 1 << 31;
         return tmp;
     }
 
     public static long[] oneAsLongs()
     {
-        long[] tmp = new long[2];
+        long[] tmp = new long[SIZE_LONGS];
         tmp[0] = 1L << 63;
         return tmp;
     }
 
+    public static byte areEqual(byte[] x, byte[] y)
+    {
+        int d = 0;
+        for (int i = 0; i < SIZE_BYTES; ++i)
+        {
+            d |= x[i] ^ y[i];
+        }
+        d = (d >>> 1) | (d & 1);
+        return (byte)((d - 1) >> 31);
+    }
+
+    public static int areEqual(int[] x, int[] y)
+    {
+        int d = 0;
+        d |= x[0] ^ y[0];
+        d |= x[1] ^ y[1];
+        d |= x[2] ^ y[2];
+        d |= x[3] ^ y[3];
+        d = (d >>> 1) | (d & 1);
+        return (d - 1) >> 31;
+    }
+
+    public static long areEqual(long[] x, long[] y)
+    {
+        long d = 0L;
+        d |= x[0] ^ y[0];
+        d |= x[1] ^ y[1];
+        d = (d >>> 1) | (d & 1L);
+        return (d - 1L) >> 63;
+    }
+
     public static byte[] asBytes(int[] x)
     {
-        byte[] z = new byte[16];
-        Pack.intToBigEndian(x, z, 0);
+        byte[] z = new byte[SIZE_BYTES];
+        Pack.intToBigEndian(x, 0, SIZE_INTS, z, 0);
         return z;
     }
 
     public static void asBytes(int[] x, byte[] z)
     {
-        Pack.intToBigEndian(x, z, 0);
+        Pack.intToBigEndian(x, 0, SIZE_INTS, z, 0);
     }
 
     public static byte[] asBytes(long[] x)
     {
-        byte[] z = new byte[16];
-        Pack.longToBigEndian(x, z, 0);
+        byte[] z = new byte[SIZE_BYTES];
+        Pack.longToBigEndian(x, 0, SIZE_LONGS, z, 0);
         return z;
     }
 
     public static void asBytes(long[] x, byte[] z)
     {
-        Pack.longToBigEndian(x, z, 0);
+        Pack.longToBigEndian(x, 0, SIZE_LONGS, z, 0);
     }
 
     public static int[] asInts(byte[] x)
     {
-        int[] z = new int[4];
-        Pack.bigEndianToInt(x, 0, z);
+        int[] z = new int[SIZE_INTS];
+        Pack.bigEndianToInt(x, 0, z, 0, SIZE_INTS);
         return z;
     }
 
     public static void asInts(byte[] x, int[] z)
     {
-        Pack.bigEndianToInt(x, 0, z);
+        Pack.bigEndianToInt(x, 0, z, 0, SIZE_INTS);
     }
 
     public static long[] asLongs(byte[] x)
     {
-        long[] z = new long[2];
-        Pack.bigEndianToLong(x, 0, z);
+        long[] z = new long[SIZE_LONGS];
+        Pack.bigEndianToLong(x, 0, z, 0, SIZE_LONGS);
         return z;
     }
 
     public static void asLongs(byte[] x, long[] z)
     {
-        Pack.bigEndianToLong(x, 0, z);
+        Pack.bigEndianToLong(x, 0, z, 0, SIZE_LONGS);
+    }
+
+    public static void copy(byte[] x, byte[] z)
+    {
+        for (int i = 0; i < SIZE_BYTES; ++i)
+        {
+            z[i] = x[i];
+        }
     }
 
     public static void copy(int[] x, int[] z)
@@ -103,10 +146,48 @@
 
     public static void multiply(byte[] x, byte[] y)
     {
-        long[] t1 = GCMUtil.asLongs(x);
-        long[] t2 = GCMUtil.asLongs(y);
-        GCMUtil.multiply(t1, t2);
-        GCMUtil.asBytes(t1, x);
+        long[] t1 = asLongs(x);
+        long[] t2 = asLongs(y);
+        multiply(t1, t2);
+        asBytes(t1, x);
+    }
+
+    static void multiply(byte[] x, long[] y)
+    {
+        /*
+         * "Three-way recursion" as described in "Batch binary Edwards", Daniel J. Bernstein.
+         *
+         * Without access to the high part of a 64x64 product x * y, we use a bit reversal to calculate it:
+         *     rev(x) * rev(y) == rev((x * y) << 1) 
+         */
+
+        long x0 = Pack.bigEndianToLong(x, 0);
+        long x1 = Pack.bigEndianToLong(x, 8);
+        long y0 = y[0], y1 = y[1];
+        long x0r = Longs.reverse(x0), x1r = Longs.reverse(x1);
+        long y0r = Longs.reverse(y0), y1r = Longs.reverse(y1);
+
+        long h0  = Longs.reverse(implMul64(x0r, y0r));
+        long h1  = implMul64(x0, y0) << 1;
+        long h2  = Longs.reverse(implMul64(x1r, y1r));
+        long h3  = implMul64(x1, y1) << 1;
+        long h4  = Longs.reverse(implMul64(x0r ^ x1r, y0r ^ y1r));
+        long h5  = implMul64(x0 ^ x1, y0 ^ y1) << 1;
+
+        long z0  = h0;
+        long z1  = h1 ^ h0 ^ h2 ^ h4;
+        long z2  = h2 ^ h1 ^ h3 ^ h5;
+        long z3  = h3;
+
+        z1 ^= z3 ^ (z3 >>>  1) ^ (z3 >>>  2) ^ (z3 >>>  7);
+//      z2 ^=      (z3 <<  63) ^ (z3 <<  62) ^ (z3 <<  57);
+        z2 ^=                    (z3 <<  62) ^ (z3 <<  57);
+
+        z0 ^= z2 ^ (z2 >>>  1) ^ (z2 >>>  2) ^ (z2 >>>  7);
+        z1 ^=      (z2 <<  63) ^ (z2 <<  62) ^ (z2 <<  57);
+
+        Pack.longToBigEndian(z0, x, 0);
+        Pack.longToBigEndian(z1, x, 8);
     }
 
     public static void multiply(int[] x, int[] y)
@@ -114,7 +195,7 @@
         int y0 = y[0], y1 = y[1], y2 = y[2], y3 = y[3];
         int z0 = 0, z1 = 0, z2 = 0, z3 = 0;
 
-        for (int i = 0; i < 4; ++i)
+        for (int i = 0; i < SIZE_INTS; ++i)
         {
             int bits = x[i];
             for (int j = 0; j < 32; ++j)
@@ -297,16 +378,24 @@
         y[1] = (x1 >>> 8) | (x0 << 56);
     }
 
+    public static void multiplyP16(long[] x)
+    {
+        long x0 = x[0], x1 = x[1];
+        long c = x1 << 48;
+        x[0] = (x0 >>> 16) ^ c ^ (c >>> 1) ^ (c >>> 2) ^ (c >>> 7);
+        x[1] = (x1 >>> 16) | (x0 << 48);
+    }
+
     public static long[] pAsLongs()
     {
-        long[] tmp = new long[2];
+        long[] tmp = new long[SIZE_LONGS];
         tmp[0] = 1L << 62;
         return tmp;
     }
 
     public static void square(long[] x, long[] z)
     {
-        long[] t  = new long[4];
+        long[] t  = new long[SIZE_LONGS * 2];
         Interleave.expand64To128Rev(x[0], t, 0);
         Interleave.expand64To128Rev(x[1], t, 2);
 
@@ -332,7 +421,7 @@
             x[i] ^= y[i]; ++i;
             x[i] ^= y[i]; ++i;
         }
-        while (i < 16);
+        while (i < SIZE_BYTES);
     }
 
     public static void xor(byte[] x, byte[] y, int yOff)
@@ -345,7 +434,7 @@
             x[i] ^= y[yOff + i]; ++i;
             x[i] ^= y[yOff + i]; ++i;
         }
-        while (i < 16);
+        while (i < SIZE_BYTES);
     }
 
     public static void xor(byte[] x, int xOff, byte[] y, int yOff, byte[] z, int zOff)
@@ -358,7 +447,7 @@
             z[zOff + i] = (byte)(x[xOff + i] ^ y[yOff + i]); ++i;
             z[zOff + i] = (byte)(x[xOff + i] ^ y[yOff + i]); ++i;
         }
-        while (i < 16);
+        while (i < SIZE_BYTES);
     }
 
     public static void xor(byte[] x, byte[] y, int yOff, int yLen)
@@ -387,7 +476,7 @@
             z[i] = (byte)(x[i] ^ y[i]); ++i;
             z[i] = (byte)(x[i] ^ y[i]); ++i;
         }
-        while (i < 16);
+        while (i < SIZE_BYTES);
     }
 
     public static void xor(int[] x, int[] y)
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/modes/gcm/Tables1kGCMExponentiator.java b/bcprov/src/main/java/org/bouncycastle/crypto/modes/gcm/Tables1kGCMExponentiator.java
index b8766bd..1a22a04 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/modes/gcm/Tables1kGCMExponentiator.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/modes/gcm/Tables1kGCMExponentiator.java
@@ -1,26 +1,25 @@
 package org.bouncycastle.crypto.modes.gcm;
 
-import java.util.Vector;
-
-import org.bouncycastle.util.Arrays;
+import java.util.ArrayList;
+import java.util.List;
 
 public class Tables1kGCMExponentiator
     implements GCMExponentiator
 {
     // A lookup table of the power-of-two powers of 'x'
     // - lookupPowX2[i] = x^(2^i)
-    private Vector lookupPowX2;
+    private List lookupPowX2;
 
     public void init(byte[] x)
     {
         long[] y = GCMUtil.asLongs(x);
-        if (lookupPowX2 != null && Arrays.areEqual(y, (long[])lookupPowX2.elementAt(0)))
+        if (lookupPowX2 != null && 0L != GCMUtil.areEqual(y, (long[])lookupPowX2.get(0)))
         {
             return;
         }
 
-        lookupPowX2 = new Vector(8);
-        lookupPowX2.addElement(y);
+        lookupPowX2 = new ArrayList(8);
+        lookupPowX2.add(y);
     }
 
     public void exponentiateX(long pow, byte[] output)
@@ -31,8 +30,7 @@
         {
             if ((pow & 1L) != 0)
             {
-                ensureAvailable(bit);
-                GCMUtil.multiply(y, (long[])lookupPowX2.elementAt(bit));
+                GCMUtil.multiply(y, getPowX2(bit));
             }
             ++bit;
             pow >>>= 1;
@@ -41,19 +39,22 @@
         GCMUtil.asBytes(y, output);
     }
 
-    private void ensureAvailable(int bit)
+    private long[] getPowX2(int bit)
     {
-        int count = lookupPowX2.size();
-        if (count <= bit)
+        int last = lookupPowX2.size() - 1;
+        if (last < bit)
         {
-            long[] tmp = (long[])lookupPowX2.elementAt(count - 1);
+            long[] prev = (long[])lookupPowX2.get(last);
             do
             {
-                tmp = Arrays.clone(tmp);
-                GCMUtil.square(tmp, tmp);
-                lookupPowX2.addElement(tmp);
+                long[] next = new long[GCMUtil.SIZE_LONGS];
+                GCMUtil.square(prev, next);
+                lookupPowX2.add(next);
+                prev = next;
             }
-            while (++count <= bit);
+            while (++last < bit);
         }
+
+        return (long[])lookupPowX2.get(bit);
     }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/modes/gcm/Tables4kGCMMultiplier.java b/bcprov/src/main/java/org/bouncycastle/crypto/modes/gcm/Tables4kGCMMultiplier.java
index e5ea748..760c8e2 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/modes/gcm/Tables4kGCMMultiplier.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/modes/gcm/Tables4kGCMMultiplier.java
@@ -1,6 +1,5 @@
 package org.bouncycastle.crypto.modes.gcm;
 
-import org.bouncycastle.util.Arrays;
 import org.bouncycastle.util.Pack;
 
 public class Tables4kGCMMultiplier
@@ -15,12 +14,13 @@
         {
             T = new long[256][2];
         }
-        else if (Arrays.areEqual(this.H, H))
+        else if (0 != GCMUtil.areEqual(this.H, H))
         {
             return;
         }
 
-        this.H = Arrays.clone(H);
+        this.H = new byte[GCMUtil.SIZE_BYTES];
+        GCMUtil.copy(H, this.H);
 
         // T[0] = 0
 
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/modes/gcm/Tables8kGCMMultiplier.java b/bcprov/src/main/java/org/bouncycastle/crypto/modes/gcm/Tables8kGCMMultiplier.java
index 6c3ae26..e2061ea 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/modes/gcm/Tables8kGCMMultiplier.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/modes/gcm/Tables8kGCMMultiplier.java
@@ -1,6 +1,5 @@
 package org.bouncycastle.crypto.modes.gcm;
 
-import org.bouncycastle.util.Arrays;
 import org.bouncycastle.util.Pack;
 
 public class Tables8kGCMMultiplier
@@ -13,16 +12,17 @@
     {
         if (T == null)
         {
-            T = new long[32][16][2];
+            T = new long[2][256][2];
         }
-        else if (Arrays.areEqual(this.H, H))
+        else if (0 != GCMUtil.areEqual(this.H, H))
         {
             return;
         }
 
-        this.H = Arrays.clone(H);
+        this.H = new byte[GCMUtil.SIZE_BYTES];
+        GCMUtil.copy(H, this.H);
 
-        for (int i = 0; i < 32; ++i)
+        for (int i = 0; i < 2; ++i)
         {
             long[][] t = T[i];
 
@@ -30,17 +30,17 @@
 
             if (i == 0)
             {
-                // t[1] = H.p^3
+                // t[1] = H.p^7
                 GCMUtil.asLongs(this.H, t[1]);
-                GCMUtil.multiplyP3(t[1], t[1]);
+                GCMUtil.multiplyP7(t[1], t[1]);
             }
             else
             {
-                // t[1] = T[i-1][1].p^4
-                GCMUtil.multiplyP4(T[i - 1][1], t[1]);
+                // t[1] = T[i-1][1].p^8
+                GCMUtil.multiplyP8(T[i - 1][1], t[1]);
             }
 
-            for (int n = 2; n < 16; n += 2)
+            for (int n = 2; n < 256; n += 2)
             {
                 // t[2.n] = t[n].p^-1
                 GCMUtil.divideP(t[n >> 1], t[n]);
@@ -49,28 +49,33 @@
                 GCMUtil.xor(t[n], t[1], t[n + 1]);
             }
         }
-
     }
 
     public void multiplyH(byte[] x)
     {
+        long[][] T0 = T[0], T1 = T[1];
+
 //        long[] z = new long[2];
-//        for (int i = 15; i >= 0; --i)
+//        for (int i = 14; i >= 0; i -= 2)
 //        {
-//            GCMUtil.xor(z, T[i + i + 1][(x[i] & 0x0F)]);
-//            GCMUtil.xor(z, T[i + i    ][(x[i] & 0xF0) >>> 4]);
+//            GCMUtil.multiplyP16(z);
+//            GCMUtil.xor(z, T0[x[i] & 0xFF]);
+//            GCMUtil.xor(z, T1[x[i + 1] & 0xFF]);
 //        }
 //        Pack.longToBigEndian(z, x, 0);
 
-        long z0 = 0, z1 = 0;
+        long[] u = T0[x[14] & 0xFF];
+        long[] v = T1[x[15] & 0xFF];
+        long z0 = u[0] ^ v[0], z1 = u[1] ^ v[1];
 
-        for (int i = 15; i >= 0; --i)
+        for (int i = 12; i >= 0; i -= 2)
         {
-            long[] u = T[i + i + 1][(x[i] & 0x0F)];
-            long[] v = T[i + i    ][(x[i] & 0xF0) >>> 4];
+            u = T0[x[i] & 0xFF];
+            v = T1[x[i + 1] & 0xFF];
 
-            z0 ^= u[0] ^ v[0];
-            z1 ^= u[1] ^ v[1];
+            long c = z1 << 48;
+            z1 = u[1] ^ v[1] ^ ((z1 >>> 16) | (z0 << 48));
+            z0 = u[0] ^ v[0] ^ (z0 >>> 16) ^ c ^ (c >>> 1) ^ (c >>> 2) ^ (c >>> 7);
         }
 
         Pack.longToBigEndian(z0, x, 0);
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/paddings/ISO10126d2Padding.java b/bcprov/src/main/java/org/bouncycastle/crypto/paddings/ISO10126d2Padding.java
index 0ad1e38..ff2f375 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/paddings/ISO10126d2Padding.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/paddings/ISO10126d2Padding.java
@@ -61,9 +61,11 @@
     public int padCount(byte[] in)
         throws InvalidCipherTextException
     {
-        int count = in[in.length - 1] & 0xff;
+        int count = in[in.length - 1] & 0xFF;
+        int position = in.length - count;
 
-        if (count > in.length)
+        int failed = (position | (count - 1)) >> 31;
+        if (failed != 0)
         {
             throw new InvalidCipherTextException("pad block corrupted");
         }
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/paddings/ISO7816d4Padding.java b/bcprov/src/main/java/org/bouncycastle/crypto/paddings/ISO7816d4Padding.java
index 54c31a9..716dfe7 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/paddings/ISO7816d4Padding.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/paddings/ISO7816d4Padding.java
@@ -60,18 +60,20 @@
     public int padCount(byte[] in)
         throws InvalidCipherTextException
     {
-        int count = in.length - 1;
-
-        while (count > 0 && in[count] == 0)
+        int position = -1, still00Mask = -1;
+        int i = in.length;
+        while (--i >= 0)
         {
-            count--;
+            int next = in[i] & 0xFF;
+            int match00Mask = ((next ^ 0x00) - 1) >> 31;
+            int match80Mask = ((next ^ 0x80) - 1) >> 31;
+            position ^= (i ^ position) & (still00Mask & match80Mask);
+            still00Mask &= match00Mask;
         }
-
-        if (in[count] != (byte)0x80)
+        if (position < 0)
         {
             throw new InvalidCipherTextException("pad block corrupted");
         }
-        
-        return in.length - count;
+        return in.length - position;
     }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/paddings/PKCS7Padding.java b/bcprov/src/main/java/org/bouncycastle/crypto/paddings/PKCS7Padding.java
index 8b30398..e1e4b52 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/paddings/PKCS7Padding.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/paddings/PKCS7Padding.java
@@ -56,18 +56,16 @@
     public int padCount(byte[] in)
         throws InvalidCipherTextException
     {
-        int count = in[in.length - 1] & 0xff;
-        byte countAsbyte = (byte)count;
+        byte countAsByte = in[in.length - 1];
+        int count = countAsByte & 0xFF;
+        int position = in.length - count;
 
-        // constant time version
-        boolean failed = (count > in.length | count == 0);
-
-        for (int i = 0; i < in.length; i++)
+        int failed = (position | (count - 1)) >> 31;
+        for (int i = 0; i < in.length; ++i)
         {
-            failed |= (in.length - i <= count) & (in[i] != countAsbyte);
+            failed |= (in[i] ^ countAsByte) & ~((i - position) >> 31);
         }
-
-        if (failed)
+        if (failed != 0)
         {
             throw new InvalidCipherTextException("pad block corrupted");
         }
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/paddings/PaddedBufferedBlockCipher.java b/bcprov/src/main/java/org/bouncycastle/crypto/paddings/PaddedBufferedBlockCipher.java
index d5928f7..28ec78b 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/paddings/PaddedBufferedBlockCipher.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/paddings/PaddedBufferedBlockCipher.java
@@ -1,9 +1,9 @@
 package org.bouncycastle.crypto.paddings;
 
 import org.bouncycastle.crypto.BlockCipher;
-import org.bouncycastle.crypto.BufferedBlockCipher;
 import org.bouncycastle.crypto.CipherParameters;
 import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.DefaultBufferedBlockCipher;
 import org.bouncycastle.crypto.InvalidCipherTextException;
 import org.bouncycastle.crypto.OutputLengthException;
 import org.bouncycastle.crypto.params.ParametersWithRandom;
@@ -16,7 +16,7 @@
  * The default padding mechanism used is the one outlined in PKCS5/PKCS7.
  */
 public class PaddedBufferedBlockCipher
-    extends BufferedBlockCipher
+    extends DefaultBufferedBlockCipher
 {
     BlockCipherPadding  padding;
 
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/paddings/TBCPadding.java b/bcprov/src/main/java/org/bouncycastle/crypto/paddings/TBCPadding.java
index 219912f..90975a0 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/paddings/TBCPadding.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/paddings/TBCPadding.java
@@ -76,14 +76,15 @@
     public int padCount(byte[] in)
         throws InvalidCipherTextException
     {
-        byte code = in[in.length - 1];
-
-        int index = in.length - 1;
-        while (index > 0 && in[index - 1] == code)
+        int i = in.length;
+        int code = in[--i] & 0xFF, count = 1, countingMask = -1;
+        while (--i >= 0)
         {
-            index--;
+            int next = in[i] & 0xFF;
+            int matchMask = ((next ^ code) - 1) >> 31;
+            countingMask &= matchMask;
+            count -= countingMask;
         }
-
-        return in.length - index;
+        return count;
     }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/paddings/X923Padding.java b/bcprov/src/main/java/org/bouncycastle/crypto/paddings/X923Padding.java
index d4d34aa..cf960e3 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/paddings/X923Padding.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/paddings/X923Padding.java
@@ -68,9 +68,11 @@
     public int padCount(byte[] in)
         throws InvalidCipherTextException
     {
-        int count = in[in.length - 1] & 0xff;
+        int count = in[in.length - 1] & 0xFF;
+        int position = in.length - count;
 
-        if (count > in.length)
+        int failed = (position | (count - 1)) >> 31;
+        if (failed != 0)
         {
             throw new InvalidCipherTextException("pad block corrupted");
         }
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/paddings/ZeroBytePadding.java b/bcprov/src/main/java/org/bouncycastle/crypto/paddings/ZeroBytePadding.java
index c756028..908ce19 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/paddings/ZeroBytePadding.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/paddings/ZeroBytePadding.java
@@ -56,18 +56,15 @@
     public int padCount(byte[] in)
         throws InvalidCipherTextException
     {
-        int count = in.length;
-
-        while (count > 0)
+        int count = 0, still00Mask = -1;
+        int i = in.length;
+        while (--i >= 0)
         {
-            if (in[count - 1] != 0)
-            {
-                break;
-            }
-
-            count--;
+            int next = in[i] & 0xFF;
+            int match00Mask = ((next ^ 0x00) - 1) >> 31;
+            still00Mask &= match00Mask;
+            count -= still00Mask;
         }
-
-        return in.length - count;
+        return count;
     }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/params/Blake3Parameters.java b/bcprov/src/main/java/org/bouncycastle/crypto/params/Blake3Parameters.java
new file mode 100644
index 0000000..e0e4000
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/params/Blake3Parameters.java
@@ -0,0 +1,84 @@
+package org.bouncycastle.crypto.params;
+
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.util.Arrays;
+
+/**
+ * Blake3 Parameters.
+ */
+public class Blake3Parameters
+        implements CipherParameters
+{
+    /**
+     * The key length.
+     */
+    private static final int KEYLEN = 32;
+
+    /**
+     * The key.
+     */
+    private byte[] theKey;
+
+    /**
+     * The context.
+     */
+    private byte[] theContext;
+
+    /**
+     * Create a key parameter.
+     * @param pContext the context
+     * @return the parameter
+     */
+    public static Blake3Parameters context(final byte[] pContext)
+    {
+        if (pContext == null)
+        {
+            throw new IllegalArgumentException("Invalid context");
+        }
+        final Blake3Parameters myParams = new Blake3Parameters();
+        myParams.theContext = Arrays.clone(pContext);
+        return myParams;
+    }
+
+    /**
+     * Create a key parameter.
+     * @param pKey the key
+     * @return the parameter
+     */
+    public static Blake3Parameters key(final byte[] pKey)
+    {
+        if (pKey == null || pKey.length != KEYLEN)
+        {
+            throw new IllegalArgumentException("Invalid keyLength");
+        }
+        final Blake3Parameters myParams = new Blake3Parameters();
+        myParams.theKey = Arrays.clone(pKey);
+        return myParams;
+    }
+
+    /**
+     * Obtain the key.
+     * @return the key
+     */
+    public byte[] getKey()
+    {
+        return Arrays.clone(theKey);
+    }
+
+    /**
+     * Clear the key bytes.
+      */
+    public void clearKey()
+    {
+        Arrays.fill(theKey, (byte) 0);
+    }
+
+    /**
+     * Obtain the salt.
+     * @return the salt
+     */
+    public byte[] getContext()
+    {
+        return Arrays.clone(theContext);
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/params/FPEParameters.java b/bcprov/src/main/java/org/bouncycastle/crypto/params/FPEParameters.java
new file mode 100644
index 0000000..e5357a1
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/params/FPEParameters.java
@@ -0,0 +1,57 @@
+package org.bouncycastle.crypto.params;
+
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.util.RadixConverter;
+import org.bouncycastle.util.Arrays;
+
+public final class FPEParameters
+    implements CipherParameters
+{
+    private final KeyParameter key;
+    private final RadixConverter radixConverter;
+    private final byte[] tweak;
+    private final boolean useInverse;
+
+    public FPEParameters(KeyParameter key, int radix, byte[] tweak)
+    {
+        this(key, radix, tweak, false);
+    }
+
+    public FPEParameters(KeyParameter key, int radix, byte[] tweak, boolean useInverse)
+    {
+        this(key, new RadixConverter(radix), tweak, useInverse);
+    }
+
+    public FPEParameters(KeyParameter key, RadixConverter radixConverter, byte[] tweak, boolean useInverse)
+    {
+        this.key = key;
+        this.radixConverter = radixConverter;
+        this.tweak = Arrays.clone(tweak);
+        this.useInverse = useInverse;
+    }
+
+    public KeyParameter getKey()
+    {
+        return key;
+    }
+
+    public int getRadix()
+    {
+        return radixConverter.getRadix();
+    }
+
+    public RadixConverter getRadixConverter()
+    {
+        return radixConverter;
+    }
+
+    public byte[] getTweak()
+    {
+        return Arrays.clone(tweak);
+    }
+
+    public boolean isUsingInverseFunction()
+    {
+        return useInverse;
+    }
+}
\ No newline at end of file
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/params/KeyParameter.java b/bcprov/src/main/java/org/bouncycastle/crypto/params/KeyParameter.java
index 5c4fe0e..e163995 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/params/KeyParameter.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/params/KeyParameter.java
@@ -1,6 +1,7 @@
 package org.bouncycastle.crypto.params;
 
 import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.util.Arrays;
 
 public class KeyParameter
     implements CipherParameters
@@ -18,13 +19,38 @@
         int     keyOff,
         int     keyLen)
     {
-        this.key = new byte[keyLen];
+        this(keyLen);
 
         System.arraycopy(key, keyOff, this.key, 0, keyLen);
     }
 
+    private KeyParameter(int length)
+    {
+        this.key = new byte[length];
+    }
+
+    public void copyTo(byte[] buf, int off, int len)
+    {
+        if (key.length != len)
+            throw new IllegalArgumentException("len");
+
+        System.arraycopy(key, 0, buf, off, len);
+    }
+
     public byte[] getKey()
     {
         return key;
     }
+
+    public int getKeyLength()
+    {
+        return key.length;
+    }
+
+    public KeyParameter reverse()
+    {
+        KeyParameter reversed = new KeyParameter(key.length);
+        Arrays.reverse(this.key, reversed.key);
+        return reversed;
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/params/RSAKeyParameters.java b/bcprov/src/main/java/org/bouncycastle/crypto/params/RSAKeyParameters.java
index 911f200..65b0b84 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/params/RSAKeyParameters.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/params/RSAKeyParameters.java
@@ -2,11 +2,16 @@
 
 import java.math.BigInteger;
 
+import org.bouncycastle.crypto.CryptoServicesRegistrar;
+import org.bouncycastle.math.Primes;
+import org.bouncycastle.util.BigIntegers;
 import org.bouncycastle.util.Properties;
 
 public class RSAKeyParameters
     extends AsymmetricKeyParameter
 {
+    private static final BigIntegers.Cache validated = new BigIntegers.Cache();
+
     // Hexadecimal value of the product of the 131 smallest odd primes from 3 to 743
     private static final BigInteger SMALL_PRIMES_PRODUCT = new BigInteger(
               "8138e8a0fcf3a4e84a771d40fd305d7f4aa59306d7251de54d98af8fe95729a1f"
@@ -25,6 +30,15 @@
         BigInteger  modulus,
         BigInteger  exponent)
     {
+        this(isPrivate, modulus, exponent, false);
+    }   
+
+    public RSAKeyParameters(
+        boolean     isPrivate,
+        BigInteger  modulus,
+        BigInteger  exponent,
+        boolean     isInternal)
+    {
         super(isPrivate);
 
         if (!isPrivate)
@@ -34,13 +48,20 @@
                 throw new IllegalArgumentException("RSA publicExponent is even");
             }
         }
-
-        this.modulus = validate(modulus);
+  
+        this.modulus = validated.contains(modulus) ? modulus : validate(modulus, isInternal);
         this.exponent = exponent;
-    }   
+    }
 
-    private BigInteger validate(BigInteger modulus)
+    private BigInteger validate(BigInteger modulus, boolean isInternal)
     {
+        if (isInternal)
+        {
+            validated.add(modulus);
+
+            return modulus;
+        }
+
         if ((modulus.intValue() & 1) == 0)
         {
             throw new IllegalArgumentException("RSA modulus is even");
@@ -53,16 +74,45 @@
             return modulus;
         }
 
+        int maxBitLength = Properties.asInteger("org.bouncycastle.rsa.max_size", 15360);
+
+        int modBitLength = modulus.bitLength();
+        if (maxBitLength < modBitLength)
+        {
+            throw new IllegalArgumentException("modulus value out of range");
+        }
+
         if (!modulus.gcd(SMALL_PRIMES_PRODUCT).equals(ONE))
         {
             throw new IllegalArgumentException("RSA modulus has a small prime factor");
         }
 
-        // TODO: add additional primePower/Composite test - expensive!!
+        int bits = modulus.bitLength() / 2;
+        int iterations = Properties.asInteger("org.bouncycastle.rsa.max_mr_tests", getMRIterations(bits));
 
+        if (iterations > 0)
+        {
+            Primes.MROutput mr = Primes.enhancedMRProbablePrimeTest(modulus, CryptoServicesRegistrar.getSecureRandom(), iterations);
+            if (!mr.isProvablyComposite())
+            {
+                throw new IllegalArgumentException("RSA modulus is not composite");
+            }
+        }
+
+        validated.add(modulus);
+        
         return modulus;
     }
 
+    private static int getMRIterations(int bits)
+    {
+        int iterations = bits >= 1536 ? 3
+            : bits >= 1024 ? 4
+            : bits >= 512 ? 7
+            : 50;
+        return iterations;
+    }
+
     public BigInteger getModulus()
     {
         return modulus;
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/params/RSAPrivateCrtKeyParameters.java b/bcprov/src/main/java/org/bouncycastle/crypto/params/RSAPrivateCrtKeyParameters.java
index b61cb5c..612cc09 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/params/RSAPrivateCrtKeyParameters.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/params/RSAPrivateCrtKeyParameters.java
@@ -16,6 +16,19 @@
      * 
      */
     public RSAPrivateCrtKeyParameters(
+         BigInteger  modulus,
+         BigInteger  publicExponent,
+         BigInteger  privateExponent,
+         BigInteger  p,
+         BigInteger  q,
+         BigInteger  dP,
+         BigInteger  dQ,
+         BigInteger  qInv)
+     {
+         this(modulus, publicExponent, privateExponent, p, q, dP, dQ, qInv, false);
+     }
+
+    public RSAPrivateCrtKeyParameters(
         BigInteger  modulus,
         BigInteger  publicExponent,
         BigInteger  privateExponent,
@@ -23,9 +36,10 @@
         BigInteger  q,
         BigInteger  dP,
         BigInteger  dQ,
-        BigInteger  qInv)
+        BigInteger  qInv,
+        boolean     isInternal)
     {
-        super(true, modulus, privateExponent);
+        super(true, modulus, privateExponent, isInternal);
 
         this.e = publicExponent;
         this.p = p;
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/signers/DSASigner.java b/bcprov/src/main/java/org/bouncycastle/crypto/signers/DSASigner.java
index 314a096..b84895d 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/signers/DSASigner.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/signers/DSASigner.java
@@ -68,6 +68,8 @@
             this.key = (DSAPublicKeyParameters)param;
         }
 
+        CryptoServicesRegistrar.checkConstraints(Utils.getDefaultProperties("DSA", key, forSigning));
+
         this.random = initSecureRandom(forSigning && !kCalculator.isDeterministic(), providedRandom);
     }
 
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/signers/ECDSASigner.java b/bcprov/src/main/java/org/bouncycastle/crypto/signers/ECDSASigner.java
index c8f687b..c962d1c 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/signers/ECDSASigner.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/signers/ECDSASigner.java
@@ -74,6 +74,8 @@
             this.key = (ECPublicKeyParameters)param;
         }
 
+        CryptoServicesRegistrar.checkConstraints(Utils.getDefaultProperties("ECDSA", key, forSigning));
+
         this.random = initSecureRandom(forSigning && !kCalculator.isDeterministic(), providedRandom);
     }
 
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/signers/Utils.java b/bcprov/src/main/java/org/bouncycastle/crypto/signers/Utils.java
new file mode 100644
index 0000000..9f411dc
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/signers/Utils.java
@@ -0,0 +1,40 @@
+package org.bouncycastle.crypto.signers;
+
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.CryptoServiceProperties;
+import org.bouncycastle.crypto.CryptoServicePurpose;
+import org.bouncycastle.crypto.constraints.ConstraintUtils;
+import org.bouncycastle.crypto.constraints.DefaultServiceProperties;
+import org.bouncycastle.crypto.params.DSAKeyParameters;
+import org.bouncycastle.crypto.params.ECKeyParameters;
+// Android-removed: unsupported algorithm
+// import org.bouncycastle.crypto.params.GOST3410KeyParameters;
+
+class Utils
+{
+    static CryptoServiceProperties getDefaultProperties(String algorithm, DSAKeyParameters k, boolean forSigning)
+    {
+        return new DefaultServiceProperties(algorithm, ConstraintUtils.bitsOfSecurityFor(k.getParameters().getP()), k, getPurpose(forSigning));
+    }
+
+    // Android-removed: unsupported algorithm
+    // static CryptoServiceProperties getDefaultProperties(String algorithm, GOST3410KeyParameters k, boolean forSigning)
+    // {
+    //     return new DefaultServiceProperties(algorithm, ConstraintUtils.bitsOfSecurityFor(k.getParameters().getP()), k, getPurpose(forSigning));
+    // }
+
+    static CryptoServiceProperties getDefaultProperties(String algorithm, ECKeyParameters k, boolean forSigning)
+    {
+        return new DefaultServiceProperties(algorithm, ConstraintUtils.bitsOfSecurityFor(k.getParameters().getCurve()), k, getPurpose(forSigning));
+    }
+
+    static CryptoServiceProperties getDefaultProperties(String algorithm, int bitsOfSecurity, CipherParameters k, boolean forSigning)
+    {
+        return new DefaultServiceProperties(algorithm, bitsOfSecurity, k, getPurpose(forSigning));
+    }
+
+    static CryptoServicePurpose getPurpose(boolean forSigning)
+    {
+        return forSigning ? CryptoServicePurpose.SIGNING : CryptoServicePurpose.VERIFYING;
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/util/BasicAlphabetMapper.java b/bcprov/src/main/java/org/bouncycastle/crypto/util/BasicAlphabetMapper.java
new file mode 100644
index 0000000..d03cc73
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/util/BasicAlphabetMapper.java
@@ -0,0 +1,105 @@
+package org.bouncycastle.crypto.util;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.bouncycastle.crypto.AlphabetMapper;
+
+/**
+ * A basic alphabet mapper that just creates a mapper based on the
+ * passed in array of characters.
+ */
+public class BasicAlphabetMapper
+    implements AlphabetMapper
+{
+    private Map<Character, Integer> indexMap = new HashMap<Character, Integer>();
+    private Map<Integer, Character> charMap = new HashMap<Integer, Character>();
+
+    /**
+     * Base constructor.
+     *
+     * @param alphabet a String of characters making up the alphabet.
+     */
+    public BasicAlphabetMapper(String alphabet)
+    {
+        this(alphabet.toCharArray());
+    }
+
+    /**
+     * Base constructor.
+     *
+     * @param alphabet an array of characters making up the alphabet.
+     */
+    public BasicAlphabetMapper(char[] alphabet)
+    {
+        for (int i = 0; i != alphabet.length; i++)
+        {
+            if (indexMap.containsKey(alphabet[i]))
+            {
+                throw new IllegalArgumentException("duplicate key detected in alphabet: " + alphabet[i]);
+            }
+            indexMap.put(alphabet[i], i);
+            charMap.put(i, alphabet[i]);
+        }
+    }
+
+    public int getRadix()
+    {
+        return indexMap.size();
+    }
+
+    public byte[] convertToIndexes(char[] input)
+    {
+        byte[] out;
+
+        if (indexMap.size() <= 256)
+        {
+            out = new byte[input.length];
+            for (int i = 0; i != input.length; i++)
+            {
+                out[i] = indexMap.get(input[i]).byteValue();
+            }
+        }
+        else
+        {
+            out = new byte[input.length * 2];
+            for (int i = 0; i != input.length; i++)
+            {
+                int idx = indexMap.get(input[i]);
+                out[i * 2] = (byte)((idx >> 8) & 0xff);
+                out[i * 2  + 1] = (byte)(idx & 0xff);
+            }
+        }
+
+        return out;
+    }
+
+    public char[] convertToChars(byte[] input)
+    {
+        char[] out;
+
+        if (charMap.size() <= 256)
+        {
+            out = new char[input.length];
+            for (int i = 0; i != input.length; i++)
+            {
+                out[i] = charMap.get(input[i] & 0xff);
+            }
+        }
+        else
+        {
+            if ((input.length & 0x1) != 0)
+            {
+                throw new IllegalArgumentException("two byte radix and input string odd length");
+            }
+            
+            out = new char[input.length / 2];
+            for (int i = 0; i != input.length; i += 2)
+            {
+                out[i / 2] = charMap.get(((input[i] << 8) & 0xff00) | (input[i + 1] & 0xff));
+            }
+        }
+
+        return out;
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/util/PrivateKeyFactory.java b/bcprov/src/main/java/org/bouncycastle/crypto/util/PrivateKeyFactory.java
index 900c6c3..94f2b5c 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/util/PrivateKeyFactory.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/util/PrivateKeyFactory.java
@@ -68,6 +68,14 @@
     public static AsymmetricKeyParameter createKey(byte[] privateKeyInfoData)
         throws IOException
     {
+        if (privateKeyInfoData == null)
+        {
+            throw new IllegalArgumentException("privateKeyInfoData array null");
+        }
+        if (privateKeyInfoData.length == 0)
+        {
+            throw new IllegalArgumentException("privateKeyInfoData array empty");
+        }
         return createKey(PrivateKeyInfo.getInstance(ASN1Primitive.fromByteArray(privateKeyInfoData)));
     }
 
@@ -95,6 +103,11 @@
     public static AsymmetricKeyParameter createKey(PrivateKeyInfo keyInfo)
         throws IOException
     {
+        if (keyInfo == null)
+        {
+            throw new IllegalArgumentException("keyInfo argument null");
+        }
+
         AlgorithmIdentifier algId = keyInfo.getPrivateKeyAlgorithm();
         ASN1ObjectIdentifier algOID = algId.getAlgorithm();
 
@@ -137,12 +150,12 @@
         else if (algOID.equals(X9ObjectIdentifiers.id_dsa))
         {
             ASN1Integer derX = (ASN1Integer)keyInfo.parsePrivateKey();
-            ASN1Encodable de = algId.getParameters();
+            ASN1Encodable algParameters = algId.getParameters();
 
             DSAParameters parameters = null;
-            if (de != null)
+            if (algParameters != null)
             {
-                DSAParameter params = DSAParameter.getInstance(de.toASN1Primitive());
+                DSAParameter params = DSAParameter.getInstance(algParameters.toASN1Primitive());
                 parameters = new DSAParameters(params.getP(), params.getQ(), params.getG());
             }
 
@@ -182,32 +195,44 @@
         /*
         else if (algOID.equals(EdECObjectIdentifiers.id_X25519))
         {
-            return new X25519PrivateKeyParameters(getRawKey(keyInfo, X25519PrivateKeyParameters.KEY_SIZE), 0);
+            // Java 11 bug: exact length of X25519/X448 secret used in Java 11
+            if (X25519PrivateKeyParameters.KEY_SIZE == keyInfo.getPrivateKeyLength())
+            {
+                return new X25519PrivateKeyParameters(keyInfo.getPrivateKey().getOctets());
+            }
+
+            return new X25519PrivateKeyParameters(getRawKey(keyInfo));
         }
         else if (algOID.equals(EdECObjectIdentifiers.id_X448))
         {
-            return new X448PrivateKeyParameters(getRawKey(keyInfo, X448PrivateKeyParameters.KEY_SIZE), 0);
+            // Java 11 bug: exact length of X25519/X448 secret used in Java 11
+            if (X448PrivateKeyParameters.KEY_SIZE == keyInfo.getPrivateKeyLength())
+            {
+                return new X448PrivateKeyParameters(keyInfo.getPrivateKey().getOctets());
+            }
+
+            return new X448PrivateKeyParameters(getRawKey(keyInfo));
         }
         else if (algOID.equals(EdECObjectIdentifiers.id_Ed25519))
         {
-            return new Ed25519PrivateKeyParameters(getRawKey(keyInfo, Ed25519PrivateKeyParameters.KEY_SIZE), 0);
+            return new Ed25519PrivateKeyParameters(getRawKey(keyInfo));
         }
         else if (algOID.equals(EdECObjectIdentifiers.id_Ed448))
         {
-            return new Ed448PrivateKeyParameters(getRawKey(keyInfo, Ed448PrivateKeyParameters.KEY_SIZE), 0);
+            return new Ed448PrivateKeyParameters(getRawKey(keyInfo));
         }
         else if (
             algOID.equals(CryptoProObjectIdentifiers.gostR3410_2001) ||
                 algOID.equals(RosstandartObjectIdentifiers.id_tc26_gost_3410_12_512) ||
                 algOID.equals(RosstandartObjectIdentifiers.id_tc26_gost_3410_12_256))
         {
-            GOST3410PublicKeyAlgParameters gostParams = GOST3410PublicKeyAlgParameters.getInstance(keyInfo.getPrivateKeyAlgorithm().getParameters());
+            ASN1Encodable algParameters = algId.getParameters();
+            GOST3410PublicKeyAlgParameters gostParams = GOST3410PublicKeyAlgParameters.getInstance(algParameters);
             ECGOST3410Parameters ecSpec = null;
             BigInteger d = null;
-            ASN1Primitive p = keyInfo.getPrivateKeyAlgorithm().getParameters().toASN1Primitive();
+            ASN1Primitive p = algParameters.toASN1Primitive();
             if (p instanceof ASN1Sequence && (ASN1Sequence.getInstance(p).size() == 2 || ASN1Sequence.getInstance(p).size() == 3))
             {
-
                 X9ECParameters ecP = ECGOST3410NamedCurves.getByOIDX9(gostParams.getPublicKeyParamSet());
 
                 ecSpec = new ECGOST3410Parameters(
@@ -216,11 +241,12 @@
                     gostParams.getPublicKeyParamSet(),
                     gostParams.getDigestParamSet(),
                     gostParams.getEncryptionParamSet());
-                ASN1OctetString privEnc = keyInfo.getPrivateKey();
 
-                if (privEnc.getOctets().length == 32 || privEnc.getOctets().length == 64)
+                int privateKeyLength = keyInfo.getPrivateKeyLength();
+
+                if (privateKeyLength == 32 || privateKeyLength == 64)
                 {
-                    d = new BigInteger(1, Arrays.reverse(privEnc.getOctets()));
+                    d = new BigInteger(1, Arrays.reverse(keyInfo.getPrivateKey().getOctets()));
                 }
                 else
                 {
@@ -238,7 +264,7 @@
             }
             else
             {
-                X962Parameters params = X962Parameters.getInstance(keyInfo.getPrivateKeyAlgorithm().getParameters());
+                X962Parameters params = X962Parameters.getInstance(algId.getParameters());
 
                 if (params.isNamedCurve())
                 {
@@ -294,14 +320,8 @@
         }
     }
 
-    private static byte[] getRawKey(PrivateKeyInfo keyInfo, int expectedSize)
-        throws IOException
+    private static byte[] getRawKey(PrivateKeyInfo keyInfo) throws IOException
     {
-        byte[] result = ASN1OctetString.getInstance(keyInfo.parsePrivateKey()).getOctets();
-        if (expectedSize != result.length)
-        {
-            throw new RuntimeException("private key encoding has incorrect length");
-        }
-        return result;
+        return ASN1OctetString.getInstance(keyInfo.parsePrivateKey()).getOctets();
     }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/util/PublicKeyFactory.java b/bcprov/src/main/java/org/bouncycastle/crypto/util/PublicKeyFactory.java
index 1a42913..8495cd8 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/util/PublicKeyFactory.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/util/PublicKeyFactory.java
@@ -6,13 +6,13 @@
 import java.util.HashMap;
 import java.util.Map;
 
+import org.bouncycastle.asn1.ASN1BitString;
 import org.bouncycastle.asn1.ASN1Encodable;
 import org.bouncycastle.asn1.ASN1InputStream;
 import org.bouncycastle.asn1.ASN1Integer;
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
 import org.bouncycastle.asn1.ASN1OctetString;
 import org.bouncycastle.asn1.ASN1Primitive;
-import org.bouncycastle.asn1.DERBitString;
 import org.bouncycastle.asn1.DEROctetString;
 // Android-removed: Unsupported algorithms
 // import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers;
@@ -113,6 +113,14 @@
     public static AsymmetricKeyParameter createKey(byte[] keyInfoData)
         throws IOException
     {
+        if (keyInfoData == null)
+        {
+            throw new IllegalArgumentException("keyInfoData array null");
+        }
+        if (keyInfoData.length == 0)
+        {
+            throw new IllegalArgumentException("keyInfoData array empty");
+        }
         return createKey(SubjectPublicKeyInfo.getInstance(ASN1Primitive.fromByteArray(keyInfoData)));
     }
 
@@ -139,6 +147,10 @@
     public static AsymmetricKeyParameter createKey(SubjectPublicKeyInfo keyInfo)
         throws IOException
     {
+        if (keyInfo == null)
+        {
+            throw new IllegalArgumentException("keyInfo argument null");
+        }
         return createKey(keyInfo, null);
     }
 
@@ -153,6 +165,11 @@
     public static AsymmetricKeyParameter createKey(SubjectPublicKeyInfo keyInfo, Object defaultParams)
         throws IOException
     {
+        if (keyInfo == null)
+        {
+            throw new IllegalArgumentException("keyInfo argument null");
+        }
+
         AlgorithmIdentifier algID = keyInfo.getAlgorithm();
 
         SubjectPublicKeyInfoConverter converter = (SubjectPublicKeyInfoConverter)converters.get(algID.getAlgorithm());
@@ -304,7 +321,7 @@
                 dParams = new ECDomainParameters(x9);
             }
 
-            DERBitString bits = keyInfo.getPublicKeyData();
+            ASN1BitString bits = keyInfo.getPublicKeyData();
             byte[] data = bits.getBytes();
             ASN1OctetString key = new DEROctetString(data);
 
@@ -481,7 +498,7 @@
                 }
                 BigInteger b = new BigInteger(1, b_bytes);
                 DSTU4145BinaryField field = binary.getField();
-                ECCurve curve = new ECCurve.F2m(field.getM(), field.getK1(), field.getK2(), field.getK3(), binary.getA(), b);
+                ECCurve curve = new ECCurve.F2m(field.getM(), field.getK1(), field.getK2(), field.getK3(), binary.getA(), b, null, null);
                 byte[] g_bytes = binary.getG();
                 if (algOid.equals(UAObjectIdentifiers.dstu4145le))
                 {
@@ -514,7 +531,7 @@
     {
         AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Object defaultParams)
         {
-            return new X25519PublicKeyParameters(getRawKey(keyInfo, defaultParams, X25519PublicKeyParameters.KEY_SIZE), 0);
+            return new X25519PublicKeyParameters(getRawKey(keyInfo, defaultParams));
         }
     }
 
@@ -523,7 +540,7 @@
     {
         AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Object defaultParams)
         {
-            return new X448PublicKeyParameters(getRawKey(keyInfo, defaultParams, X448PublicKeyParameters.KEY_SIZE), 0);
+            return new X448PublicKeyParameters(getRawKey(keyInfo, defaultParams));
         }
     }
 
@@ -532,7 +549,7 @@
     {
         AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Object defaultParams)
         {
-            return new Ed25519PublicKeyParameters(getRawKey(keyInfo, defaultParams, Ed25519PublicKeyParameters.KEY_SIZE), 0);
+            return new Ed25519PublicKeyParameters(getRawKey(keyInfo, defaultParams));
         }
     }
 
@@ -541,24 +558,19 @@
     {
         AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Object defaultParams)
         {
-            return new Ed448PublicKeyParameters(getRawKey(keyInfo, defaultParams, Ed448PublicKeyParameters.KEY_SIZE), 0);
+            return new Ed448PublicKeyParameters(getRawKey(keyInfo, defaultParams));
         }
     }
     */
     // END Android-removed: Unsupported algorithms
 
-    private static byte[] getRawKey(SubjectPublicKeyInfo keyInfo, Object defaultParams, int expectedSize)
+    private static byte[] getRawKey(SubjectPublicKeyInfo keyInfo, Object defaultParams)
     {
         /*
          * TODO[RFC 8422]
          * - Require defaultParams == null?
          * - Require keyInfo.getAlgorithm().getParameters() == null?
          */
-        byte[] result = keyInfo.getPublicKeyData().getOctets();
-        if (expectedSize != result.length)
-        {
-            throw new RuntimeException("public key encoding has incorrect length");
-        }
-        return result;
+        return keyInfo.getPublicKeyData().getOctets();
     }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/util/RadixConverter.java b/bcprov/src/main/java/org/bouncycastle/crypto/util/RadixConverter.java
new file mode 100644
index 0000000..9263dfb
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/util/RadixConverter.java
@@ -0,0 +1,188 @@
+package org.bouncycastle.crypto.util;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.util.BigIntegers;
+
+/**
+ * Utility class to convert decimal numbers (BigInteger) into a number in the base provided and the other way round.
+ * <p>For an application of this see the FPE parameter classes.</p>
+ */
+public class RadixConverter
+{
+
+    /*
+    The conversions in this class are more complex than the standard ways of converting between basis because we want to improve the performance by limiting the
+    operations on BigInteger which are not very efficient.
+    The general idea is to perform math operations on primitive long as much as we can and just work with BigInteger when necessary.
+    Converting between basis uses the fact that a number in base 'B' have a unique representation in polynomial form.
+
+    num = ... + r8B^8 + r7B^7 + r6B^6 + r5B^5 + r4B^4 + r3B^3 + r2B^2 + r1B + r0
+
+    We can compute how many digits in base 'B' can fit in a long. For example, for a radix R=2^16 the number of digits 'n' that we can fit into a long is the
+    max 'n' that still satisfies: R^n < Long.MAX_VALUE (i.e. 2^63 -1). In this case 'n' is 3. To convert 'num' from its decimal representation to base 'B'
+    representation we can write down 'num' in a polynomial form of B^3:
+
+    num = (((...)B^3 + r8B^2 + r7B + r6)B^3 + r5B^2 + r4B + r3)B^3 + (r2B^2 + r1B + r0)
+
+    B^3 would be our intermediate base. We can convert numbers in base B^3 while operating on primitive long.
+    To convert a decimal num to its representation in base B we can first build its B^3 representation and then figure out the digits in base B from the single
+    digit in base B^3. num % B^3 gives us a single digit in base B^3 which corresponds to a group of 3 digits in base B.
+
+    An equivalent way of writing the polynomial form of num would be:
+
+    num = (...)B^9 + (r8B^8 + r7B^7 + r6B^6)B^6 + (r5B^5 + r4B^4 + r3)B^3 + (r2B^2 + r1B + r0)
+
+    In this form it becomes clear that to obtain 'num' from a sequence of digits, one can group the digits in group of 3 and compute the corresponding decimal
+    number for the group in base B. We can then multiply the decimal numbers by the corresponding power of B^3 and sum up the result to obtain the decimal
+    representation of num in base B,
+     */
+    private static final double LOG_LONG_MAX_VALUE = Math.log(Long.MAX_VALUE);
+    private static final int DEFAULT_POWERS_TO_CACHE = 10;
+    // the max number of digits in base 'radix' that fits in a long
+    private final int digitsGroupLength;
+    // the total number of digits combination in a group. radix ^ digitsGroupLength
+    private final BigInteger digitsGroupSpaceSize;
+    private final int radix;
+    private final BigInteger[] digitsGroupSpacePowers;
+
+    /**
+     * @param radix                the radix to use for base conversions
+     * @param numberOfCachedPowers number of intermediate base powers to precompute and cache.
+     */
+    public RadixConverter(int radix, int numberOfCachedPowers)
+    {
+        this.radix = radix;
+        // solves radix^n < Long.MAX_VALUE to find n (maxDigitsFitsInLong)
+        this.digitsGroupLength = (int)Math.floor(LOG_LONG_MAX_VALUE / Math.log(radix));
+        this.digitsGroupSpaceSize = BigInteger.valueOf(radix).pow(digitsGroupLength);
+        this.digitsGroupSpacePowers = precomputeDigitsGroupPowers(numberOfCachedPowers, digitsGroupSpaceSize);
+    }
+
+    /**
+     * @param radix the radix to use for base conversions.
+     */
+    public RadixConverter(int radix)
+    {
+        this(radix, DEFAULT_POWERS_TO_CACHE);
+    }
+
+    public int getRadix()
+    {
+        return radix;
+    }
+
+    public void toEncoding(BigInteger number, int messageLength, short[] out)
+    {
+        if (number.signum() < 0)
+        {
+            throw new IllegalArgumentException();
+        }
+        // convert number into its representation in base 'radix'.
+        // writes leading '0' if the messageLength is greater than the number of digits required to encode in base 'radix'
+        int digitIndex = messageLength - 1;
+        do
+        {
+            if (number.equals(BigInteger.ZERO))
+            {
+                out[digitIndex--] = 0;
+                continue;
+            }
+            BigInteger[] quotientAndRemainder = number.divideAndRemainder(digitsGroupSpaceSize);
+            number = quotientAndRemainder[0];
+            digitIndex = toEncoding(quotientAndRemainder[1].longValue(), digitIndex, out);
+        }
+        while (digitIndex >= 0);
+        if (number.signum() != 0)
+        {
+            throw new IllegalArgumentException();
+        }
+    }
+
+    private int toEncoding(long number, int digitIndex, short[] out)
+    {
+        for (int i = 0; i < digitsGroupLength && digitIndex >= 0; i++)
+        {
+            if (number == 0)
+            {
+                out[digitIndex--] = 0;
+                continue;
+            }
+            out[digitIndex--] = (short)(number % radix);
+            number = number / radix;
+        }
+        if (number != 0)
+        {
+            throw new IllegalStateException("Failed to convert decimal number");
+        }
+        return digitIndex;
+    }
+
+    public BigInteger fromEncoding(short[] digits)
+    {
+        // from a sequence of digits in base 'radix' to a decimal number
+        // iterate through groups of digits right to left
+        // digitsGroupLength = 2;  digits: [22, 45, 11, 31, 24]
+        // groups are, in order of iteration: [31, 24], [45, 11], [22]
+        BigInteger currentGroupCardinality = BigIntegers.ONE;
+        BigInteger res = null;
+        int indexGroup = 0;
+        int numberOfDigits = digits.length;
+        for (int groupStartDigitIndex = numberOfDigits - digitsGroupLength;
+             groupStartDigitIndex > -digitsGroupLength;
+             groupStartDigitIndex = groupStartDigitIndex - digitsGroupLength)
+        {
+            int actualDigitsInGroup = digitsGroupLength;
+            if (groupStartDigitIndex < 0)
+            {
+                // last group might contain fewer digits so adjust offsets
+                actualDigitsInGroup = digitsGroupLength + groupStartDigitIndex;
+                groupStartDigitIndex = 0;
+            }
+            int groupEndDigitIndex = Math.min(groupStartDigitIndex + actualDigitsInGroup, numberOfDigits);
+            long groupInBaseRadix = fromEncoding(groupStartDigitIndex, groupEndDigitIndex, digits);
+            BigInteger bigInteger = BigInteger.valueOf(groupInBaseRadix);
+            if (indexGroup == 0)
+            {
+                res = bigInteger;
+            }
+            else
+            {
+                currentGroupCardinality =
+                    indexGroup <= digitsGroupSpacePowers.length
+                        ? digitsGroupSpacePowers[indexGroup - 1]
+                        : currentGroupCardinality.multiply(digitsGroupSpaceSize);
+                res = res.add(bigInteger.multiply(currentGroupCardinality));
+            }
+            indexGroup++;
+        }
+        return res;
+    }
+
+    public int getDigitsGroupLength()
+    {
+        return digitsGroupLength;
+    }
+
+    private long fromEncoding(int groupStartDigitIndex, int groupEndDigitIndex, short[] digits)
+    {
+        long decimalNumber = 0;
+        for (int digitIndex = groupStartDigitIndex; digitIndex < groupEndDigitIndex; digitIndex++)
+        {
+            decimalNumber = (decimalNumber * radix) + (digits[digitIndex] & 0xFFFF);
+        }
+        return decimalNumber;
+    }
+
+    private BigInteger[] precomputeDigitsGroupPowers(int numberOfCachedPowers, BigInteger digitsGroupSpaceSize)
+    {
+        BigInteger[] cachedPowers = new BigInteger[numberOfCachedPowers];
+        BigInteger currentPower = digitsGroupSpaceSize;
+        for (int i = 0; i < numberOfCachedPowers; i++)
+        {
+            cachedPowers[i] = currentPower;
+            currentPower = currentPower.multiply(digitsGroupSpaceSize);
+        }
+        return cachedPowers;
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/util/SSHNamedCurves.java b/bcprov/src/main/java/org/bouncycastle/crypto/util/SSHNamedCurves.java
index 25d0c6c..e6a0e56 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/util/SSHNamedCurves.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/util/SSHNamedCurves.java
@@ -69,8 +69,8 @@
             while (e.hasMoreElements())
             {
                 String name = (String)e.nextElement();
-                X9ECParameters parameters = CustomNamedCurves.getByName(name);
-                put(parameters.getCurve(), name);
+                ECCurve curve = CustomNamedCurves.getByNameLazy(name).getCurve();
+                put(curve, name);
             }
 
         }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cms/GCMParameters.java b/bcprov/src/main/java/org/bouncycastle/internal/asn1/cms/GCMParameters.java
similarity index 90%
rename from bcprov/src/main/java/org/bouncycastle/asn1/cms/GCMParameters.java
rename to bcprov/src/main/java/org/bouncycastle/internal/asn1/cms/GCMParameters.java
index 76a98dc..de88211 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/cms/GCMParameters.java
+++ b/bcprov/src/main/java/org/bouncycastle/internal/asn1/cms/GCMParameters.java
@@ -1,4 +1,4 @@
-package org.bouncycastle.asn1.cms;
+package org.bouncycastle.internal.asn1.cms;
 
 import org.bouncycastle.asn1.ASN1EncodableVector;
 import org.bouncycastle.asn1.ASN1Integer;
@@ -31,8 +31,8 @@
      * Accepted inputs:
      * <ul>
      * <li> null &rarr; null
-     * <li> {@link org.bouncycastle.asn1.cms.GCMParameters} object
-     * <li> {@link org.bouncycastle.asn1.ASN1Sequence#getInstance(Object) ASN1Sequence} input formats with GCMParameters structure inside
+     * <li> {@link GCMParameters} object
+     * <li> {@link ASN1Sequence#getInstance(Object) ASN1Sequence} input formats with GCMParameters structure inside
      * </ul>
      *
      * @param obj the object we want converted.
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/isismtt/ISISMTTObjectIdentifiers.java b/bcprov/src/main/java/org/bouncycastle/internal/asn1/isismtt/ISISMTTObjectIdentifiers.java
similarity index 95%
rename from bcprov/src/main/java/org/bouncycastle/asn1/isismtt/ISISMTTObjectIdentifiers.java
rename to bcprov/src/main/java/org/bouncycastle/internal/asn1/isismtt/ISISMTTObjectIdentifiers.java
index 6b75fde..aa54635 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/isismtt/ISISMTTObjectIdentifiers.java
+++ b/bcprov/src/main/java/org/bouncycastle/internal/asn1/isismtt/ISISMTTObjectIdentifiers.java
@@ -1,4 +1,4 @@
-package org.bouncycastle.asn1.isismtt;
+package org.bouncycastle.internal.asn1.isismtt;
 
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
 
@@ -103,8 +103,6 @@
      * </pre>
      * <p>
      * OID: 1.3.36.8.3.8
-     * 
-     * @see org.bouncycastle.asn1.isismtt.x509.Restriction
      */
     static final ASN1ObjectIdentifier id_isismtt_at_restriction = id_isismtt_at.branch("8");
 
@@ -129,8 +127,6 @@
      * returned in this extension.
      * <p>
      * OID: 1.3.36.8.3.10
-     * 
-     * @see org.bouncycastle.asn1.isismtt.ocsp.RequestedCertificate
      */
     static final ASN1ObjectIdentifier id_isismtt_at_requestedCertificate = id_isismtt_at.branch("10");
 
@@ -159,8 +155,7 @@
      * Hash of a certificate in OCSP.
      * <p>
      * OID: 1.3.36.8.3.13
-     * 
-     * @see org.bouncycastle.asn1.isismtt.ocsp.CertHash
+     *
      */
     static final ASN1ObjectIdentifier id_isismtt_at_certHash = id_isismtt_at.branch("13");
 
@@ -186,8 +181,7 @@
      * </pre>
      * <p>
      * OID: 1.3.36.8.3.15
-     * 
-     * @see org.bouncycastle.asn1.isismtt.x509.AdditionalInformationSyntax
+     *
      */
     static final ASN1ObjectIdentifier id_isismtt_at_additionalInformation = id_isismtt_at.branch("15");
 
diff --git a/bcprov/src/main/java/org/bouncycastle/its/asn1/EndEntityType.java b/bcprov/src/main/java/org/bouncycastle/its/asn1/EndEntityType.java
deleted file mode 100644
index 622f75e..0000000
--- a/bcprov/src/main/java/org/bouncycastle/its/asn1/EndEntityType.java
+++ /dev/null
@@ -1,54 +0,0 @@
-package org.bouncycastle.its.asn1;
-
-import org.bouncycastle.asn1.ASN1BitString;
-import org.bouncycastle.asn1.ASN1Object;
-import org.bouncycastle.asn1.ASN1Primitive;
-import org.bouncycastle.asn1.DERBitString;
-
-/**
- * <pre>
- *     EndEntityType ::= BIT STRING { app(0), enrol(1) } (SIZE (8)) (ALL EXCEPT ())
- * </pre>
- */
-public class EndEntityType
-    extends ASN1Object
-{
-    public static final int        app = (1 << 7);
-    public static final int        enrol = (1 << 6);
-
-    private final ASN1BitString type;
-
-    public EndEntityType(int type)
-    {
-        if (type != app && type != enrol)
-        {
-            throw new IllegalArgumentException("value out of range");
-        }
-
-        this.type = new DERBitString(type);
-    }
-
-    private EndEntityType(DERBitString str)
-    {
-        this.type = str;
-    }
-
-    public static EndEntityType getInstance(Object src)
-    {
-        if (src instanceof EndEntityType)
-        {
-            return (EndEntityType)src;
-        }
-        else if (src != null)
-        {
-            return new EndEntityType(DERBitString.getInstance(src));
-        }
-
-        return null;
-    }
-
-    public ASN1Primitive toASN1Primitive()
-    {
-        return type;
-    }
-}
diff --git a/bcprov/src/main/java/org/bouncycastle/its/asn1/PsidGroupPermissions.java b/bcprov/src/main/java/org/bouncycastle/its/asn1/PsidGroupPermissions.java
deleted file mode 100644
index 2519614..0000000
--- a/bcprov/src/main/java/org/bouncycastle/its/asn1/PsidGroupPermissions.java
+++ /dev/null
@@ -1,59 +0,0 @@
-package org.bouncycastle.its.asn1;
-
-import java.math.BigInteger;
-
-import org.bouncycastle.asn1.ASN1Integer;
-import org.bouncycastle.asn1.ASN1Object;
-import org.bouncycastle.asn1.ASN1Primitive;
-import org.bouncycastle.asn1.ASN1Sequence;
-
-/**
- * <pre>
- *     PsidGroupPermissions ::= SEQUENCE {
- *         subjectPermissions SubjectPermissions,
- *         minChainLength INTEGER DEFAULT 1,
- *         chainLengthRange INTEGER DEFAULT 0,
- *         eeType EndEntityType DEFAULT (app)
- *     }
- * </pre>
- */
-public class PsidGroupPermissions
-    extends ASN1Object
-{
-    private final SubjectPermissions subjectPermissions;
-    private final BigInteger minChainLength;
-    private final BigInteger chainLengthRange;
-    private final Object eeType;
-
-    private PsidGroupPermissions(ASN1Sequence seq)
-    {
-        if (seq.size() != 2)
-        {
-            throw new IllegalArgumentException("sequence not length 2");
-        }
-
-        this.subjectPermissions = SubjectPermissions.getInstance(seq.getObjectAt(0));
-        this.minChainLength = ASN1Integer.getInstance(seq.getObjectAt(1)).getValue();
-        this.chainLengthRange = ASN1Integer.getInstance(seq.getObjectAt(2)).getValue();
-        this.eeType = EndEntityType.getInstance(seq.getObjectAt(3));
-    }
-
-    public static PsidGroupPermissions getInstance(Object src)
-    {
-        if (src instanceof PsidGroupPermissions)
-        {
-            return (PsidGroupPermissions)src;
-        }
-        else if (src != null)
-        {
-            return new PsidGroupPermissions(ASN1Sequence.getInstance(src));
-        }
-
-        return null;
-    }
-
-    public ASN1Primitive toASN1Primitive()
-    {
-        return null;
-    }
-}
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/BCLoadStoreParameter.java b/bcprov/src/main/java/org/bouncycastle/jcajce/BCLoadStoreParameter.java
new file mode 100644
index 0000000..0a299cb
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/BCLoadStoreParameter.java
@@ -0,0 +1,71 @@
+package org.bouncycastle.jcajce;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.security.KeyStore;
+
+public class BCLoadStoreParameter
+    implements KeyStore.LoadStoreParameter
+{
+    private final InputStream in;
+    private final OutputStream out;
+    private final KeyStore.ProtectionParameter protectionParameter;
+
+    /**
+     * Base constructor for
+     *
+     * @param out
+     * @param password
+     */
+    public BCLoadStoreParameter(OutputStream out, char[] password)
+    {
+        this(out, new KeyStore.PasswordProtection(password));
+    }
+
+    public BCLoadStoreParameter(InputStream in, char[] password)
+    {
+        this(in, new KeyStore.PasswordProtection(password));
+    }
+
+    public BCLoadStoreParameter(InputStream in, KeyStore.ProtectionParameter protectionParameter)
+    {
+        this(in, null, protectionParameter);
+    }
+
+    public BCLoadStoreParameter(OutputStream out, KeyStore.ProtectionParameter protectionParameter)
+    {
+        this(null, out, protectionParameter);
+    }
+
+    BCLoadStoreParameter(InputStream in, OutputStream out, KeyStore.ProtectionParameter protectionParameter)
+    {
+        this.in = in;
+        this.out = out;
+        this.protectionParameter = protectionParameter;
+    }
+
+    public KeyStore.ProtectionParameter getProtectionParameter()
+    {
+        return protectionParameter;
+    }
+
+    public OutputStream getOutputStream()
+    {
+        if (out == null)
+        {
+            throw new UnsupportedOperationException("parameter not configured for storage - no OutputStream");
+        }
+
+        return out;
+    }
+
+    public InputStream getInputStream()
+    {
+        if (out != null)
+        {
+            throw new UnsupportedOperationException("parameter configured for storage OutputStream present");
+        }
+
+        return in;
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/CompositePrivateKey.java b/bcprov/src/main/java/org/bouncycastle/jcajce/CompositePrivateKey.java
index e30881f..038eed6 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/CompositePrivateKey.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/CompositePrivateKey.java
@@ -73,7 +73,7 @@
         try
         {
             return new PrivateKeyInfo(
-                new AlgorithmIdentifier(MiscObjectIdentifiers.id_alg_composite), new DERSequence(v)).getEncoded(ASN1Encoding.DER);
+                new AlgorithmIdentifier(MiscObjectIdentifiers.id_composite_key), new DERSequence(v)).getEncoded(ASN1Encoding.DER);
         }
         catch (IOException e)
         {
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/CompositePublicKey.java b/bcprov/src/main/java/org/bouncycastle/jcajce/CompositePublicKey.java
index 6a1ed2c..7491ad5 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/CompositePublicKey.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/CompositePublicKey.java
@@ -73,7 +73,7 @@
         try
         {
             return new SubjectPublicKeyInfo(
-                new AlgorithmIdentifier(MiscObjectIdentifiers.id_alg_composite), new DERSequence(v)).getEncoded(ASN1Encoding.DER);
+                new AlgorithmIdentifier(MiscObjectIdentifiers.id_composite_key), new DERSequence(v)).getEncoded(ASN1Encoding.DER);
         }
         catch (IOException e)
         {
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/ExternalPublicKey.java b/bcprov/src/main/java/org/bouncycastle/jcajce/ExternalPublicKey.java
new file mode 100644
index 0000000..668f464
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/ExternalPublicKey.java
@@ -0,0 +1,103 @@
+package org.bouncycastle.jcajce;
+
+import java.io.IOException;
+import java.security.MessageDigest;
+import java.security.PublicKey;
+
+import org.bouncycastle.asn1.ASN1Encoding;
+import org.bouncycastle.asn1.bc.BCObjectIdentifiers;
+import org.bouncycastle.asn1.bc.ExternalValue;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.asn1.x509.GeneralName;
+import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.bouncycastle.jcajce.util.MessageDigestUtils;
+import org.bouncycastle.util.Arrays;
+
+/**
+ * Wrapper class which returns an "ExternalValue" for the public key encoding. In this case
+ * the key encoding is a hash and the actual key needs to be looked up somewhere else. Useful
+ * for where the public keys are really large but it's required to keep certificates small.
+ */
+public class ExternalPublicKey
+    implements PublicKey
+{
+    private final GeneralName location;
+    private final AlgorithmIdentifier digestAlg;
+    private final byte[] digest;
+
+    /**
+     * Base constructor with fundamental contents.
+     *
+     * @param location location URI for the actual public key.
+     * @param digestAlg hashing algorithm used to hash the actual public key encoding.
+     * @param digest digest of the actual public key.
+     */
+    public ExternalPublicKey(GeneralName location, AlgorithmIdentifier digestAlg, byte[] digest)
+    {
+        this.location = location;
+        this.digestAlg = digestAlg;
+        this.digest = Arrays.clone(digest);
+    }
+
+    /**
+     * Helper constructor with JCA contents.
+     *
+     * @param key the public key we are externalising.
+     * @param location location URI for the actual public key.
+     * @param digest digest to use for hashing the key.
+     */
+    // Android-removed: unsupported
+    // public ExternalPublicKey(PublicKey key, GeneralName location, MessageDigest digest)
+    // {
+    //     this(location, MessageDigestUtils.getDigestAlgID(digest.getAlgorithm()), digest.digest(key.getEncoded()));
+    // }
+
+    /**
+     * Base constructor with ASN.1 structure.
+     *
+     * @param extKey structure with location, hashing algorithm and hash for the public key.
+     */
+    public ExternalPublicKey(ExternalValue extKey)
+    {
+        this(extKey.getLocation(), extKey.getHashAlg(), extKey.getHashValue());
+    }
+
+    /**
+     * Return "ExternalKey"
+     *
+     * @return  "ExternalKey"
+     */
+    public String getAlgorithm()
+    {
+        return "ExternalKey";
+    }
+
+    /**
+     * Return "X.509" (DER encoded SubjectPublicKeyInfo)
+     *
+     * @return  "X.509"
+     */
+    public String getFormat()
+    {
+        return "X.509";
+    }
+
+    /**
+     * Return a SubjectPublicKeyInfo structure containing an ExternalValue encoding for the key.
+     *
+     * @return a DER encoding of SubjectPublicKeyInfo containing an ExternalValue structure.
+     */
+    public byte[] getEncoded()
+    {
+        try
+        {
+            return new SubjectPublicKeyInfo(
+                new AlgorithmIdentifier(BCObjectIdentifiers.external_value),
+                    new ExternalValue(location, digestAlg, digest)).getEncoded(ASN1Encoding.DER);
+        }
+        catch (IOException e)
+        {
+            throw new IllegalStateException("unable to encode composite key: " + e.getMessage());
+        }
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/SecretKeyWithEncapsulation.java b/bcprov/src/main/java/org/bouncycastle/jcajce/SecretKeyWithEncapsulation.java
new file mode 100644
index 0000000..aae15b4
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/SecretKeyWithEncapsulation.java
@@ -0,0 +1,77 @@
+package org.bouncycastle.jcajce;
+
+import javax.crypto.SecretKey;
+
+import org.bouncycastle.util.Arrays;
+
+/**
+ * Carrier class for a KEM/KTS secret key plus its encapsulation.
+ */
+public final class SecretKeyWithEncapsulation
+    implements SecretKey
+{
+    private final SecretKey secretKey;
+    private final byte[] encapsulation;
+
+    /**
+     * Basic constructor.
+     *
+     * @param secretKey the secret key that was arrived at.
+     * @param encapsulation the encapsulation the key data was carried in.
+     */
+    public SecretKeyWithEncapsulation(SecretKey secretKey, byte[] encapsulation)
+    {
+        this.secretKey = secretKey;
+        this.encapsulation = Arrays.clone(encapsulation);
+    }
+
+    /**
+     * Return the algorithm for the agreed secret key.
+     *
+     * @return the secret key value.
+     */
+    public String getAlgorithm()
+    {
+        return secretKey.getAlgorithm();
+    }
+
+    /**
+     * Return the format for the agreed secret key.
+     *
+     * @return the secret key format.
+     */
+    public String getFormat()
+    {
+        return secretKey.getFormat();
+    }
+
+    /**
+     * Return the encoding of the agreed secret key.
+     *
+     * @return the secret key encoding.
+     */
+    public byte[] getEncoded()
+    {
+        return secretKey.getEncoded();
+    }
+
+    /**
+     * Return the encapsulation that carried the key material used in creating the agreed secret key.
+     *
+     * @return the encrypted encapsulation of the agreed secret key.
+     */
+    public byte[] getEncapsulation()
+    {
+        return Arrays.clone(encapsulation);
+    }
+
+    public boolean equals(Object o)
+    {
+        return secretKey.equals(o);
+    }
+
+    public int hashCode()
+    {
+        return secretKey.hashCode();
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/io/SignatureUpdatingOutputStream.java b/bcprov/src/main/java/org/bouncycastle/jcajce/io/SignatureUpdatingOutputStream.java
index f092df1..9d82ddf 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/io/SignatureUpdatingOutputStream.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/io/SignatureUpdatingOutputStream.java
@@ -5,6 +5,8 @@
 import java.security.Signature;
 import java.security.SignatureException;
 
+import org.bouncycastle.util.Exceptions;
+
 class SignatureUpdatingOutputStream
     extends OutputStream
 {
@@ -24,7 +26,7 @@
         }
         catch (SignatureException e)
         {
-            throw new IOException(e.getMessage());
+            throw Exceptions.ioException(e.getMessage(), e);
         }
     }
 
@@ -37,7 +39,7 @@
         }
         catch (SignatureException e)
         {
-            throw new IOException(e.getMessage());
+            throw Exceptions.ioException(e.getMessage(), e);
         }
     }
 
@@ -50,7 +52,7 @@
         }
         catch (SignatureException e)
         {
-            throw new IOException(e.getMessage());
+            throw Exceptions.ioException(e.getMessage(), e);
         }
     }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/COMPOSITE.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/COMPOSITE.java
new file mode 100644
index 0000000..db24653
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/COMPOSITE.java
@@ -0,0 +1,139 @@
+package org.bouncycastle.jcajce.provider.asymmetric;
+
+import java.io.IOException;
+import java.security.InvalidKeyException;
+import java.security.Key;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.misc.MiscObjectIdentifiers;
+import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
+import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.bouncycastle.jcajce.CompositePrivateKey;
+import org.bouncycastle.jcajce.CompositePublicKey;
+import org.bouncycastle.jcajce.provider.asymmetric.util.BaseKeyFactorySpi;
+import org.bouncycastle.jcajce.provider.config.ConfigurableProvider;
+import org.bouncycastle.jcajce.provider.util.AsymmetricAlgorithmProvider;
+import org.bouncycastle.jcajce.provider.util.AsymmetricKeyInfoConverter;
+
+public class COMPOSITE
+{
+    private static final String PREFIX = "org.bouncycastle.jcajce.provider.asymmetric.COMPOSITE";
+
+    private static final Map<String, String> compositeAttributes = new HashMap<String, String>();
+
+    static
+    {
+        compositeAttributes.put("SupportedKeyClasses", "org.bouncycastle.jcajce.CompositePublicKey|org.bouncycastle.jcajce.CompositePrivateKey");
+        compositeAttributes.put("SupportedKeyFormats", "PKCS#8|X.509");
+    }
+
+    private static AsymmetricKeyInfoConverter baseConverter;
+
+    public static class KeyFactory
+        extends BaseKeyFactorySpi
+    {
+        protected Key engineTranslateKey(Key key)
+            throws InvalidKeyException
+        {
+            try
+            {
+                if (key instanceof PrivateKey)
+                {
+                    return generatePrivate(PrivateKeyInfo.getInstance(key.getEncoded()));
+                }
+                else if (key instanceof PublicKey)
+                {
+                    return generatePublic(SubjectPublicKeyInfo.getInstance(key.getEncoded()));
+                }
+            }
+            catch (IOException e)
+            {
+                throw new InvalidKeyException("key could not be parsed: " + e.getMessage());
+            }
+
+            throw new InvalidKeyException("key not recognized");
+        }
+
+        public PrivateKey generatePrivate(PrivateKeyInfo keyInfo)
+            throws IOException
+        {
+            return baseConverter.generatePrivate(keyInfo);
+        }
+
+        public PublicKey generatePublic(SubjectPublicKeyInfo keyInfo)
+            throws IOException
+        {
+            return baseConverter.generatePublic(keyInfo);
+        }
+    }
+
+    private static class CompositeKeyInfoConverter
+        implements AsymmetricKeyInfoConverter
+    {
+        private final ConfigurableProvider provider;
+
+        public CompositeKeyInfoConverter(ConfigurableProvider provider)
+        {
+            this.provider = provider;
+        }
+
+        public PrivateKey generatePrivate(PrivateKeyInfo keyInfo)
+            throws IOException
+        {
+            ASN1Sequence keySeq = ASN1Sequence.getInstance(keyInfo.parsePrivateKey());
+            PrivateKey[] privKeys = new PrivateKey[keySeq.size()];
+
+            for (int i = 0; i != keySeq.size(); i++)
+            {
+                PrivateKeyInfo privInfo = PrivateKeyInfo.getInstance(keySeq.getObjectAt(i));
+
+                privKeys[i] = provider.getKeyInfoConverter(
+                    privInfo.getPrivateKeyAlgorithm().getAlgorithm()).generatePrivate(privInfo);
+            }
+
+            return new CompositePrivateKey(privKeys);
+        }
+
+        public PublicKey generatePublic(SubjectPublicKeyInfo keyInfo)
+            throws IOException
+        {
+            ASN1Sequence keySeq = ASN1Sequence.getInstance(keyInfo.getPublicKeyData().getBytes());
+            PublicKey[] pubKeys = new PublicKey[keySeq.size()];
+
+            for (int i = 0; i != keySeq.size(); i++)
+            {
+                SubjectPublicKeyInfo pubInfo = SubjectPublicKeyInfo.getInstance(keySeq.getObjectAt(i));
+
+                pubKeys[i] = provider.getKeyInfoConverter((pubInfo.getAlgorithm().getAlgorithm())).generatePublic(pubInfo);
+            }
+
+            return new CompositePublicKey(pubKeys);
+        }
+    }
+
+    public static class Mappings
+        extends AsymmetricAlgorithmProvider
+    {
+        public Mappings()
+        {
+        }
+
+        public void configure(ConfigurableProvider provider)
+        {
+            provider.addAlgorithm("KeyFactory.COMPOSITE", PREFIX + "$KeyFactory");
+            provider.addAlgorithm("KeyFactory." + MiscObjectIdentifiers.id_alg_composite, PREFIX + "$KeyFactory");
+            provider.addAlgorithm("KeyFactory.OID." + MiscObjectIdentifiers.id_alg_composite, PREFIX + "$KeyFactory");
+            provider.addAlgorithm("KeyFactory." + MiscObjectIdentifiers.id_composite_key, PREFIX + "$KeyFactory");
+            provider.addAlgorithm("KeyFactory.OID." + MiscObjectIdentifiers.id_composite_key, PREFIX + "$KeyFactory");
+
+            baseConverter = new CompositeKeyInfoConverter(provider);
+
+            provider.addKeyInfoConverter(MiscObjectIdentifiers.id_alg_composite, baseConverter);
+            provider.addKeyInfoConverter(MiscObjectIdentifiers.id_composite_key, baseConverter);
+        }
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/DSA.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/DSA.java
index 6323b75..c95d233 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/DSA.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/DSA.java
@@ -17,7 +17,7 @@
         public Mappings()
         {
         }
-        
+
         public void configure(ConfigurableProvider provider)
         {
             provider.addAlgorithm("AlgorithmParameters.DSA", PREFIX + "AlgorithmParametersSpi");
@@ -84,6 +84,8 @@
             provider.addAlgorithm("Alg.Alias.Signature.DSAWithSHA1", "SHA1withDSA");
             // END Android-changed: Change primary ID from DSA to SHA1withDSA
 
+            // addSignatureAlgorithm(provider, "RIPEMD160", "DSA", PREFIX + "DSASigner$dsaRMD160");
+
             AsymmetricKeyInfoConverter keyFact = new KeyFactorySpi();
 
             for (int i = 0; i != DSAUtil.dsaOids.length; i++)
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/EC.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/EC.java
index f900e90..aee8c39 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/EC.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/EC.java
@@ -11,6 +11,9 @@
 // import org.bouncycastle.asn1.teletrust.TeleTrusTObjectIdentifiers;
 // END Android-removed: Unsupported algorithms
 import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
+// import org.bouncycastle.internal.asn1.bsi.BSIObjectIdentifiers;
+// import org.bouncycastle.internal.asn1.cms.CMSObjectIdentifiers;
+// import org.bouncycastle.internal.asn1.eac.EACObjectIdentifiers;
 import org.bouncycastle.jcajce.provider.asymmetric.ec.KeyFactorySpi;
 import org.bouncycastle.jcajce.provider.config.ConfigurableProvider;
 import org.bouncycastle.jcajce.provider.util.AsymmetricAlgorithmProvider;
@@ -43,77 +46,73 @@
             /*
             provider.addAlgorithm("AlgorithmParameters.EC", PREFIX + "AlgorithmParametersSpi");
 
-            provider.addAttributes("KeyAgreement.ECDH", generalEcAttributes);
-            provider.addAlgorithm("KeyAgreement.ECDH", PREFIX + "KeyAgreementSpi$DH");
-            provider.addAttributes("KeyAgreement.ECDHC", generalEcAttributes);
-            provider.addAlgorithm("KeyAgreement.ECDHC", PREFIX + "KeyAgreementSpi$DHC");
-            provider.addAttributes("KeyAgreement.ECCDH", generalEcAttributes);
-            provider.addAlgorithm("KeyAgreement.ECCDH", PREFIX + "KeyAgreementSpi$DHC");
+            provider.addAlgorithm("KeyAgreement.ECDH", PREFIX + "KeyAgreementSpi$DH", generalEcAttributes);
+            provider.addAlgorithm("KeyAgreement.ECDHC", PREFIX + "KeyAgreementSpi$DHC", generalEcAttributes);
+            provider.addAlgorithm("KeyAgreement.ECCDH", PREFIX + "KeyAgreementSpi$DHC", generalEcAttributes);
+            
+            provider.addAlgorithm("KeyAgreement.ECCDHU", PREFIX + "KeyAgreementSpi$DHUC", generalEcAttributes);
 
-            provider.addAttributes("KeyAgreement.ECCDHU", generalEcAttributes);
-            provider.addAlgorithm("KeyAgreement.ECCDHU", PREFIX + "KeyAgreementSpi$DHUC");
+            provider.addAlgorithm("KeyAgreement.ECDHWITHSHA1KDF", PREFIX + "KeyAgreementSpi$DHwithSHA1KDFAndSharedInfo", generalEcAttributes);
+            provider.addAlgorithm("KeyAgreement.ECCDHWITHSHA1KDF", PREFIX + "KeyAgreementSpi$CDHwithSHA1KDFAndSharedInfo", generalEcAttributes);
 
-            provider.addAlgorithm("KeyAgreement.ECDHWITHSHA1KDF", PREFIX + "KeyAgreementSpi$DHwithSHA1KDFAndSharedInfo");
-            provider.addAlgorithm("KeyAgreement.ECCDHWITHSHA1KDF", PREFIX + "KeyAgreementSpi$CDHwithSHA1KDFAndSharedInfo");
+            provider.addAlgorithm("KeyAgreement.ECDHWITHSHA224KDF", PREFIX + "KeyAgreementSpi$DHwithSHA224KDFAndSharedInfo", generalEcAttributes);
+            provider.addAlgorithm("KeyAgreement.ECCDHWITHSHA224KDF", PREFIX + "KeyAgreementSpi$CDHwithSHA224KDFAndSharedInfo", generalEcAttributes);
 
-            provider.addAlgorithm("KeyAgreement.ECDHWITHSHA224KDF", PREFIX + "KeyAgreementSpi$DHwithSHA224KDFAndSharedInfo");
-            provider.addAlgorithm("KeyAgreement.ECCDHWITHSHA224KDF", PREFIX + "KeyAgreementSpi$CDHwithSHA224KDFAndSharedInfo");
+            provider.addAlgorithm("KeyAgreement.ECDHWITHSHA256KDF", PREFIX + "KeyAgreementSpi$DHwithSHA256KDFAndSharedInfo", generalEcAttributes);
+            provider.addAlgorithm("KeyAgreement.ECCDHWITHSHA256KDF", PREFIX + "KeyAgreementSpi$CDHwithSHA256KDFAndSharedInfo", generalEcAttributes);
 
-            provider.addAlgorithm("KeyAgreement.ECDHWITHSHA256KDF", PREFIX + "KeyAgreementSpi$DHwithSHA256KDFAndSharedInfo");
-            provider.addAlgorithm("KeyAgreement.ECCDHWITHSHA256KDF", PREFIX + "KeyAgreementSpi$CDHwithSHA256KDFAndSharedInfo");
+            provider.addAlgorithm("KeyAgreement.ECDHWITHSHA384KDF", PREFIX + "KeyAgreementSpi$DHwithSHA384KDFAndSharedInfo", generalEcAttributes);
+            provider.addAlgorithm("KeyAgreement.ECCDHWITHSHA384KDF", PREFIX + "KeyAgreementSpi$CDHwithSHA384KDFAndSharedInfo", generalEcAttributes);
 
-            provider.addAlgorithm("KeyAgreement.ECDHWITHSHA384KDF", PREFIX + "KeyAgreementSpi$DHwithSHA384KDFAndSharedInfo");
-            provider.addAlgorithm("KeyAgreement.ECCDHWITHSHA384KDF", PREFIX + "KeyAgreementSpi$CDHwithSHA384KDFAndSharedInfo");
+            provider.addAlgorithm("KeyAgreement.ECDHWITHSHA512KDF", PREFIX + "KeyAgreementSpi$DHwithSHA512KDFAndSharedInfo", generalEcAttributes);
+            provider.addAlgorithm("KeyAgreement.ECCDHWITHSHA512KDF", PREFIX + "KeyAgreementSpi$CDHwithSHA512KDFAndSharedInfo", generalEcAttributes);
 
-            provider.addAlgorithm("KeyAgreement.ECDHWITHSHA512KDF", PREFIX + "KeyAgreementSpi$DHwithSHA512KDFAndSharedInfo");
-            provider.addAlgorithm("KeyAgreement.ECCDHWITHSHA512KDF", PREFIX + "KeyAgreementSpi$CDHwithSHA512KDFAndSharedInfo");
+            provider.addAlgorithm("KeyAgreement", X9ObjectIdentifiers.dhSinglePass_stdDH_sha1kdf_scheme, PREFIX + "KeyAgreementSpi$DHwithSHA1KDFAndSharedInfo", generalEcAttributes);
+            provider.addAlgorithm("KeyAgreement", X9ObjectIdentifiers.dhSinglePass_cofactorDH_sha1kdf_scheme, PREFIX + "KeyAgreementSpi$CDHwithSHA1KDFAndSharedInfo", generalEcAttributes);
 
-            provider.addAlgorithm("KeyAgreement", X9ObjectIdentifiers.dhSinglePass_stdDH_sha1kdf_scheme, PREFIX + "KeyAgreementSpi$DHwithSHA1KDFAndSharedInfo");
-            provider.addAlgorithm("KeyAgreement", X9ObjectIdentifiers.dhSinglePass_cofactorDH_sha1kdf_scheme, PREFIX + "KeyAgreementSpi$CDHwithSHA1KDFAndSharedInfo");
+            provider.addAlgorithm("KeyAgreement", SECObjectIdentifiers.dhSinglePass_stdDH_sha224kdf_scheme, PREFIX + "KeyAgreementSpi$DHwithSHA224KDFAndSharedInfo", generalEcAttributes);
+            provider.addAlgorithm("KeyAgreement", SECObjectIdentifiers.dhSinglePass_cofactorDH_sha224kdf_scheme, PREFIX + "KeyAgreementSpi$CDHwithSHA224KDFAndSharedInfo", generalEcAttributes);
 
-            provider.addAlgorithm("KeyAgreement", SECObjectIdentifiers.dhSinglePass_stdDH_sha224kdf_scheme, PREFIX + "KeyAgreementSpi$DHwithSHA224KDFAndSharedInfo");
-            provider.addAlgorithm("KeyAgreement", SECObjectIdentifiers.dhSinglePass_cofactorDH_sha224kdf_scheme, PREFIX + "KeyAgreementSpi$CDHwithSHA224KDFAndSharedInfo");
+            provider.addAlgorithm("KeyAgreement", SECObjectIdentifiers.dhSinglePass_stdDH_sha256kdf_scheme, PREFIX + "KeyAgreementSpi$DHwithSHA256KDFAndSharedInfo", generalEcAttributes);
+            provider.addAlgorithm("KeyAgreement", SECObjectIdentifiers.dhSinglePass_cofactorDH_sha256kdf_scheme, PREFIX + "KeyAgreementSpi$CDHwithSHA256KDFAndSharedInfo", generalEcAttributes);
 
-            provider.addAlgorithm("KeyAgreement", SECObjectIdentifiers.dhSinglePass_stdDH_sha256kdf_scheme, PREFIX + "KeyAgreementSpi$DHwithSHA256KDFAndSharedInfo");
-            provider.addAlgorithm("KeyAgreement", SECObjectIdentifiers.dhSinglePass_cofactorDH_sha256kdf_scheme, PREFIX + "KeyAgreementSpi$CDHwithSHA256KDFAndSharedInfo");
+            provider.addAlgorithm("KeyAgreement", SECObjectIdentifiers.dhSinglePass_stdDH_sha384kdf_scheme, PREFIX + "KeyAgreementSpi$DHwithSHA384KDFAndSharedInfo", generalEcAttributes);
+            provider.addAlgorithm("KeyAgreement", SECObjectIdentifiers.dhSinglePass_cofactorDH_sha384kdf_scheme, PREFIX + "KeyAgreementSpi$CDHwithSHA384KDFAndSharedInfo", generalEcAttributes);
 
-            provider.addAlgorithm("KeyAgreement", SECObjectIdentifiers.dhSinglePass_stdDH_sha384kdf_scheme, PREFIX + "KeyAgreementSpi$DHwithSHA384KDFAndSharedInfo");
-            provider.addAlgorithm("KeyAgreement", SECObjectIdentifiers.dhSinglePass_cofactorDH_sha384kdf_scheme, PREFIX + "KeyAgreementSpi$CDHwithSHA384KDFAndSharedInfo");
+            provider.addAlgorithm("KeyAgreement", SECObjectIdentifiers.dhSinglePass_stdDH_sha512kdf_scheme, PREFIX + "KeyAgreementSpi$DHwithSHA512KDFAndSharedInfo", generalEcAttributes);
+            provider.addAlgorithm("KeyAgreement", SECObjectIdentifiers.dhSinglePass_cofactorDH_sha512kdf_scheme, PREFIX + "KeyAgreementSpi$CDHwithSHA512KDFAndSharedInfo", generalEcAttributes);
 
-            provider.addAlgorithm("KeyAgreement", SECObjectIdentifiers.dhSinglePass_stdDH_sha512kdf_scheme, PREFIX + "KeyAgreementSpi$DHwithSHA512KDFAndSharedInfo");
-            provider.addAlgorithm("KeyAgreement", SECObjectIdentifiers.dhSinglePass_cofactorDH_sha512kdf_scheme, PREFIX + "KeyAgreementSpi$CDHwithSHA512KDFAndSharedInfo");
+            provider.addAlgorithm("KeyAgreement.ECCDHWITHSHA1CKDF", PREFIX + "KeyAgreementSpi$DHwithSHA1CKDF", generalEcAttributes);
+            provider.addAlgorithm("KeyAgreement.ECCDHWITHSHA256CKDF", PREFIX + "KeyAgreementSpi$DHwithSHA256CKDF", generalEcAttributes);
+            provider.addAlgorithm("KeyAgreement.ECCDHWITHSHA384CKDF", PREFIX + "KeyAgreementSpi$DHwithSHA384CKDF", generalEcAttributes);
+            provider.addAlgorithm("KeyAgreement.ECCDHWITHSHA512CKDF", PREFIX + "KeyAgreementSpi$DHwithSHA512CKDF", generalEcAttributes);
 
-            provider.addAlgorithm("KeyAgreement.ECCDHWITHSHA1CKDF", PREFIX + "KeyAgreementSpi$DHwithSHA1CKDF");
-            provider.addAlgorithm("KeyAgreement.ECCDHWITHSHA256CKDF", PREFIX + "KeyAgreementSpi$DHwithSHA256CKDF");
-            provider.addAlgorithm("KeyAgreement.ECCDHWITHSHA384CKDF", PREFIX + "KeyAgreementSpi$DHwithSHA384CKDF");
-            provider.addAlgorithm("KeyAgreement.ECCDHWITHSHA512CKDF", PREFIX + "KeyAgreementSpi$DHwithSHA512CKDF");
+            provider.addAlgorithm("KeyAgreement.ECCDHUWITHSHA1CKDF", PREFIX + "KeyAgreementSpi$DHUwithSHA1CKDF", generalEcAttributes);
+            provider.addAlgorithm("KeyAgreement.ECCDHUWITHSHA224CKDF", PREFIX + "KeyAgreementSpi$DHUwithSHA224CKDF", generalEcAttributes);
+            provider.addAlgorithm("KeyAgreement.ECCDHUWITHSHA256CKDF", PREFIX + "KeyAgreementSpi$DHUwithSHA256CKDF", generalEcAttributes);
+            provider.addAlgorithm("KeyAgreement.ECCDHUWITHSHA384CKDF", PREFIX + "KeyAgreementSpi$DHUwithSHA384CKDF", generalEcAttributes);
+            provider.addAlgorithm("KeyAgreement.ECCDHUWITHSHA512CKDF", PREFIX + "KeyAgreementSpi$DHUwithSHA512CKDF", generalEcAttributes);
 
-            provider.addAlgorithm("KeyAgreement.ECCDHUWITHSHA1CKDF", PREFIX + "KeyAgreementSpi$DHUwithSHA1CKDF");
-            provider.addAlgorithm("KeyAgreement.ECCDHUWITHSHA224CKDF", PREFIX + "KeyAgreementSpi$DHUwithSHA224CKDF");
-            provider.addAlgorithm("KeyAgreement.ECCDHUWITHSHA256CKDF", PREFIX + "KeyAgreementSpi$DHUwithSHA256CKDF");
-            provider.addAlgorithm("KeyAgreement.ECCDHUWITHSHA384CKDF", PREFIX + "KeyAgreementSpi$DHUwithSHA384CKDF");
-            provider.addAlgorithm("KeyAgreement.ECCDHUWITHSHA512CKDF", PREFIX + "KeyAgreementSpi$DHUwithSHA512CKDF");
+            provider.addAlgorithm("KeyAgreement.ECCDHUWITHSHA1KDF", PREFIX + "KeyAgreementSpi$DHUwithSHA1KDF", generalEcAttributes);
+            provider.addAlgorithm("KeyAgreement.ECCDHUWITHSHA224KDF", PREFIX + "KeyAgreementSpi$DHUwithSHA224KDF", generalEcAttributes);
+            provider.addAlgorithm("KeyAgreement.ECCDHUWITHSHA256KDF", PREFIX + "KeyAgreementSpi$DHUwithSHA256KDF", generalEcAttributes);
+            provider.addAlgorithm("KeyAgreement.ECCDHUWITHSHA384KDF", PREFIX + "KeyAgreementSpi$DHUwithSHA384KDF", generalEcAttributes);
+            provider.addAlgorithm("KeyAgreement.ECCDHUWITHSHA512KDF", PREFIX + "KeyAgreementSpi$DHUwithSHA512KDF", generalEcAttributes);
 
-            provider.addAlgorithm("KeyAgreement.ECCDHUWITHSHA1KDF", PREFIX + "KeyAgreementSpi$DHUwithSHA1KDF");
-            provider.addAlgorithm("KeyAgreement.ECCDHUWITHSHA224KDF", PREFIX + "KeyAgreementSpi$DHUwithSHA224KDF");
-            provider.addAlgorithm("KeyAgreement.ECCDHUWITHSHA256KDF", PREFIX + "KeyAgreementSpi$DHUwithSHA256KDF");
-            provider.addAlgorithm("KeyAgreement.ECCDHUWITHSHA384KDF", PREFIX + "KeyAgreementSpi$DHUwithSHA384KDF");
-            provider.addAlgorithm("KeyAgreement.ECCDHUWITHSHA512KDF", PREFIX + "KeyAgreementSpi$DHUwithSHA512KDF");
+            provider.addAlgorithm("KeyAgreement.ECKAEGWITHSHA1KDF", PREFIX + "KeyAgreementSpi$ECKAEGwithSHA1KDF", generalEcAttributes);
+            provider.addAlgorithm("KeyAgreement.ECKAEGWITHSHA224KDF", PREFIX + "KeyAgreementSpi$ECKAEGwithSHA224KDF", generalEcAttributes);
+            provider.addAlgorithm("KeyAgreement.ECKAEGWITHSHA256KDF", PREFIX + "KeyAgreementSpi$ECKAEGwithSHA256KDF", generalEcAttributes);
+            provider.addAlgorithm("KeyAgreement.ECKAEGWITHSHA384KDF", PREFIX + "KeyAgreementSpi$ECKAEGwithSHA384KDF", generalEcAttributes);
+            provider.addAlgorithm("KeyAgreement.ECKAEGWITHSHA512KDF", PREFIX + "KeyAgreementSpi$ECKAEGwithSHA512KDF", generalEcAttributes);
 
-            provider.addAlgorithm("KeyAgreement.ECKAEGWITHSHA1KDF", PREFIX + "KeyAgreementSpi$ECKAEGwithSHA1KDF");
-            provider.addAlgorithm("KeyAgreement.ECKAEGWITHSHA224KDF", PREFIX + "KeyAgreementSpi$ECKAEGwithSHA224KDF");
-            provider.addAlgorithm("KeyAgreement.ECKAEGWITHSHA256KDF", PREFIX + "KeyAgreementSpi$ECKAEGwithSHA256KDF");
-            provider.addAlgorithm("KeyAgreement.ECKAEGWITHSHA384KDF", PREFIX + "KeyAgreementSpi$ECKAEGwithSHA384KDF");
-            provider.addAlgorithm("KeyAgreement.ECKAEGWITHSHA512KDF", PREFIX + "KeyAgreementSpi$ECKAEGwithSHA512KDF");
+            provider.addAlgorithm("KeyAgreement", BSIObjectIdentifiers.ecka_eg_X963kdf_SHA1, PREFIX + "KeyAgreementSpi$ECKAEGwithSHA1KDF", generalEcAttributes);
+            provider.addAlgorithm("KeyAgreement", BSIObjectIdentifiers.ecka_eg_X963kdf_SHA224, PREFIX + "KeyAgreementSpi$ECKAEGwithSHA224KDF", generalEcAttributes);
+            provider.addAlgorithm("KeyAgreement", BSIObjectIdentifiers.ecka_eg_X963kdf_SHA256, PREFIX + "KeyAgreementSpi$ECKAEGwithSHA256KDF", generalEcAttributes);
+            provider.addAlgorithm("KeyAgreement", BSIObjectIdentifiers.ecka_eg_X963kdf_SHA384, PREFIX + "KeyAgreementSpi$ECKAEGwithSHA384KDF", generalEcAttributes);
+            provider.addAlgorithm("KeyAgreement", BSIObjectIdentifiers.ecka_eg_X963kdf_SHA512, PREFIX + "KeyAgreementSpi$ECKAEGwithSHA512KDF", generalEcAttributes);
 
-            provider.addAlgorithm("KeyAgreement", BSIObjectIdentifiers.ecka_eg_X963kdf_SHA1, PREFIX + "KeyAgreementSpi$ECKAEGwithSHA1KDF");
-            provider.addAlgorithm("KeyAgreement", BSIObjectIdentifiers.ecka_eg_X963kdf_SHA224, PREFIX + "KeyAgreementSpi$ECKAEGwithSHA224KDF");
-            provider.addAlgorithm("KeyAgreement", BSIObjectIdentifiers.ecka_eg_X963kdf_SHA256, PREFIX + "KeyAgreementSpi$ECKAEGwithSHA256KDF");
-            provider.addAlgorithm("KeyAgreement", BSIObjectIdentifiers.ecka_eg_X963kdf_SHA384, PREFIX + "KeyAgreementSpi$ECKAEGwithSHA384KDF");
-            provider.addAlgorithm("KeyAgreement", BSIObjectIdentifiers.ecka_eg_X963kdf_SHA512, PREFIX + "KeyAgreementSpi$ECKAEGwithSHA512KDF");
-
-            provider.addAlgorithm("KeyAgreement", BSIObjectIdentifiers.ecka_eg_X963kdf_RIPEMD160, PREFIX + "KeyAgreementSpi$ECKAEGwithRIPEMD160KDF");
-            provider.addAlgorithm("KeyAgreement.ECKAEGWITHRIPEMD160KDF", PREFIX + "KeyAgreementSpi$ECKAEGwithRIPEMD160KDF");
+            provider.addAlgorithm("KeyAgreement", BSIObjectIdentifiers.ecka_eg_X963kdf_RIPEMD160, PREFIX + "KeyAgreementSpi$ECKAEGwithRIPEMD160KDF", generalEcAttributes);
+            provider.addAlgorithm("KeyAgreement.ECKAEGWITHRIPEMD160KDF", PREFIX + "KeyAgreementSpi$ECKAEGwithRIPEMD160KDF", generalEcAttributes);
 
             registerOid(provider, X9ObjectIdentifiers.id_ecPublicKey, "EC", new KeyFactorySpi.EC());
 
@@ -151,25 +150,25 @@
 
             if (!Properties.isOverrideSet("org.bouncycastle.ec.disable_mqv"))
             {
-                provider.addAlgorithm("KeyAgreement.ECMQV", PREFIX + "KeyAgreementSpi$MQV");
+                provider.addAlgorithm("KeyAgreement.ECMQV", PREFIX + "KeyAgreementSpi$MQV", generalEcAttributes);
 
-                provider.addAlgorithm("KeyAgreement.ECMQVWITHSHA1CKDF", PREFIX + "KeyAgreementSpi$MQVwithSHA1CKDF");
-                provider.addAlgorithm("KeyAgreement.ECMQVWITHSHA224CKDF", PREFIX + "KeyAgreementSpi$MQVwithSHA224CKDF");
-                provider.addAlgorithm("KeyAgreement.ECMQVWITHSHA256CKDF", PREFIX + "KeyAgreementSpi$MQVwithSHA256CKDF");
-                provider.addAlgorithm("KeyAgreement.ECMQVWITHSHA384CKDF", PREFIX + "KeyAgreementSpi$MQVwithSHA384CKDF");
-                provider.addAlgorithm("KeyAgreement.ECMQVWITHSHA512CKDF", PREFIX + "KeyAgreementSpi$MQVwithSHA512CKDF");
+                provider.addAlgorithm("KeyAgreement.ECMQVWITHSHA1CKDF", PREFIX + "KeyAgreementSpi$MQVwithSHA1CKDF", generalEcAttributes);
+                provider.addAlgorithm("KeyAgreement.ECMQVWITHSHA224CKDF", PREFIX + "KeyAgreementSpi$MQVwithSHA224CKDF", generalEcAttributes);
+                provider.addAlgorithm("KeyAgreement.ECMQVWITHSHA256CKDF", PREFIX + "KeyAgreementSpi$MQVwithSHA256CKDF", generalEcAttributes);
+                provider.addAlgorithm("KeyAgreement.ECMQVWITHSHA384CKDF", PREFIX + "KeyAgreementSpi$MQVwithSHA384CKDF", generalEcAttributes);
+                provider.addAlgorithm("KeyAgreement.ECMQVWITHSHA512CKDF", PREFIX + "KeyAgreementSpi$MQVwithSHA512CKDF", generalEcAttributes);
 
-                provider.addAlgorithm("KeyAgreement.ECMQVWITHSHA1KDF", PREFIX + "KeyAgreementSpi$MQVwithSHA1KDF");
-                provider.addAlgorithm("KeyAgreement.ECMQVWITHSHA224KDF", PREFIX + "KeyAgreementSpi$MQVwithSHA224KDF");
-                provider.addAlgorithm("KeyAgreement.ECMQVWITHSHA256KDF", PREFIX + "KeyAgreementSpi$MQVwithSHA256KDF");
-                provider.addAlgorithm("KeyAgreement.ECMQVWITHSHA384KDF", PREFIX + "KeyAgreementSpi$MQVwithSHA384KDF");
-                provider.addAlgorithm("KeyAgreement.ECMQVWITHSHA512KDF", PREFIX + "KeyAgreementSpi$MQVwithSHA512KDF");
+                provider.addAlgorithm("KeyAgreement.ECMQVWITHSHA1KDF", PREFIX + "KeyAgreementSpi$MQVwithSHA1KDF", generalEcAttributes);
+                provider.addAlgorithm("KeyAgreement.ECMQVWITHSHA224KDF", PREFIX + "KeyAgreementSpi$MQVwithSHA224KDF", generalEcAttributes);
+                provider.addAlgorithm("KeyAgreement.ECMQVWITHSHA256KDF", PREFIX + "KeyAgreementSpi$MQVwithSHA256KDF", generalEcAttributes);
+                provider.addAlgorithm("KeyAgreement.ECMQVWITHSHA384KDF", PREFIX + "KeyAgreementSpi$MQVwithSHA384KDF", generalEcAttributes);
+                provider.addAlgorithm("KeyAgreement.ECMQVWITHSHA512KDF", PREFIX + "KeyAgreementSpi$MQVwithSHA512KDF", generalEcAttributes);
 
-                provider.addAlgorithm("KeyAgreement." + X9ObjectIdentifiers.mqvSinglePass_sha1kdf_scheme, PREFIX + "KeyAgreementSpi$MQVwithSHA1KDFAndSharedInfo");
-                provider.addAlgorithm("KeyAgreement." + SECObjectIdentifiers.mqvSinglePass_sha224kdf_scheme, PREFIX + "KeyAgreementSpi$MQVwithSHA224KDFAndSharedInfo");
-                provider.addAlgorithm("KeyAgreement." + SECObjectIdentifiers.mqvSinglePass_sha256kdf_scheme, PREFIX + "KeyAgreementSpi$MQVwithSHA256KDFAndSharedInfo");
-                provider.addAlgorithm("KeyAgreement." + SECObjectIdentifiers.mqvSinglePass_sha384kdf_scheme, PREFIX + "KeyAgreementSpi$MQVwithSHA384KDFAndSharedInfo");
-                provider.addAlgorithm("KeyAgreement." + SECObjectIdentifiers.mqvSinglePass_sha512kdf_scheme, PREFIX + "KeyAgreementSpi$MQVwithSHA512KDFAndSharedInfo");
+                provider.addAlgorithm("KeyAgreement." + X9ObjectIdentifiers.mqvSinglePass_sha1kdf_scheme, PREFIX + "KeyAgreementSpi$MQVwithSHA1KDFAndSharedInfo", generalEcAttributes);
+                provider.addAlgorithm("KeyAgreement." + SECObjectIdentifiers.mqvSinglePass_sha224kdf_scheme, PREFIX + "KeyAgreementSpi$MQVwithSHA224KDFAndSharedInfo", generalEcAttributes);
+                provider.addAlgorithm("KeyAgreement." + SECObjectIdentifiers.mqvSinglePass_sha256kdf_scheme, PREFIX + "KeyAgreementSpi$MQVwithSHA256KDFAndSharedInfo", generalEcAttributes);
+                provider.addAlgorithm("KeyAgreement." + SECObjectIdentifiers.mqvSinglePass_sha384kdf_scheme, PREFIX + "KeyAgreementSpi$MQVwithSHA384KDFAndSharedInfo", generalEcAttributes);
+                provider.addAlgorithm("KeyAgreement." + SECObjectIdentifiers.mqvSinglePass_sha512kdf_scheme, PREFIX + "KeyAgreementSpi$MQVwithSHA512KDFAndSharedInfo", generalEcAttributes);
 
                 registerOid(provider, X9ObjectIdentifiers.dhSinglePass_stdDH_sha1kdf_scheme, "EC", new KeyFactorySpi.EC());
             
@@ -203,17 +202,42 @@
             provider.addAlgorithm("KeyPairGenerator.ECDHC", PREFIX + "KeyPairGeneratorSpi$ECDHC");
             provider.addAlgorithm("KeyPairGenerator.ECIES", PREFIX + "KeyPairGeneratorSpi$ECDH");
 
-            provider.addAlgorithm("Cipher.ECIES", PREFIX + "IESCipher$ECIES");
+            provider.addAlgorithm("Cipher.ECIES", PREFIX + "IESCipher$ECIES", generalEcAttributes);
+            provider.addAlgorithm("Cipher.ECIESwithSHA1", PREFIX + "IESCipher$ECIES", generalEcAttributes);
+            provider.addAlgorithm("Cipher.ECIESWITHSHA1", PREFIX + "IESCipher$ECIES", generalEcAttributes);
+            provider.addAlgorithm("Cipher.ECIESwithSHA256", PREFIX + "IESCipher$ECIESwithSHA256", generalEcAttributes);
+            provider.addAlgorithm("Cipher.ECIESWITHSHA256", PREFIX + "IESCipher$ECIESwithSHA256", generalEcAttributes);
+            provider.addAlgorithm("Cipher.ECIESwithSHA384", PREFIX + "IESCipher$ECIESwithSHA384", generalEcAttributes);
+            provider.addAlgorithm("Cipher.ECIESWITHSHA384", PREFIX + "IESCipher$ECIESwithSHA384", generalEcAttributes);
+            provider.addAlgorithm("Cipher.ECIESwithSHA512", PREFIX + "IESCipher$ECIESwithSHA512", generalEcAttributes);
+            provider.addAlgorithm("Cipher.ECIESWITHSHA512", PREFIX + "IESCipher$ECIESwithSHA512", generalEcAttributes);
 
-            provider.addAlgorithm("Cipher.ECIESwithAES-CBC", PREFIX + "IESCipher$ECIESwithAESCBC");
-            provider.addAlgorithm("Cipher.ECIESWITHAES-CBC", PREFIX + "IESCipher$ECIESwithAESCBC");
-            provider.addAlgorithm("Cipher.ECIESwithDESEDE-CBC", PREFIX + "IESCipher$ECIESwithDESedeCBC");
-            provider.addAlgorithm("Cipher.ECIESWITHDESEDE-CBC", PREFIX + "IESCipher$ECIESwithDESedeCBC");
+            provider.addAlgorithm("Cipher.ECIESwithAES-CBC", PREFIX + "IESCipher$ECIESwithAESCBC", generalEcAttributes);
+            provider.addAlgorithm("Cipher.ECIESWITHAES-CBC", PREFIX + "IESCipher$ECIESwithAESCBC", generalEcAttributes);
+            provider.addAlgorithm("Cipher.ECIESwithSHA1andAES-CBC", PREFIX + "IESCipher$ECIESwithAESCBC", generalEcAttributes);
+            provider.addAlgorithm("Cipher.ECIESWITHSHA1ANDAES-CBC", PREFIX + "IESCipher$ECIESwithAESCBC", generalEcAttributes);
+            provider.addAlgorithm("Cipher.ECIESwithSHA256andAES-CBC", PREFIX + "IESCipher$ECIESwithSHA256andAESCBC", generalEcAttributes);
+            provider.addAlgorithm("Cipher.ECIESWITHSHA256ANDAES-CBC", PREFIX + "IESCipher$ECIESwithSHA256andAESCBC", generalEcAttributes);
+            provider.addAlgorithm("Cipher.ECIESwithSHA384andAES-CBC", PREFIX + "IESCipher$ECIESwithSHA384andAESCBC", generalEcAttributes);
+            provider.addAlgorithm("Cipher.ECIESWITHSHA384ANDAES-CBC", PREFIX + "IESCipher$ECIESwithSHA384andAESCBC", generalEcAttributes);
+            provider.addAlgorithm("Cipher.ECIESwithSHA512andAES-CBC", PREFIX + "IESCipher$ECIESwithSHA512andAESCBC", generalEcAttributes);
+            provider.addAlgorithm("Cipher.ECIESWITHSHA512ANDAES-CBC", PREFIX + "IESCipher$ECIESwithSHA512andAESCBC", generalEcAttributes);
 
-            provider.addAlgorithm("Signature.ECDSA", PREFIX + "SignatureSpi$ecDSA");
+            provider.addAlgorithm("Cipher.ECIESwithDESEDE-CBC", PREFIX + "IESCipher$ECIESwithDESedeCBC", generalEcAttributes);
+            provider.addAlgorithm("Cipher.ECIESWITHDESEDE-CBC", PREFIX + "IESCipher$ECIESwithDESedeCBC", generalEcAttributes);
+            provider.addAlgorithm("Cipher.ECIESwithSHA1andDESEDE-CBC", PREFIX + "IESCipher$ECIESwithDESedeCBC", generalEcAttributes);
+            provider.addAlgorithm("Cipher.ECIESWITHSHA1ANDDESEDE-CBC", PREFIX + "IESCipher$ECIESwithDESedeCBC", generalEcAttributes);
+            provider.addAlgorithm("Cipher.ECIESwithSHA256andDESEDE-CBC", PREFIX + "IESCipher$ECIESwithSHA256andDESedeCBC", generalEcAttributes);
+            provider.addAlgorithm("Cipher.ECIESWITHSHA256ANDDESEDE-CBC", PREFIX + "IESCipher$ECIESwithSHA256andDESedeCBC", generalEcAttributes);
+            provider.addAlgorithm("Cipher.ECIESwithSHA384andDESEDE-CBC", PREFIX + "IESCipher$ECIESwithSHA384andDESedeCBC", generalEcAttributes);
+            provider.addAlgorithm("Cipher.ECIESWITHSHA384ANDDESEDE-CBC", PREFIX + "IESCipher$ECIESwithSHA384andDESedeCBC", generalEcAttributes);
+            provider.addAlgorithm("Cipher.ECIESwithSHA512andDESEDE-CBC", PREFIX + "IESCipher$ECIESwithSHA512andDESedeCBC", generalEcAttributes);
+            provider.addAlgorithm("Cipher.ECIESWITHSHA512ANDDESEDE-CBC", PREFIX + "IESCipher$ECIESwithSHA512andDESedeCBC", generalEcAttributes);
 
-            provider.addAlgorithm("Signature.SHA1withECDSA", PREFIX + "SignatureSpi$ecDSA");
-            provider.addAlgorithm("Signature.NONEwithECDSA", PREFIX + "SignatureSpi$ecDSAnone");
+            provider.addAlgorithm("Cipher.ETSIKEMWITHSHA256", PREFIX + "IESKEMCipher$KEMwithSHA256", generalEcAttributes);
+
+            provider.addAlgorithm("Signature.ECDSA", PREFIX + "SignatureSpi$ecDSA", generalEcAttributes);
+            provider.addAlgorithm("Signature.NONEwithECDSA", PREFIX + "SignatureSpi$ecDSAnone", generalEcAttributes);
 
             provider.addAlgorithm("Alg.Alias.Signature.ECDSA", "SHA1withECDSA");
             provider.addAlgorithm("Alg.Alias.Signature.ECDSAwithSHA1", "SHA1withECDSA");
@@ -224,16 +248,16 @@
             provider.addAlgorithm("Alg.Alias.Signature.1.2.840.10045.4.1", "SHA1withECDSA");
             provider.addAlgorithm("Alg.Alias.Signature." + TeleTrusTObjectIdentifiers.ecSignWithSha1, "ECDSA");
 
-            provider.addAlgorithm("Signature.ECDDSA", PREFIX + "SignatureSpi$ecDetDSA");
-            provider.addAlgorithm("Signature.SHA1WITHECDDSA", PREFIX + "SignatureSpi$ecDetDSA");
-            provider.addAlgorithm("Signature.SHA224WITHECDDSA", PREFIX + "SignatureSpi$ecDetDSA224");
-            provider.addAlgorithm("Signature.SHA256WITHECDDSA", PREFIX + "SignatureSpi$ecDetDSA256");
-            provider.addAlgorithm("Signature.SHA384WITHECDDSA", PREFIX + "SignatureSpi$ecDetDSA384");
-            provider.addAlgorithm("Signature.SHA512WITHECDDSA", PREFIX + "SignatureSpi$ecDetDSA512");
-            provider.addAlgorithm("Signature.SHA3-224WITHECDDSA", PREFIX + "SignatureSpi$ecDetDSASha3_224");
-            provider.addAlgorithm("Signature.SHA3-256WITHECDDSA", PREFIX + "SignatureSpi$ecDetDSASha3_256");
-            provider.addAlgorithm("Signature.SHA3-384WITHECDDSA", PREFIX + "SignatureSpi$ecDetDSASha3_384");
-            provider.addAlgorithm("Signature.SHA3-512WITHECDDSA", PREFIX + "SignatureSpi$ecDetDSASha3_512");
+            provider.addAlgorithm("Signature.ECDDSA", PREFIX + "SignatureSpi$ecDetDSA", generalEcAttributes);
+            provider.addAlgorithm("Signature.SHA1WITHECDDSA", PREFIX + "SignatureSpi$ecDetDSA", generalEcAttributes);
+            provider.addAlgorithm("Signature.SHA224WITHECDDSA", PREFIX + "SignatureSpi$ecDetDSA224", generalEcAttributes);
+            provider.addAlgorithm("Signature.SHA256WITHECDDSA", PREFIX + "SignatureSpi$ecDetDSA256", generalEcAttributes);
+            provider.addAlgorithm("Signature.SHA384WITHECDDSA", PREFIX + "SignatureSpi$ecDetDSA384", generalEcAttributes);
+            provider.addAlgorithm("Signature.SHA512WITHECDDSA", PREFIX + "SignatureSpi$ecDetDSA512", generalEcAttributes);
+            provider.addAlgorithm("Signature.SHA3-224WITHECDDSA", PREFIX + "SignatureSpi$ecDetDSASha3_224", generalEcAttributes);
+            provider.addAlgorithm("Signature.SHA3-256WITHECDDSA", PREFIX + "SignatureSpi$ecDetDSASha3_256", generalEcAttributes);
+            provider.addAlgorithm("Signature.SHA3-384WITHECDDSA", PREFIX + "SignatureSpi$ecDetDSASha3_384", generalEcAttributes);
+            provider.addAlgorithm("Signature.SHA3-512WITHECDDSA", PREFIX + "SignatureSpi$ecDetDSASha3_512", generalEcAttributes);
 
             provider.addAlgorithm("Alg.Alias.Signature.DETECDSA", "ECDDSA");
             provider.addAlgorithm("Alg.Alias.Signature.SHA1WITHDETECDSA", "SHA1WITHECDDSA");
@@ -242,22 +266,29 @@
             provider.addAlgorithm("Alg.Alias.Signature.SHA384WITHDETECDSA", "SHA384WITHECDDSA");
             provider.addAlgorithm("Alg.Alias.Signature.SHA512WITHDETECDSA", "SHA512WITHECDDSA");
 
-            addSignatureAlgorithm(provider, "SHA224", "ECDSA", PREFIX + "SignatureSpi$ecDSA224", X9ObjectIdentifiers.ecdsa_with_SHA224);
-            addSignatureAlgorithm(provider, "SHA256", "ECDSA", PREFIX + "SignatureSpi$ecDSA256", X9ObjectIdentifiers.ecdsa_with_SHA256);
-            addSignatureAlgorithm(provider, "SHA384", "ECDSA", PREFIX + "SignatureSpi$ecDSA384", X9ObjectIdentifiers.ecdsa_with_SHA384);
-            addSignatureAlgorithm(provider, "SHA512", "ECDSA", PREFIX + "SignatureSpi$ecDSA512", X9ObjectIdentifiers.ecdsa_with_SHA512);
-            addSignatureAlgorithm(provider, "SHA3-224", "ECDSA", PREFIX + "SignatureSpi$ecDSASha3_224", NISTObjectIdentifiers.id_ecdsa_with_sha3_224);
-            addSignatureAlgorithm(provider, "SHA3-256", "ECDSA", PREFIX + "SignatureSpi$ecDSASha3_256", NISTObjectIdentifiers.id_ecdsa_with_sha3_256);
-            addSignatureAlgorithm(provider, "SHA3-384", "ECDSA", PREFIX + "SignatureSpi$ecDSASha3_384", NISTObjectIdentifiers.id_ecdsa_with_sha3_384);
-            addSignatureAlgorithm(provider, "SHA3-512", "ECDSA", PREFIX + "SignatureSpi$ecDSASha3_512", NISTObjectIdentifiers.id_ecdsa_with_sha3_512);
+            addSignatureAlgorithm(provider, "SHA224", "ECDSA", PREFIX + "SignatureSpi$ecDSA224", X9ObjectIdentifiers.ecdsa_with_SHA224, generalEcAttributes);
+            addSignatureAlgorithm(provider, "SHA256", "ECDSA", PREFIX + "SignatureSpi$ecDSA256", X9ObjectIdentifiers.ecdsa_with_SHA256, generalEcAttributes);
+            addSignatureAlgorithm(provider, "SHA384", "ECDSA", PREFIX + "SignatureSpi$ecDSA384", X9ObjectIdentifiers.ecdsa_with_SHA384, generalEcAttributes);
+            addSignatureAlgorithm(provider, "SHA512", "ECDSA", PREFIX + "SignatureSpi$ecDSA512", X9ObjectIdentifiers.ecdsa_with_SHA512, generalEcAttributes);
+            addSignatureAlgorithm(provider, "SHA3-224", "ECDSA", PREFIX + "SignatureSpi$ecDSASha3_224", NISTObjectIdentifiers.id_ecdsa_with_sha3_224, generalEcAttributes);
+            addSignatureAlgorithm(provider, "SHA3-256", "ECDSA", PREFIX + "SignatureSpi$ecDSASha3_256", NISTObjectIdentifiers.id_ecdsa_with_sha3_256, generalEcAttributes);
+            addSignatureAlgorithm(provider, "SHA3-384", "ECDSA", PREFIX + "SignatureSpi$ecDSASha3_384", NISTObjectIdentifiers.id_ecdsa_with_sha3_384, generalEcAttributes);
+            addSignatureAlgorithm(provider, "SHA3-512", "ECDSA", PREFIX + "SignatureSpi$ecDSASha3_512", NISTObjectIdentifiers.id_ecdsa_with_sha3_512, generalEcAttributes);
+            addSignatureAlgorithm(provider, "SHAKE128", "ECDSA", PREFIX + "SignatureSpi$ecDSAShake128", CMSObjectIdentifiers.id_ecdsa_with_shake128, generalEcAttributes);
+            addSignatureAlgorithm(provider, "SHAKE256", "ECDSA", PREFIX + "SignatureSpi$ecDSAShake256", CMSObjectIdentifiers.id_ecdsa_with_shake256, generalEcAttributes);
+            addSignatureAlgorithm(provider, "RIPEMD160", "ECDSA", PREFIX + "SignatureSpi$ecDSARipeMD160",TeleTrusTObjectIdentifiers.ecSignWithRipemd160, generalEcAttributes);
 
-            addSignatureAlgorithm(provider, "RIPEMD160", "ECDSA", PREFIX + "SignatureSpi$ecDSARipeMD160",TeleTrusTObjectIdentifiers.ecSignWithRipemd160);
+            provider.addAlgorithm("Signature.SHA1WITHECNR", PREFIX + "SignatureSpi$ecNR", generalEcAttributes);
+            provider.addAlgorithm("Signature.SHA224WITHECNR", PREFIX + "SignatureSpi$ecNR224", generalEcAttributes);
+            provider.addAlgorithm("Signature.SHA256WITHECNR", PREFIX + "SignatureSpi$ecNR256", generalEcAttributes);
+            provider.addAlgorithm("Signature.SHA384WITHECNR", PREFIX + "SignatureSpi$ecNR384", generalEcAttributes);
+            provider.addAlgorithm("Signature.SHA512WITHECNR", PREFIX + "SignatureSpi$ecNR512", generalEcAttributes);
 
-            provider.addAlgorithm("Signature.SHA1WITHECNR", PREFIX + "SignatureSpi$ecNR");
-            provider.addAlgorithm("Signature.SHA224WITHECNR", PREFIX + "SignatureSpi$ecNR224");
-            provider.addAlgorithm("Signature.SHA256WITHECNR", PREFIX + "SignatureSpi$ecNR256");
-            provider.addAlgorithm("Signature.SHA384WITHECNR", PREFIX + "SignatureSpi$ecNR384");
-            provider.addAlgorithm("Signature.SHA512WITHECNR", PREFIX + "SignatureSpi$ecNR512");
+            addSignatureAlgorithm(provider, "SHA1", "CVC-ECDSA", PREFIX + "SignatureSpi$ecCVCDSA", EACObjectIdentifiers.id_TA_ECDSA_SHA_1, generalEcAttributes);
+            addSignatureAlgorithm(provider, "SHA224", "CVC-ECDSA", PREFIX + "SignatureSpi$ecCVCDSA224", EACObjectIdentifiers.id_TA_ECDSA_SHA_224, generalEcAttributes);
+            addSignatureAlgorithm(provider, "SHA256", "CVC-ECDSA", PREFIX + "SignatureSpi$ecCVCDSA256", EACObjectIdentifiers.id_TA_ECDSA_SHA_256, generalEcAttributes);
+            addSignatureAlgorithm(provider, "SHA384", "CVC-ECDSA", PREFIX + "SignatureSpi$ecCVCDSA384", EACObjectIdentifiers.id_TA_ECDSA_SHA_384, generalEcAttributes);
+            addSignatureAlgorithm(provider, "SHA512", "CVC-ECDSA", PREFIX + "SignatureSpi$ecCVCDSA512", EACObjectIdentifiers.id_TA_ECDSA_SHA_512, generalEcAttributes);
 
             addSignatureAlgorithm(provider, "SHA1", "CVC-ECDSA", PREFIX + "SignatureSpi$ecCVCDSA", EACObjectIdentifiers.id_TA_ECDSA_SHA_1);
             addSignatureAlgorithm(provider, "SHA224", "CVC-ECDSA", PREFIX + "SignatureSpi$ecCVCDSA224", EACObjectIdentifiers.id_TA_ECDSA_SHA_224);
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/EXTERNAL.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/EXTERNAL.java
new file mode 100644
index 0000000..46d3e5e
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/EXTERNAL.java
@@ -0,0 +1,118 @@
+package org.bouncycastle.jcajce.provider.asymmetric;
+
+import java.io.IOException;
+import java.security.InvalidKeyException;
+import java.security.Key;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.bouncycastle.asn1.bc.BCObjectIdentifiers;
+import org.bouncycastle.asn1.bc.ExternalValue;
+import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
+import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.bouncycastle.jcajce.ExternalPublicKey;
+import org.bouncycastle.jcajce.provider.asymmetric.util.BaseKeyFactorySpi;
+import org.bouncycastle.jcajce.provider.config.ConfigurableProvider;
+import org.bouncycastle.jcajce.provider.util.AsymmetricAlgorithmProvider;
+import org.bouncycastle.jcajce.provider.util.AsymmetricKeyInfoConverter;
+
+public class EXTERNAL
+{
+    private static final String PREFIX = "org.bouncycastle.jcajce.provider.asymmetric.EXTERNAL";
+
+    private static final Map<String, String> externalAttributes = new HashMap<String, String>();
+
+    static
+    {
+        externalAttributes.put("SupportedKeyClasses", "org.bouncycastle.jcajce.ExternalPublicKey");
+        externalAttributes.put("SupportedKeyFormats", "X.509");
+    }
+
+    private static AsymmetricKeyInfoConverter baseConverter;
+
+    public static class KeyFactory
+        extends BaseKeyFactorySpi
+    {
+        protected Key engineTranslateKey(Key key)
+            throws InvalidKeyException
+        {
+            try
+            {
+                if (key instanceof PrivateKey)
+                {
+                    return generatePrivate(PrivateKeyInfo.getInstance(key.getEncoded()));
+                }
+                else if (key instanceof PublicKey)
+                {
+                    return generatePublic(SubjectPublicKeyInfo.getInstance(key.getEncoded()));
+                }
+            }
+            catch (IOException e)
+            {
+                throw new InvalidKeyException("key could not be parsed: " + e.getMessage());
+            }
+
+            throw new InvalidKeyException("key not recognized");
+        }
+
+        public PrivateKey generatePrivate(PrivateKeyInfo keyInfo)
+            throws IOException
+        {
+            return baseConverter.generatePrivate(keyInfo);
+        }
+
+        public PublicKey generatePublic(SubjectPublicKeyInfo keyInfo)
+            throws IOException
+        {
+            return baseConverter.generatePublic(keyInfo);
+        }
+    }
+
+    private static class ExternalKeyInfoConverter
+        implements AsymmetricKeyInfoConverter
+    {
+        private final ConfigurableProvider provider;
+
+        public ExternalKeyInfoConverter(ConfigurableProvider provider)
+        {
+            this.provider = provider;
+        }
+
+        public PrivateKey generatePrivate(PrivateKeyInfo keyInfo)
+            throws IOException
+        {
+            throw new UnsupportedOperationException("no support for private key");
+        }
+
+        public PublicKey generatePublic(SubjectPublicKeyInfo keyInfo)
+            throws IOException
+        {
+            ExternalValue extKey = ExternalValue.getInstance(keyInfo.parsePublicKey());
+
+            // TODO: maybe implement some sort of cache lookup?
+
+            return new ExternalPublicKey(extKey);
+        }
+    }
+
+    public static class Mappings
+        extends AsymmetricAlgorithmProvider
+    {
+        public Mappings()
+        {
+        }
+
+        public void configure(ConfigurableProvider provider)
+        {
+            provider.addAlgorithm("KeyFactory.EXTERNAL", PREFIX + "$KeyFactory");
+            provider.addAlgorithm("KeyFactory." + BCObjectIdentifiers.external_value, PREFIX + "$KeyFactory");
+            provider.addAlgorithm("KeyFactory.OID." + BCObjectIdentifiers.external_value, PREFIX + "$KeyFactory");
+
+            baseConverter = new ExternalKeyInfoConverter(provider);
+
+            provider.addKeyInfoConverter(BCObjectIdentifiers.external_value, baseConverter);
+        }
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/LMS.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/LMS.java
new file mode 100644
index 0000000..11bbf8e
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/LMS.java
@@ -0,0 +1,30 @@
+package org.bouncycastle.jcajce.provider.asymmetric;
+
+import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.bouncycastle.jcajce.provider.config.ConfigurableProvider;
+import org.bouncycastle.jcajce.provider.util.AsymmetricAlgorithmProvider;
+
+public class LMS
+{
+    private static final String PREFIX = "org.bouncycastle.pqc.jcajce.provider" + ".lms.";
+
+    public static class Mappings
+        extends AsymmetricAlgorithmProvider
+    {
+        public Mappings()
+        {
+        }
+
+        public void configure(ConfigurableProvider provider)
+        {
+            provider.addAlgorithm("KeyFactory.LMS", PREFIX + "LMSKeyFactorySpi");
+            provider.addAlgorithm("Alg.Alias.KeyFactory." + PKCSObjectIdentifiers.id_alg_hss_lms_hashsig, "LMS");
+
+            provider.addAlgorithm("KeyPairGenerator.LMS", PREFIX + "LMSKeyPairGeneratorSpi");
+            provider.addAlgorithm("Alg.Alias.KeyPairGenerator." + PKCSObjectIdentifiers.id_alg_hss_lms_hashsig, "LMS");
+
+            provider.addAlgorithm("Signature.LMS", PREFIX + "LMSSignatureSpi$generic");
+            provider.addAlgorithm("Alg.Alias.Signature." + PKCSObjectIdentifiers.id_alg_hss_lms_hashsig, "LMS");
+        }
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/RSA.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/RSA.java
index b9afff8..6db61ae 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/RSA.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/RSA.java
@@ -10,6 +10,7 @@
 // Android-removed: Unsupported algorithms
 // import org.bouncycastle.asn1.teletrust.TeleTrusTObjectIdentifiers;
 import org.bouncycastle.asn1.x509.X509ObjectIdentifiers;
+// import org.bouncycastle.internal.asn1.cms.CMSObjectIdentifiers;
 import org.bouncycastle.jcajce.provider.asymmetric.rsa.KeyFactorySpi;
 import org.bouncycastle.jcajce.provider.config.ConfigurableProvider;
 import org.bouncycastle.jcajce.provider.util.AsymmetricAlgorithmProvider;
@@ -23,7 +24,7 @@
 
     static
     {
-        generalRsaAttributes.put("SupportedKeyClasses", "javax.crypto.interfaces.RSAPublicKey|javax.crypto.interfaces.RSAPrivateKey");
+        generalRsaAttributes.put("SupportedKeyClasses", "java.security.interfaces.RSAPublicKey|java.security.interfaces.RSAPrivateKey");
         generalRsaAttributes.put("SupportedKeyFormats", "PKCS#8|X.509");
     }
 
@@ -73,8 +74,8 @@
             // BEGIN Android-removed: Unsupported algorithms
             /*
             provider.addAlgorithm("Cipher.RSA/PKCS1", PREFIX + "CipherSpi$PKCS1v1_5Padding");
-            provider.addAlgorithm("Cipher", PKCSObjectIdentifiers.rsaEncryption, PREFIX + "CipherSpi$PKCS1v1_5Padding");
-            provider.addAlgorithm("Cipher", X509ObjectIdentifiers.id_ea_rsa, PREFIX + "CipherSpi$PKCS1v1_5Padding");
+            provider.addAlgorithm("Cipher", PKCSObjectIdentifiers.rsaEncryption, PREFIX + "CipherSpi$PKCS1v1_5Padding", generalRsaAttributes);
+            provider.addAlgorithm("Cipher", X509ObjectIdentifiers.id_ea_rsa, PREFIX + "CipherSpi$PKCS1v1_5Padding", generalRsaAttributes);
             provider.addAlgorithm("Cipher.RSA/1", PREFIX + "CipherSpi$PKCS1v1_5Padding_PrivateOnly");
             provider.addAlgorithm("Cipher.RSA/2", PREFIX + "CipherSpi$PKCS1v1_5Padding_PublicOnly");
             provider.addAlgorithm("Cipher.RSA/OAEP", PREFIX + "CipherSpi$OAEPPadding");
@@ -115,12 +116,12 @@
             registerOidAlgorithmParameters(provider, PKCSObjectIdentifiers.id_RSAES_OAEP, "OAEP");
             registerOidAlgorithmParameters(provider, PKCSObjectIdentifiers.id_RSASSA_PSS, "PSS");
 
-            provider.addAlgorithm("Signature.RSASSA-PSS", PREFIX + "PSSSignatureSpi$PSSwithRSA");
-            provider.addAlgorithm("Signature." + PKCSObjectIdentifiers.id_RSASSA_PSS, PREFIX + "PSSSignatureSpi$PSSwithRSA");
-            provider.addAlgorithm("Signature.OID." + PKCSObjectIdentifiers.id_RSASSA_PSS, PREFIX + "PSSSignatureSpi$PSSwithRSA");
+            provider.addAlgorithm("Signature.RSASSA-PSS", PREFIX + "PSSSignatureSpi$PSSwithRSA", generalRsaAttributes);
+            provider.addAlgorithm("Alg.Alias.Signature." + PKCSObjectIdentifiers.id_RSASSA_PSS, "RSASSA-PSS");
+            provider.addAlgorithm("Alg.Alias.Signature.OID." + PKCSObjectIdentifiers.id_RSASSA_PSS, "RSASSA-PSS");
 
-            provider.addAlgorithm("Signature.RSA", PREFIX + "DigestSignatureSpi$noneRSA");
-            provider.addAlgorithm("Signature.RAWRSASSA-PSS", PREFIX + "PSSSignatureSpi$nonePSS");
+            provider.addAlgorithm("Signature.RSA", PREFIX + "DigestSignatureSpi$noneRSA", generalRsaAttributes);
+            provider.addAlgorithm("Signature.RAWRSASSA-PSS", PREFIX + "PSSSignatureSpi$nonePSS", generalRsaAttributes);
 
             provider.addAlgorithm("Alg.Alias.Signature.RAWRSA", "RSA");
             provider.addAlgorithm("Alg.Alias.Signature.NONEWITHRSA", "RSA");
@@ -130,17 +131,43 @@
             provider.addAlgorithm("Alg.Alias.Signature.NONEWITHRSAANDMGF1", "RAWRSASSA-PSS");
             provider.addAlgorithm("Alg.Alias.Signature.RSAPSS", "RSASSA-PSS");
 
-            addPSSSignature(provider, "SHA224", PREFIX + "PSSSignatureSpi$SHA224withRSA");
-            addPSSSignature(provider, "SHA256", PREFIX + "PSSSignatureSpi$SHA256withRSA");
-            addPSSSignature(provider, "SHA384", PREFIX + "PSSSignatureSpi$SHA384withRSA");
-            addPSSSignature(provider, "SHA512", PREFIX + "PSSSignatureSpi$SHA512withRSA");
-            addPSSSignature(provider, "SHA512(224)", PREFIX + "PSSSignatureSpi$SHA512_224withRSA");
-            addPSSSignature(provider, "SHA512(256)", PREFIX + "PSSSignatureSpi$SHA512_256withRSA");
+            addPSSSignature(provider, "SHA224", "MGF1", PREFIX + "PSSSignatureSpi$SHA224withRSA");
+            addPSSSignature(provider, "SHA256", "MGF1", PREFIX + "PSSSignatureSpi$SHA256withRSA");
+            addPSSSignature(provider, "SHA384", "MGF1", PREFIX + "PSSSignatureSpi$SHA384withRSA");
+            addPSSSignature(provider, "SHA512", "MGF1", PREFIX + "PSSSignatureSpi$SHA512withRSA");
+            addPSSSignature(provider, "SHA512(224)", "MGF1", PREFIX + "PSSSignatureSpi$SHA512_224withRSA");
+            addPSSSignature(provider, "SHA512(256)", "MGF1", PREFIX + "PSSSignatureSpi$SHA512_256withRSA");
 
-            addPSSSignature(provider, "SHA3-224", PREFIX + "PSSSignatureSpi$SHA3_224withRSA");
-            addPSSSignature(provider, "SHA3-256", PREFIX + "PSSSignatureSpi$SHA3_256withRSA");
-            addPSSSignature(provider, "SHA3-384", PREFIX + "PSSSignatureSpi$SHA3_384withRSA");
-            addPSSSignature(provider, "SHA3-512", PREFIX + "PSSSignatureSpi$SHA3_512withRSA");
+            addPSSSignature(provider, "SHA3-224", "MGF1", PREFIX + "PSSSignatureSpi$SHA3_224withRSA");
+            addPSSSignature(provider, "SHA3-256", "MGF1", PREFIX + "PSSSignatureSpi$SHA3_256withRSA");
+            addPSSSignature(provider, "SHA3-384", "MGF1", PREFIX + "PSSSignatureSpi$SHA3_384withRSA");
+            addPSSSignature(provider, "SHA3-512", "MGF1", PREFIX + "PSSSignatureSpi$SHA3_512withRSA");
+            addPSSSignature(provider, "SHAKE128", PREFIX + "PSSSignatureSpi$SHAKE128WithRSAPSS", CMSObjectIdentifiers.id_RSASSA_PSS_SHAKE128);
+            addPSSSignature(provider, "SHAKE256", PREFIX + "PSSSignatureSpi$SHAKE256WithRSAPSS", CMSObjectIdentifiers.id_RSASSA_PSS_SHAKE256);
+
+            addPSSSignature(provider, "SHA224", "SHAKE128", PREFIX + "PSSSignatureSpi$SHA224withRSAandSHAKE128");
+            addPSSSignature(provider, "SHA256", "SHAKE128", PREFIX + "PSSSignatureSpi$SHA256withRSAandSHAKE128");
+            addPSSSignature(provider, "SHA384", "SHAKE128", PREFIX + "PSSSignatureSpi$SHA384withRSAandSHAKE128");
+            addPSSSignature(provider, "SHA512", "SHAKE128", PREFIX + "PSSSignatureSpi$SHA512withRSAandSHAKE128");
+            addPSSSignature(provider, "SHA512(224)", "SHAKE128", PREFIX + "PSSSignatureSpi$SHA512_224withRSAandSHAKE128");
+            addPSSSignature(provider, "SHA512(256)", "SHAKE128", PREFIX + "PSSSignatureSpi$SHA512_256withRSAandSHAKE128");
+
+            addPSSSignature(provider, "SHA224", "SHAKE256", PREFIX + "PSSSignatureSpi$SHA224withRSAandSHAKE256");
+            addPSSSignature(provider, "SHA256", "SHAKE256", PREFIX + "PSSSignatureSpi$SHA256withRSAandSHAKE256");
+            addPSSSignature(provider, "SHA384", "SHAKE256", PREFIX + "PSSSignatureSpi$SHA384withRSAandSHAKE256");
+            addPSSSignature(provider, "SHA512", "SHAKE256", PREFIX + "PSSSignatureSpi$SHA512withRSAandSHAKE256");
+            addPSSSignature(provider, "SHA512(224)", "SHAKE256", PREFIX + "PSSSignatureSpi$SHA512_224withRSAandSHAKE256");
+            addPSSSignature(provider, "SHA512(256)", "SHAKE256", PREFIX + "PSSSignatureSpi$SHA512_256withRSAandSHAKE256");
+
+            addPSSSignature(provider, "SHA3-224", "SHAKE128", PREFIX + "PSSSignatureSpi$SHA3_224withRSAandSHAKE128");
+            addPSSSignature(provider, "SHA3-256", "SHAKE128", PREFIX + "PSSSignatureSpi$SHA3_256withRSAandSHAKE128");
+            addPSSSignature(provider, "SHA3-384", "SHAKE128", PREFIX + "PSSSignatureSpi$SHA3_384withRSAandSHAKE128");
+            addPSSSignature(provider, "SHA3-512", "SHAKE128", PREFIX + "PSSSignatureSpi$SHA3_512withRSAandSHAKE128");
+
+            addPSSSignature(provider, "SHA3-224", "SHAKE256", PREFIX + "PSSSignatureSpi$SHA3_224withRSAandSHAKE256");
+            addPSSSignature(provider, "SHA3-256", "SHAKE256", PREFIX + "PSSSignatureSpi$SHA3_256withRSAandSHAKE256");
+            addPSSSignature(provider, "SHA3-384", "SHAKE256", PREFIX + "PSSSignatureSpi$SHA3_384withRSAandSHAKE256");
+            addPSSSignature(provider, "SHA3-512", "SHAKE256", PREFIX + "PSSSignatureSpi$SHA3_512withRSAandSHAKE256");
 
             if (provider.hasAlgorithm("MessageDigest", "MD2"))
             {
@@ -163,7 +190,9 @@
                 provider.addAlgorithm("Alg.Alias.AlgorithmParameters.SHA1withRSA/PSS", "PSS");
                 provider.addAlgorithm("Alg.Alias.AlgorithmParameters.SHA1WITHRSAANDMGF1", "PSS");
 
-                addPSSSignature(provider, "SHA1", PREFIX + "PSSSignatureSpi$SHA1withRSA");
+                addPSSSignature(provider, "SHA1", "MGF1", PREFIX + "PSSSignatureSpi$SHA1withRSA");
+                addPSSSignature(provider, "SHA1", "SHAKE128", PREFIX + "PSSSignatureSpi$SHA1withRSAandSHAKE128");
+                addPSSSignature(provider, "SHA1", "SHAKE256", PREFIX + "PSSSignatureSpi$SHA1withRSAandSHAKE256");
                 addDigestSignature(provider, "SHA1", PREFIX + "DigestSignatureSpi$SHA1", PKCSObjectIdentifiers.sha1WithRSAEncryption);
                 addISO9796Signature(provider, "SHA1", PREFIX + "ISOSignatureSpi$SHA1WithRSAEncryption");
 
@@ -263,6 +292,7 @@
                 provider.addAlgorithm("Alg.Alias.Signature." + oid, mainName);
                 provider.addAlgorithm("Alg.Alias.Signature.OID." + oid, mainName);
             }
+            provider.addAttributes("Signature." + mainName, generalRsaAttributes);
         }
 
         private void addISO9796Signature(
@@ -273,12 +303,37 @@
             provider.addAlgorithm("Alg.Alias.Signature." + digest + "withRSA/ISO9796-2", digest + "WITHRSA/ISO9796-2");
             provider.addAlgorithm("Alg.Alias.Signature." + digest + "WithRSA/ISO9796-2", digest + "WITHRSA/ISO9796-2");
             provider.addAlgorithm("Signature." + digest + "WITHRSA/ISO9796-2", className);
+            provider.addAttributes("Signature." + digest + "WITHRSA/ISO9796-2", generalRsaAttributes);
+        }
+
+        private void addPSSSignature(
+            ConfigurableProvider provider,
+            String digest, String mgf,
+            String className)
+        {
+            String stem = "WITHRSAAND" + mgf;
+            if (mgf.equals("MGF1"))
+            {
+                provider.addAlgorithm("Alg.Alias.Signature." + digest + "withRSA/PSS", digest + stem);
+                provider.addAlgorithm("Alg.Alias.Signature." + digest + "WithRSA/PSS", digest + stem);
+                provider.addAlgorithm("Alg.Alias.Signature." + digest + "WITHRSA/PSS", digest + stem);
+                provider.addAlgorithm("Alg.Alias.Signature." + digest + "withRSASSA-PSS", digest + stem);
+                provider.addAlgorithm("Alg.Alias.Signature." + digest + "WithRSASSA-PSS", digest + stem);
+                provider.addAlgorithm("Alg.Alias.Signature." + digest + "WITHRSASSA-PSS", digest + stem);
+            }
+
+            provider.addAlgorithm("Alg.Alias.Signature." + digest + "withRSAand" + mgf, digest + stem);
+            provider.addAlgorithm("Alg.Alias.Signature." + digest + "WithRSAAnd" + mgf, digest + stem);
+
+            provider.addAlgorithm("Signature." + digest + "WITHRSAAND" + mgf, className);
+            provider.addAttributes("Signature." + digest + "WITHRSAAND" + mgf, generalRsaAttributes);
         }
 
         private void addPSSSignature(
             ConfigurableProvider provider,
             String digest,
-            String className)
+            String className,
+            ASN1ObjectIdentifier sigOid)
         {
             provider.addAlgorithm("Alg.Alias.Signature." + digest + "withRSA/PSS", digest + "WITHRSAANDMGF1");
             provider.addAlgorithm("Alg.Alias.Signature." + digest + "WithRSA/PSS", digest + "WITHRSAANDMGF1");
@@ -299,6 +354,7 @@
             provider.addAlgorithm("Alg.Alias.Signature." + digest + "withRSA/X9.31", digest + "WITHRSA/X9.31");
             provider.addAlgorithm("Alg.Alias.Signature." + digest + "WithRSA/X9.31", digest + "WITHRSA/X9.31");
             provider.addAlgorithm("Signature." + digest + "WITHRSA/X9.31", className);
+            provider.addAttributes("Signature." + digest + "WITHRSA/X9.31", generalRsaAttributes);
         }
     }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/dh/KeyAgreementSpi.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/dh/KeyAgreementSpi.java
index 502854d..6a61e76 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/dh/KeyAgreementSpi.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/dh/KeyAgreementSpi.java
@@ -40,6 +40,7 @@
 // import org.bouncycastle.jcajce.spec.DHUParameterSpec;
 // import org.bouncycastle.jcajce.spec.MQVParameterSpec;
 import org.bouncycastle.jcajce.spec.UserKeyingMaterialSpec;
+import org.bouncycastle.util.BigIntegers;
 
 /**
  * Diffie-Hellman key agreement. There's actually a better way of doing this
@@ -115,30 +116,9 @@
         //
         int expectedLength = (p.bitLength() + 7) / 8;
 
-        byte[]    tmp = r.toByteArray();
-
-        if (tmp.length == expectedLength)
-        {
-            return tmp;
-        }
-
-        if (tmp[0] == 0 && tmp.length == expectedLength + 1)
-        {
-            byte[]    rv = new byte[tmp.length - 1];
-            
-            System.arraycopy(tmp, 1, rv, 0, rv.length);
-            return rv;
-        }
-
-        // tmp must be shorter than expectedLength
-        // pad to the left with zeros.
-        byte[]    rv = new byte[expectedLength];
-
-        System.arraycopy(tmp, 0, rv, rv.length - tmp.length, tmp.length);
-
-        return rv;
+        return BigIntegers.asUnsignedByteArray(expectedLength, r);
     }
-    
+
     protected Key engineDoPhase(
         Key     key,
         boolean lastPhase) 
@@ -266,7 +246,7 @@
         return super.engineGenerateSecret(algorithm);
     }
 
-    protected void engineInit(
+    protected void doInitFromKey(
         Key                     key,
         AlgorithmParameterSpec  params,
         SecureRandom            random) 
@@ -385,7 +365,7 @@
         this.result = bigIntToBytes(x);
     }
 
-    protected byte[] calcSecret()
+    protected byte[] doCalcSecret()
     {
         return result;
     }
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/dsa/AlgorithmParameterGeneratorSpi.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/dsa/AlgorithmParameterGeneratorSpi.java
index 1cc7cb3..d3a8ec4 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/dsa/AlgorithmParameterGeneratorSpi.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/dsa/AlgorithmParameterGeneratorSpi.java
@@ -67,7 +67,7 @@
         }
         else
         {
-            pGen = new DSAParametersGenerator(new SHA256Digest());
+            pGen = new DSAParametersGenerator(SHA256Digest.newInstance());
         }
 
         if (random == null)
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/dsa/DSASigner.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/dsa/DSASigner.java
index b50c49e..0571f3d 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/dsa/DSASigner.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/dsa/DSASigner.java
@@ -1,5 +1,5 @@
-package org.bouncycastle.jcajce.provider.asymmetric.dsa;
 
+package org.bouncycastle.jcajce.provider.asymmetric.dsa;
 import java.math.BigInteger;
 import java.security.AlgorithmParameters;
 import java.security.InvalidKeyException;
@@ -9,7 +9,6 @@
 import java.security.SignatureException;
 import java.security.SignatureSpi;
 import java.security.spec.AlgorithmParameterSpec;
-
 import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
 import org.bouncycastle.asn1.x509.X509ObjectIdentifiers;
 import org.bouncycastle.crypto.CipherParameters;
@@ -28,7 +27,6 @@
 // import org.bouncycastle.crypto.util.DigestFactory;
 import org.bouncycastle.crypto.digests.AndroidDigestFactory;
 import org.bouncycastle.util.Arrays;
-
 public class DSASigner
     extends SignatureSpi
     implements PKCSObjectIdentifiers, X509ObjectIdentifiers
@@ -37,7 +35,6 @@
     private DSAExt                  signer;
     private DSAEncoding             encoding = StandardDSAEncoding.INSTANCE;
     private SecureRandom            random;
-
     protected DSASigner(
         Digest digest,
         DSAExt signer)
@@ -45,17 +42,14 @@
         this.digest = digest;
         this.signer = signer;
     }
-
     protected void engineInitVerify(
         PublicKey   publicKey)
         throws InvalidKeyException
     {
         CipherParameters    param = DSAUtil.generatePublicKeyParameter(publicKey);
-
         digest.reset();
         signer.init(false, param);
     }
-
     protected void engineInitSign(
         PrivateKey      privateKey,
         SecureRandom    random)
@@ -64,53 +58,43 @@
         this.random = random;
         engineInitSign(privateKey);
     }
-
     protected void engineInitSign(
         PrivateKey  privateKey)
         throws InvalidKeyException
     {
         CipherParameters    param = DSAUtil.generatePrivateKeyParameter(privateKey);
-
         // Android-added: Check DSA keys when generated
         DSAParameters dsaParam = ((DSAKeyParameters) param).getParameters();
         checkKey(dsaParam);
-
         if (random != null)
         {
             param = new ParametersWithRandom(param, random);
         }
-
         digest.reset();
         signer.init(true, param);
     }
-
     protected void engineUpdate(
         byte    b)
         throws SignatureException
     {
         digest.update(b);
     }
-
     protected void engineUpdate(
         byte[]  b,
         int     off,
-        int     len) 
+        int     len)
         throws SignatureException
     {
         digest.update(b, off, len);
     }
-
     protected byte[] engineSign()
         throws SignatureException
     {
         byte[]  hash = new byte[digest.getDigestSize()];
-
         digest.doFinal(hash, 0);
-
         try
         {
             BigInteger[] sig = signer.generateSignature(hash);
-
             return encoding.encode(signer.getOrder(), sig[0], sig[1]);
         }
         catch (Exception e)
@@ -118,17 +102,13 @@
             throw new SignatureException(e.toString());
         }
     }
-
     protected boolean engineVerify(
-        byte[]  sigBytes) 
+        byte[]  sigBytes)
         throws SignatureException
     {
         byte[]  hash = new byte[digest.getDigestSize()];
-
         digest.doFinal(hash, 0);
-
         BigInteger[] sig;
-
         try
         {
             sig = encoding.decode(signer.getOrder(), sigBytes);
@@ -137,27 +117,22 @@
         {
             throw new SignatureException("error decoding signature bytes.");
         }
-
         return signer.verifySignature(hash, sig[0], sig[1]);
     }
-
     protected AlgorithmParameters engineGetParameters()
     {
         return null;
     }
-
     protected void engineSetParameter(
         AlgorithmParameterSpec params)
     {
         throw new UnsupportedOperationException("engineSetParameter unsupported");
     }
-
     // BEGIN Android-added: Check DSA keys when generated
     protected void checkKey(DSAParameters params) throws InvalidKeyException {
         int valueL = params.getP().bitLength();
         int valueN = params.getQ().bitLength();
         int digestSize = digest.getDigestSize();
-
         // The checks are consistent with DSAParametersGenerator's init method.
         if ((valueL < 1024 || valueL > 3072) || valueL % 1024 != 0) {
             throw new InvalidKeyException("valueL values must be between 1024 and 3072 and a multiple of 1024");
@@ -172,7 +147,6 @@
             throw new InvalidKeyException("Key is too strong for this signature algorithm");
         }
     }
-
     // END Android-added: Check DSA keys when generated
     /**
      * @deprecated replaced with #engineSetParameter(java.security.spec.AlgorithmParameterSpec)
@@ -183,7 +157,6 @@
     {
         throw new UnsupportedOperationException("engineSetParameter unsupported");
     }
-
     /**
      * @deprecated
      */
@@ -192,7 +165,6 @@
     {
         throw new UnsupportedOperationException("engineGetParameter unsupported");
     }
-
     static public class stdDSA
         extends DSASigner
     {
@@ -203,7 +175,6 @@
             super(AndroidDigestFactory.getSHA1(), new org.bouncycastle.crypto.signers.DSASigner());
         }
     }
-
     // BEGIN Android-removed: Unsupported algorithm
     /*
     static public class detDSA
@@ -214,6 +185,14 @@
             super(DigestFactory.createSHA1(), new org.bouncycastle.crypto.signers.DSASigner(new HMacDSAKCalculator(DigestFactory.createSHA1())));
         }
     }
+    static public class dsaRMD160
+        extends DSASigner
+    {
+        public dsaRMD160()
+        {
+            super(new RIPEMD160Digest(), new org.bouncycastle.crypto.signers.DSASigner());
+        }
+    }
     */
     // END Android-removed: Unsupported algorithm
 
@@ -227,7 +206,6 @@
             super(AndroidDigestFactory.getSHA224(), new org.bouncycastle.crypto.signers.DSASigner());
         }
     }
-
     // BEGIN Android-removed: Unsupported algorithm
     /*
     static public class detDSA224
@@ -240,7 +218,6 @@
     }
     */
     // END Android-removed: Unsupported algorithm
-
     static public class dsa256
         extends DSASigner
     {
@@ -251,7 +228,6 @@
             super(AndroidDigestFactory.getSHA256(), new org.bouncycastle.crypto.signers.DSASigner());
         }
     }
-
     // BEGIN Android-removed: Unsupported algorithms
     /*
     static public class detDSA256
@@ -262,7 +238,6 @@
             super(DigestFactory.createSHA256(), new org.bouncycastle.crypto.signers.DSASigner(new HMacDSAKCalculator(DigestFactory.createSHA256())));
         }
     }
-
     static public class dsa384
         extends DSASigner
     {
@@ -271,7 +246,6 @@
             super(DigestFactory.createSHA384(), new org.bouncycastle.crypto.signers.DSASigner());
         }
     }
-
     static public class detDSA384
         extends DSASigner
     {
@@ -280,7 +254,6 @@
             super(DigestFactory.createSHA384(), new org.bouncycastle.crypto.signers.DSASigner(new HMacDSAKCalculator(DigestFactory.createSHA384())));
         }
     }
-
     static public class dsa512
         extends DSASigner
     {
@@ -289,7 +262,6 @@
             super(DigestFactory.createSHA512(), new org.bouncycastle.crypto.signers.DSASigner());
         }
     }
-
     static public class detDSA512
         extends DSASigner
     {
@@ -298,7 +270,6 @@
             super(DigestFactory.createSHA512(), new org.bouncycastle.crypto.signers.DSASigner(new HMacDSAKCalculator(DigestFactory.createSHA512())));
         }
     }
-
     static public class dsaSha3_224
         extends DSASigner
     {
@@ -307,7 +278,6 @@
             super(DigestFactory.createSHA3_224(), new org.bouncycastle.crypto.signers.DSASigner());
         }
     }
-
     static public class detDSASha3_224
         extends DSASigner
     {
@@ -316,7 +286,6 @@
             super(DigestFactory.createSHA3_224(), new org.bouncycastle.crypto.signers.DSASigner(new HMacDSAKCalculator(DigestFactory.createSHA3_224())));
         }
     }
-
     static public class dsaSha3_256
         extends DSASigner
     {
@@ -325,7 +294,6 @@
             super(DigestFactory.createSHA3_256(), new org.bouncycastle.crypto.signers.DSASigner());
         }
     }
-
     static public class detDSASha3_256
         extends DSASigner
     {
@@ -334,7 +302,6 @@
             super(DigestFactory.createSHA3_256(), new org.bouncycastle.crypto.signers.DSASigner(new HMacDSAKCalculator(DigestFactory.createSHA3_256())));
         }
     }
-
     static public class dsaSha3_384
         extends DSASigner
     {
@@ -343,7 +310,6 @@
             super(DigestFactory.createSHA3_384(), new org.bouncycastle.crypto.signers.DSASigner());
         }
     }
-
     static public class detDSASha3_384
         extends DSASigner
     {
@@ -352,7 +318,6 @@
             super(DigestFactory.createSHA3_384(), new org.bouncycastle.crypto.signers.DSASigner(new HMacDSAKCalculator(DigestFactory.createSHA3_384())));
         }
     }
-
     static public class dsaSha3_512
         extends DSASigner
     {
@@ -361,7 +326,6 @@
             super(DigestFactory.createSHA3_512(), new org.bouncycastle.crypto.signers.DSASigner());
         }
     }
-
     static public class detDSASha3_512
         extends DSASigner
     {
@@ -372,7 +336,6 @@
     }
     */
     // END Android-removed: Unsupported algorithms
-
     static public class noneDSA
         extends DSASigner
     {
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/dsa/KeyPairGeneratorSpi.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/dsa/KeyPairGeneratorSpi.java
index a7ed3e0..0b640f5 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/dsa/KeyPairGeneratorSpi.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/dsa/KeyPairGeneratorSpi.java
@@ -148,7 +148,7 @@
                         else if (strength > 1024)
                         {
                             dsaParams = new DSAParameterGenerationParameters(strength, 256, certainty, random);
-                            pGen = new DSAParametersGenerator(new SHA256Digest());
+                            pGen = new DSAParametersGenerator(SHA256Digest.newInstance());
                             pGen.init(dsaParams);
                         }
                         else
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ec/AlgorithmParametersSpi.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ec/AlgorithmParametersSpi.java
index 7d84ecb..7f55c78 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ec/AlgorithmParametersSpi.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ec/AlgorithmParametersSpi.java
@@ -15,6 +15,7 @@
 import org.bouncycastle.asn1.x9.X9ECPoint;
 import org.bouncycastle.jcajce.provider.asymmetric.util.EC5Util;
 import org.bouncycastle.jcajce.provider.asymmetric.util.ECUtil;
+import org.bouncycastle.jcajce.provider.config.ProviderConfiguration;
 import org.bouncycastle.jce.provider.BouncyCastleProvider;
 import org.bouncycastle.jce.spec.ECNamedCurveSpec;
 import org.bouncycastle.math.ec.ECCurve;
@@ -37,12 +38,14 @@
         if (algorithmParameterSpec instanceof ECGenParameterSpec)
         {
             ECGenParameterSpec ecGenParameterSpec = (ECGenParameterSpec)algorithmParameterSpec;
-            X9ECParameters params = ECUtils.getDomainParametersFromGenSpec(ecGenParameterSpec);
+            ProviderConfiguration configuration = BouncyCastleProvider.CONFIGURATION;
 
-            if (params == null)
+            X9ECParameters params = ECUtils.getDomainParametersFromGenSpec(ecGenParameterSpec, configuration);
+            if (null == params)
             {
                 throw new InvalidParameterSpecException("EC curve name not recognized: " + ecGenParameterSpec.getName());
             }
+
             curveName = ecGenParameterSpec.getName();
             ECParameterSpec baseSpec = EC5Util.convertToSpec(params);
             ecParameterSpec = new ECNamedCurveSpec(curveName,
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ec/BCECPrivateKey.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ec/BCECPrivateKey.java
index e1d737a..444b215 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ec/BCECPrivateKey.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ec/BCECPrivateKey.java
@@ -10,18 +10,20 @@
 import java.security.spec.EllipticCurve;
 import java.util.Enumeration;
 
+import org.bouncycastle.asn1.ASN1BitString;
 import org.bouncycastle.asn1.ASN1Encodable;
 import org.bouncycastle.asn1.ASN1Encoding;
 import org.bouncycastle.asn1.ASN1Integer;
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
 import org.bouncycastle.asn1.ASN1Primitive;
-import org.bouncycastle.asn1.DERBitString;
 import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.bouncycastle.asn1.x9.ECNamedCurveTable;
 import org.bouncycastle.asn1.x9.X962Parameters;
 import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
 import org.bouncycastle.crypto.params.ECDomainParameters;
+import org.bouncycastle.crypto.params.ECNamedDomainParameters;
 import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
 import org.bouncycastle.jcajce.provider.asymmetric.util.EC5Util;
 import org.bouncycastle.jcajce.provider.asymmetric.util.ECUtil;
@@ -30,23 +32,29 @@
 import org.bouncycastle.jce.interfaces.ECPointEncoder;
 import org.bouncycastle.jce.interfaces.PKCS12BagAttributeCarrier;
 import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.jce.spec.ECNamedCurveParameterSpec;
 import org.bouncycastle.math.ec.ECCurve;
+import org.bouncycastle.util.Arrays;
 
 public class BCECPrivateKey
     implements ECPrivateKey, org.bouncycastle.jce.interfaces.ECPrivateKey, PKCS12BagAttributeCarrier, ECPointEncoder
 {
     static final long serialVersionUID = 994553197664784084L;
 
-    private String          algorithm = "EC";
-    private boolean         withCompression;
+    private String algorithm = "EC";
+    private boolean withCompression;
 
-    private transient BigInteger              d;
-    private transient ECParameterSpec         ecSpec;
-    private transient ProviderConfiguration   configuration;
-    private transient DERBitString            publicKey;
+    private transient BigInteger d;
+    private transient ECParameterSpec ecSpec;
+    private transient ProviderConfiguration configuration;
+    private transient ASN1BitString publicKey;
+    private transient PrivateKeyInfo privateKeyInfo;
+    private transient byte[] encoding;
 
+    private transient ECPrivateKeyParameters baseKey;
     private transient PKCS12BagAttributeCarrierImpl attrCarrier = new PKCS12BagAttributeCarrierImpl();
 
+
     protected BCECPrivateKey()
     {
     }
@@ -59,6 +67,7 @@
         this.algorithm = key.getAlgorithm();
         this.ecSpec = key.getParams();
         this.configuration = configuration;
+        this.baseKey = convertToBaseKey(this);
     }
 
     public BCECPrivateKey(
@@ -84,6 +93,7 @@
         }
 
         this.configuration = configuration;
+        this.baseKey = convertToBaseKey(this);
     }
 
 
@@ -96,6 +106,7 @@
         this.d = spec.getS();
         this.ecSpec = spec.getParams();
         this.configuration = configuration;
+        this.baseKey = convertToBaseKey(this);
     }
 
     public BCECPrivateKey(
@@ -109,6 +120,7 @@
         this.attrCarrier = key.attrCarrier;
         this.publicKey = key.publicKey;
         this.configuration = key.configuration;
+        this.baseKey = key.baseKey;
     }
 
     public BCECPrivateKey(
@@ -121,6 +133,7 @@
         this.algorithm = algorithm;
         this.d = params.getD();
         this.configuration = configuration;
+        this.baseKey = params;
 
         if (spec == null)
         {
@@ -151,6 +164,7 @@
         this.algorithm = algorithm;
         this.d = params.getD();
         this.configuration = configuration;
+        this.baseKey = params;
 
         if (spec == null)
         {
@@ -189,10 +203,11 @@
         this.d = params.getD();
         this.ecSpec = null;
         this.configuration = configuration;
+        this.baseKey = params;
     }
 
     BCECPrivateKey(
-        String         algorithm,
+        String algorithm,
         PrivateKeyInfo info,
         ProviderConfiguration configuration)
         throws IOException
@@ -213,7 +228,7 @@
         ASN1Encodable privKey = info.parsePrivateKey();
         if (privKey instanceof ASN1Integer)
         {
-            ASN1Integer          derD = ASN1Integer.getInstance(privKey);
+            ASN1Integer derD = ASN1Integer.getInstance(privKey);
 
             this.d = derD.getValue();
         }
@@ -224,6 +239,7 @@
             this.d = ec.getKey();
             this.publicKey = ec.getPublicKey();
         }
+        this.baseKey = convertToBaseKey(this);
     }
 
     public String getAlgorithm()
@@ -249,40 +265,71 @@
      */
     public byte[] getEncoded()
     {
-        X962Parameters  params = ECUtils.getDomainParametersFromName(ecSpec, withCompression);
+        if (encoding == null)
+        {
+            PrivateKeyInfo info = getPrivateKeyInfo();
 
-        int orderBitLength;
-        if (ecSpec == null)
-        {
-            orderBitLength = ECUtil.getOrderBitLength(configuration, null, this.getS());
-        }
-        else
-        {
-            orderBitLength = ECUtil.getOrderBitLength(configuration, ecSpec.getOrder(), this.getS());
-        }
-        
-        PrivateKeyInfo          info;
-        org.bouncycastle.asn1.sec.ECPrivateKey            keyStructure;
+            if (info == null)
+            {
+                return null;
+            }
 
-        if (publicKey != null)
-        {
-            keyStructure = new org.bouncycastle.asn1.sec.ECPrivateKey(orderBitLength, this.getS(), publicKey, params);
-        }
-        else
-        {
-            keyStructure = new org.bouncycastle.asn1.sec.ECPrivateKey(orderBitLength, this.getS(), params);
+            try
+            {
+                encoding = info.getEncoded(ASN1Encoding.DER);
+            }
+            catch (IOException e)
+            {
+                return null;
+            }
         }
 
-        try
-        {
-            info = new PrivateKeyInfo(new AlgorithmIdentifier(X9ObjectIdentifiers.id_ecPublicKey, params), keyStructure);
+        return Arrays.clone(encoding);
+    }
 
-            return info.getEncoded(ASN1Encoding.DER);
-        }
-        catch (IOException e)
+    private PrivateKeyInfo getPrivateKeyInfo()
+    {
+        if (privateKeyInfo == null)
         {
-            return null;
+            X962Parameters params = ECUtils.getDomainParametersFromName(ecSpec, withCompression);
+
+            int orderBitLength;
+            if (ecSpec == null)
+            {
+                orderBitLength = ECUtil.getOrderBitLength(configuration, null, this.getS());
+            }
+            else
+            {
+                orderBitLength = ECUtil.getOrderBitLength(configuration, ecSpec.getOrder(), this.getS());
+            }
+
+            org.bouncycastle.asn1.sec.ECPrivateKey keyStructure;
+
+            if (publicKey != null)
+            {
+                keyStructure = new org.bouncycastle.asn1.sec.ECPrivateKey(orderBitLength, this.getS(), publicKey, params);
+            }
+            else
+            {
+                keyStructure = new org.bouncycastle.asn1.sec.ECPrivateKey(orderBitLength, this.getS(), params);
+            }
+
+            try
+            {
+                privateKeyInfo = new PrivateKeyInfo(new AlgorithmIdentifier(X9ObjectIdentifiers.id_ecPublicKey, params), keyStructure);
+            }
+            catch (IOException e)
+            {
+                return null;
+            }
         }
+
+        return privateKeyInfo;
+    }
+
+    public ECPrivateKeyParameters engineGetKeyParameters()
+    {
+        return baseKey;
     }
 
     public ECParameterSpec getParams()
@@ -296,7 +343,6 @@
         {
             return null;
         }
-        
         return EC5Util.convertSpec(ecSpec);
     }
 
@@ -319,10 +365,10 @@
     {
         return d;
     }
-    
+
     public void setBagAttribute(
         ASN1ObjectIdentifier oid,
-        ASN1Encodable        attribute)
+        ASN1Encodable attribute)
     {
         attrCarrier.setBagAttribute(oid, attribute);
     }
@@ -340,19 +386,37 @@
 
     public void setPointFormat(String style)
     {
-       withCompression = !("UNCOMPRESSED".equalsIgnoreCase(style));
+        withCompression = !("UNCOMPRESSED".equalsIgnoreCase(style));
     }
 
     public boolean equals(Object o)
     {
-        if (!(o instanceof BCECPrivateKey))
+        if (o instanceof ECPrivateKey)
         {
-            return false;
+            ECPrivateKey other = (ECPrivateKey)o;
+
+            PrivateKeyInfo info = this.getPrivateKeyInfo();
+            PrivateKeyInfo otherInfo = (other instanceof BCECPrivateKey) ? ((BCECPrivateKey)other).getPrivateKeyInfo() : PrivateKeyInfo.getInstance(other.getEncoded());
+
+            if (info == null || otherInfo == null)
+            {
+                return false;
+            }
+
+            try
+            {
+                boolean algEquals = Arrays.constantTimeAreEqual(info.getPrivateKeyAlgorithm().getEncoded(), otherInfo.getPrivateKeyAlgorithm().getEncoded());
+                boolean keyEquals = Arrays.constantTimeAreEqual(this.getS().toByteArray(), other.getS().toByteArray());
+
+                return algEquals & keyEquals;
+            }
+            catch (IOException e)
+            {
+                return false;
+            }
         }
 
-        BCECPrivateKey other = (BCECPrivateKey)o;
-
-        return getD().equals(other.getD()) && (engineGetSpec().equals(other.engineGetSpec()));
+        return false;
     }
 
     public int hashCode()
@@ -365,7 +429,7 @@
         return ECUtil.privateKeyToString("EC", d, engineGetSpec());
     }
 
-    private DERBitString getPublicKeyDetails(BCECPublicKey pub)
+    private ASN1BitString getPublicKeyDetails(BCECPublicKey pub)
     {
         try
         {
@@ -402,4 +466,31 @@
 
         out.writeObject(this.getEncoded());
     }
+
+    private static ECPrivateKeyParameters convertToBaseKey(BCECPrivateKey key)
+    {
+        org.bouncycastle.jce.interfaces.ECPrivateKey k = (org.bouncycastle.jce.interfaces.ECPrivateKey)key;
+        org.bouncycastle.jce.spec.ECParameterSpec s = k.getParameters();
+
+        if (s == null)
+        {
+            s = BouncyCastleProvider.CONFIGURATION.getEcImplicitlyCa();
+        }
+
+        if (k.getParameters() instanceof ECNamedCurveParameterSpec)
+        {
+            String name = ((ECNamedCurveParameterSpec)k.getParameters()).getName();
+            if (name != null)
+            {
+                return new ECPrivateKeyParameters(
+                    k.getD(),
+                    new ECNamedDomainParameters(ECNamedCurveTable.getOID(name),
+                        s.getCurve(), s.getG(), s.getN(), s.getH(), s.getSeed()));
+            }
+        }
+
+        return new ECPrivateKeyParameters(
+                k.getD(),
+                new ECDomainParameters(s.getCurve(), s.getG(), s.getN(), s.getH(), s.getSeed()));
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ec/BCECPublicKey.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ec/BCECPublicKey.java
index cad65c4..bb66f71 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ec/BCECPublicKey.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ec/BCECPublicKey.java
@@ -9,9 +9,9 @@
 import java.security.spec.ECPublicKeySpec;
 import java.security.spec.EllipticCurve;
 
+import org.bouncycastle.asn1.ASN1BitString;
 import org.bouncycastle.asn1.ASN1OctetString;
 import org.bouncycastle.asn1.ASN1Primitive;
-import org.bouncycastle.asn1.DERBitString;
 import org.bouncycastle.asn1.DEROctetString;
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
@@ -28,6 +28,7 @@
 import org.bouncycastle.jce.interfaces.ECPointEncoder;
 import org.bouncycastle.jce.provider.BouncyCastleProvider;
 import org.bouncycastle.math.ec.ECCurve;
+import org.bouncycastle.util.Arrays;
 import org.bouncycastle.util.Properties;
 
 public class BCECPublicKey
@@ -41,6 +42,8 @@
     private transient ECPublicKeyParameters   ecPublicKey;
     private transient ECParameterSpec         ecSpec;
     private transient ProviderConfiguration   configuration;
+    private transient byte[]                  encoding;
+    private transient boolean                 oldPcSet;
 
     public BCECPublicKey(
         String algorithm,
@@ -193,7 +196,7 @@
         ECCurve curve = EC5Util.getCurve(configuration, params);
         ecSpec = EC5Util.convertToSpec(params, curve);
 
-        DERBitString    bits = info.getPublicKeyData();
+        ASN1BitString   bits = info.getPublicKeyData();
         byte[]          data = bits.getBytes();
         ASN1OctetString key = new DEROctetString(data);
 
@@ -235,16 +238,23 @@
 
     public byte[] getEncoded()
     {
-        boolean compress = withCompression || Properties.isOverrideSet("org.bouncycastle.ec.enable_pc");
+        boolean pcSet = Properties.isOverrideSet("org.bouncycastle.ec.enable_pc");
+        if (encoding == null || oldPcSet != pcSet)
+        {
+            boolean compress = withCompression || pcSet;
 
-        AlgorithmIdentifier algId = new AlgorithmIdentifier(
-            X9ObjectIdentifiers.id_ecPublicKey,
-            ECUtils.getDomainParametersFromName(ecSpec, compress));
+            AlgorithmIdentifier algId = new AlgorithmIdentifier(
+                X9ObjectIdentifiers.id_ecPublicKey,
+                ECUtils.getDomainParametersFromName(ecSpec, compress));
 
-        byte[] pubKeyOctets = ecPublicKey.getQ().getEncoded(compress);
+            byte[] pubKeyOctets = ecPublicKey.getQ().getEncoded(compress);
 
-        // stored curve is null if ImplicitlyCa
-        return KeyUtil.getEncodedSubjectPublicKeyInfo(algId, pubKeyOctets);
+            // stored curve is null if ImplicitlyCa
+            encoding = KeyUtil.getEncodedSubjectPublicKeyInfo(algId, pubKeyOctets);
+            oldPcSet = pcSet;
+        }
+
+        return Arrays.clone(encoding);
     }
 
     public ECParameterSpec getParams()
@@ -302,18 +312,26 @@
     public void setPointFormat(String style)
     {
        withCompression = !("UNCOMPRESSED".equalsIgnoreCase(style));
+       encoding = null;
     }
 
     public boolean equals(Object o)
     {
-        if (!(o instanceof BCECPublicKey))
+        if (o instanceof BCECPublicKey)
         {
-            return false;
+            BCECPublicKey other = (BCECPublicKey)o;
+
+            return ecPublicKey.getQ().equals(other.ecPublicKey.getQ()) && (engineGetSpec().equals(other.engineGetSpec()));
         }
 
-        BCECPublicKey other = (BCECPublicKey)o;
+        if (o instanceof ECPublicKey)
+        {
+            ECPublicKey other = (ECPublicKey)o;
 
-        return ecPublicKey.getQ().equals(other.ecPublicKey.getQ()) && (engineGetSpec().equals(other.engineGetSpec()));
+            return Arrays.areEqual(getEncoded(), other.getEncoded());
+        }
+
+        return false;
     }
 
     public int hashCode()
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ec/ECUtils.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ec/ECUtils.java
index 4070b1c..ec97fe1 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ec/ECUtils.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ec/ECUtils.java
@@ -2,9 +2,11 @@
 
 import java.math.BigInteger;
 import java.security.InvalidKeyException;
+import java.security.PrivateKey;
 import java.security.PublicKey;
 import java.security.spec.ECGenParameterSpec;
 import java.security.spec.ECParameterSpec;
+import java.util.Map;
 
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
 import org.bouncycastle.asn1.DERNull;
@@ -14,6 +16,7 @@
 import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
 import org.bouncycastle.jcajce.provider.asymmetric.util.EC5Util;
 import org.bouncycastle.jcajce.provider.asymmetric.util.ECUtil;
+import org.bouncycastle.jcajce.provider.config.ProviderConfiguration;
 import org.bouncycastle.jce.spec.ECNamedCurveSpec;
 import org.bouncycastle.math.ec.ECCurve;
 
@@ -26,39 +29,49 @@
         return (key instanceof BCECPublicKey) ? ((BCECPublicKey)key).engineGetKeyParameters() : ECUtil.generatePublicKeyParameter(key);
     }
 
-    static X9ECParameters getDomainParametersFromGenSpec(ECGenParameterSpec genSpec)
+    static AsymmetricKeyParameter generatePrivateKeyParameter(
+            PrivateKey key)
+        throws InvalidKeyException
     {
-        return getDomainParametersFromName(genSpec.getName());
+        return (key instanceof BCECPrivateKey) ? ((BCECPrivateKey)key).engineGetKeyParameters() : ECUtil.generatePrivateKeyParameter(key);
     }
 
-    static X9ECParameters getDomainParametersFromName(String curveName)
+    static X9ECParameters getDomainParametersFromGenSpec(ECGenParameterSpec genSpec, ProviderConfiguration configuration)
     {
-        X9ECParameters domainParameters;
-        try
+        return getDomainParametersFromName(genSpec.getName(), configuration);
+    }
+
+    static X9ECParameters getDomainParametersFromName(String curveName, ProviderConfiguration configuration)
+    {
+        if (null == curveName || curveName.length() < 1)
         {
-            if (curveName.charAt(0) >= '0' && curveName.charAt(0) <= '2')
+            return null;
+        }
+
+        int spacePos = curveName.indexOf(' ');
+        if (spacePos > 0)
+        {
+            curveName = curveName.substring(spacePos + 1);
+        }
+
+        ASN1ObjectIdentifier oid = getOID(curveName);
+        if (null == oid)
+        {
+            return ECUtil.getNamedCurveByName(curveName);
+        }
+
+        X9ECParameters x9 = ECUtil.getNamedCurveByOid(oid);
+        if (null == x9)
+        {
+            if (null != configuration)
             {
-                ASN1ObjectIdentifier oidID = new ASN1ObjectIdentifier(curveName);
-                domainParameters = ECUtil.getNamedCurveByOid(oidID);
-            }
-            else
-            {
-                if (curveName.indexOf(' ') > 0)
-                {
-                    curveName = curveName.substring(curveName.indexOf(' ') + 1);
-                    domainParameters = ECUtil.getNamedCurveByName(curveName);
-                }
-                else
-                {
-                    domainParameters = ECUtil.getNamedCurveByName(curveName);
-                }
+                Map extraCurves = configuration.getAdditionalECParameters();
+
+                x9 = (X9ECParameters)extraCurves.get(oid);
             }
         }
-        catch (IllegalArgumentException ex)
-        {
-            domainParameters = ECUtil.getNamedCurveByName(curveName);
-        }
-        return domainParameters;
+
+        return x9;
     }
 
     static X962Parameters getDomainParametersFromName(ECParameterSpec ecSpec, boolean withCompression)
@@ -94,4 +107,20 @@
 
         return params;
     }
+
+    private static ASN1ObjectIdentifier getOID(String curveName)
+    {
+        char firstChar = curveName.charAt(0);
+        if (firstChar >= '0' && firstChar <= '2')
+        {
+            try
+            {
+                return new ASN1ObjectIdentifier(curveName);
+            }
+            catch (Exception e)
+            {
+            }
+        }
+        return null;
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ec/GMKeyPairGeneratorSpi.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ec/GMKeyPairGeneratorSpi.java
new file mode 100644
index 0000000..e1d509a
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ec/GMKeyPairGeneratorSpi.java
@@ -0,0 +1,249 @@
+package org.bouncycastle.jcajce.provider.asymmetric.ec;
+
+import java.math.BigInteger;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidParameterException;
+import java.security.KeyPair;
+import java.security.SecureRandom;
+import java.security.spec.AlgorithmParameterSpec;
+import java.security.spec.ECGenParameterSpec;
+import java.util.Hashtable;
+
+import org.bouncycastle.asn1.x9.X9ECParameters;
+import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
+import org.bouncycastle.crypto.CryptoServicesRegistrar;
+import org.bouncycastle.crypto.generators.ECKeyPairGenerator;
+import org.bouncycastle.crypto.params.ECDomainParameters;
+import org.bouncycastle.crypto.params.ECKeyGenerationParameters;
+import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
+import org.bouncycastle.crypto.params.ECPublicKeyParameters;
+import org.bouncycastle.jcajce.provider.asymmetric.util.EC5Util;
+import org.bouncycastle.jcajce.provider.asymmetric.util.ECUtil;
+import org.bouncycastle.jcajce.provider.config.ProviderConfiguration;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.jce.spec.ECNamedCurveGenParameterSpec;
+import org.bouncycastle.jce.spec.ECNamedCurveSpec;
+import org.bouncycastle.jce.spec.ECParameterSpec;
+import org.bouncycastle.math.ec.ECCurve;
+import org.bouncycastle.math.ec.ECPoint;
+import org.bouncycastle.util.Integers;
+
+public abstract class GMKeyPairGeneratorSpi
+    extends java.security.KeyPairGenerator
+{
+    public GMKeyPairGeneratorSpi(String algorithmName)
+    {
+        super(algorithmName);
+    }
+
+    public static class BaseSM2
+        extends GMKeyPairGeneratorSpi
+    {
+        ECKeyGenerationParameters   param;
+        ECKeyPairGenerator          engine = new ECKeyPairGenerator();
+        Object                      ecParams = null;
+        int                         strength = 239;
+        SecureRandom                random = CryptoServicesRegistrar.getSecureRandom();
+        boolean                     initialised = false;
+        String                      algorithm;
+        ProviderConfiguration       configuration;
+
+        static private Hashtable    ecParameters;
+
+        static
+        {
+            ecParameters = new Hashtable();
+
+            ecParameters.put(Integers.valueOf(192), new ECNamedCurveGenParameterSpec("prime192v1")); // a.k.a P-192
+            ecParameters.put(Integers.valueOf(239), new ECNamedCurveGenParameterSpec("prime239v1"));
+            ecParameters.put(Integers.valueOf(256), new ECNamedCurveGenParameterSpec("prime256v1")); // a.k.a P-256
+
+            ecParameters.put(Integers.valueOf(224), new ECNamedCurveGenParameterSpec("P-224"));
+            ecParameters.put(Integers.valueOf(384), new ECNamedCurveGenParameterSpec("P-384"));
+            ecParameters.put(Integers.valueOf(521), new ECNamedCurveGenParameterSpec("P-521"));
+        }
+
+        public BaseSM2()
+        {
+            super("EC");
+            this.algorithm = "EC";
+            this.configuration = BouncyCastleProvider.CONFIGURATION;
+        }
+
+        public BaseSM2(
+            String  algorithm,
+            ProviderConfiguration configuration)
+        {
+            super(algorithm);
+            this.algorithm = algorithm;
+            this.configuration = configuration;
+        }
+
+        public void initialize(
+            int             strength,
+            SecureRandom    random)
+        {
+            this.strength = strength;
+            this.random = random;
+
+            ECNamedCurveGenParameterSpec ecParams = (ECNamedCurveGenParameterSpec)ecParameters.get(Integers.valueOf(strength));
+            if (ecParams == null)
+            {
+                throw new InvalidParameterException("unknown key size.");
+            }
+
+            try
+            {
+                initialize(ecParams, random);
+            }
+            catch (InvalidAlgorithmParameterException e)
+            {
+                throw new InvalidParameterException("key size not configurable.");
+            }
+        }
+
+        public void initialize(
+            AlgorithmParameterSpec  params,
+            SecureRandom            random)
+            throws InvalidAlgorithmParameterException
+        {
+            if (params == null)
+            {
+                ECParameterSpec implicitCA = configuration.getEcImplicitlyCa();
+                if (implicitCA == null)
+                {
+                    throw new InvalidAlgorithmParameterException("null parameter passed but no implicitCA set");
+                }
+
+                this.ecParams = null;
+                this.param = createKeyGenParamsBC(implicitCA, random);
+            }
+            else if (params instanceof ECParameterSpec)
+            {
+                this.ecParams = params;
+                this.param = createKeyGenParamsBC((ECParameterSpec)params, random);
+            }
+            else if (params instanceof java.security.spec.ECParameterSpec)
+            {
+                this.ecParams = params;
+                this.param = createKeyGenParamsJCE((java.security.spec.ECParameterSpec)params, random);
+            }
+            else if (params instanceof ECGenParameterSpec)
+            {
+                initializeNamedCurve(((ECGenParameterSpec)params).getName(), random);
+            }
+            else if (params instanceof ECNamedCurveGenParameterSpec)
+            {
+                initializeNamedCurve(((ECNamedCurveGenParameterSpec)params).getName(), random);
+            }
+            else
+            {
+                String name = ECUtil.getNameFrom(params);
+
+                if (name != null)
+                {
+                    initializeNamedCurve(name, random);
+                }
+                else
+                {
+                    throw new InvalidAlgorithmParameterException("invalid parameterSpec: " + params);
+                }
+            }
+
+            engine.init(param);
+            initialised = true;
+        }
+
+        public KeyPair generateKeyPair()
+        {
+            if (!initialised)
+            {
+                initialize(strength, new SecureRandom());
+            }
+
+            AsymmetricCipherKeyPair     pair = engine.generateKeyPair();
+            ECPublicKeyParameters       pub = (ECPublicKeyParameters)pair.getPublic();
+            ECPrivateKeyParameters      priv = (ECPrivateKeyParameters)pair.getPrivate();
+
+            if (ecParams instanceof ECParameterSpec)
+            {
+                ECParameterSpec p = (ECParameterSpec)ecParams;
+
+                BCECPublicKey pubKey = new BCECPublicKey(algorithm, pub, p, configuration);
+                return new KeyPair(pubKey,
+                                   new BCECPrivateKey(algorithm, priv, pubKey, p, configuration));
+            }
+            else if (ecParams == null)
+            {
+               return new KeyPair(new BCECPublicKey(algorithm, pub, configuration),
+                                   new BCECPrivateKey(algorithm, priv, configuration));
+            }
+            else
+            {
+                java.security.spec.ECParameterSpec p = (java.security.spec.ECParameterSpec)ecParams;
+
+                BCECPublicKey pubKey = new BCECPublicKey(algorithm, pub, p, configuration);
+                
+                return new KeyPair(pubKey, new BCECPrivateKey(algorithm, priv, pubKey, p, configuration));
+            }
+        }
+
+        protected ECKeyGenerationParameters createKeyGenParamsBC(ECParameterSpec p, SecureRandom r)
+        {
+            return new ECKeyGenerationParameters(new ECDomainParameters(p.getCurve(), p.getG(), p.getN(), p.getH()), r);
+        }
+
+        protected ECKeyGenerationParameters createKeyGenParamsJCE(java.security.spec.ECParameterSpec p, SecureRandom r)
+        {
+            if (p instanceof ECNamedCurveSpec)
+            {
+                String curveName = ((ECNamedCurveSpec)p).getName();
+
+                X9ECParameters x9 = ECUtils.getDomainParametersFromName(curveName, configuration);
+                if (null != x9)
+                {
+                    return createKeyGenParamsJCE(x9, r);
+                }
+            }
+
+            ECCurve curve = EC5Util.convertCurve(p.getCurve());
+            ECPoint g = EC5Util.convertPoint(curve, p.getGenerator());
+            BigInteger n = p.getOrder();
+            BigInteger h = BigInteger.valueOf(p.getCofactor());
+            ECDomainParameters dp = new ECDomainParameters(curve, g, n, h);
+            return new ECKeyGenerationParameters(dp, r);
+        }
+
+        protected ECKeyGenerationParameters createKeyGenParamsJCE(X9ECParameters x9, SecureRandom r)
+        {
+            ECDomainParameters dp = new ECDomainParameters(x9.getCurve(), x9.getG(), x9.getN(), x9.getH());
+
+            return new ECKeyGenerationParameters(dp, r);
+        }
+
+        protected void initializeNamedCurve(String curveName, SecureRandom random)
+            throws InvalidAlgorithmParameterException
+        {
+            X9ECParameters x9 = ECUtils.getDomainParametersFromName(curveName, configuration);
+            if (null == x9)
+            {
+                throw new InvalidAlgorithmParameterException("unknown curve name: " + curveName);
+            }
+
+            // Work-around for JDK bug -- it won't look up named curves properly if seed is present
+            byte[] seed = null; //p.getSeed();
+
+            this.ecParams = new ECNamedCurveSpec(curveName, x9.getCurve(), x9.getG(), x9.getN(), x9.getH(), seed);
+            this.param = createKeyGenParamsJCE(x9, random);
+        }
+    }
+
+    public static class SM2
+        extends BaseSM2
+    {
+        public SM2()
+        {
+            super("SM2", BouncyCastleProvider.CONFIGURATION);
+        }
+    }
+}
\ No newline at end of file
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ec/KeyAgreementSpi.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ec/KeyAgreementSpi.java
index 7070df8..b4faf37 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ec/KeyAgreementSpi.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ec/KeyAgreementSpi.java
@@ -195,21 +195,18 @@
         return null;
     }
 
-    protected void engineInit(
-        Key key,
-        AlgorithmParameterSpec params,
-        SecureRandom random)
+    protected void doInitFromKey(Key key, AlgorithmParameterSpec parameterSpec, SecureRandom random)
         throws InvalidKeyException, InvalidAlgorithmParameterException
     {
         // Android-removed: Unsupported algorithms
-        // if (params != null &&
-        //     !(params instanceof MQVParameterSpec || params instanceof UserKeyingMaterialSpec || params instanceof DHUParameterSpec))
-        if (params != null && !(params instanceof UserKeyingMaterialSpec))
+        // if (parameterSpec != null &&
+        //     !(parameterSpec instanceof MQVParameterSpec || parameterSpec instanceof UserKeyingMaterialSpec || params instanceof DHUParameterSpec))
+        if (parameterSpec != null && !(parameterSpec instanceof UserKeyingMaterialSpec))
         {
             throw new InvalidAlgorithmParameterException("No algorithm parameters supported");
         }
 
-        initFromKey(key, params);
+        initFromKey(key, parameterSpec);
     }
 
     protected void engineInit(
@@ -249,9 +246,9 @@
             {
                 MQVPrivateKey mqvPrivKey = (MQVPrivateKey)key;
                 staticPrivKey = (ECPrivateKeyParameters)
-                    ECUtil.generatePrivateKeyParameter(mqvPrivKey.getStaticPrivateKey());
+                    ECUtils.generatePrivateKeyParameter(mqvPrivKey.getStaticPrivateKey());
                 ephemPrivKey = (ECPrivateKeyParameters)
-                    ECUtil.generatePrivateKeyParameter(mqvPrivKey.getEphemeralPrivateKey());
+                    ECUtils.generatePrivateKeyParameter(mqvPrivKey.getEphemeralPrivateKey());
 
                 ephemPubKey = null;
                 if (mqvPrivKey.getEphemeralPublicKey() != null)
@@ -265,9 +262,9 @@
                 MQVParameterSpec mqvParameterSpec = (MQVParameterSpec)parameterSpec;
 
                 staticPrivKey = (ECPrivateKeyParameters)
-                    ECUtil.generatePrivateKeyParameter((PrivateKey)key);
+                    ECUtils.generatePrivateKeyParameter((PrivateKey)key);
                 ephemPrivKey = (ECPrivateKeyParameters)
-                    ECUtil.generatePrivateKeyParameter(mqvParameterSpec.getEphemeralPrivateKey());
+                    ECUtils.generatePrivateKeyParameter(mqvParameterSpec.getEphemeralPrivateKey());
 
                 ephemPubKey = null;
                 if (mqvParameterSpec.getEphemeralPublicKey() != null)
@@ -299,9 +296,9 @@
             ECPublicKeyParameters ephemPubKey;
 
             staticPrivKey = (ECPrivateKeyParameters)
-                ECUtil.generatePrivateKeyParameter((PrivateKey)key);
+                ECUtils.generatePrivateKeyParameter((PrivateKey)key);
             ephemPrivKey = (ECPrivateKeyParameters)
-                ECUtil.generatePrivateKeyParameter(dheParameterSpec.getEphemeralPrivateKey());
+                ECUtils.generatePrivateKeyParameter(dheParameterSpec.getEphemeralPrivateKey());
 
             ephemPubKey = null;
             if (dheParameterSpec.getEphemeralPublicKey() != null)
@@ -330,7 +327,7 @@
             {
                 throw new InvalidAlgorithmParameterException("no KDF specified for UserKeyingMaterialSpec");
             }
-            ECPrivateKeyParameters privKey = (ECPrivateKeyParameters)ECUtil.generatePrivateKeyParameter((PrivateKey)key);
+            ECPrivateKeyParameters privKey = (ECPrivateKeyParameters)ECUtils.generatePrivateKeyParameter((PrivateKey)key);
             this.parameters = privKey.getParameters();
             ukmParameters = (parameterSpec instanceof UserKeyingMaterialSpec) ? ((UserKeyingMaterialSpec)parameterSpec).getUserKeyingMaterial() : null;
             ((BasicAgreement)agreement).init(privKey);
@@ -344,7 +341,7 @@
         return fullName.substring(fullName.lastIndexOf('.') + 1);
     }
     
-    protected byte[] calcSecret()
+    protected byte[] doCalcSecret()
     {
         return Arrays.clone(result);
     }
@@ -757,8 +754,8 @@
    		{
    			super("ECKAEGwithSHA1KDF", new ECDHBasicAgreement(),
                    new KDF2BytesGenerator(DigestFactory.createSHA1()));
-   		}
-   	}
+           }
+       }
 
     /**
    	 * KeyAgreement according to BSI TR-03111 chapter 4.3.1
@@ -770,8 +767,8 @@
    		{
    			super("ECKAEGwithRIPEMD160KDF", new ECDHBasicAgreement(),
                    new KDF2BytesGenerator(new RIPEMD160Digest()));
-   		}
-   	}
+           }
+       }
 
     /**
    	 * KeyAgreement according to BSI TR-03111 chapter 4.3.1
@@ -783,8 +780,8 @@
    		{
    			super("ECKAEGwithSHA224KDF", new ECDHBasicAgreement(),
                    new KDF2BytesGenerator(DigestFactory.createSHA224()));
-   		}
-   	}
+           }
+       }
 
 	/**
 	 * KeyAgreement according to BSI TR-03111 chapter 4.3.1
@@ -796,8 +793,8 @@
 		{
 			super("ECKAEGwithSHA256KDF", new ECDHBasicAgreement(),
                 new KDF2BytesGenerator(DigestFactory.createSHA256()));
-		}
-	}
+        }
+    }
 
 	/**
 	 * KeyAgreement according to BSI TR-03111 chapter 4.3.1
@@ -809,8 +806,8 @@
 		{
 			super("ECKAEGwithSHA384KDF", new ECDHBasicAgreement(),
                 new KDF2BytesGenerator(DigestFactory.createSHA384()));
-		}
-	}
+        }
+    }
 
 	/**
 	 * KeyAgreement according to BSI TR-03111 chapter 4.3.1
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ec/KeyFactorySpi.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ec/KeyFactorySpi.java
index 9a5f0ed..d16f62f 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ec/KeyFactorySpi.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ec/KeyFactorySpi.java
@@ -237,7 +237,11 @@
 
             try
             {
-                return new BCECPrivateKey(algorithm, new PrivateKeyInfo(new AlgorithmIdentifier(X9ObjectIdentifiers.id_ecPublicKey, ecKey.getParameters()), ecKey), configuration);
+                return new BCECPrivateKey(algorithm,
+                    new PrivateKeyInfo(
+                        new AlgorithmIdentifier(X9ObjectIdentifiers.id_ecPublicKey, ecKey.getParametersObject()),
+                        ecKey),
+                    configuration);
             }
             catch (IOException e)
             {
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ec/KeyPairGeneratorSpi.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ec/KeyPairGeneratorSpi.java
index 6636662..e17fe03 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ec/KeyPairGeneratorSpi.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ec/KeyPairGeneratorSpi.java
@@ -8,10 +8,7 @@
 import java.security.spec.AlgorithmParameterSpec;
 import java.security.spec.ECGenParameterSpec;
 import java.util.Hashtable;
-import java.util.Map;
 
-import org.bouncycastle.asn1.ASN1ObjectIdentifier;
-import org.bouncycastle.asn1.x9.ECNamedCurveTable;
 import org.bouncycastle.asn1.x9.X9ECParameters;
 import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
 import org.bouncycastle.crypto.CryptoServicesRegistrar;
@@ -56,7 +53,8 @@
 
         static private Hashtable    ecParameters;
 
-        static {
+        static
+        {
             ecParameters = new Hashtable();
 
             ecParameters.put(Integers.valueOf(192), new ECGenParameterSpec("prime192v1")); // a.k.a P-192
@@ -213,13 +211,12 @@
         {
             if (p instanceof ECNamedCurveSpec)
             {
-                X9ECParameters x9P = ECUtils.getDomainParametersFromName(((ECNamedCurveSpec)p).getName());
+                String curveName = ((ECNamedCurveSpec)p).getName();
 
-                if (x9P != null)
+                X9ECParameters x9 = ECUtils.getDomainParametersFromName(curveName, configuration);
+                if (null != x9)
                 {
-                    ECDomainParameters dp = new ECDomainParameters(x9P.getCurve(), x9P.getG(), x9P.getN(), x9P.getH());
-
-                    return new ECKeyGenerationParameters(dp, r);
+                    return createKeyGenParamsJCE(x9, r);
                 }
             }
 
@@ -231,48 +228,27 @@
             return new ECKeyGenerationParameters(dp, r);
         }
 
-        protected ECNamedCurveSpec createNamedCurveSpec(String curveName)
-            throws InvalidAlgorithmParameterException
+        protected ECKeyGenerationParameters createKeyGenParamsJCE(X9ECParameters x9, SecureRandom r)
         {
-            // NOTE: Don't bother with custom curves here as the curve will be converted to JCE type shortly
+            ECDomainParameters dp = new ECDomainParameters(x9.getCurve(), x9.getG(), x9.getN(), x9.getH());
 
-            X9ECParameters p = ECUtils.getDomainParametersFromName(curveName);
-            if (p == null)
-            {
-                try
-                {
-                    // Check whether it's actually an OID string (SunJSSE ServerHandshaker setupEphemeralECDHKeys bug)
-                    p = ECNamedCurveTable.getByOID(new ASN1ObjectIdentifier(curveName));
-                    if (p == null)
-                    {
-                        Map extraCurves = configuration.getAdditionalECParameters();
-
-                        p = (X9ECParameters)extraCurves.get(new ASN1ObjectIdentifier(curveName));
-
-                        if (p == null)
-                        {
-                            throw new InvalidAlgorithmParameterException("unknown curve OID: " + curveName);
-                        }
-                    }
-                }
-                catch (IllegalArgumentException ex)
-                {
-                    throw new InvalidAlgorithmParameterException("unknown curve name: " + curveName);
-                }
-            }
-
-            // Work-around for JDK bug -- it won't look up named curves properly if seed is present
-            byte[] seed = null; //p.getSeed();
-
-            return new ECNamedCurveSpec(curveName, p.getCurve(), p.getG(), p.getN(), p.getH(), seed);
+            return new ECKeyGenerationParameters(dp, r);
         }
 
         protected void initializeNamedCurve(String curveName, SecureRandom random)
             throws InvalidAlgorithmParameterException
         {
-            ECNamedCurveSpec namedCurve = createNamedCurveSpec(curveName);
-            this.ecParams = namedCurve;
-            this.param = createKeyGenParamsJCE(namedCurve, random);
+            X9ECParameters x9 = ECUtils.getDomainParametersFromName(curveName, configuration);
+            if (null == x9)
+            {
+                throw new InvalidAlgorithmParameterException("unknown curve name: " + curveName);
+            }
+
+            // Work-around for JDK bug -- it won't look up named curves properly if seed is present
+            byte[] seed = null; //p.getSeed();
+
+            this.ecParams = new ECNamedCurveSpec(curveName, x9.getCurve(), x9.getG(), x9.getN(), x9.getH(), seed);
+            this.param = createKeyGenParamsJCE(x9, random);
         }
     }
 
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ec/SignatureSpi.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ec/SignatureSpi.java
index 9635e18..427dae9 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ec/SignatureSpi.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ec/SignatureSpi.java
@@ -11,6 +11,7 @@
 import org.bouncycastle.crypto.digests.NullDigest;
 // BEGIN Android-removed: Unsupported algorithms
 // import org.bouncycastle.crypto.digests.RIPEMD160Digest;
+// import org.bouncycastle.crypto.digests.SHAKEDigest;
 // END Android-removed: Unsupported algorithms
 import org.bouncycastle.crypto.params.ParametersWithRandom;
 import org.bouncycastle.crypto.signers.DSAEncoding;
@@ -25,7 +26,6 @@
 // import org.bouncycastle.crypto.util.DigestFactory;
 import org.bouncycastle.crypto.digests.AndroidDigestFactory;
 import org.bouncycastle.jcajce.provider.asymmetric.util.DSABase;
-import org.bouncycastle.jcajce.provider.asymmetric.util.ECUtil;
 
 public class SignatureSpi
     extends DSABase
@@ -48,7 +48,7 @@
         PrivateKey privateKey)
         throws InvalidKeyException
     {
-        CipherParameters param = ECUtil.generatePrivateKeyParameter(privateKey);
+        CipherParameters param = ECUtils.generatePrivateKeyParameter(privateKey);
 
         digest.reset();
 
@@ -275,6 +275,26 @@
         }
     }
 
+    static public class ecDSAShake128
+         extends SignatureSpi
+    {
+        public ecDSAShake128()
+        {
+            // RFC 8702 specifies deterministic DSA
+            super(new SHAKEDigest(128), new ECDSASigner(new HMacDSAKCalculator(new SHAKEDigest(128))), StandardDSAEncoding.INSTANCE);
+        }
+    }
+
+    static public class ecDSAShake256
+        extends SignatureSpi
+    {
+        public ecDSAShake256()
+        {
+            // RFC 8702 specifies deterministic DSA
+            super(new SHAKEDigest(256), new ECDSASigner(new HMacDSAKCalculator(new SHAKEDigest(256))), StandardDSAEncoding.INSTANCE);
+        }
+    }
+
     static public class ecNR
         extends SignatureSpi
     {
@@ -373,6 +393,41 @@
             super(new RIPEMD160Digest(), new ECDSASigner(), PlainDSAEncoding.INSTANCE);
         }
     }
+    static public class ecCVCDSA3_224
+        extends SignatureSpi
+    {
+        public ecCVCDSA3_224()
+        {
+            super(DigestFactory.createSHA3_224(), new ECDSASigner(), PlainDSAEncoding.INSTANCE);
+        }
+    }
+
+    static public class ecCVCDSA3_256
+        extends SignatureSpi
+    {
+        public ecCVCDSA3_256()
+        {
+            super(DigestFactory.createSHA3_256(), new ECDSASigner(), PlainDSAEncoding.INSTANCE);
+        }
+    }
+
+    static public class ecCVCDSA3_384
+        extends SignatureSpi
+    {
+        public ecCVCDSA3_384()
+        {
+            super(DigestFactory.createSHA3_384(), new ECDSASigner(), PlainDSAEncoding.INSTANCE);
+        }
+    }
+
+    static public class ecCVCDSA3_512
+        extends SignatureSpi
+    {
+        public ecCVCDSA3_512()
+        {
+            super(DigestFactory.createSHA3_512(), new ECDSASigner(), PlainDSAEncoding.INSTANCE);
+        }
+    }
     */
     // END Android-removed: Unsupported algorithms
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/rsa/AlgorithmParametersSpi.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/rsa/AlgorithmParametersSpi.java
index fc37d6e..baa4ed9 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/rsa/AlgorithmParametersSpi.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/rsa/AlgorithmParametersSpi.java
@@ -11,9 +11,11 @@
 
 import org.bouncycastle.asn1.ASN1Encoding;
 import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
 import org.bouncycastle.asn1.ASN1OctetString;
 import org.bouncycastle.asn1.DERNull;
 import org.bouncycastle.asn1.DEROctetString;
+import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
 import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
 import org.bouncycastle.asn1.pkcs.RSAESOAEPparams;
 import org.bouncycastle.asn1.pkcs.RSASSAPSSparams;
@@ -174,16 +176,36 @@
             throws IOException
         {
             PSSParameterSpec pssSpec = currentSpec;
-            AlgorithmIdentifier hashAlgorithm = new AlgorithmIdentifier(
-                                                DigestFactory.getOID(pssSpec.getDigestAlgorithm()),
-                                                DERNull.INSTANCE);
-            MGF1ParameterSpec mgfSpec = (MGF1ParameterSpec)pssSpec.getMGFParameters();
-            AlgorithmIdentifier maskGenAlgorithm = new AlgorithmIdentifier(
-                                                PKCSObjectIdentifiers.id_mgf1,
-                                                new AlgorithmIdentifier(DigestFactory.getOID(mgfSpec.getDigestAlgorithm()), DERNull.INSTANCE));
-            RSASSAPSSparams pssP = new RSASSAPSSparams(hashAlgorithm, maskGenAlgorithm, new ASN1Integer(pssSpec.getSaltLength()), new ASN1Integer(pssSpec.getTrailerField()));
+            ASN1ObjectIdentifier digOid = DigestFactory.getOID(pssSpec.getDigestAlgorithm());
+            AlgorithmIdentifier hashAlgorithm;
+            // RFC 8072
+            if (NISTObjectIdentifiers.id_shake128.equals(digOid) || NISTObjectIdentifiers.id_shake256.equals(digOid))
+            {
+                hashAlgorithm = new AlgorithmIdentifier(digOid);
+            }
+            else
+            {
+                hashAlgorithm = new AlgorithmIdentifier(digOid, DERNull.INSTANCE);
+            }
             
-            return pssP.getEncoded("DER");
+            MGF1ParameterSpec mgfSpec = (MGF1ParameterSpec)pssSpec.getMGFParameters();
+            if (mgfSpec != null)
+            {
+                AlgorithmIdentifier maskGenAlgorithm = new AlgorithmIdentifier(
+                    PKCSObjectIdentifiers.id_mgf1,
+                    new AlgorithmIdentifier(DigestFactory.getOID(mgfSpec.getDigestAlgorithm()), DERNull.INSTANCE));
+                RSASSAPSSparams pssP = new RSASSAPSSparams(hashAlgorithm, maskGenAlgorithm, new ASN1Integer(pssSpec.getSaltLength()), new ASN1Integer(pssSpec.getTrailerField()));
+
+                return pssP.getEncoded("DER");
+            }
+            else
+            {
+                AlgorithmIdentifier maskGenAlgorithm = new AlgorithmIdentifier(
+                    pssSpec.getMGFAlgorithm().equals("SHAKE128") ? NISTObjectIdentifiers.id_shake128 : NISTObjectIdentifiers.id_shake256);
+                RSASSAPSSparams pssP = new RSASSAPSSparams(hashAlgorithm, maskGenAlgorithm, new ASN1Integer(pssSpec.getSaltLength()), new ASN1Integer(pssSpec.getTrailerField()));
+
+                return pssP.getEncoded("DER");
+            }
         }
     
         protected byte[] engineGetEncoded(
@@ -231,17 +253,29 @@
             {
                 RSASSAPSSparams pssP = RSASSAPSSparams.getInstance(params);
 
-                if (!pssP.getMaskGenAlgorithm().getAlgorithm().equals(PKCSObjectIdentifiers.id_mgf1))
+                ASN1ObjectIdentifier mgfOid = pssP.getMaskGenAlgorithm().getAlgorithm();
+                if (mgfOid.equals(PKCSObjectIdentifiers.id_mgf1))
+                {
+                    currentSpec = new PSSParameterSpec(
+                        MessageDigestUtils.getDigestName(pssP.getHashAlgorithm().getAlgorithm()),
+                        PSSParameterSpec.DEFAULT.getMGFAlgorithm(),
+                        new MGF1ParameterSpec(MessageDigestUtils.getDigestName(AlgorithmIdentifier.getInstance(pssP.getMaskGenAlgorithm().getParameters()).getAlgorithm())),
+                        pssP.getSaltLength().intValue(),
+                        pssP.getTrailerField().intValue());
+                }
+                else if (mgfOid.equals(NISTObjectIdentifiers.id_shake128) || mgfOid.equals(NISTObjectIdentifiers.id_shake256))
+                {
+                    currentSpec = new PSSParameterSpec(
+                        MessageDigestUtils.getDigestName(pssP.getHashAlgorithm().getAlgorithm()),
+                        mgfOid.equals(NISTObjectIdentifiers.id_shake128) ? "SHAKE128" : "SHAKE256",
+                        null,
+                        pssP.getSaltLength().intValue(),
+                        pssP.getTrailerField().intValue());
+                }
+                else
                 {
                     throw new IOException("unknown mask generation function: " + pssP.getMaskGenAlgorithm().getAlgorithm());
                 }
-
-                currentSpec = new PSSParameterSpec(
-                                       MessageDigestUtils.getDigestName(pssP.getHashAlgorithm().getAlgorithm()),
-                                       PSSParameterSpec.DEFAULT.getMGFAlgorithm(),
-                                       new MGF1ParameterSpec(MessageDigestUtils.getDigestName(AlgorithmIdentifier.getInstance(pssP.getMaskGenAlgorithm().getParameters()).getAlgorithm())),
-                                       pssP.getSaltLength().intValue(),
-                                       pssP.getTrailerField().intValue());
             }
             catch (ClassCastException e)
             {
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/rsa/CipherSpi.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/rsa/CipherSpi.java
index 274f6fa..141bbff 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/rsa/CipherSpi.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/rsa/CipherSpi.java
@@ -30,7 +30,6 @@
 // Android-removed: Unsupported algorithm
 // import org.bouncycastle.crypto.encodings.ISO9796d1Encoding;
 import org.bouncycastle.crypto.encodings.OAEPEncoding;
-import org.bouncycastle.crypto.encodings.PKCS1Encoding;
 import org.bouncycastle.crypto.engines.RSABlindedEngine;
 import org.bouncycastle.crypto.params.ParametersWithRandom;
 import org.bouncycastle.jcajce.provider.asymmetric.util.BaseCipherSpi;
@@ -205,7 +204,7 @@
         }
         else if (pad.equals("PKCS1PADDING"))
         {
-            cipher = new PKCS1Encoding(new RSABlindedEngine());
+            cipher = new CustomPKCS1Encoding(new RSABlindedEngine());
         }
         // BEGIN Android-removed: Unsupported algorithm
         // else if (pad.equals("ISO9796-1PADDING"))
@@ -542,15 +541,26 @@
     {
         try
         {
-            return cipher.processBlock(bOut.getBuf(), 0, bOut.size());
-        }
-        catch (InvalidCipherTextException e)
-        {
-            throw new BadBlockException("unable to decrypt block", e);
-        }
-        catch (ArrayIndexOutOfBoundsException e)
-        {
-            throw new BadBlockException("unable to decrypt block", e);
+            byte[] output;
+            try
+            {
+                output = cipher.processBlock(bOut.getBuf(), 0, bOut.size());
+            }
+            catch (InvalidCipherTextException e)
+            {
+                throw new BadBlockException("unable to decrypt block", e);
+            }
+            catch (ArrayIndexOutOfBoundsException e)
+            {
+                throw new BadBlockException("unable to decrypt block", e);
+            }
+
+            if (output == null)
+            {
+                throw new BadBlockException("unable to decrypt block", null);
+            }
+
+            return output;
         }
         finally
         {
@@ -578,7 +588,7 @@
     {
         public PKCS1v1_5Padding()
         {
-            super(new PKCS1Encoding(new RSABlindedEngine()));
+            super(new CustomPKCS1Encoding(new RSABlindedEngine()));
         }
     }
 
@@ -587,7 +597,7 @@
     {
         public PKCS1v1_5Padding_PrivateOnly()
         {
-            super(false, true, new PKCS1Encoding(new RSABlindedEngine()));
+            super(false, true, new CustomPKCS1Encoding(new RSABlindedEngine()));
         }
     }
 
@@ -596,7 +606,7 @@
     {
         public PKCS1v1_5Padding_PublicOnly()
         {
-            super(true, false, new PKCS1Encoding(new RSABlindedEngine()));
+            super(true, false, new CustomPKCS1Encoding(new RSABlindedEngine()));
         }
     }
 
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/rsa/CustomPkcs1Encoding.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/rsa/CustomPkcs1Encoding.java
new file mode 100644
index 0000000..1fc4334
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/rsa/CustomPkcs1Encoding.java
@@ -0,0 +1,263 @@
+package org.bouncycastle.jcajce.provider.asymmetric.rsa;
+
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.AsymmetricBlockCipher;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.CryptoServicesRegistrar;
+import org.bouncycastle.crypto.InvalidCipherTextException;
+import org.bouncycastle.crypto.encodings.PKCS1Encoding;
+import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
+import org.bouncycastle.crypto.params.ParametersWithRandom;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.Properties;
+
+/**
+ * this does your basic PKCS 1 v1.5 padding - whether or not you should be using this
+ * depends on your application - see PKCS1 Version 2 for details.
+ */
+class CustomPKCS1Encoding
+    implements AsymmetricBlockCipher
+{
+    private static final int HEADER_LENGTH = 10;
+
+    private SecureRandom random;
+    private AsymmetricBlockCipher engine;
+    private boolean forEncryption;
+    private boolean forPrivateKey;
+    private boolean useStrictLength;
+    private byte[] blockBuffer;
+
+    /**
+     * Basic constructor.
+     *
+     * @param cipher
+     */
+    CustomPKCS1Encoding(AsymmetricBlockCipher cipher)
+    {
+        this.engine = cipher;
+        this.useStrictLength = useStrict();
+    }
+
+    //
+    // for J2ME compatibility
+    //
+    private boolean useStrict()
+    {
+        if (Properties.isOverrideSetTo(PKCS1Encoding.NOT_STRICT_LENGTH_ENABLED_PROPERTY, true))
+        {
+            return false;
+        }
+
+        return !Properties.isOverrideSetTo(PKCS1Encoding.STRICT_LENGTH_ENABLED_PROPERTY, false);
+    }
+
+    public AsymmetricBlockCipher getUnderlyingCipher()
+    {
+        return engine;
+    }
+
+    public void init(boolean forEncryption, CipherParameters param)
+    {
+        AsymmetricKeyParameter kParam;
+
+        if (param instanceof ParametersWithRandom)
+        {
+            ParametersWithRandom rParam = (ParametersWithRandom)param;
+
+            this.random = rParam.getRandom();
+            kParam = (AsymmetricKeyParameter)rParam.getParameters();
+        }
+        else
+        {
+            kParam = (AsymmetricKeyParameter)param;
+            if (!kParam.isPrivate() && forEncryption)
+            {
+                this.random = CryptoServicesRegistrar.getSecureRandom();
+            }
+        }
+
+        engine.init(forEncryption, param);
+
+        this.forPrivateKey = kParam.isPrivate();
+        this.forEncryption = forEncryption;
+        this.blockBuffer = new byte[engine.getOutputBlockSize()];
+    }
+
+    public int getInputBlockSize()
+    {
+        int baseBlockSize = engine.getInputBlockSize();
+
+        if (forEncryption)
+        {
+            return baseBlockSize - HEADER_LENGTH;
+        }
+        else
+        {
+            return baseBlockSize;
+        }
+    }
+
+    public int getOutputBlockSize()
+    {
+        int baseBlockSize = engine.getOutputBlockSize();
+
+        if (forEncryption)
+        {
+            return baseBlockSize;
+        }
+        else
+        {
+            return baseBlockSize - HEADER_LENGTH;
+        }
+    }
+
+    public byte[] processBlock(byte[] in, int inOff, int inLen) throws InvalidCipherTextException
+    {
+        if (forEncryption)
+        {
+            return encodeBlock(in, inOff, inLen);
+        }
+        else
+        {
+            return decodeBlock(in, inOff, inLen);
+        }
+    }
+
+    private byte[] encodeBlock(byte[] in, int inOff, int inLen) throws InvalidCipherTextException
+    {
+        if (inLen > getInputBlockSize())
+        {
+            throw new IllegalArgumentException("input data too large");
+        }
+
+        byte[] block = new byte[engine.getInputBlockSize()];
+
+        if (forPrivateKey)
+        {
+            block[0] = 0x01;                        // type code 1
+
+            for (int i = 1; i != block.length - inLen - 1; i++)
+            {
+                block[i] = (byte)0xFF;
+            }
+        }
+        else
+        {
+            random.nextBytes(block);                // random fill
+
+            block[0] = 0x02;                        // type code 2
+
+            //
+            // a zero byte marks the end of the padding, so all
+            // the pad bytes must be non-zero.
+            //
+            for (int i = 1; i != block.length - inLen - 1; i++)
+            {
+                while (block[i] == 0)
+                {
+                    block[i] = (byte)random.nextInt();
+                }
+            }
+        }
+
+        block[block.length - inLen - 1] = 0x00;       // mark the end of the padding
+        System.arraycopy(in, inOff, block, block.length - inLen, inLen);
+
+        return engine.processBlock(block, 0, block.length);
+    }
+
+    /**
+     * Check the argument is a valid encoding with type 1. Returns the plaintext length if valid, or -1 if invalid.
+     */
+    private static int checkPkcs1Encoding1(byte[] buf)
+    {
+        int foundZeroMask = 0;
+        int lastPadPos = 0;
+
+        // The first byte should be 0x01
+        int badPadSign = -((buf[0] & 0xFF) ^ 0x01);
+
+        // There must be a zero terminator for the padding somewhere
+        for (int i = 1; i < buf.length; ++i)
+        {
+            int padByte = buf[i] & 0xFF;
+            int is0x00Mask = ((padByte ^ 0x00) - 1) >> 31;
+            int is0xFFMask = ((padByte ^ 0xFF) - 1) >> 31;
+            lastPadPos ^= i & ~foundZeroMask & is0x00Mask;
+            foundZeroMask |= is0x00Mask;
+            badPadSign |= ~(foundZeroMask | is0xFFMask);
+        }
+
+        // The header should be at least 10 bytes
+        badPadSign |= lastPadPos - 9;
+
+        int plaintextLength = buf.length - 1 - lastPadPos;
+        return plaintextLength | badPadSign >> 31;
+    }
+
+    /**
+     * Check the argument is a valid encoding with type 2. Returns the plaintext length if valid, or -1 if invalid.
+     */
+    private static int checkPkcs1Encoding2(byte[] buf)
+    {
+        int foundZeroMask = 0;
+        int lastPadPos = 0;
+
+        // The first byte should be 0x02
+        int badPadSign = -((buf[0] & 0xFF) ^ 0x02);
+
+        // There must be a zero terminator for the padding somewhere
+        for (int i = 1; i < buf.length; ++i)
+        {
+            int padByte = buf[i] & 0xFF;
+            int is0x00Mask = ((padByte ^ 0x00) - 1) >> 31;
+            lastPadPos ^= i & ~foundZeroMask & is0x00Mask;
+            foundZeroMask |= is0x00Mask;
+        }
+
+        // The header should be at least 10 bytes
+        badPadSign |= lastPadPos - 9;
+
+        int plaintextLength = buf.length - 1 - lastPadPos;
+        return plaintextLength | badPadSign >> 31;
+    }
+
+    /**
+     * @throws InvalidCipherTextException if the decrypted block is not in PKCS1 format.
+     */
+    private byte[] decodeBlock(byte[] in, int inOff, int inLen)
+        throws InvalidCipherTextException
+    {
+        int strictBlockSize = engine.getOutputBlockSize();
+        byte[] block = engine.processBlock(in, inOff, inLen);
+
+        boolean incorrectLength = useStrictLength & (block.length != strictBlockSize);
+
+        byte[] data = block;
+        if (block.length < strictBlockSize)
+        {
+            data = blockBuffer;
+        }
+
+        int plaintextLength = forPrivateKey ? checkPkcs1Encoding2(data) : checkPkcs1Encoding1(data);
+
+        try
+        {
+            if (plaintextLength < 0 | incorrectLength)
+            {
+                // Special behaviour to avoid throw/catch/throw in CipherSpi
+                return null;
+            }
+
+            byte[] result = new byte[plaintextLength];
+            System.arraycopy(data, data.length - plaintextLength, result, 0, plaintextLength);
+            return result;
+        }
+        finally
+        {
+            Arrays.fill(block, (byte)0);
+            Arrays.fill(blockBuffer, 0, Math.max(0, blockBuffer.length - block.length), (byte)0);
+        }
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/util/BaseAgreementSpi.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/util/BaseAgreementSpi.java
index b449dc5..850031c 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/util/BaseAgreementSpi.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/util/BaseAgreementSpi.java
@@ -1,6 +1,11 @@
 package org.bouncycastle.jcajce.provider.asymmetric.util;
 
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.Key;
 import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.security.spec.AlgorithmParameterSpec;
 import java.util.HashMap;
 import java.util.Hashtable;
 import java.util.Map;
@@ -26,6 +31,7 @@
 // import org.bouncycastle.crypto.agreement.kdf.DHKEKGenerator;
 import org.bouncycastle.crypto.params.DESParameters;
 import org.bouncycastle.crypto.params.KDFParameters;
+import org.bouncycastle.jcajce.spec.HybridValueParameterSpec;
 import org.bouncycastle.util.Arrays;
 import org.bouncycastle.util.Integers;
 import org.bouncycastle.util.Strings;
@@ -148,6 +154,7 @@
     protected final DerivationFunction kdf;
 
     protected byte[]     ukmParameters;
+    private HybridValueParameterSpec hybridSpec;
 
     public BaseAgreementSpi(String kaAlgorithm, DerivationFunction kdf)
     {
@@ -221,6 +228,40 @@
         }
     }
 
+    protected void engineInit(
+        Key             key,
+        SecureRandom    random)
+        throws InvalidKeyException
+    {
+        try
+        {
+            doInitFromKey(key, null, random);
+        }
+        catch (InvalidAlgorithmParameterException e)
+        {
+            // this should never occur.
+            throw new InvalidKeyException(e.getMessage());
+        }
+    }
+
+    protected void engineInit(
+        Key key,
+        AlgorithmParameterSpec params,
+        SecureRandom random)
+        throws InvalidKeyException, InvalidAlgorithmParameterException
+    {
+        if (params instanceof HybridValueParameterSpec)
+        {
+            this.hybridSpec = (HybridValueParameterSpec)params;
+            doInitFromKey(key, hybridSpec.getBaseParameterSpec(), random);
+        }
+        else
+        {
+            this.hybridSpec = null;
+            doInitFromKey(key, params, random);
+        }
+    }
+
     protected byte[] engineGenerateSecret()
         throws IllegalStateException
     {
@@ -347,5 +388,26 @@
         }
     }
 
-    protected abstract byte[] calcSecret();
+    private byte[] calcSecret()
+    {
+        if (hybridSpec != null)
+        {
+            // Set Z' to Z || T
+            byte[] s = doCalcSecret();
+            byte[] sec = Arrays.concatenate(s, hybridSpec.getT());
+
+            Arrays.clear(s);
+
+            return sec;
+        }
+        else
+        {
+            return doCalcSecret();
+        }
+    }
+
+    protected abstract byte[] doCalcSecret();
+
+    protected abstract void doInitFromKey(Key key, AlgorithmParameterSpec parameterSpec, SecureRandom random)
+        throws InvalidKeyException, InvalidAlgorithmParameterException;
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/util/EC5Util.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/util/EC5Util.java
index 7741339..a01f9a8 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/util/EC5Util.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/util/EC5Util.java
@@ -19,6 +19,7 @@
 import org.bouncycastle.asn1.x9.ECNamedCurveTable;
 import org.bouncycastle.asn1.x9.X962Parameters;
 import org.bouncycastle.asn1.x9.X9ECParameters;
+import org.bouncycastle.asn1.x9.X9ECParametersHolder;
 import org.bouncycastle.crypto.ec.CustomNamedCurves;
 import org.bouncycastle.crypto.params.ECDomainParameters;
 import org.bouncycastle.jcajce.provider.config.ProviderConfiguration;
@@ -35,20 +36,41 @@
 
 public class EC5Util
 {
-    private static Map customCurves = new HashMap();
-
-    static
+    private static class CustomCurves
     {
-        Enumeration e = CustomNamedCurves.getNames();
-        while (e.hasMoreElements())
-        {
-            String name = (String)e.nextElement();
+        private static Map CURVE_MAP = createCurveMap();
 
-            X9ECParameters curveParams = ECNamedCurveTable.getByName(name);
-            if (curveParams != null)  // there may not be a regular curve, may just be a custom curve.
+        private static Map createCurveMap()
+        {
+            Map map = new HashMap();
+
+            Enumeration e = CustomNamedCurves.getNames();
+            while (e.hasMoreElements())
             {
-                customCurves.put(curveParams.getCurve(), CustomNamedCurves.getByName(name).getCurve());
+                String name = (String)e.nextElement();
+
+                X9ECParametersHolder curveParams = ECNamedCurveTable.getByNameLazy(name);
+                if (curveParams != null)  // there may not be a regular curve, may just be a custom curve.
+                {
+                    ECCurve curve = curveParams.getCurve();
+                    if (ECAlgorithms.isFpCurve(curve))
+                    {
+                        map.put(curve, CustomNamedCurves.getByNameLazy(name).getCurve());
+                    }
+                }
             }
+
+            ECCurve c_25519 = CustomNamedCurves.getByNameLazy("Curve25519").getCurve();
+
+            map.put(new ECCurve.Fp(
+                c_25519.getField().getCharacteristic(),
+                c_25519.getA().toBigInteger(),
+                c_25519.getB().toBigInteger(),
+                c_25519.getOrder(),
+                c_25519.getCofactor(),
+                true), c_25519);
+
+            return map;
         }
 
         // BEGIN Android-removed: Unsupported curves
@@ -65,6 +87,11 @@
             ), c_25519);
         */
         // END Android-removed: Unsupported curves
+        static ECCurve substitute(ECCurve c)
+        {
+            ECCurve custom = (ECCurve)CURVE_MAP.get(c);
+            return null != custom ? custom : c;
+        }
     }
 
     public static ECCurve getCurve(
@@ -276,21 +303,14 @@
 
         if (field instanceof ECFieldFp)
         {
-            ECCurve.Fp curve = new ECCurve.Fp(((ECFieldFp)field).getP(), a, b);
-
-            if (customCurves.containsKey(curve))
-            {
-                return (ECCurve)customCurves.get(curve);
-            }
-
-            return curve;
+            return CustomCurves.substitute(new ECCurve.Fp(((ECFieldFp)field).getP(), a, b, null, null));
         }
         else
         {
             ECFieldF2m fieldF2m = (ECFieldF2m)field;
             int m = fieldF2m.getM();
             int ks[] = ECUtil.convertMidTerms(fieldF2m.getMidTermsOfReductionPolynomial());
-            return new ECCurve.F2m(m, ks[0], ks[1], ks[2], a, b); 
+            return new ECCurve.F2m(m, ks[0], ks[1], ks[2], a, b, null, null);
         }
     }
 
@@ -304,7 +324,7 @@
         {
             Polynomial poly = ((PolynomialExtensionField)field).getMinimalPolynomial();
             int[] exponents = poly.getExponentsPresent();
-            int[] ks = Arrays.reverse(Arrays.copyOfRange(exponents, 1, exponents.length - 1));
+            int[] ks = Arrays.reverseInPlace(Arrays.copyOfRange(exponents, 1, exponents.length - 1));
             return new ECFieldF2m(poly.getDegree(), ks);
         }
     }
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/util/ECUtil.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/util/ECUtil.java
index 5ab9163..c1c93b8 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/util/ECUtil.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/util/ECUtil.java
@@ -291,6 +291,11 @@
     {
         if (order == null)     // implicitly CA
         {
+            if (configuration == null)
+            {
+                return privateValue.bitLength();   // a guess but better than an exception!
+            }
+
             ECParameterSpec implicitCA = configuration.getEcImplicitlyCa();
 
             if (implicitCA == null)
@@ -309,26 +314,24 @@
     public static ASN1ObjectIdentifier getNamedCurveOid(
         String curveName)
     {
-        String name = curveName;
+        if (null == curveName || curveName.length() < 1)
+        {
+            return null;
+        }
 
-        int spacePos = name.indexOf(' ');
+        int spacePos = curveName.indexOf(' ');
         if (spacePos > 0)
         {
-            name = name.substring(spacePos + 1);
+            curveName = curveName.substring(spacePos + 1);
         }
 
-        try
+        ASN1ObjectIdentifier oid = getOID(curveName);
+        if (null != oid)
         {
-            if (name.charAt(0) >= '0' && name.charAt(0) <= '2')
-            {
-                return new ASN1ObjectIdentifier(name);
-            }
-        }
-        catch (IllegalArgumentException ex)
-        {
+            return oid;
         }
 
-        return ECNamedCurveTable.getOID(name);
+        return ECNamedCurveTable.getOID(curveName);
     }
 
     public static ASN1ObjectIdentifier getNamedCurveOid(
@@ -446,4 +449,20 @@
             }
         });
     }
+
+    private static ASN1ObjectIdentifier getOID(String curveName)
+    {
+        char firstChar = curveName.charAt(0);
+        if (firstChar >= '0' && firstChar <= '2')
+        {
+            try
+            {
+                return new ASN1ObjectIdentifier(curveName);
+            }
+            catch (Exception e)
+            {
+            }
+        }
+        return null;
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/x509/CertificateFactory.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/x509/CertificateFactory.java
index 4b84ddd..9a1d13a 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/x509/CertificateFactory.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/x509/CertificateFactory.java
@@ -63,10 +63,11 @@
     }
 
     private java.security.cert.Certificate readPEMCertificate(
-        InputStream in)
+        InputStream in,
+        boolean isFirst)
         throws IOException, CertificateParsingException
     {
-        return getCertificate(PEM_CERT_PARSER.readPEMObject(in));
+        return getCertificate(PEM_CERT_PARSER.readPEMObject(in, isFirst));
     }
 
     private java.security.cert.Certificate getCertificate(ASN1Sequence seq)
@@ -121,10 +122,11 @@
     }
     
     private CRL readPEMCRL(
-        InputStream in)
+        InputStream in,
+        boolean isFirst)
         throws IOException, CRLException
     {
-        return getCRL(PEM_CRL_PARSER.readPEMObject(in));
+        return getCRL(PEM_CRL_PARSER.readPEMObject(in, isFirst));
     }
 
     private CRL readDERCRL(
@@ -179,6 +181,14 @@
         InputStream in)
         throws CertificateException
     {
+        return doGenerateCertificate(in, true);
+    }
+
+   private java.security.cert.Certificate doGenerateCertificate(
+            InputStream in,
+            boolean isFirst)
+            throws CertificateException
+   {
         if (currentStream == null)
         {
             currentStream = in;
@@ -252,7 +262,7 @@
 
             if (tag != 0x30)  // assume ascii PEM encoded.
             {
-                return readPEMCertificate(pis);
+                return readPEMCertificate(pis, isFirst);
             }
             else
             {
@@ -284,7 +294,8 @@
 
         // Android-changed: Read from original stream
         // while ((cert = engineGenerateCertificate(in)) != null)
-        while ((cert = engineGenerateCertificate(inStream)) != null)
+        // if we do read some certificates we'll return them even if junk at end of file
+        while ((cert = doGenerateCertificate(inStream, certs.isEmpty())) != null)
         {
             certs.add(cert);
         }
@@ -300,6 +311,18 @@
         InputStream in)
         throws CRLException
     {
+        return doGenerateCRL(in, true);
+    }
+
+    /**
+     * Generates a certificate revocation list (CRL) object and initializes
+     * it with the data read from the input stream inStream.
+     */
+    private CRL doGenerateCRL(
+        InputStream in,
+        boolean     isFirst)
+        throws CRLException
+    {
         if (currentCrlStream == null)
         {
             currentCrlStream = in;
@@ -351,7 +374,7 @@
             pis.reset();
             if (tag != 0x30)  // assume ascii PEM encoded.
             {
-                return readPEMCRL(pis);
+                return readPEMCRL(pis, isFirst);
             }
             else
             {       // lazy evaluate to help processing of large CRLs
@@ -385,7 +408,8 @@
         List crls = new ArrayList();
         BufferedInputStream in = new BufferedInputStream(inStream);
 
-        while ((crl = engineGenerateCRL(in)) != null)
+        // if we do read some certificates we'll return them even if junk at end of file
+        while ((crl = doGenerateCRL(in, crls.isEmpty())) != null)
         {
             crls.add(crl);
         }
@@ -433,7 +457,7 @@
         return new PKIXCertPath(certificates);
     }
 
-    private class ExCertificateException
+    private static class ExCertificateException
         extends CertificateException
     {
         private Throwable cause;
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/x509/PEMUtil.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/x509/PEMUtil.java
index 36c7159..305643e 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/x509/PEMUtil.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/x509/PEMUtil.java
@@ -13,7 +13,7 @@
      * current PEM object.
      *
      */
-    private class Boundaries
+    private static class Boundaries
     {
         private final String _header;
         private final String _footer;
@@ -112,7 +112,8 @@
     }
 
     ASN1Sequence readPEMObject(
-        InputStream in)
+        InputStream in,
+        boolean     isFirst)
         throws IOException
     {
         String line;
@@ -131,6 +132,11 @@
 
         if (header == null)
         {
+            if (!isFirst)
+            {
+                // just ignore the data
+                return null;
+            }
             throw new IOException("malformed PEM data: no header found");
         }
 
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/x509/X509CRLImpl.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/x509/X509CRLImpl.java
index e6c8fe9..e73374d 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/x509/X509CRLImpl.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/x509/X509CRLImpl.java
@@ -28,6 +28,7 @@
 
 import javax.security.auth.x500.X500Principal;
 
+import org.bouncycastle.asn1.ASN1BitString;
 import org.bouncycastle.asn1.ASN1Encodable;
 import org.bouncycastle.asn1.ASN1Encoding;
 import org.bouncycastle.asn1.ASN1InputStream;
@@ -36,7 +37,6 @@
 import org.bouncycastle.asn1.ASN1OctetString;
 import org.bouncycastle.asn1.ASN1Primitive;
 import org.bouncycastle.asn1.ASN1Sequence;
-import org.bouncycastle.asn1.DERBitString;
 import org.bouncycastle.asn1.util.ASN1Dump;
 import org.bouncycastle.asn1.x500.X500Name;
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
@@ -158,19 +158,6 @@
         return null;
     }
 
-    public byte[] getEncoded()
-        throws CRLException
-    {
-        try
-        {
-            return c.getEncoded(ASN1Encoding.DER);
-        }
-        catch (IOException e)
-        {
-            throw new CRLException(e.toString());
-        }
-    }
-
     public void verify(PublicKey key)
         throws CRLException, NoSuchAlgorithmException,
         InvalidKeyException, NoSuchProviderException, SignatureException
@@ -255,7 +242,7 @@
         {
             List<PublicKey> pubKeys = ((CompositePublicKey)key).getPublicKeys();
             ASN1Sequence keySeq = ASN1Sequence.getInstance(c.getSignatureAlgorithm().getParameters());
-            ASN1Sequence sigSeq = ASN1Sequence.getInstance(DERBitString.getInstance(c.getSignature()).getBytes());
+            ASN1Sequence sigSeq = ASN1Sequence.getInstance(ASN1BitString.getInstance(c.getSignature()).getBytes());
 
             boolean success = false;
             for (int i = 0; i != pubKeys.size(); i++)
@@ -277,7 +264,7 @@
                     checkSignature(
                         (PublicKey)pubKeys.get(i), signature,
                         sigAlg.getParameters(),
-                        DERBitString.getInstance(sigSeq.getObjectAt(i)).getBytes());
+                        ASN1BitString.getInstance(sigSeq.getObjectAt(i)).getBytes());
                     success = true;
                 }
                 catch (SignatureException e)
@@ -299,7 +286,7 @@
         else if (X509SignatureUtil.isCompositeAlgorithm(c.getSignatureAlgorithm()))
         {
             ASN1Sequence keySeq = ASN1Sequence.getInstance(c.getSignatureAlgorithm().getParameters());
-            ASN1Sequence sigSeq = ASN1Sequence.getInstance(DERBitString.getInstance(c.getSignature()).getBytes());
+            ASN1Sequence sigSeq = ASN1Sequence.getInstance(ASN1BitString.getInstance(c.getSignature()).getBytes());
 
             boolean success = false;
             for (int i = 0; i != sigSeq.size(); i++)
@@ -316,7 +303,7 @@
                     checkSignature(
                         key, signature,
                         sigAlg.getParameters(),
-                        DERBitString.getInstance(sigSeq.getObjectAt(i)).getBytes());
+                        ASN1BitString.getInstance(sigSeq.getObjectAt(i)).getBytes());
 
                     success = true;
                 }
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/x509/X509CRLInternal.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/x509/X509CRLInternal.java
index 6bc0324..08c6f52 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/x509/X509CRLInternal.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/x509/X509CRLInternal.java
@@ -5,25 +5,42 @@
 import org.bouncycastle.asn1.x509.CertificateList;
 import org.bouncycastle.jcajce.util.JcaJceHelper;
 
+/**
+ * This class exists to let {@link #equals(Object)} and {@link #hashCode()} methods be delegated efficiently
+ * to the platform default implementations (especially important for compatibility of {@link #hashCode()}
+ * calculations). Those methods fall back to calling {@link #getEncoded()} for third-party subclasses, and
+ * this class allows us to avoid cloning the return value of {@link #getEncoded()} for those callers.
+ */
 class X509CRLInternal extends X509CRLImpl
 {
     private final byte[] encoding;
+    private final CRLException exception;
 
     X509CRLInternal(JcaJceHelper bcHelper, CertificateList c, String sigAlgName, byte[] sigAlgParams, boolean isIndirect,
-        byte[] encoding)
+        byte[] encoding, CRLException exception)
     {
         super(bcHelper, c, sigAlgName, sigAlgParams, isIndirect);
 
         this.encoding = encoding;
+        this.exception = exception;
     }
 
     public byte[] getEncoded() throws CRLException
     {
+        if (null != exception)
+        {
+            throw exception;
+        }
+
         if (null == encoding)
         {
             throw new CRLException();
         }
 
+        /*
+         * NOTE: Don't clone this return value. See class javadoc for details. Any necessary cloning is
+         * handled by the X509CRLObject that is holding this instance.
+         */
         return encoding;
     }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/x509/X509CRLObject.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/x509/X509CRLObject.java
index 3a1af42..8e4efe2 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/x509/X509CRLObject.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/x509/X509CRLObject.java
@@ -1,5 +1,6 @@
 package org.bouncycastle.jcajce.provider.asymmetric.x509;
 
+import java.io.IOException;
 import java.security.cert.CRLException;
 
 import org.bouncycastle.asn1.ASN1BitString;
@@ -9,6 +10,7 @@
 import org.bouncycastle.asn1.x509.Extension;
 import org.bouncycastle.asn1.x509.IssuingDistributionPoint;
 import org.bouncycastle.jcajce.util.JcaJceHelper;
+import org.bouncycastle.util.Arrays;
 
 class X509CRLObject
     extends X509CRLImpl
@@ -24,6 +26,11 @@
         super(bcHelper, c, createSigAlgName(c), createSigAlgParams(c), isIndirectCRL(c));
     }
 
+    public byte[] getEncoded() throws CRLException
+    {
+        return Arrays.clone(getInternalCRL().getEncoded());
+    }
+
     public boolean equals(Object other)
     {
         if (this == other)
@@ -50,6 +57,8 @@
                     return false;
                 }
             }
+
+            return getInternalCRL().equals(otherBC.getInternalCRL());
         }
 
         return getInternalCRL().equals(other);
@@ -76,17 +85,19 @@
             }
         }
 
-        byte[] encoding;
+        byte[] encoding = null;
+        CRLException exception = null;
         try
         {
-            encoding = getEncoded();
+            encoding = c.getEncoded(ASN1Encoding.DER);
         }
-        catch (CRLException e)
+        catch (IOException e)
         {
-            encoding = null;
+            exception = new X509CRLException(e);
         }
 
-        X509CRLInternal temp = new X509CRLInternal(bcHelper, c, sigAlgName,sigAlgParams, isIndirect, encoding);
+        X509CRLInternal temp = new X509CRLInternal(bcHelper, c, sigAlgName,sigAlgParams, isIndirect, encoding,
+            exception);
 
         synchronized (cacheLock)
         {
@@ -107,7 +118,7 @@
         }
         catch (Exception e)
         {
-            throw new CRLException("CRL contents invalid: " + e);
+            throw new X509CRLException("CRL contents invalid: " + e.getMessage(), e);
         }
     }
 
@@ -146,4 +157,26 @@
             throw new ExtCRLException("Exception reading IssuingDistributionPoint", e);
         }
     }
+
+    private static class X509CRLException
+        extends CRLException
+    {
+        private final Throwable cause;
+
+        X509CRLException(String msg, Throwable cause)
+        {
+            super(msg);
+            this.cause = cause;
+        }
+
+        X509CRLException(Throwable cause)
+        {
+            this.cause = cause;
+        }
+
+        public Throwable getCause()
+        {
+            return cause;
+        }
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/x509/X509CertificateImpl.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/x509/X509CertificateImpl.java
index 4907fb5..be27fa8 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/x509/X509CertificateImpl.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/x509/X509CertificateImpl.java
@@ -31,16 +31,17 @@
 
 import javax.security.auth.x500.X500Principal;
 
+import org.bouncycastle.asn1.ASN1BitString;
 import org.bouncycastle.asn1.ASN1Encodable;
 import org.bouncycastle.asn1.ASN1Encoding;
+import org.bouncycastle.asn1.ASN1IA5String;
 import org.bouncycastle.asn1.ASN1InputStream;
+import org.bouncycastle.asn1.ASN1Integer;
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
 import org.bouncycastle.asn1.ASN1OctetString;
 import org.bouncycastle.asn1.ASN1Primitive;
 import org.bouncycastle.asn1.ASN1Sequence;
 import org.bouncycastle.asn1.ASN1String;
-import org.bouncycastle.asn1.DERBitString;
-import org.bouncycastle.asn1.DERIA5String;
 import org.bouncycastle.asn1.DERNull;
 import org.bouncycastle.asn1.DEROctetString;
 import org.bouncycastle.asn1.misc.MiscObjectIdentifiers;
@@ -64,6 +65,7 @@
 import org.bouncycastle.jce.X509Principal;
 import org.bouncycastle.jce.provider.BouncyCastleProvider;
 import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.Exceptions;
 import org.bouncycastle.util.Integers;
 import org.bouncycastle.util.Properties;
 import org.bouncycastle.util.Strings;
@@ -78,7 +80,6 @@
     protected boolean[] keyUsage;
     protected String sigAlgName;
     protected byte[] sigAlgParams;
-
     X509CertificateImpl(JcaJceHelper bcHelper, org.bouncycastle.asn1.x509.Certificate c,
         BasicConstraints basicConstraints, boolean[] keyUsage, String sigAlgName, byte[] sigAlgParams)
     {
@@ -229,7 +230,7 @@
 
     public boolean[] getIssuerUniqueID()
     {
-        DERBitString    id = c.getTBSCertificate().getIssuerUniqueId();
+        ASN1BitString    id = c.getTBSCertificate().getIssuerUniqueId();
 
         if (id != null)
         {
@@ -249,7 +250,7 @@
 
     public boolean[] getSubjectUniqueID()
     {
-        DERBitString    id = c.getTBSCertificate().getSubjectUniqueId();
+        ASN1BitString id = c.getTBSCertificate().getSubjectUniqueId();
 
         if (id != null)
         {
@@ -297,29 +298,21 @@
             throw new CertificateParsingException("error processing extended key usage extension");
         }
     }
-    
+
     public int getBasicConstraints()
     {
-        if (basicConstraints != null)
+        if (basicConstraints == null || !basicConstraints.isCA())
         {
-            if (basicConstraints.isCA())
-            {
-                if (basicConstraints.getPathLenConstraint() == null)
-                {
-                    return Integer.MAX_VALUE;
-                }
-                else
-                {
-                    return basicConstraints.getPathLenConstraint().intValue();
-                }
-            }
-            else
-            {
-                return -1;
-            }
+            return -1;
         }
 
-        return -1;
+        ASN1Integer pathLenConstraint = basicConstraints.getPathLenConstraintInteger();
+        if (pathLenConstraint == null)
+        {
+            return Integer.MAX_VALUE;
+        }
+
+        return pathLenConstraint.intPositiveValueExact();
     }
 
     public Collection getSubjectAlternativeNames()
@@ -374,7 +367,7 @@
             }
             catch (Exception e)
             {
-                throw new IllegalStateException("error parsing " + e.toString());
+                throw Exceptions.illegalStateException("error parsing " + e.getMessage(), e);
             }
         }
 
@@ -460,20 +453,7 @@
         }
         catch (IOException e)
         {
-            return null;   // should never happen...
-        }
-    }
-
-    public byte[] getEncoded()
-        throws CertificateEncodingException
-    {
-        try
-        {
-            return c.getEncoded(ASN1Encoding.DER);
-        }
-        catch (IOException e)
-        {
-            throw new CertificateEncodingException(e.toString());
+            throw Exceptions.illegalStateException("failed to recover public key: " + e.getMessage(), e);
         }
     }
 
@@ -526,15 +506,15 @@
                         }
                         else if (oid.equals(MiscObjectIdentifiers.netscapeCertType))
                         {
-                            buf.append(new NetscapeCertType(DERBitString.getInstance(dIn.readObject()))).append(nl);
+                            buf.append(new NetscapeCertType(ASN1BitString.getInstance(dIn.readObject()))).append(nl);
                         }
                         else if (oid.equals(MiscObjectIdentifiers.netscapeRevocationURL))
                         {
-                            buf.append(new NetscapeRevocationURL(DERIA5String.getInstance(dIn.readObject()))).append(nl);
+                            buf.append(new NetscapeRevocationURL(ASN1IA5String.getInstance(dIn.readObject()))).append(nl);
                         }
                         else if (oid.equals(MiscObjectIdentifiers.verisignCzagExtension))
                         {
-                            buf.append(new VerisignCzagExtension(DERIA5String.getInstance(dIn.readObject()))).append(nl);
+                            buf.append(new VerisignCzagExtension(ASN1IA5String.getInstance(dIn.readObject()))).append(nl);
                         }
                         else 
                         {
@@ -646,7 +626,7 @@
         {
             List<PublicKey> pubKeys = ((CompositePublicKey)key).getPublicKeys();
             ASN1Sequence keySeq = ASN1Sequence.getInstance(c.getSignatureAlgorithm().getParameters());
-            ASN1Sequence sigSeq = ASN1Sequence.getInstance(DERBitString.getInstance(c.getSignature()).getBytes());
+            ASN1Sequence sigSeq = ASN1Sequence.getInstance(ASN1BitString.getInstance(c.getSignature()).getBytes());
 
             boolean success = false;
             for (int i = 0; i != pubKeys.size(); i++)
@@ -667,7 +647,7 @@
                     checkSignature(
                         (PublicKey)pubKeys.get(i), signature,
                         sigAlg.getParameters(),
-                        DERBitString.getInstance(sigSeq.getObjectAt(i)).getBytes());
+                        ASN1BitString.getInstance(sigSeq.getObjectAt(i)).getBytes());
                     success = true;
                 }
                 catch (SignatureException e)
@@ -689,7 +669,7 @@
         else if (X509SignatureUtil.isCompositeAlgorithm(c.getSignatureAlgorithm()))
         {
             ASN1Sequence keySeq = ASN1Sequence.getInstance(c.getSignatureAlgorithm().getParameters());
-            ASN1Sequence sigSeq = ASN1Sequence.getInstance(DERBitString.getInstance(c.getSignature()).getBytes());
+            ASN1Sequence sigSeq = ASN1Sequence.getInstance(ASN1BitString.getInstance(c.getSignature()).getBytes());
 
             boolean success = false;
             for (int i = 0; i != sigSeq.size(); i++)
@@ -706,7 +686,7 @@
                     checkSignature(
                         key, signature,
                         sigAlg.getParameters(),
-                        DERBitString.getInstance(sigSeq.getObjectAt(i)).getBytes());
+                        ASN1BitString.getInstance(sigSeq.getObjectAt(i)).getBytes());
 
                     success = true;
                 }
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/x509/X509CertificateInternal.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/x509/X509CertificateInternal.java
index b6d8ada..63d3b89 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/x509/X509CertificateInternal.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/x509/X509CertificateInternal.java
@@ -5,25 +5,43 @@
 import org.bouncycastle.asn1.x509.BasicConstraints;
 import org.bouncycastle.jcajce.util.JcaJceHelper;
 
+/**
+ * This class exists to let {@link #equals(Object)} and {@link #hashCode()} methods be delegated efficiently
+ * to the platform default implementations (especially important for compatibility of {@link #hashCode()}
+ * calculations). Those methods fall back to calling {@link #getEncoded()} for third-party subclasses, and
+ * this class allows us to avoid cloning the return value of {@link #getEncoded()} for those callers.
+ */
 class X509CertificateInternal extends X509CertificateImpl
 {
     private final byte[] encoding;
+    private final CertificateEncodingException exception;
 
     X509CertificateInternal(JcaJceHelper bcHelper, org.bouncycastle.asn1.x509.Certificate c,
-        BasicConstraints basicConstraints, boolean[] keyUsage, String sigAlgName, byte[] sigAlgParams, byte[] encoding)
+        BasicConstraints basicConstraints, boolean[] keyUsage, String sigAlgName, byte[] sigAlgParams, byte[] encoding,
+        CertificateEncodingException exception)
     {
         super(bcHelper, c, basicConstraints, keyUsage, sigAlgName, sigAlgParams);
 
         this.encoding = encoding;
+        this.exception = exception;
     }
 
     public byte[] getEncoded() throws CertificateEncodingException
     {
+        if (null != exception)
+        {
+            throw exception;
+        }
+
         if (null == encoding)
         {
             throw new CertificateEncodingException();
         }
 
+        /*
+         * NOTE: Don't clone this return value. See class javadoc for details. Any necessary cloning is
+         * handled by the X509CertificateObject that is holding this instance.
+         */
         return encoding;
     }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/x509/X509CertificateObject.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/x509/X509CertificateObject.java
index f90da30..38b33b2 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/x509/X509CertificateObject.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/x509/X509CertificateObject.java
@@ -1,56 +1,21 @@
 package org.bouncycastle.jcajce.provider.asymmetric.x509;
 
-import java.io.ByteArrayOutputStream;
 import java.io.IOException;
-import java.math.BigInteger;
-import java.net.InetAddress;
-import java.net.UnknownHostException;
-import java.security.InvalidKeyException;
-import java.security.NoSuchAlgorithmException;
-import java.security.NoSuchProviderException;
-import java.security.Principal;
-import java.security.Provider;
 import java.security.PublicKey;
-import java.security.Signature;
-import java.security.SignatureException;
 import java.security.cert.CertificateEncodingException;
-import java.security.cert.CertificateException;
 import java.security.cert.CertificateExpiredException;
 import java.security.cert.CertificateNotYetValidException;
 import java.security.cert.CertificateParsingException;
-import java.security.cert.X509Certificate;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
 import java.util.Date;
 import java.util.Enumeration;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
 
 import javax.security.auth.x500.X500Principal;
 
 import org.bouncycastle.asn1.ASN1BitString;
 import org.bouncycastle.asn1.ASN1Encodable;
 import org.bouncycastle.asn1.ASN1Encoding;
-import org.bouncycastle.asn1.ASN1InputStream;
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
-import org.bouncycastle.asn1.ASN1OutputStream;
 import org.bouncycastle.asn1.ASN1Primitive;
-import org.bouncycastle.asn1.ASN1Sequence;
-import org.bouncycastle.asn1.ASN1String;
-import org.bouncycastle.asn1.DERBitString;
-import org.bouncycastle.asn1.DERIA5String;
-import org.bouncycastle.asn1.DERNull;
-import org.bouncycastle.asn1.DEROctetString;
-import org.bouncycastle.asn1.misc.MiscObjectIdentifiers;
-import org.bouncycastle.asn1.misc.NetscapeCertType;
-import org.bouncycastle.asn1.misc.NetscapeRevocationURL;
-import org.bouncycastle.asn1.misc.VerisignCzagExtension;
-import org.bouncycastle.asn1.util.ASN1Dump;
-import org.bouncycastle.asn1.x500.X500Name;
-import org.bouncycastle.asn1.x500.style.RFC4519Style;
-import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 import org.bouncycastle.asn1.x509.BasicConstraints;
 import org.bouncycastle.asn1.x509.Extension;
 import org.bouncycastle.asn1.x509.Extensions;
@@ -61,12 +26,8 @@
 // END Android-added: Unknown reason
 import org.bouncycastle.jcajce.provider.asymmetric.util.PKCS12BagAttributeCarrierImpl;
 import org.bouncycastle.jcajce.util.JcaJceHelper;
-import org.bouncycastle.jce.X509Principal;
 import org.bouncycastle.jce.interfaces.PKCS12BagAttributeCarrier;
-import org.bouncycastle.jce.provider.BouncyCastleProvider;
-import org.bouncycastle.util.Integers;
-import org.bouncycastle.util.Strings;
-import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.Arrays;
 
 class X509CertificateObject
     extends X509CertificateImpl
@@ -253,6 +214,8 @@
                     return false;
                 }
             }
+
+            return getInternalCertificate().equals(otherBC.getInternalCertificate());
         }
 
         return getInternalCertificate().equals(other);
@@ -317,18 +280,19 @@
             }
         }
 
-        byte[] encoding;
+        byte[] encoding = null;
+        CertificateEncodingException exception = null;
         try
         {
-            encoding = getEncoded();
+            encoding = c.getEncoded(ASN1Encoding.DER);
         }
-        catch (CertificateEncodingException e)
+        catch (IOException e)
         {
-            encoding = null;
+            exception = new X509CertificateEncodingException(e);
         }
 
         X509CertificateInternal temp = new X509CertificateInternal(bcHelper, c, basicConstraints, keyUsage, sigAlgName,
-            sigAlgParams, encoding);
+            sigAlgParams, encoding, exception);
 
         synchronized (cacheLock)
         {
@@ -370,7 +334,7 @@
                 return null;
             }
 
-            ASN1BitString bits = DERBitString.getInstance(ASN1Primitive.fromByteArray(extOctets));
+            ASN1BitString bits = ASN1BitString.getInstance(ASN1Primitive.fromByteArray(extOctets));
 
             byte[] bytes = bits.getBytes();
             int length = (bytes.length * 8) - bits.getPadBits();
@@ -419,4 +383,20 @@
             throw new CertificateParsingException("cannot construct SigAlgParams: " + e);
         }
     }
+
+    private static class X509CertificateEncodingException
+        extends CertificateEncodingException
+    {
+        private final Throwable cause;
+
+        X509CertificateEncodingException(Throwable cause)
+        {
+            this.cause = cause;
+        }
+
+        public Throwable getCause()
+        {
+            return cause;
+        }
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/x509/X509SignatureUtil.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/x509/X509SignatureUtil.java
index f83f213..e893b40 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/x509/X509SignatureUtil.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/x509/X509SignatureUtil.java
@@ -180,6 +180,11 @@
 
     static void prettyPrintSignature(byte[] sig, StringBuffer buf, String nl)
     {
+        // -DM Hex.toHexString
+        // -DM Hex.toHexString
+        // -DM Hex.toHexString
+        // -DM Hex.toHexString
+
         if (sig.length > 20)
         {
             buf.append("            Signature: ").append(Hex.toHexString(sig, 0, 20)).append(nl);
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/config/ConfigurableProvider.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/config/ConfigurableProvider.java
index 831b497..a119425 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/config/ConfigurableProvider.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/config/ConfigurableProvider.java
@@ -45,8 +45,12 @@
 
     void addAlgorithm(String key, String value);
 
+    void addAlgorithm(String key, String value, Map<String, String> attributes);
+
     void addAlgorithm(String type, ASN1ObjectIdentifier oid, String className);
 
+    void addAlgorithm(String type, ASN1ObjectIdentifier oid, String className, Map<String, String> attributes);
+
     boolean hasAlgorithm(String type, String name);
 
     void addKeyInfoConverter(ASN1ObjectIdentifier oid, AsymmetricKeyInfoConverter keyInfoConverter);
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/digest/BCMessageDigest.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/digest/BCMessageDigest.java
index 9c3bddc..b388569 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/digest/BCMessageDigest.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/digest/BCMessageDigest.java
@@ -1,5 +1,6 @@
 package org.bouncycastle.jcajce.provider.digest;
 
+import java.security.DigestException;
 import java.security.MessageDigest;
 
 import org.bouncycastle.crypto.Digest;
@@ -65,4 +66,16 @@
 
         return digestBytes;
     }
+
+    public int engineDigest(byte[] buf, int off, int len) throws DigestException
+    {
+        if (len < digestSize)
+            throw new DigestException("partial digests not returned");
+        if (buf.length - off < digestSize)
+            throw new DigestException("insufficient space in the output buffer to store the digest");
+
+        digest.doFinal(buf, off);
+
+        return digestSize;
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/digest/DigestAlgorithmProvider.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/digest/DigestAlgorithmProvider.java
index 2325f59..a196f3b 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/digest/DigestAlgorithmProvider.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/digest/DigestAlgorithmProvider.java
@@ -23,6 +23,19 @@
         provider.addAlgorithm("Alg.Alias.KeyGenerator.HMAC/" + algorithm, mainName);
     }
 
+    protected void addKMACAlgorithm(
+        ConfigurableProvider provider,
+        String algorithm,
+        String algorithmClassName,
+        String keyGeneratorClassName)
+    {
+        String mainName = "KMAC" + algorithm;
+
+        provider.addAlgorithm("Mac." + mainName, algorithmClassName);
+        provider.addAlgorithm("KeyGenerator." + mainName, keyGeneratorClassName);
+        provider.addAlgorithm("Alg.Alias.KeyGenerator.KMAC" + algorithm, mainName);
+    }
+
     protected void addHMACAlias(
         ConfigurableProvider provider,
         String algorithm,
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/digest/Haraka.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/digest/Haraka.java
new file mode 100644
index 0000000..7877da6
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/digest/Haraka.java
@@ -0,0 +1,67 @@
+package org.bouncycastle.jcajce.provider.digest;
+
+import org.bouncycastle.crypto.digests.Haraka256Digest;
+import org.bouncycastle.crypto.digests.Haraka512Digest;
+import org.bouncycastle.jcajce.provider.config.ConfigurableProvider;
+
+public class Haraka
+{
+    private Haraka()
+    {
+
+    }
+
+    static public class Digest256
+        extends BCMessageDigest
+        implements Cloneable
+    {
+        public Digest256()
+        {
+            super(new Haraka256Digest());
+        }
+
+        public Object clone()
+            throws CloneNotSupportedException
+        {
+            Digest256 d = (Digest256)super.clone();
+            d.digest = new Haraka256Digest((Haraka256Digest)digest);
+
+            return d;
+        }
+    }
+
+    static public class Digest512
+        extends BCMessageDigest
+        implements Cloneable
+    {
+        public Digest512()
+        {
+            super(new Haraka512Digest());
+        }
+
+        public Object clone()
+            throws CloneNotSupportedException
+        {
+            Digest512 d = (Digest512)super.clone();
+            d.digest = new Haraka512Digest((Haraka512Digest)digest);
+
+            return d;
+        }
+    }
+
+    public static class Mappings
+        extends DigestAlgorithmProvider
+    {
+        private static final String PREFIX = Haraka.class.getName();
+
+        public Mappings()
+        {
+        }
+
+        public void configure(ConfigurableProvider provider)
+        {
+            provider.addAlgorithm("MessageDigest.HARAKA-256", PREFIX + "$Digest256");
+            provider.addAlgorithm("MessageDigest.HARAKA-512", PREFIX + "$Digest512");
+        }
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/digest/SHA256.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/digest/SHA256.java
index ae4c82f..da58b08 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/digest/SHA256.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/digest/SHA256.java
@@ -23,14 +23,14 @@
     {
         public Digest()
         {
-            super(new SHA256Digest());
+            super(SHA256Digest.newInstance());
         }
 
         public Object clone()
             throws CloneNotSupportedException
         {
             Digest d = (Digest)super.clone();
-            d.digest = new SHA256Digest((SHA256Digest)digest);
+            d.digest = SHA256Digest.newInstance(digest);
 
             return d;
         }
@@ -41,7 +41,7 @@
     {
         public HashMac()
         {
-            super(new HMac(new SHA256Digest()));
+            super(new HMac(SHA256Digest.newInstance()));
         }
     }
 
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/digest/SHA512.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/digest/SHA512.java
index 335d0d6..203195c 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/digest/SHA512.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/digest/SHA512.java
@@ -176,10 +176,16 @@
 
             provider.addAlgorithm("MessageDigest.SHA-512/224", PREFIX + "$DigestT224");
             provider.addAlgorithm("Alg.Alias.MessageDigest.SHA512/224", "SHA-512/224");
+            provider.addAlgorithm("Alg.Alias.MessageDigest.SHA512224", "SHA-512/224");
+            provider.addAlgorithm("Alg.Alias.MessageDigest.SHA-512(224)", "SHA-512/224");
+            provider.addAlgorithm("Alg.Alias.MessageDigest.SHA512(224)", "SHA-512/224");
             provider.addAlgorithm("Alg.Alias.MessageDigest." + NISTObjectIdentifiers.id_sha512_224, "SHA-512/224");
 
             provider.addAlgorithm("MessageDigest.SHA-512/256", PREFIX + "$DigestT256");
+            provider.addAlgorithm("Alg.Alias.MessageDigest.SHA512/256", "SHA-512/256");
             provider.addAlgorithm("Alg.Alias.MessageDigest.SHA512256", "SHA-512/256");
+            provider.addAlgorithm("Alg.Alias.MessageDigest.SHA-512(256)", "SHA-512/256");
+            provider.addAlgorithm("Alg.Alias.MessageDigest.SHA512(256)", "SHA-512/256");
             provider.addAlgorithm("Alg.Alias.MessageDigest." + NISTObjectIdentifiers.id_sha512_256, "SHA-512/256");
 
             provider.addAlgorithm("Mac.OLDHMACSHA512", PREFIX + "$OldSHA512");
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/drbg/EntropyDaemon.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/drbg/EntropyDaemon.java
new file mode 100644
index 0000000..856b16a
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/drbg/EntropyDaemon.java
@@ -0,0 +1,65 @@
+package org.bouncycastle.jcajce.provider.drbg;
+
+import java.util.LinkedList;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+class EntropyDaemon
+    implements Runnable
+{
+    private static final Logger LOG = Logger.getLogger(EntropyDaemon.class.getName());
+
+    private final LinkedList<Runnable> tasks = new LinkedList<Runnable>();
+
+    public EntropyDaemon()
+    {
+    }
+
+    void addTask(Runnable task)
+    {
+        synchronized (tasks)
+        {
+            tasks.add(task);
+        }
+    }
+
+    public void run()
+    {
+        while (!Thread.currentThread().isInterrupted())
+        {
+            Runnable task;
+            synchronized (tasks)
+            {
+                task = tasks.poll();
+            }
+
+            if (task != null)
+            {
+                try
+                {
+                    task.run();
+                }
+                catch (Throwable e)
+                {
+                    // ignore
+                }
+            }
+            else
+            {
+                try
+                {
+                    Thread.sleep(5000);
+                }
+                catch (InterruptedException e)
+                {
+                    Thread.currentThread().interrupt();
+                }
+            }
+        }
+
+        if (LOG.isLoggable(Level.FINE))
+        {
+            LOG.fine("entropy thread interrupted - exiting");
+        }
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/keystore/BC.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/keystore/BC.java
index 5bc0aa8..f84bc67 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/keystore/BC.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/keystore/BC.java
@@ -2,6 +2,7 @@
 
 import org.bouncycastle.jcajce.provider.config.ConfigurableProvider;
 import org.bouncycastle.jcajce.provider.util.AsymmetricAlgorithmProvider;
+import org.bouncycastle.util.Properties;
 
 public class BC
 {
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/keystore/bc/BcKeyStoreSpi.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/keystore/bc/BcKeyStoreSpi.java
index 5ec21c8..2434da2 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/keystore/bc/BcKeyStoreSpi.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/keystore/bc/BcKeyStoreSpi.java
@@ -57,6 +57,7 @@
 import org.bouncycastle.jce.interfaces.BCKeyStore;
 import org.bouncycastle.jce.provider.BouncyCastleProvider;
 import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.Properties;
 import org.bouncycastle.util.io.Streams;
 import org.bouncycastle.util.io.TeeOutputStream;
 
@@ -394,6 +395,11 @@
     {
         byte[]      enc = key.getEncoded();
 
+        if (enc == null)
+        {
+            throw new IOException("unable to store encoding of protected key");
+        }
+
         if (key instanceof PrivateKey)
         {
             dOut.write(KEY_PRIVATE);
@@ -672,9 +678,18 @@
         Certificate[]   chain) 
         throws KeyStoreException
     {
-        if ((key instanceof PrivateKey) && (chain == null))
+        if ((key instanceof PrivateKey))
         {
-            throw new KeyStoreException("no certificate chain for private key");
+            if (chain == null)
+            {
+                throw new KeyStoreException("no certificate chain for private key");
+            }
+            if (key.getEncoded() == null)
+            {
+                // we ingore the password as the key is already protected.
+                table.put(alias, new StoreEntry(alias, new Date(), KEY, key, chain));
+                return;
+            }
         }
 
         try
@@ -1118,6 +1133,10 @@
         public Version1()
         {
             super(1);
+            if (!Properties.isOverrideSet("org.bouncycastle.bks.enable_v1"))
+            {
+                 throw new IllegalStateException("BKS-V1 not enabled");
+            }
         }
     }
 
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/keystore/pkcs12/PKCS12KeyStoreSpi.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/keystore/pkcs12/PKCS12KeyStoreSpi.java
index 263c63d..5077097 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/keystore/pkcs12/PKCS12KeyStoreSpi.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/keystore/pkcs12/PKCS12KeyStoreSpi.java
@@ -2,6 +2,7 @@
 
 import java.io.BufferedInputStream;
 import java.io.ByteArrayInputStream;
+import java.io.EOFException;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
@@ -46,6 +47,7 @@
 import javax.crypto.spec.PBEKeySpec;
 import javax.crypto.spec.PBEParameterSpec;
 
+import org.bouncycastle.asn1.ASN1BMPString;
 import org.bouncycastle.asn1.ASN1Encodable;
 import org.bouncycastle.asn1.ASN1EncodableVector;
 import org.bouncycastle.asn1.ASN1Encoding;
@@ -65,6 +67,7 @@
 // Android-removed: Unsupported algorithms
 // import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers;
 // import org.bouncycastle.asn1.cryptopro.GOST28147Parameters;
+// import org.bouncycastle.asn1.misc.MiscObjectIdentifiers;
 import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
 // import org.bouncycastle.asn1.ntt.NTTObjectIdentifiers;
 import org.bouncycastle.asn1.oiw.OIWObjectIdentifiers;
@@ -83,9 +86,13 @@
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 import org.bouncycastle.asn1.x509.AuthorityKeyIdentifier;
 import org.bouncycastle.asn1.x509.DigestInfo;
+import org.bouncycastle.asn1.x509.ExtendedKeyUsage;
 import org.bouncycastle.asn1.x509.Extension;
+import org.bouncycastle.asn1.x509.Extensions;
+import org.bouncycastle.asn1.x509.KeyPurposeId;
 import org.bouncycastle.asn1.x509.SubjectKeyIdentifier;
 import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.bouncycastle.asn1.x509.TBSCertificate;
 import org.bouncycastle.asn1.x509.X509ObjectIdentifiers;
 import org.bouncycastle.crypto.CryptoServicesRegistrar;
 import org.bouncycastle.crypto.Digest;
@@ -96,6 +103,9 @@
 import org.bouncycastle.jcajce.PKCS12StoreParameter;
 // Android-removed: Unsupported algorithms
 // import org.bouncycastle.jcajce.spec.GOST28147ParameterSpec;
+// import org.bouncycastle.jcajce.BCLoadStoreParameter;
+import org.bouncycastle.jcajce.provider.keystore.util.AdaptingKeyStoreSpi;
+import org.bouncycastle.jcajce.provider.keystore.util.ParameterUtil;
 import org.bouncycastle.jcajce.spec.PBKDF2KeySpec;
 import org.bouncycastle.jcajce.util.BCJcaJceHelper;
 import org.bouncycastle.jcajce.util.DefaultJcaJceHelper;
@@ -129,7 +139,7 @@
     private static final DefaultSecretKeyProvider keySizeProvider = new DefaultSecretKeyProvider();
 
     private IgnoresCaseHashtable keys = new IgnoresCaseHashtable();
-    private Hashtable localIds = new Hashtable();
+    private IgnoresCaseHashtable localIds = new IgnoresCaseHashtable();
     private IgnoresCaseHashtable certs = new IgnoresCaseHashtable();
     private Hashtable chainCerts = new Hashtable();
     private Hashtable keyCerts = new Hashtable();
@@ -253,6 +263,12 @@
         this.random = rand;
     }
 
+    public boolean engineProbe(InputStream stream)
+        throws IOException
+    {
+        return false;
+    }
+
     public Enumeration engineAliases()
     {
         Hashtable tab = new Hashtable();
@@ -291,25 +307,23 @@
         String alias)
         throws KeyStoreException
     {
-        Key k = (Key)keys.remove(alias);
-
-        Certificate c = (Certificate)certs.remove(alias);
-
-        if (c != null)
+        Certificate cert = (Certificate)certs.remove(alias);
+        if (cert != null)
         {
-            chainCerts.remove(new CertId(c.getPublicKey()));
+            chainCerts.remove(new CertId(cert.getPublicKey()));
         }
 
-        if (k != null)
+        Key key = (Key)keys.remove(alias);
+        if (key != null)
         {
             String id = (String)localIds.remove(alias);
             if (id != null)
             {
-                c = (Certificate)keyCerts.remove(id);
-            }
-            if (c != null)
-            {
-                chainCerts.remove(new CertId(c.getPublicKey()));
+                Certificate keyCert = (Certificate)keyCerts.remove(id);
+                if (keyCert != null)
+                {
+                    chainCerts.remove(new CertId(keyCert.getPublicKey()));
+                }
             }
         }
     }
@@ -654,7 +668,7 @@
 
         try
         {
-            SecretKeyFactory keyFact =  helper.createSecretKeyFactory(algorithm);
+            SecretKeyFactory keyFact = helper.createSecretKeyFactory(algorithm);
             PBEParameterSpec defParams = new PBEParameterSpec(
                 pbeParams.getIV(),
                 pbeParams.getIterations().intValue());
@@ -704,7 +718,7 @@
                 throw new IOException("exception decrypting data - " + e.toString());
             }
         }
-        else  if (algorithm.equals(PKCSObjectIdentifiers.id_PBES2))
+        else if (algorithm.equals(PKCSObjectIdentifiers.id_PBES2))
         {
             try
             {
@@ -767,6 +781,31 @@
         return cipher;
     }
 
+  
+    // BEGIN Android-removed: Unsupported algorithms
+    /*
+    public void engineLoad(KeyStore.LoadStoreParameter loadStoreParameter)
+        throws IOException, NoSuchAlgorithmException, CertificateException
+    {
+        if (loadStoreParameter == null)
+        {
+            engineLoad(null, null);
+        }
+        else if (loadStoreParameter instanceof BCLoadStoreParameter)
+        {
+            BCLoadStoreParameter bcParam = (BCLoadStoreParameter)loadStoreParameter;
+
+            engineLoad(bcParam.getInputStream(), ParameterUtil.extractPassword(loadStoreParameter));
+        }
+        else
+        {
+            throw new IllegalArgumentException(
+                "no support for 'param' of type " + loadStoreParameter.getClass().getName());
+        }
+    }
+    */
+    // END Android-removed: Unsupported algorithms
+
     public void engineLoad(
         InputStream stream,
         char[] password)
@@ -782,7 +821,10 @@
         bufIn.mark(10);
 
         int head = bufIn.read();
-
+        if (head < 0)
+        {
+            throw new EOFException("no data in keystore stream");
+        }
         if (head != 0x30)
         {
             throw new IOException("stream does not represent a PKCS12 key store");
@@ -791,7 +833,7 @@
         bufIn.reset();
 
         ASN1InputStream bIn = new ASN1InputStream(bufIn);
-        
+
         Pfx bag;
         try
         {
@@ -868,7 +910,7 @@
         // END Android-removed: keep v1.61 behaviour to keep backwards-compatibility
 
         keys = new IgnoresCaseHashtable();
-        localIds = new Hashtable();
+        localIds = new IgnoresCaseHashtable();
 
         if (info.getContentType().equals(data))
         {
@@ -888,86 +930,19 @@
                         SafeBag b = SafeBag.getInstance(seq.getObjectAt(j));
                         if (b.getBagId().equals(pkcs8ShroudedKeyBag))
                         {
-                            org.bouncycastle.asn1.pkcs.EncryptedPrivateKeyInfo eIn = org.bouncycastle.asn1.pkcs.EncryptedPrivateKeyInfo.getInstance(b.getBagValue());
-                            PrivateKey privKey = unwrapKey(eIn.getEncryptionAlgorithm(), eIn.getEncryptedData(), password, wrongPKCS12Zero);
-
-                            //
-                            // set the attributes on the key
-                            //
-                            String alias = null;
-                            ASN1OctetString localId = null;
-
-                            if (b.getBagAttributes() != null)
-                            {
-                                Enumeration e = b.getBagAttributes().getObjects();
-                                while (e.hasMoreElements())
-                                {
-                                    ASN1Sequence sq = (ASN1Sequence)e.nextElement();
-                                    ASN1ObjectIdentifier aOid = (ASN1ObjectIdentifier)sq.getObjectAt(0);
-                                    ASN1Set attrSet = (ASN1Set)sq.getObjectAt(1);
-                                    ASN1Primitive attr = null;
-
-                                    if (attrSet.size() > 0)
-                                    {
-                                        attr = (ASN1Primitive)attrSet.getObjectAt(0);
-
-                                        if (privKey instanceof PKCS12BagAttributeCarrier)
-                                        {
-                                            PKCS12BagAttributeCarrier bagAttr = (PKCS12BagAttributeCarrier)privKey;
-                                            ASN1Encodable existing = bagAttr.getBagAttribute(aOid);
-                                            if (existing != null)
-                                            {
-                                                // OK, but the value has to be the same
-                                                if (!existing.toASN1Primitive().equals(attr))
-                                                {
-                                                    throw new IOException(
-                                                        "attempt to add existing attribute with different value");
-                                                }
-                                            }
-                                            else
-                                            {
-                                                bagAttr.setBagAttribute(aOid, attr);
-                                            }
-                                        }
-                                    }
-
-                                    if (aOid.equals(pkcs_9_at_friendlyName))
-                                    {
-                                        alias = ((DERBMPString)attr).getString();
-                                        keys.put(alias, privKey);
-                                    }
-                                    else if (aOid.equals(pkcs_9_at_localKeyId))
-                                    {
-                                        localId = (ASN1OctetString)attr;
-                                    }
-                                }
-                            }
-
-                            if (localId != null)
-                            {
-                                String name = new String(Hex.encode(localId.getOctets()));
-
-                                if (alias == null)
-                                {
-                                    keys.put(name, privKey);
-                                }
-                                else
-                                {
-                                    localIds.put(alias, name);
-                                }
-                            }
-                            else
-                            {
-                                unmarkedKey = true;
-                                keys.put("unmarked", privKey);
-                            }
+                            unmarkedKey = processShroudedKeyBag(b, password, wrongPKCS12Zero);
                         }
                         else if (b.getBagId().equals(certBag))
                         {
                             chain.addElement(b);
                         }
+                        else if (b.getBagId().equals(keyBag))
+                        {
+                            processKeyBag(b);
+                        }
                         else
                         {
+                            // -DM 2 System.out.println
                             System.out.println("extra in data " + b.getBagId());
                             System.out.println(ASN1Dump.dumpAsString(b));
                         }
@@ -983,137 +958,21 @@
                     for (int j = 0; j != seq.size(); j++)
                     {
                         SafeBag b = SafeBag.getInstance(seq.getObjectAt(j));
-
                         if (b.getBagId().equals(certBag))
                         {
                             chain.addElement(b);
                         }
                         else if (b.getBagId().equals(pkcs8ShroudedKeyBag))
                         {
-                            org.bouncycastle.asn1.pkcs.EncryptedPrivateKeyInfo eIn = org.bouncycastle.asn1.pkcs.EncryptedPrivateKeyInfo.getInstance(b.getBagValue());
-                            PrivateKey privKey = unwrapKey(eIn.getEncryptionAlgorithm(), eIn.getEncryptedData(), password, wrongPKCS12Zero);
-
-                            //
-                            // set the attributes on the key
-                            //
-                            PKCS12BagAttributeCarrier bagAttr = (PKCS12BagAttributeCarrier)privKey;
-                            String alias = null;
-                            ASN1OctetString localId = null;
-
-                            Enumeration e = b.getBagAttributes().getObjects();
-                            while (e.hasMoreElements())
-                            {
-                                ASN1Sequence sq = (ASN1Sequence)e.nextElement();
-                                ASN1ObjectIdentifier aOid = (ASN1ObjectIdentifier)sq.getObjectAt(0);
-                                ASN1Set attrSet = (ASN1Set)sq.getObjectAt(1);
-                                ASN1Primitive attr = null;
-
-                                if (attrSet.size() > 0)
-                                {
-                                    attr = (ASN1Primitive)attrSet.getObjectAt(0);
-
-                                    ASN1Encodable existing = bagAttr.getBagAttribute(aOid);
-                                    if (existing != null)
-                                    {
-                                        // OK, but the value has to be the same
-                                        if (!existing.toASN1Primitive().equals(attr))
-                                        {
-                                            throw new IOException(
-                                                "attempt to add existing attribute with different value");
-                                        }
-                                    }
-                                    else
-                                    {
-                                        bagAttr.setBagAttribute(aOid, attr);
-                                    }
-                                }
-
-                                if (aOid.equals(pkcs_9_at_friendlyName))
-                                {
-                                    alias = ((DERBMPString)attr).getString();
-                                    keys.put(alias, privKey);
-                                }
-                                else if (aOid.equals(pkcs_9_at_localKeyId))
-                                {
-                                    localId = (ASN1OctetString)attr;
-                                }
-                            }
-
-                            String name = new String(Hex.encode(localId.getOctets()));
-
-                            if (alias == null)
-                            {
-                                keys.put(name, privKey);
-                            }
-                            else
-                            {
-                                localIds.put(alias, name);
-                            }
+                            unmarkedKey = processShroudedKeyBag(b, password, wrongPKCS12Zero);
                         }
                         else if (b.getBagId().equals(keyBag))
                         {
-                            org.bouncycastle.asn1.pkcs.PrivateKeyInfo kInfo = org.bouncycastle.asn1.pkcs.PrivateKeyInfo.getInstance(b.getBagValue());
-                            PrivateKey privKey = BouncyCastleProvider.getPrivateKey(kInfo);
-
-                            //
-                            // set the attributes on the key
-                            //
-                            PKCS12BagAttributeCarrier bagAttr = (PKCS12BagAttributeCarrier)privKey;
-                            String alias = null;
-                            ASN1OctetString localId = null;
-
-                            Enumeration e = b.getBagAttributes().getObjects();
-                            while (e.hasMoreElements())
-                            {
-                                ASN1Sequence sq = ASN1Sequence.getInstance(e.nextElement());
-                                ASN1ObjectIdentifier aOid = ASN1ObjectIdentifier.getInstance(sq.getObjectAt(0));
-                                ASN1Set attrSet = ASN1Set.getInstance(sq.getObjectAt(1));
-                                ASN1Primitive attr = null;
-
-                                if (attrSet.size() > 0)
-                                {
-                                    attr = (ASN1Primitive)attrSet.getObjectAt(0);
-
-                                    ASN1Encodable existing = bagAttr.getBagAttribute(aOid);
-                                    if (existing != null)
-                                    {
-                                        // OK, but the value has to be the same
-                                        if (!existing.toASN1Primitive().equals(attr))
-                                        {
-                                            throw new IOException(
-                                                "attempt to add existing attribute with different value");
-                                        }
-                                    }
-                                    else
-                                    {
-                                        bagAttr.setBagAttribute(aOid, attr);
-                                    }
-
-                                    if (aOid.equals(pkcs_9_at_friendlyName))
-                                    {
-                                        alias = ((DERBMPString)attr).getString();
-                                        keys.put(alias, privKey);
-                                    }
-                                    else if (aOid.equals(pkcs_9_at_localKeyId))
-                                    {
-                                        localId = (ASN1OctetString)attr;
-                                    }
-                                }
-                            }
-
-                            String name = new String(Hex.encode(localId.getOctets()));
-
-                            if (alias == null)
-                            {
-                                keys.put(name, privKey);
-                            }
-                            else
-                            {
-                                localIds.put(alias, name);
-                            }
+                            processKeyBag(b);
                         }
                         else
                         {
+                            // -DM 2 System.out.println
                             System.out.println("extra in encryptedData " + b.getBagId());
                             System.out.println(ASN1Dump.dumpAsString(b));
                         }
@@ -1121,6 +980,7 @@
                 }
                 else
                 {
+                    // -DM 2 System.out.println
                     System.out.println("extra " + c[i].getContentType().getId());
                     System.out.println("extra " + ASN1Dump.dumpAsString(c[i].getContent()));
                 }
@@ -1181,6 +1041,17 @@
                             ASN1Encodable existing = bagAttr.getBagAttribute(oid);
                             if (existing != null)
                             {
+                                // we've found more than one - one might be incorrect
+                                if (oid.equals(pkcs_9_at_localKeyId))
+                                {
+                                    // -DM Hex.toHexString
+                                    String id = Hex.toHexString(((ASN1OctetString)attr).getOctets());
+                                    if (!(keys.keys.containsKey(id) || localIds.keys.containsKey(id)))
+                                    {
+                                        continue; // ignore this one - it's not valid
+                                    }
+                                }
+
                                 // OK, but the value has to be the same
                                 if (!existing.toASN1Primitive().equals(attr))
                                 {
@@ -1190,13 +1061,20 @@
                             }
                             else
                             {
-                                bagAttr.setBagAttribute(oid, attr);
+                                if (attrSet.size() > 1)
+                                {
+                                    bagAttr.setBagAttribute(oid, attrSet);
+                                }
+                                else
+                                {
+                                    bagAttr.setBagAttribute(oid, attr);
+                                }
                             }
                         }
 
                         if (oid.equals(pkcs_9_at_friendlyName))
                         {
-                            alias = ((DERBMPString)attr).getString();
+                            alias = ((ASN1BMPString)attr).getString();
                         }
                         else if (oid.equals(pkcs_9_at_localKeyId))
                         {
@@ -1237,6 +1115,149 @@
         }
     }
 
+    private boolean processShroudedKeyBag(SafeBag b, char[] password, boolean wrongPKCS12Zero)
+        throws IOException
+    {
+        org.bouncycastle.asn1.pkcs.EncryptedPrivateKeyInfo eIn = org.bouncycastle.asn1.pkcs.EncryptedPrivateKeyInfo.getInstance(b.getBagValue());
+        PrivateKey privKey = unwrapKey(eIn.getEncryptionAlgorithm(), eIn.getEncryptedData(), password, wrongPKCS12Zero);
+
+        //
+        // set the attributes on the key
+        //
+        String alias = null;
+        ASN1OctetString localId = null;
+
+        if (b.getBagAttributes() != null)
+        {
+            Enumeration e = b.getBagAttributes().getObjects();
+            while (e.hasMoreElements())
+            {
+                ASN1Sequence sq = (ASN1Sequence)e.nextElement();
+                ASN1ObjectIdentifier aOid = (ASN1ObjectIdentifier)sq.getObjectAt(0);
+                ASN1Set attrSet = (ASN1Set)sq.getObjectAt(1);
+                ASN1Primitive attr = null;
+
+                if (attrSet.size() > 0)
+                {
+                    attr = (ASN1Primitive)attrSet.getObjectAt(0);
+
+                    if (privKey instanceof PKCS12BagAttributeCarrier)
+                    {
+                        PKCS12BagAttributeCarrier bagAttr = (PKCS12BagAttributeCarrier)privKey;
+                        ASN1Encodable existing = bagAttr.getBagAttribute(aOid);
+                        if (existing != null)
+                        {
+                            // OK, but the value has to be the same
+                            if (!existing.toASN1Primitive().equals(attr))
+                            {
+                                throw new IOException(
+                                    "attempt to add existing attribute with different value");
+                            }
+                        }
+                        else
+                        {
+                            bagAttr.setBagAttribute(aOid, attr);
+                        }
+                    }
+                }
+
+                if (aOid.equals(pkcs_9_at_friendlyName))
+                {
+                    alias = ((ASN1BMPString)attr).getString();
+                    keys.put(alias, privKey);
+                }
+                else if (aOid.equals(pkcs_9_at_localKeyId))
+                {
+                    localId = (ASN1OctetString)attr;
+                }
+            }
+        }
+
+        if (localId != null)
+        {
+            String name = new String(Hex.encode(localId.getOctets()));
+
+            if (alias == null)
+            {
+                keys.put(name, privKey);
+            }
+            else
+            {
+                localIds.put(alias, name);
+            }
+            return false;  // key properly marked
+        }
+        else
+        {
+            keys.put("unmarked", privKey);
+            return true;  // key properly marked
+        }
+    }
+
+    private void processKeyBag(SafeBag b)
+        throws IOException
+    {
+        org.bouncycastle.asn1.pkcs.PrivateKeyInfo kInfo = org.bouncycastle.asn1.pkcs.PrivateKeyInfo.getInstance(b.getBagValue());
+        PrivateKey privKey = BouncyCastleProvider.getPrivateKey(kInfo);
+
+        //
+        // set the attributes on the key
+        //
+        PKCS12BagAttributeCarrier bagAttr = (PKCS12BagAttributeCarrier)privKey;
+        String alias = null;
+        ASN1OctetString localId = null;
+
+        Enumeration e = b.getBagAttributes().getObjects();
+        while (e.hasMoreElements())
+        {
+            ASN1Sequence sq = ASN1Sequence.getInstance(e.nextElement());
+            ASN1ObjectIdentifier aOid = ASN1ObjectIdentifier.getInstance(sq.getObjectAt(0));
+            ASN1Set attrSet = ASN1Set.getInstance(sq.getObjectAt(1));
+            ASN1Primitive attr = null;
+
+            if (attrSet.size() > 0)
+            {
+                attr = (ASN1Primitive)attrSet.getObjectAt(0);
+
+                ASN1Encodable existing = bagAttr.getBagAttribute(aOid);
+                if (existing != null)
+                {
+                    // OK, but the value has to be the same
+                    if (!existing.toASN1Primitive().equals(attr))
+                    {
+                        throw new IOException(
+                            "attempt to add existing attribute with different value");
+                    }
+                }
+                else
+                {
+                    bagAttr.setBagAttribute(aOid, attr);
+                }
+
+                if (aOid.equals(pkcs_9_at_friendlyName))
+                {
+                    alias = ((ASN1BMPString)attr).getString();
+                    keys.put(alias, privKey);
+                }
+                else if (aOid.equals(pkcs_9_at_localKeyId))
+                {
+                    localId = (ASN1OctetString)attr;
+                }
+            }
+        }
+
+        String name = new String(Hex.encode(localId.getOctets()));
+
+        if (alias == null)
+        {
+            keys.put(name, privKey);
+        }
+        else
+        {
+            localIds.put(alias, name);
+        }
+    }
+
     private int validateIterationCount(BigInteger i)
     {
         int count = i.intValue();
@@ -1318,7 +1339,55 @@
         // See CtsKeystoreTestCases:android.keystore.cts.KeyStoreTest
         if (password == null)
         {
-            throw new NullPointerException("No password supplied for PKCS#12 KeyStore.");
+            if (password == null)
+            {
+                Enumeration cs = certs.keys();
+
+                ASN1EncodableVector certSeq = new ASN1EncodableVector();
+
+                while (cs.hasMoreElements())
+                {
+                    try
+                    {
+                        String certId = (String)cs.nextElement();
+                        Certificate cert = (Certificate)certs.get(certId);
+
+                        SafeBag sBag = createSafeBag(certId, cert);
+
+                        certSeq.add(sBag);
+                    }
+                    catch (CertificateEncodingException e)
+                    {
+                        throw new IOException("Error encoding certificate: " + e.toString());
+                    }
+                }
+
+                if (useDEREncoding)
+                {
+                    ContentInfo bagInfo = new ContentInfo(PKCSObjectIdentifiers.data, new DEROctetString(new DERSequence(certSeq).getEncoded()));
+
+                    Pfx pfx = new Pfx(new ContentInfo(PKCSObjectIdentifiers.data, new DEROctetString(new DERSequence(bagInfo).getEncoded())), null);
+
+                    pfx.encodeTo(stream, ASN1Encoding.DER);
+                }
+                else
+                {
+                    ContentInfo bagInfo = new ContentInfo(PKCSObjectIdentifiers.data, new BEROctetString(new BERSequence(certSeq).getEncoded()));
+
+                    Pfx pfx = new Pfx(new ContentInfo(PKCSObjectIdentifiers.data, new BEROctetString(new BERSequence(bagInfo).getEncoded())), null);
+
+                    pfx.encodeTo(stream, ASN1Encoding.BER);
+                }
+
+                return;
+            }
+        }
+        else
+        {
+            if (password == null)
+            {
+                throw new NullPointerException("no password supplied for PKCS#12 KeyStore");
+            }
         }
         /*
         if (keys.size() == 0)
@@ -1405,7 +1474,7 @@
                 //
                 // make sure we are using the local alias on store
                 //
-                DERBMPString nm = (DERBMPString)bagAttrs.getBagAttribute(pkcs_9_at_friendlyName);
+                ASN1BMPString nm = (ASN1BMPString)bagAttrs.getBagAttribute(pkcs_9_at_friendlyName);
                 if (nm == null || !nm.getString().equals(name))
                 {
                     bagAttrs.setBagAttribute(pkcs_9_at_friendlyName, new DERBMPString(name));
@@ -1496,7 +1565,7 @@
                     //
                     // make sure we are using the local alias on store
                     //
-                    DERBMPString nm = (DERBMPString)bagAttrs.getBagAttribute(pkcs_9_at_friendlyName);
+                    ASN1BMPString nm = (ASN1BMPString)bagAttrs.getBagAttribute(pkcs_9_at_friendlyName);
                     if (nm == null || !nm.getString().equals(name))
                     {
                         bagAttrs.setBagAttribute(pkcs_9_at_friendlyName, new DERBMPString(name));
@@ -1629,6 +1698,7 @@
                     }
                 }
 
+
                 SafeBag sBag = new SafeBag(certBag, cBag.toASN1Primitive(), new DERSet(fName));
 
                 certSeq.add(sBag);
@@ -1702,7 +1772,7 @@
             //
             // make sure we are using the local alias on store
             //
-            DERBMPString nm = (DERBMPString)bagAttrs.getBagAttribute(pkcs_9_at_friendlyName);
+            ASN1BMPString nm = (ASN1BMPString)bagAttrs.getBagAttribute(pkcs_9_at_friendlyName);
             if (nm == null || !nm.getString().equals(certId))
             {
                 if (certId != null)
@@ -1746,6 +1816,43 @@
             fName.add(new DERSequence(fSeq));
         }
 
+        // Android-removed: unsupported
+        // add the trusted usage attribute - needed for Oracle key stores
+        // if (cert instanceof X509Certificate)
+        // {
+        //     TBSCertificate tbsCert = TBSCertificate.getInstance(((X509Certificate)cert).getTBSCertificate());
+        //     Extensions exts = tbsCert.getExtensions();
+        //     if (exts != null)
+        //     {
+        //         Extension extUsage = exts.getExtension(Extension.extendedKeyUsage);
+        //         if (extUsage != null)
+        //         {
+        //             ASN1EncodableVector fSeq = new ASN1EncodableVector();
+
+        //             // oracle trusted key usage OID.
+        //             fSeq.add(MiscObjectIdentifiers.id_oracle_pkcs12_trusted_key_usage);
+        //             fSeq.add(new DERSet(ExtendedKeyUsage.getInstance(extUsage.getParsedValue()).getUsages()));
+        //             fName.add(new DERSequence(fSeq));
+        //         }
+        //         else
+        //         {
+        //             ASN1EncodableVector fSeq = new ASN1EncodableVector();
+
+        //             fSeq.add(MiscObjectIdentifiers.id_oracle_pkcs12_trusted_key_usage);
+        //             fSeq.add(new DERSet(KeyPurposeId.anyExtendedKeyUsage));
+        //             fName.add(new DERSequence(fSeq));
+        //         }
+        //     }
+        //     else
+        //     {
+        //         ASN1EncodableVector fSeq = new ASN1EncodableVector();
+
+        //         fSeq.add(MiscObjectIdentifiers.id_oracle_pkcs12_trusted_key_usage);
+        //         fSeq.add(new DERSet(KeyPurposeId.anyExtendedKeyUsage));
+        //         fName.add(new DERSequence(fSeq));
+        //     }
+        // }
+
         return new SafeBag(certBag, cBag.toASN1Primitive(), new DERSet(fName));
     }
 
@@ -1753,19 +1860,19 @@
     {
         Set usedSet = new HashSet();
 
-        for (Enumeration en = keys.keys(); en.hasMoreElements();)
+        for (Enumeration en = keys.keys(); en.hasMoreElements(); )
         {
             String alias = (String)en.nextElement();
 
-                Certificate[] certs = engineGetCertificateChain(alias);
+            Certificate[] certs = engineGetCertificateChain(alias);
 
-                for (int i = 0; i != certs.length; i++)
-                {
-                    usedSet.add(certs[i]);
-                }
+            for (int i = 0; i != certs.length; i++)
+            {
+                usedSet.add(certs[i]);
+            }
         }
 
-        for (Enumeration en = certs.keys(); en.hasMoreElements();)
+        for (Enumeration en = certs.keys(); en.hasMoreElements(); )
         {
             String alias = (String)en.nextElement();
 
@@ -1797,13 +1904,14 @@
         return mac.doFinal();
     }
 
+    // Android-changed: Use default provider for JCA algorithms instead of BC
     public static class BCPKCS12KeyStore
         extends PKCS12KeyStoreSpi
     {
         public BCPKCS12KeyStore()
         {
             // Android-changed: Use default provider for JCA algorithms instead of BC
-            // Was: super(new BCJcaJceHelper(), pbeWithSHAAnd3_KeyTripleDES_CBC, pbeWithSHAAnd40BitRC2_CBC);
+            // Was: super(new BCJcaJceHelper(), new PKCS12KeyStoreSpi(new BCJcaJceHelper(), pbeWithSHAAnd3_KeyTripleDES_CBC, pbeWithSHAAnd40BitRC2_CBC));
             super(new DefaultJcaJceHelper(), pbeWithSHAAnd3_KeyTripleDES_CBC, pbeWithSHAAnd40BitRC2_CBC);
         }
     }
@@ -1811,29 +1919,29 @@
     // BEGIN Android-removed: Unsupported algorithms
     /*
     public static class BCPKCS12KeyStore3DES
-        extends PKCS12KeyStoreSpi
+        extends AdaptingKeyStoreSpi
     {
         public BCPKCS12KeyStore3DES()
         {
-            super(new BCJcaJceHelper(), pbeWithSHAAnd3_KeyTripleDES_CBC, pbeWithSHAAnd3_KeyTripleDES_CBC);
+            super(new BCJcaJceHelper(), new PKCS12KeyStoreSpi(new BCJcaJceHelper(), pbeWithSHAAnd3_KeyTripleDES_CBC, pbeWithSHAAnd3_KeyTripleDES_CBC));
         }
     }
 
     public static class DefPKCS12KeyStore
-        extends PKCS12KeyStoreSpi
+        extends AdaptingKeyStoreSpi
     {
         public DefPKCS12KeyStore()
         {
-            super(new DefaultJcaJceHelper(), pbeWithSHAAnd3_KeyTripleDES_CBC, pbeWithSHAAnd40BitRC2_CBC);
+            super(new DefaultJcaJceHelper(), new PKCS12KeyStoreSpi(new DefaultJcaJceHelper(), pbeWithSHAAnd3_KeyTripleDES_CBC, pbeWithSHAAnd40BitRC2_CBC));
         }
     }
 
     public static class DefPKCS12KeyStore3DES
-        extends PKCS12KeyStoreSpi
+        extends AdaptingKeyStoreSpi
     {
         public DefPKCS12KeyStore3DES()
         {
-            super(new DefaultJcaJceHelper(), pbeWithSHAAnd3_KeyTripleDES_CBC, pbeWithSHAAnd3_KeyTripleDES_CBC);
+            super(new DefaultJcaJceHelper(), new PKCS12KeyStoreSpi(new DefaultJcaJceHelper(), pbeWithSHAAnd3_KeyTripleDES_CBC, pbeWithSHAAnd3_KeyTripleDES_CBC));
         }
     }
     */
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/keystore/util/AdaptingKeyStoreSpi.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/keystore/util/AdaptingKeyStoreSpi.java
new file mode 100644
index 0000000..74e34d3
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/keystore/util/AdaptingKeyStoreSpi.java
@@ -0,0 +1,184 @@
+package org.bouncycastle.jcajce.provider.keystore.util;
+
+import java.io.BufferedInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.security.Key;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.KeyStoreSpi;
+import java.security.NoSuchAlgorithmException;
+import java.security.UnrecoverableKeyException;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateException;
+import java.util.Date;
+import java.util.Enumeration;
+
+import org.bouncycastle.jcajce.provider.keystore.pkcs12.PKCS12KeyStoreSpi;
+import org.bouncycastle.jcajce.util.JcaJceHelper;
+import org.bouncycastle.util.Properties;
+
+/**
+ * Implements a certificate only JKS key store.
+ */
+public class AdaptingKeyStoreSpi
+    extends KeyStoreSpi
+{
+    public static final String COMPAT_OVERRIDE = "keystore.type.compat";
+
+    private final JKSKeyStoreSpi jksStore;
+    private final KeyStoreSpi primaryStore;
+
+    private KeyStoreSpi keyStoreSpi;
+
+    public AdaptingKeyStoreSpi(JcaJceHelper helper, KeyStoreSpi primaryStore)
+    {
+        this.jksStore = new JKSKeyStoreSpi(helper);
+        this.primaryStore = primaryStore;
+        this.keyStoreSpi = primaryStore;
+    }
+
+    public boolean engineProbe(InputStream stream)
+        throws IOException
+    {
+        if (keyStoreSpi instanceof PKCS12KeyStoreSpi)
+        {
+            return ((PKCS12KeyStoreSpi)keyStoreSpi).engineProbe(stream);
+        }
+        return false;
+    }
+
+    public Key engineGetKey(String alias, char[] password)
+        throws NoSuchAlgorithmException, UnrecoverableKeyException
+    {
+        return keyStoreSpi.engineGetKey(alias, password);
+    }
+
+    public Certificate[] engineGetCertificateChain(String alias)
+    {
+        return keyStoreSpi.engineGetCertificateChain(alias);
+    }
+
+    public Certificate engineGetCertificate(String alias)
+    {
+        return keyStoreSpi.engineGetCertificate(alias);
+    }
+
+    public Date engineGetCreationDate(String alias)
+    {
+        return keyStoreSpi.engineGetCreationDate(alias);
+    }
+
+    public void engineSetKeyEntry(String alias, Key key, char[] password, Certificate[] chain)
+        throws KeyStoreException
+    {
+        keyStoreSpi.engineSetKeyEntry(alias, key, password, chain);
+    }
+
+    public void engineSetKeyEntry(String alias, byte[] key, Certificate[] chain)
+        throws KeyStoreException
+    {
+        keyStoreSpi.engineSetKeyEntry(alias, key, chain);
+    }
+
+    public void engineSetCertificateEntry(String alias, Certificate cert)
+        throws KeyStoreException
+    {
+        keyStoreSpi.engineSetCertificateEntry(alias, cert);
+    }
+
+    public void engineDeleteEntry(String alias)
+        throws KeyStoreException
+    {
+        keyStoreSpi.engineDeleteEntry(alias);
+    }
+
+    public Enumeration<String> engineAliases()
+    {
+        return keyStoreSpi.engineAliases();
+    }
+
+    public boolean engineContainsAlias(String alias)
+    {
+        return keyStoreSpi.engineContainsAlias(alias);
+    }
+
+    public int engineSize()
+    {
+        return keyStoreSpi.engineSize();
+    }
+
+    public boolean engineIsKeyEntry(String alias)
+    {
+        return keyStoreSpi.engineIsKeyEntry(alias);
+    }
+
+    public boolean engineIsCertificateEntry(String alias)
+    {
+        return keyStoreSpi.engineIsCertificateEntry(alias);
+    }
+
+    public String engineGetCertificateAlias(Certificate cert)
+    {
+        return keyStoreSpi.engineGetCertificateAlias(cert);
+    }
+
+    public void engineStore(OutputStream stream, char[] password)
+        throws IOException, NoSuchAlgorithmException, CertificateException
+    {
+        keyStoreSpi.engineStore(stream, password);
+    }
+
+    public void engineStore(KeyStore.LoadStoreParameter parameter)
+        throws IOException, NoSuchAlgorithmException, CertificateException
+    {
+        keyStoreSpi.engineStore(parameter);
+    }
+
+    public void engineLoad(InputStream stream, char[] password)
+        throws IOException, NoSuchAlgorithmException, CertificateException
+    {
+        if (stream == null)
+        {
+            keyStoreSpi = primaryStore;
+            keyStoreSpi.engineLoad(null, password);
+        }
+        else
+        {
+            // the FIPS BCFKS/JKS compatibility is explicit and doesn't use the override.
+            if (Properties.isOverrideSet(COMPAT_OVERRIDE) || !(primaryStore instanceof PKCS12KeyStoreSpi))
+            {
+                if (!stream.markSupported())
+                {
+                    stream = new BufferedInputStream(stream);
+                }
+
+                stream.mark(8);
+                if (jksStore.engineProbe(stream))
+                {
+                    keyStoreSpi = jksStore;
+                }
+                else
+                {
+                    keyStoreSpi = primaryStore;
+                }
+
+                stream.reset();
+            }
+            else
+            {
+                keyStoreSpi = primaryStore;
+            }
+
+            keyStoreSpi.engineLoad(stream, password);
+        }
+    }
+
+    public void engineLoad(KeyStore.LoadStoreParameter parameter)
+        throws IOException, NoSuchAlgorithmException, CertificateException
+    {
+        keyStoreSpi.engineLoad(parameter);
+    }
+}
+
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/keystore/util/JKSKeyStoreSpi.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/keystore/util/JKSKeyStoreSpi.java
new file mode 100644
index 0000000..db27a40
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/keystore/util/JKSKeyStoreSpi.java
@@ -0,0 +1,426 @@
+package org.bouncycastle.jcajce.provider.keystore.util;
+
+import java.io.ByteArrayInputStream;
+import java.io.DataInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.security.Key;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.KeyStoreSpi;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.UnrecoverableKeyException;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.util.Date;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.jcajce.BCLoadStoreParameter;
+import org.bouncycastle.jcajce.provider.util.DigestFactory;
+import org.bouncycastle.jcajce.util.JcaJceHelper;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.Strings;
+import org.bouncycastle.util.io.Streams;
+
+/**
+ * Implements a certificate only JKS key store.
+ */
+public class JKSKeyStoreSpi
+    extends KeyStoreSpi
+{
+    private static final String NOT_IMPLEMENTED_MESSAGE = "BC JKS store is read-only and only supports certificate entries";
+
+    private final Hashtable<String, BCJKSTrustedCertEntry> certificateEntries = new Hashtable<String, BCJKSTrustedCertEntry>();
+    private final JcaJceHelper helper;
+
+    public JKSKeyStoreSpi(JcaJceHelper helper)
+    {
+        this.helper = helper;
+    }
+
+    public boolean engineProbe(InputStream stream)
+        throws IOException
+    {
+        DataInputStream storeStream;
+        if (stream instanceof DataInputStream)
+        {
+            storeStream = (DataInputStream)stream;
+        }
+        else
+        {
+            storeStream = new DataInputStream(stream);
+        }
+
+        int magic = storeStream.readInt();
+        int storeVersion = storeStream.readInt();
+        return magic == (int)0x0000feedfeedL && (storeVersion == 1 || storeVersion == 2);
+    }
+
+    public Key engineGetKey(String alias, char[] password)
+        throws NoSuchAlgorithmException, UnrecoverableKeyException
+    {
+        return null;  // by definition
+    }
+
+    public Certificate[] engineGetCertificateChain(String alias)
+    {
+        return null;  // by definition
+    }
+
+    public Certificate engineGetCertificate(String alias)
+    {
+        synchronized (certificateEntries)
+        {
+            BCJKSTrustedCertEntry ent = certificateEntries.get(alias);
+            if (ent != null)
+            {
+                return ent.cert;
+            }
+        }
+        return null;
+    }
+
+    public Date engineGetCreationDate(String alias)
+    {
+        synchronized (certificateEntries)
+        {
+            BCJKSTrustedCertEntry ent = certificateEntries.get(alias);
+            if (ent != null)
+            {
+                return ent.date;
+            }
+        }
+        return null;
+    }
+
+    public void engineSetKeyEntry(String alias, Key key, char[] password, Certificate[] chain)
+        throws KeyStoreException
+    {
+        throw new KeyStoreException(NOT_IMPLEMENTED_MESSAGE);
+    }
+
+    public void engineSetKeyEntry(String alias, byte[] key, Certificate[] chain)
+        throws KeyStoreException
+    {
+        throw new KeyStoreException(NOT_IMPLEMENTED_MESSAGE);
+    }
+
+    public void engineSetCertificateEntry(String alias, Certificate cert)
+        throws KeyStoreException
+    {
+        throw new KeyStoreException(NOT_IMPLEMENTED_MESSAGE);
+    }
+
+    public void engineDeleteEntry(String alias)
+        throws KeyStoreException
+    {
+        throw new KeyStoreException(NOT_IMPLEMENTED_MESSAGE);
+    }
+
+    public Enumeration<String> engineAliases()
+    {
+        synchronized (certificateEntries)
+        {
+            return certificateEntries.keys();
+        }
+    }
+
+    public boolean engineContainsAlias(String alias)
+    {
+        if (alias == null)
+        {
+            throw new NullPointerException("alias value is null");
+        }
+
+        synchronized (certificateEntries)
+        {
+            return certificateEntries.containsKey(alias);
+        }
+    }
+
+    public int engineSize()
+    {
+        return certificateEntries.size();
+    }
+
+    public boolean engineIsKeyEntry(String alias)
+    {
+        return false;    // by definition
+    }
+
+    public boolean engineIsCertificateEntry(String alias)
+    {
+        synchronized (certificateEntries)
+        {
+            return certificateEntries.containsKey(alias);
+        }
+    }
+
+    public String engineGetCertificateAlias(Certificate cert)
+    {
+        synchronized (certificateEntries)
+        {
+            for (Iterator<Map.Entry<String, BCJKSTrustedCertEntry>> it = certificateEntries.entrySet().iterator(); it.hasNext(); )
+            {
+                Map.Entry<String, BCJKSTrustedCertEntry> entry = it.next();
+                if (entry.getValue().cert.equals(cert))
+                {
+                    return entry.getKey();
+                }
+            }
+            return null;
+        }
+    }
+
+    public void engineStore(OutputStream stream, char[] password)
+        throws IOException, NoSuchAlgorithmException, CertificateException
+    {
+        throw new IOException(NOT_IMPLEMENTED_MESSAGE);
+    }
+
+    public void engineLoad(KeyStore.LoadStoreParameter loadStoreParameter)
+        throws IOException, NoSuchAlgorithmException, CertificateException
+    {
+        if (loadStoreParameter == null)
+        {
+            engineLoad(null, null);
+        }
+        else if (loadStoreParameter instanceof BCLoadStoreParameter)
+        {
+            BCLoadStoreParameter bcParam = (BCLoadStoreParameter)loadStoreParameter;
+
+            engineLoad(bcParam.getInputStream(), ParameterUtil.extractPassword(loadStoreParameter));
+        }
+        else
+        {
+            throw new IllegalArgumentException(
+                "no support for 'param' of type " + loadStoreParameter.getClass().getName());
+        }
+    }
+
+    public void engineLoad(InputStream stream, char[] password)
+        throws IOException, NoSuchAlgorithmException, CertificateException
+    {
+        if (stream == null)
+        {
+            return;
+        }
+
+        ErasableByteStream storeStream = validateStream(stream, password);
+
+        synchronized (certificateEntries)
+        {
+            try
+            {
+                DataInputStream dIn = new DataInputStream(storeStream);
+
+                int magic = dIn.readInt();
+                int storeVersion = dIn.readInt();
+                if (magic == (int)0x0000feedfeedL)
+                {
+                    CertificateFactory certFact = null;
+                    Hashtable certFactories = null;
+
+                    switch (storeVersion)
+                    {
+                    case 1:  // all certs X.509
+                        certFact = createCertFactory("X.509");
+                        break;
+                    case 2:  // provision for format in store.
+                        certFactories = new Hashtable();
+                        break;
+                    default:
+                        throw new IllegalStateException("unable to discern store version");
+                    }
+
+                    int numEntries = dIn.readInt();
+                    for (int t = 0; t < numEntries; t++)
+                    {
+                        int tag = dIn.readInt();
+                        switch (tag)
+                        {
+                        case 1: // we can't process keys
+                            throw new IOException(NOT_IMPLEMENTED_MESSAGE);
+                        case 2: // certificate
+                            String alias = dIn.readUTF();
+                            Date date = new Date(dIn.readLong());
+
+                            if (storeVersion == 2)
+                            {
+                                String certFormat = dIn.readUTF();
+                                if (certFactories.containsKey(certFormat))
+                                {
+                                    certFact = (CertificateFactory)certFactories.get(certFormat);
+                                }
+                                else
+                                {
+                                    certFact = createCertFactory(certFormat);
+                                    certFactories.put(certFormat, certFact);
+                                }
+                            }
+
+                            int l = dIn.readInt();
+                            byte[] certData = new byte[l];
+                            dIn.readFully(certData);
+
+                            ErasableByteStream certStream = new ErasableByteStream(certData, 0, certData.length);
+                            Certificate cert;
+                            try
+                            {
+                                cert = certFact.generateCertificate(certStream);
+
+                                if (certStream.available() != 0)
+                                {
+                                    throw new IOException("password incorrect or store tampered with");
+                                }
+                            }
+                            finally
+                            {
+                                certStream.erase();
+                            }
+
+                            certificateEntries.put(alias, new BCJKSTrustedCertEntry(date, cert));
+                            break;
+                        default:
+                            throw new IllegalStateException("unable to discern entry type");
+                        }
+                    }
+                }
+
+                if (storeStream.available() != 0)
+                {
+                    throw new IOException("password incorrect or store tampered with");
+                }
+            }
+            finally
+            {
+                storeStream.erase();
+            }
+        }
+    }
+
+    private CertificateFactory createCertFactory(String certFormat)
+        throws CertificateException
+    {
+        if (helper != null)
+        {
+            try
+            {
+                return helper.createCertificateFactory(certFormat);
+            }
+            catch (NoSuchProviderException e)
+            {
+                throw new CertificateException(e.toString());
+            }
+        }
+        else
+        {
+            return CertificateFactory.getInstance(certFormat);
+        }
+    }
+
+    /**
+     * Process password updates the digest with the password.
+     *
+     * @param digest   The digest instance.
+     * @param password The password.
+     */
+    private void addPassword(Digest digest, char[] password)
+        throws IOException
+    {
+        for (int i = 0; i < password.length; ++i)
+        {
+            digest.update((byte)(password[i] >> 8));
+            digest.update((byte)password[i]);
+        }
+
+        //
+        // This "Mighty Aphrodite" string goes all the way back to the
+        // first java betas in the mid 90's, why who knows? But see
+        // https://cryptosense.com/mighty-aphrodite-dark-secrets-of-the-java-keystore/
+        //
+        digest.update(Strings.toByteArray("Mighty Aphrodite"), 0, 16);
+    }
+
+    /**
+     * Validate password takes the checksum of the store and will either.
+     * 1. If password is null, load the store into memory, return the result.
+     * 2. If password is not null, load the store into memory, test the checksum, and if successful return
+     * a new input stream instance of the store.
+     * 3. Fail if there is a password and an invalid checksum.
+     *
+     * @param inputStream The input stream.
+     * @param password    the password.
+     * @return Either the passed in input stream or a new input stream.
+     * @throws IOException
+     */
+    private ErasableByteStream validateStream(InputStream inputStream, char[] password)
+        throws IOException
+    {
+        Digest checksumCalculator = DigestFactory.getDigest("SHA-1");
+        byte[] rawStore = Streams.readAll(inputStream);
+
+        if (password != null)
+        {
+            addPassword(checksumCalculator, password);
+            checksumCalculator.update(rawStore, 0, rawStore.length - checksumCalculator.getDigestSize());
+
+            byte[] checksum = new byte[checksumCalculator.getDigestSize()];
+
+            checksumCalculator.doFinal(checksum, 0);
+
+            byte[] streamChecksum = new byte[checksum.length];
+            System.arraycopy(rawStore, rawStore.length - checksum.length, streamChecksum, 0, checksum.length);
+
+            if (!Arrays.constantTimeAreEqual(checksum, streamChecksum))
+            {
+                Arrays.fill(rawStore, (byte)0);
+                throw new IOException("password incorrect or store tampered with");
+            }
+
+            return new ErasableByteStream(rawStore, 0, rawStore.length - checksum.length);
+        }
+
+        return new ErasableByteStream(rawStore, 0, rawStore.length - checksumCalculator.getDigestSize());
+    }
+
+    /**
+     * BCJKSTrustedCertEntry is a internal container for the certificate entry.
+     */
+    private static final class BCJKSTrustedCertEntry
+    {
+        final Date date;
+        final Certificate cert;
+
+        public BCJKSTrustedCertEntry(Date date, Certificate cert)
+        {
+            this.date = date;
+            this.cert = cert;
+        }
+    }
+
+    private static final class ErasableByteStream
+        extends ByteArrayInputStream
+    {
+        public ErasableByteStream(byte[] buf, int offSet, int length)
+        {
+            super(buf, offSet, length);
+        }
+
+        public void erase()
+        {
+            // this will also erase the checksum from memory.
+            Arrays.fill(buf, (byte)0);
+        }
+    }
+
+
+}
+
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/keystore/util/ParameterUtil.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/keystore/util/ParameterUtil.java
new file mode 100644
index 0000000..1028dac
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/keystore/util/ParameterUtil.java
@@ -0,0 +1,49 @@
+package org.bouncycastle.jcajce.provider.keystore.util;
+
+import java.io.IOException;
+import java.security.KeyStore;
+
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.callback.PasswordCallback;
+import javax.security.auth.callback.UnsupportedCallbackException;
+
+public class ParameterUtil
+{
+    public static char[] extractPassword(KeyStore.LoadStoreParameter bcParam)
+        throws IOException
+    {
+        KeyStore.ProtectionParameter protParam = bcParam.getProtectionParameter();
+
+        if (protParam == null)
+        {
+            return null;
+        }
+        else if (protParam instanceof KeyStore.PasswordProtection)
+        {
+            return ((KeyStore.PasswordProtection)protParam).getPassword();
+        }
+        else if (protParam instanceof KeyStore.CallbackHandlerProtection)
+        {
+            CallbackHandler handler = ((KeyStore.CallbackHandlerProtection)protParam).getCallbackHandler();
+
+            PasswordCallback passwordCallback = new PasswordCallback("password: ", false);
+
+            try
+            {
+                handler.handle(new Callback[]{passwordCallback});
+
+                return passwordCallback.getPassword();
+            }
+            catch (UnsupportedCallbackException e)
+            {
+                throw new IllegalArgumentException("PasswordCallback not recognised: " + e.getMessage(), e);
+            }
+        }
+        else
+        {
+            throw new IllegalArgumentException(
+                "no support for protection parameter of type " + protParam.getClass().getName());
+        }
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/AES.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/AES.java
index d25ed90..90a9a42 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/AES.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/AES.java
@@ -18,16 +18,13 @@
 import javax.crypto.NoSuchPaddingException;
 // END Android-added: Needed for setting padding with GCM
 import org.bouncycastle.asn1.bc.BCObjectIdentifiers;
-// Android-removed: Unsupported algorithms
-// import org.bouncycastle.asn1.cms.CCMParameters;
-import org.bouncycastle.asn1.cms.GCMParameters;
 import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
 import org.bouncycastle.crypto.BlockCipher;
-import org.bouncycastle.crypto.BufferedBlockCipher;
 import org.bouncycastle.crypto.CipherKeyGenerator;
 import org.bouncycastle.crypto.CipherParameters;
 import org.bouncycastle.crypto.CryptoServicesRegistrar;
 import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.DefaultBufferedBlockCipher;
 import org.bouncycastle.crypto.InvalidCipherTextException;
 import org.bouncycastle.crypto.Mac;
 import org.bouncycastle.crypto.engines.AESEngine;
@@ -42,9 +39,13 @@
 import org.bouncycastle.crypto.modes.CBCBlockCipher;
 // Android-removed: Unsupported algorithms
 // import org.bouncycastle.crypto.modes.CCMBlockCipher;
+// import org.bouncycastle.crypto.modes.CCMModeCipher;
 import org.bouncycastle.crypto.modes.CFBBlockCipher;
 import org.bouncycastle.crypto.modes.GCMBlockCipher;
 import org.bouncycastle.crypto.modes.OFBBlockCipher;
+// Android-removed: Unsupported algorithms
+// import org.bouncycastle.internal.asn1.cms.CCMParameters;
+import org.bouncycastle.internal.asn1.cms.GCMParameters;
 import org.bouncycastle.jcajce.provider.config.ConfigurableProvider;
 // Android-removed: Unsupported algorithms
 // import org.bouncycastle.jcajce.provider.symmetric.util.BaseAlgorithmParameterGenerator;
@@ -74,7 +75,7 @@
     private AES()
     {
     }
-    
+
     public static class ECB
         extends BaseBlockCipher
     {
@@ -84,18 +85,18 @@
             {
                 public BlockCipher get()
                 {
-                    return new AESEngine();
+                    return AESEngine.newInstance();
                 }
             });
         }
     }
 
     public static class CBC
-       extends BaseBlockCipher
+        extends BaseBlockCipher
     {
         public CBC()
         {
-            super(new CBCBlockCipher(new AESEngine()), 128);
+            super(CBCBlockCipher.newInstance(AESEngine.newInstance()), 128);
         }
     }
 
@@ -104,7 +105,7 @@
     {
         public CFB()
         {
-            super(new BufferedBlockCipher(new CFBBlockCipher(new AESEngine(), 128)), 128);
+            super(new DefaultBufferedBlockCipher(CFBBlockCipher.newInstance(AESEngine.newInstance(), 128)), 128);
         }
     }
 
@@ -113,7 +114,7 @@
     {
         public OFB()
         {
-            super(new BufferedBlockCipher(new OFBBlockCipher(new AESEngine(), 128)), 128);
+            super(new DefaultBufferedBlockCipher(new OFBBlockCipher(AESEngine.newInstance(), 128)), 128);
         }
     }
 
@@ -142,7 +143,7 @@
     {
         public CCM()
         {
-            super(new CCMBlockCipher(new AESEngine()), false, 12);
+            super(CCMBlockCipher.newInstance(AESEngine.newInstance()), false, 12);
         }
     }
 
@@ -151,7 +152,7 @@
     {
         public AESCMAC()
         {
-            super(new CMac(new AESEngine()));
+            super(new CMac(AESEngine.newInstance()));
         }
     }
 
@@ -160,7 +161,7 @@
     {
         public AESGMAC()
         {
-            super(new GMac(new GCMBlockCipher(new AESEngine())));
+            super(new GMac(GCMBlockCipher.newInstance(AESEngine.newInstance())));
         }
     }
 
@@ -175,7 +176,7 @@
         private static class CCMMac
             implements Mac
         {
-            private final CCMBlockCipher ccm = new CCMBlockCipher(new AESEngine());
+            private final CCMModeCipher ccm = CCMBlockCipher.newInstance(AESEngine.newInstance());
 
             private int macLength = 8;
 
@@ -230,7 +231,7 @@
     }
 
     static public class KeyFactory
-         extends BaseSecretKeyFactory
+        extends BaseSecretKeyFactory
     {
         public KeyFactory()
         {
@@ -243,7 +244,7 @@
     {
         public Poly1305()
         {
-            super(new org.bouncycastle.crypto.macs.Poly1305(new AESEngine()));
+            super(new org.bouncycastle.crypto.macs.Poly1305(AESEngine.newInstance()));
         }
     }
 
@@ -283,7 +284,7 @@
     {
         public RFC3211Wrap()
         {
-            super(new RFC3211WrapEngine(new AESEngine()), 16);
+            super(new RFC3211WrapEngine(AESEngine.newInstance()), 16);
         }
     }
 
@@ -292,7 +293,7 @@
     {
         public RFC5649Wrap()
         {
-            super(new RFC5649WrapEngine(new AESEngine()));
+            super(new RFC5649WrapEngine(AESEngine.newInstance()));
         }
     }
     */
@@ -306,7 +307,7 @@
     {
         public PBEWithAESCBC()
         {
-            super(new CBCBlockCipher(new AESEngine()));
+            super(CBCBlockCipher.newInstance(AESEngine.newInstance()));
         }
     }
 
@@ -318,7 +319,7 @@
     {
         public PBEWithSHA1AESCBC128()
         {
-            super(new CBCBlockCipher(new AESEngine()), PKCS12, SHA1, 128, 16);
+            super(CBCBlockCipher.newInstance(AESEngine.newInstance()), PKCS12, SHA1, 128, 16);
         }
     }
 
@@ -327,7 +328,7 @@
     {
         public PBEWithSHA1AESCBC192()
         {
-            super(new CBCBlockCipher(new AESEngine()), PKCS12, SHA1, 192, 16);
+            super(CBCBlockCipher.newInstance(AESEngine.newInstance()), PKCS12, SHA1, 192, 16);
         }
     }
 
@@ -336,7 +337,7 @@
     {
         public PBEWithSHA1AESCBC256()
         {
-            super(new CBCBlockCipher(new AESEngine()), PKCS12, SHA1, 256, 16);
+            super(CBCBlockCipher.newInstance(AESEngine.newInstance()), PKCS12, SHA1, 256, 16);
         }
     }
 
@@ -348,7 +349,7 @@
     {
         public PBEWithSHA256AESCBC128()
         {
-            super(new CBCBlockCipher(new AESEngine()), PKCS12, SHA256, 128, 16);
+            super(CBCBlockCipher.newInstance(AESEngine.newInstance()), PKCS12, SHA256, 128, 16);
         }
     }
 
@@ -357,7 +358,7 @@
     {
         public PBEWithSHA256AESCBC192()
         {
-            super(new CBCBlockCipher(new AESEngine()), PKCS12, SHA256, 192, 16);
+            super(CBCBlockCipher.newInstance(AESEngine.newInstance()), PKCS12, SHA256, 192, 16);
         }
     }
 
@@ -366,7 +367,7 @@
     {
         public PBEWithSHA256AESCBC256()
         {
-            super(new CBCBlockCipher(new AESEngine()), PKCS12, SHA256, 256, 16);
+            super(CBCBlockCipher.newInstance(AESEngine.newInstance()), PKCS12, SHA256, 256, 16);
         }
     }
 
@@ -431,7 +432,7 @@
             super("PBEWithSHA1And128BitAES-CBC-BC", null, true, PKCS12, SHA1, 128, 128);
         }
     }
-    
+
     /**
      * PBEWithSHA1And192BitAES-BC
      */
@@ -443,7 +444,7 @@
             super("PBEWithSHA1And192BitAES-CBC-BC", null, true, PKCS12, SHA1, 192, 128);
         }
     }
-    
+
     /**
      * PBEWithSHA1And256BitAES-BC
      */
@@ -455,7 +456,7 @@
             super("PBEWithSHA1And256BitAES-CBC-BC", null, true, PKCS12, SHA1, 256, 128);
         }
     }
-    
+
     /**
      * PBEWithSHA256And128BitAES-BC
      */
@@ -467,7 +468,7 @@
             super("PBEWithSHA256And128BitAES-CBC-BC", null, true, PKCS12, SHA256, 128, 128);
         }
     }
-    
+
     /**
      * PBEWithSHA256And192BitAES-BC
      */
@@ -479,7 +480,7 @@
             super("PBEWithSHA256And192BitAES-CBC-BC", null, true, PKCS12, SHA256, 192, 128);
         }
     }
-    
+
     /**
      * PBEWithSHA256And256BitAES-BC
      */
@@ -491,7 +492,7 @@
             super("PBEWithSHA256And256BitAES-CBC-BC", null, true, PKCS12, SHA256, 256, 128);
         }
     }
-    
+
     /**
      * PBEWithMD5And128BitAES-OpenSSL
      */
@@ -503,7 +504,7 @@
             super("PBEWithMD5And128BitAES-CBC-OpenSSL", null, true, OPENSSL, MD5, 128, 128);
         }
     }
-    
+
     /**
      * PBEWithMD5And192BitAES-OpenSSL
      */
@@ -515,7 +516,7 @@
             super("PBEWithMD5And192BitAES-CBC-OpenSSL", null, true, OPENSSL, MD5, 192, 128);
         }
     }
-    
+
     /**
      * PBEWithMD5And256BitAES-OpenSSL
      */
@@ -543,7 +544,7 @@
 
         protected AlgorithmParameters engineGenerateParameters()
         {
-            byte[]  iv = new byte[16];
+            byte[] iv = new byte[16];
 
             if (random == null)
             {
@@ -582,7 +583,7 @@
 
         protected AlgorithmParameters engineGenerateParameters()
         {
-            byte[]  iv = new byte[12];
+            byte[] iv = new byte[12];
 
             if (random == null)
             {
@@ -621,7 +622,7 @@
 
         protected AlgorithmParameters engineGenerateParameters()
         {
-            byte[]  nonce = new byte[12];
+            byte[] nonce = new byte[12];
 
             if (random == null)
             {
@@ -723,7 +724,7 @@
         {
             if (paramSpec == AlgorithmParameterSpec.class || GcmSpecUtil.isGcmSpec(paramSpec))
             {
-                if (GcmSpecUtil.gcmSpecExists())
+                if (GcmSpecUtil.gcmSpecExtractable())
                 {
                     return GcmSpecUtil.extractGcmSpec(gcmParams.toASN1Primitive());
                 }
@@ -810,7 +811,7 @@
         {
             if (paramSpec == AlgorithmParameterSpec.class || GcmSpecUtil.isGcmSpec(paramSpec))
             {
-                if (GcmSpecUtil.gcmSpecExists())
+                if (GcmSpecUtil.gcmSpecExtractable())
                 {
                     return GcmSpecUtil.extractGcmSpec(ccmParams.toASN1Primitive());
                 }
@@ -835,7 +836,7 @@
         extends SymmetricAlgorithmProvider
     {
         private static final String PREFIX = AES.class.getName();
-        
+
         /**
          * These three got introduced in some messages as a result of a typo in an
          * early document. We don't produce anything using these OID values, but we'll
@@ -997,31 +998,31 @@
             provider.addAlgorithm("Cipher.PBEWITHSHA256AND128BITAES-CBC-BC", PREFIX + "$PBEWithSHA256AESCBC128");
             provider.addAlgorithm("Cipher.PBEWITHSHA256AND192BITAES-CBC-BC", PREFIX + "$PBEWithSHA256AESCBC192");
             provider.addAlgorithm("Cipher.PBEWITHSHA256AND256BITAES-CBC-BC", PREFIX + "$PBEWithSHA256AESCBC256");
-            
-            provider.addAlgorithm("Alg.Alias.Cipher.PBEWITHSHA1AND128BITAES-CBC-BC","PBEWITHSHAAND128BITAES-CBC-BC");
-            provider.addAlgorithm("Alg.Alias.Cipher.PBEWITHSHA1AND192BITAES-CBC-BC","PBEWITHSHAAND192BITAES-CBC-BC");
-            provider.addAlgorithm("Alg.Alias.Cipher.PBEWITHSHA1AND256BITAES-CBC-BC","PBEWITHSHAAND256BITAES-CBC-BC");
-            provider.addAlgorithm("Alg.Alias.Cipher.PBEWITHSHA-1AND128BITAES-CBC-BC","PBEWITHSHAAND128BITAES-CBC-BC");
-            provider.addAlgorithm("Alg.Alias.Cipher.PBEWITHSHA-1AND192BITAES-CBC-BC","PBEWITHSHAAND192BITAES-CBC-BC");
-            provider.addAlgorithm("Alg.Alias.Cipher.PBEWITHSHA-1AND256BITAES-CBC-BC","PBEWITHSHAAND256BITAES-CBC-BC");
-            provider.addAlgorithm("Alg.Alias.Cipher.PBEWITHSHAAND128BITAES-BC","PBEWITHSHAAND128BITAES-CBC-BC");
+
+            provider.addAlgorithm("Alg.Alias.Cipher.PBEWITHSHA1AND128BITAES-CBC-BC", "PBEWITHSHAAND128BITAES-CBC-BC");
+            provider.addAlgorithm("Alg.Alias.Cipher.PBEWITHSHA1AND192BITAES-CBC-BC", "PBEWITHSHAAND192BITAES-CBC-BC");
+            provider.addAlgorithm("Alg.Alias.Cipher.PBEWITHSHA1AND256BITAES-CBC-BC", "PBEWITHSHAAND256BITAES-CBC-BC");
+            provider.addAlgorithm("Alg.Alias.Cipher.PBEWITHSHA-1AND128BITAES-CBC-BC", "PBEWITHSHAAND128BITAES-CBC-BC");
+            provider.addAlgorithm("Alg.Alias.Cipher.PBEWITHSHA-1AND192BITAES-CBC-BC", "PBEWITHSHAAND192BITAES-CBC-BC");
+            provider.addAlgorithm("Alg.Alias.Cipher.PBEWITHSHA-1AND256BITAES-CBC-BC", "PBEWITHSHAAND256BITAES-CBC-BC");
+            provider.addAlgorithm("Alg.Alias.Cipher.PBEWITHSHAAND128BITAES-BC", "PBEWITHSHAAND128BITAES-CBC-BC");
             provider.addAlgorithm("Alg.Alias.Cipher.PBEWITHSHAAND192BITAES-BC", "PBEWITHSHAAND192BITAES-CBC-BC");
             provider.addAlgorithm("Alg.Alias.Cipher.PBEWITHSHAAND256BITAES-BC", "PBEWITHSHAAND256BITAES-CBC-BC");
-            provider.addAlgorithm("Alg.Alias.Cipher.PBEWITHSHA1AND128BITAES-BC","PBEWITHSHAAND128BITAES-CBC-BC");
-            provider.addAlgorithm("Alg.Alias.Cipher.PBEWITHSHA1AND192BITAES-BC","PBEWITHSHAAND192BITAES-CBC-BC");
-            provider.addAlgorithm("Alg.Alias.Cipher.PBEWITHSHA1AND256BITAES-BC","PBEWITHSHAAND256BITAES-CBC-BC");
-            provider.addAlgorithm("Alg.Alias.Cipher.PBEWITHSHA-1AND128BITAES-BC","PBEWITHSHAAND128BITAES-CBC-BC");
-            provider.addAlgorithm("Alg.Alias.Cipher.PBEWITHSHA-1AND192BITAES-BC","PBEWITHSHAAND192BITAES-CBC-BC");
-            provider.addAlgorithm("Alg.Alias.Cipher.PBEWITHSHA-1AND256BITAES-BC","PBEWITHSHAAND256BITAES-CBC-BC");
-            provider.addAlgorithm("Alg.Alias.Cipher.PBEWITHSHA-256AND128BITAES-CBC-BC","PBEWITHSHA256AND128BITAES-CBC-BC");
-            provider.addAlgorithm("Alg.Alias.Cipher.PBEWITHSHA-256AND192BITAES-CBC-BC","PBEWITHSHA256AND192BITAES-CBC-BC");
-            provider.addAlgorithm("Alg.Alias.Cipher.PBEWITHSHA-256AND256BITAES-CBC-BC","PBEWITHSHA256AND256BITAES-CBC-BC");
-            provider.addAlgorithm("Alg.Alias.Cipher.PBEWITHSHA256AND128BITAES-BC","PBEWITHSHA256AND128BITAES-CBC-BC");
-            provider.addAlgorithm("Alg.Alias.Cipher.PBEWITHSHA256AND192BITAES-BC","PBEWITHSHA256AND192BITAES-CBC-BC");
-            provider.addAlgorithm("Alg.Alias.Cipher.PBEWITHSHA256AND256BITAES-BC","PBEWITHSHA256AND256BITAES-CBC-BC");
-            provider.addAlgorithm("Alg.Alias.Cipher.PBEWITHSHA-256AND128BITAES-BC","PBEWITHSHA256AND128BITAES-CBC-BC");
-            provider.addAlgorithm("Alg.Alias.Cipher.PBEWITHSHA-256AND192BITAES-BC","PBEWITHSHA256AND192BITAES-CBC-BC");
-            provider.addAlgorithm("Alg.Alias.Cipher.PBEWITHSHA-256AND256BITAES-BC","PBEWITHSHA256AND256BITAES-CBC-BC");
+            provider.addAlgorithm("Alg.Alias.Cipher.PBEWITHSHA1AND128BITAES-BC", "PBEWITHSHAAND128BITAES-CBC-BC");
+            provider.addAlgorithm("Alg.Alias.Cipher.PBEWITHSHA1AND192BITAES-BC", "PBEWITHSHAAND192BITAES-CBC-BC");
+            provider.addAlgorithm("Alg.Alias.Cipher.PBEWITHSHA1AND256BITAES-BC", "PBEWITHSHAAND256BITAES-CBC-BC");
+            provider.addAlgorithm("Alg.Alias.Cipher.PBEWITHSHA-1AND128BITAES-BC", "PBEWITHSHAAND128BITAES-CBC-BC");
+            provider.addAlgorithm("Alg.Alias.Cipher.PBEWITHSHA-1AND192BITAES-BC", "PBEWITHSHAAND192BITAES-CBC-BC");
+            provider.addAlgorithm("Alg.Alias.Cipher.PBEWITHSHA-1AND256BITAES-BC", "PBEWITHSHAAND256BITAES-CBC-BC");
+            provider.addAlgorithm("Alg.Alias.Cipher.PBEWITHSHA-256AND128BITAES-CBC-BC", "PBEWITHSHA256AND128BITAES-CBC-BC");
+            provider.addAlgorithm("Alg.Alias.Cipher.PBEWITHSHA-256AND192BITAES-CBC-BC", "PBEWITHSHA256AND192BITAES-CBC-BC");
+            provider.addAlgorithm("Alg.Alias.Cipher.PBEWITHSHA-256AND256BITAES-CBC-BC", "PBEWITHSHA256AND256BITAES-CBC-BC");
+            provider.addAlgorithm("Alg.Alias.Cipher.PBEWITHSHA256AND128BITAES-BC", "PBEWITHSHA256AND128BITAES-CBC-BC");
+            provider.addAlgorithm("Alg.Alias.Cipher.PBEWITHSHA256AND192BITAES-BC", "PBEWITHSHA256AND192BITAES-CBC-BC");
+            provider.addAlgorithm("Alg.Alias.Cipher.PBEWITHSHA256AND256BITAES-BC", "PBEWITHSHA256AND256BITAES-CBC-BC");
+            provider.addAlgorithm("Alg.Alias.Cipher.PBEWITHSHA-256AND128BITAES-BC", "PBEWITHSHA256AND128BITAES-CBC-BC");
+            provider.addAlgorithm("Alg.Alias.Cipher.PBEWITHSHA-256AND192BITAES-BC", "PBEWITHSHA256AND192BITAES-CBC-BC");
+            provider.addAlgorithm("Alg.Alias.Cipher.PBEWITHSHA-256AND256BITAES-BC", "PBEWITHSHA256AND256BITAES-CBC-BC");
 
             provider.addAlgorithm("Cipher.PBEWITHMD5AND128BITAES-CBC-OPENSSL", PREFIX + "$PBEWithAESCBC");
             provider.addAlgorithm("Cipher.PBEWITHMD5AND192BITAES-CBC-OPENSSL", PREFIX + "$PBEWithAESCBC");
@@ -1035,48 +1036,48 @@
             provider.addAlgorithm("SecretKeyFactory.PBEWITHMD5AND128BITAES-CBC-OPENSSL", PREFIX + "$PBEWithMD5And128BitAESCBCOpenSSL");
             provider.addAlgorithm("SecretKeyFactory.PBEWITHMD5AND192BITAES-CBC-OPENSSL", PREFIX + "$PBEWithMD5And192BitAESCBCOpenSSL");
             provider.addAlgorithm("SecretKeyFactory.PBEWITHMD5AND256BITAES-CBC-OPENSSL", PREFIX + "$PBEWithMD5And256BitAESCBCOpenSSL");
-            
+
             provider.addAlgorithm("SecretKeyFactory.PBEWITHSHAAND128BITAES-CBC-BC", PREFIX + "$PBEWithSHAAnd128BitAESBC");
             provider.addAlgorithm("SecretKeyFactory.PBEWITHSHAAND192BITAES-CBC-BC", PREFIX + "$PBEWithSHAAnd192BitAESBC");
             provider.addAlgorithm("SecretKeyFactory.PBEWITHSHAAND256BITAES-CBC-BC", PREFIX + "$PBEWithSHAAnd256BitAESBC");
             provider.addAlgorithm("SecretKeyFactory.PBEWITHSHA256AND128BITAES-CBC-BC", PREFIX + "$PBEWithSHA256And128BitAESBC");
             provider.addAlgorithm("SecretKeyFactory.PBEWITHSHA256AND192BITAES-CBC-BC", PREFIX + "$PBEWithSHA256And192BitAESBC");
             provider.addAlgorithm("SecretKeyFactory.PBEWITHSHA256AND256BITAES-CBC-BC", PREFIX + "$PBEWithSHA256And256BitAESBC");
-            provider.addAlgorithm("Alg.Alias.SecretKeyFactory.PBEWITHSHA1AND128BITAES-CBC-BC","PBEWITHSHAAND128BITAES-CBC-BC");
-            provider.addAlgorithm("Alg.Alias.SecretKeyFactory.PBEWITHSHA1AND192BITAES-CBC-BC","PBEWITHSHAAND192BITAES-CBC-BC");
-            provider.addAlgorithm("Alg.Alias.SecretKeyFactory.PBEWITHSHA1AND256BITAES-CBC-BC","PBEWITHSHAAND256BITAES-CBC-BC");
-            provider.addAlgorithm("Alg.Alias.SecretKeyFactory.PBEWITHSHA-1AND128BITAES-CBC-BC","PBEWITHSHAAND128BITAES-CBC-BC");
-            provider.addAlgorithm("Alg.Alias.SecretKeyFactory.PBEWITHSHA-1AND192BITAES-CBC-BC","PBEWITHSHAAND192BITAES-CBC-BC");
-            provider.addAlgorithm("Alg.Alias.SecretKeyFactory.PBEWITHSHA-1AND256BITAES-CBC-BC","PBEWITHSHAAND256BITAES-CBC-BC");
-            provider.addAlgorithm("Alg.Alias.SecretKeyFactory.PBEWITHSHA-256AND128BITAES-CBC-BC","PBEWITHSHA256AND128BITAES-CBC-BC");
-            provider.addAlgorithm("Alg.Alias.SecretKeyFactory.PBEWITHSHA-256AND192BITAES-CBC-BC","PBEWITHSHA256AND192BITAES-CBC-BC");
-            provider.addAlgorithm("Alg.Alias.SecretKeyFactory.PBEWITHSHA-256AND256BITAES-CBC-BC","PBEWITHSHA256AND256BITAES-CBC-BC");
-            provider.addAlgorithm("Alg.Alias.SecretKeyFactory.PBEWITHSHA-256AND128BITAES-BC","PBEWITHSHA256AND128BITAES-CBC-BC");
-            provider.addAlgorithm("Alg.Alias.SecretKeyFactory.PBEWITHSHA-256AND192BITAES-BC","PBEWITHSHA256AND192BITAES-CBC-BC");
-            provider.addAlgorithm("Alg.Alias.SecretKeyFactory.PBEWITHSHA-256AND256BITAES-BC","PBEWITHSHA256AND256BITAES-CBC-BC");
+            provider.addAlgorithm("Alg.Alias.SecretKeyFactory.PBEWITHSHA1AND128BITAES-CBC-BC", "PBEWITHSHAAND128BITAES-CBC-BC");
+            provider.addAlgorithm("Alg.Alias.SecretKeyFactory.PBEWITHSHA1AND192BITAES-CBC-BC", "PBEWITHSHAAND192BITAES-CBC-BC");
+            provider.addAlgorithm("Alg.Alias.SecretKeyFactory.PBEWITHSHA1AND256BITAES-CBC-BC", "PBEWITHSHAAND256BITAES-CBC-BC");
+            provider.addAlgorithm("Alg.Alias.SecretKeyFactory.PBEWITHSHA-1AND128BITAES-CBC-BC", "PBEWITHSHAAND128BITAES-CBC-BC");
+            provider.addAlgorithm("Alg.Alias.SecretKeyFactory.PBEWITHSHA-1AND192BITAES-CBC-BC", "PBEWITHSHAAND192BITAES-CBC-BC");
+            provider.addAlgorithm("Alg.Alias.SecretKeyFactory.PBEWITHSHA-1AND256BITAES-CBC-BC", "PBEWITHSHAAND256BITAES-CBC-BC");
+            provider.addAlgorithm("Alg.Alias.SecretKeyFactory.PBEWITHSHA-256AND128BITAES-CBC-BC", "PBEWITHSHA256AND128BITAES-CBC-BC");
+            provider.addAlgorithm("Alg.Alias.SecretKeyFactory.PBEWITHSHA-256AND192BITAES-CBC-BC", "PBEWITHSHA256AND192BITAES-CBC-BC");
+            provider.addAlgorithm("Alg.Alias.SecretKeyFactory.PBEWITHSHA-256AND256BITAES-CBC-BC", "PBEWITHSHA256AND256BITAES-CBC-BC");
+            provider.addAlgorithm("Alg.Alias.SecretKeyFactory.PBEWITHSHA-256AND128BITAES-BC", "PBEWITHSHA256AND128BITAES-CBC-BC");
+            provider.addAlgorithm("Alg.Alias.SecretKeyFactory.PBEWITHSHA-256AND192BITAES-BC", "PBEWITHSHA256AND192BITAES-CBC-BC");
+            provider.addAlgorithm("Alg.Alias.SecretKeyFactory.PBEWITHSHA-256AND256BITAES-BC", "PBEWITHSHA256AND256BITAES-CBC-BC");
             provider.addAlgorithm("Alg.Alias.SecretKeyFactory", BCObjectIdentifiers.bc_pbe_sha1_pkcs12_aes128_cbc, "PBEWITHSHAAND128BITAES-CBC-BC");
             provider.addAlgorithm("Alg.Alias.SecretKeyFactory", BCObjectIdentifiers.bc_pbe_sha1_pkcs12_aes192_cbc, "PBEWITHSHAAND192BITAES-CBC-BC");
             provider.addAlgorithm("Alg.Alias.SecretKeyFactory", BCObjectIdentifiers.bc_pbe_sha1_pkcs12_aes256_cbc, "PBEWITHSHAAND256BITAES-CBC-BC");
             provider.addAlgorithm("Alg.Alias.SecretKeyFactory", BCObjectIdentifiers.bc_pbe_sha256_pkcs12_aes128_cbc, "PBEWITHSHA256AND128BITAES-CBC-BC");
             provider.addAlgorithm("Alg.Alias.SecretKeyFactory", BCObjectIdentifiers.bc_pbe_sha256_pkcs12_aes192_cbc, "PBEWITHSHA256AND192BITAES-CBC-BC");
             provider.addAlgorithm("Alg.Alias.SecretKeyFactory", BCObjectIdentifiers.bc_pbe_sha256_pkcs12_aes256_cbc, "PBEWITHSHA256AND256BITAES-CBC-BC");
-            
+
             provider.addAlgorithm("Alg.Alias.AlgorithmParameters.PBEWITHSHAAND128BITAES-CBC-BC", "PKCS12PBE");
             provider.addAlgorithm("Alg.Alias.AlgorithmParameters.PBEWITHSHAAND192BITAES-CBC-BC", "PKCS12PBE");
             provider.addAlgorithm("Alg.Alias.AlgorithmParameters.PBEWITHSHAAND256BITAES-CBC-BC", "PKCS12PBE");
             provider.addAlgorithm("Alg.Alias.AlgorithmParameters.PBEWITHSHA256AND128BITAES-CBC-BC", "PKCS12PBE");
             provider.addAlgorithm("Alg.Alias.AlgorithmParameters.PBEWITHSHA256AND192BITAES-CBC-BC", "PKCS12PBE");
             provider.addAlgorithm("Alg.Alias.AlgorithmParameters.PBEWITHSHA256AND256BITAES-CBC-BC", "PKCS12PBE");
-            provider.addAlgorithm("Alg.Alias.AlgorithmParameters.PBEWITHSHA1AND128BITAES-CBC-BC","PKCS12PBE");
-            provider.addAlgorithm("Alg.Alias.AlgorithmParameters.PBEWITHSHA1AND192BITAES-CBC-BC","PKCS12PBE");
-            provider.addAlgorithm("Alg.Alias.AlgorithmParameters.PBEWITHSHA1AND256BITAES-CBC-BC","PKCS12PBE");
-            provider.addAlgorithm("Alg.Alias.AlgorithmParameters.PBEWITHSHA-1AND128BITAES-CBC-BC","PKCS12PBE");
-            provider.addAlgorithm("Alg.Alias.AlgorithmParameters.PBEWITHSHA-1AND192BITAES-CBC-BC","PKCS12PBE");
-            provider.addAlgorithm("Alg.Alias.AlgorithmParameters.PBEWITHSHA-1AND256BITAES-CBC-BC","PKCS12PBE");
-            provider.addAlgorithm("Alg.Alias.AlgorithmParameters.PBEWITHSHA-256AND128BITAES-CBC-BC","PKCS12PBE");
-            provider.addAlgorithm("Alg.Alias.AlgorithmParameters.PBEWITHSHA-256AND192BITAES-CBC-BC","PKCS12PBE");
-            provider.addAlgorithm("Alg.Alias.AlgorithmParameters.PBEWITHSHA-256AND256BITAES-CBC-BC","PKCS12PBE"); 
-            
+            provider.addAlgorithm("Alg.Alias.AlgorithmParameters.PBEWITHSHA1AND128BITAES-CBC-BC", "PKCS12PBE");
+            provider.addAlgorithm("Alg.Alias.AlgorithmParameters.PBEWITHSHA1AND192BITAES-CBC-BC", "PKCS12PBE");
+            provider.addAlgorithm("Alg.Alias.AlgorithmParameters.PBEWITHSHA1AND256BITAES-CBC-BC", "PKCS12PBE");
+            provider.addAlgorithm("Alg.Alias.AlgorithmParameters.PBEWITHSHA-1AND128BITAES-CBC-BC", "PKCS12PBE");
+            provider.addAlgorithm("Alg.Alias.AlgorithmParameters.PBEWITHSHA-1AND192BITAES-CBC-BC", "PKCS12PBE");
+            provider.addAlgorithm("Alg.Alias.AlgorithmParameters.PBEWITHSHA-1AND256BITAES-CBC-BC", "PKCS12PBE");
+            provider.addAlgorithm("Alg.Alias.AlgorithmParameters.PBEWITHSHA-256AND128BITAES-CBC-BC", "PKCS12PBE");
+            provider.addAlgorithm("Alg.Alias.AlgorithmParameters.PBEWITHSHA-256AND192BITAES-CBC-BC", "PKCS12PBE");
+            provider.addAlgorithm("Alg.Alias.AlgorithmParameters.PBEWITHSHA-256AND256BITAES-CBC-BC", "PKCS12PBE");
+
             provider.addAlgorithm("Alg.Alias.AlgorithmParameters." + BCObjectIdentifiers.bc_pbe_sha1_pkcs12_aes128_cbc.getId(), "PKCS12PBE");
             provider.addAlgorithm("Alg.Alias.AlgorithmParameters." + BCObjectIdentifiers.bc_pbe_sha1_pkcs12_aes192_cbc.getId(), "PKCS12PBE");
             provider.addAlgorithm("Alg.Alias.AlgorithmParameters." + BCObjectIdentifiers.bc_pbe_sha1_pkcs12_aes256_cbc.getId(), "PKCS12PBE");
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/GcmSpecUtil.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/GcmSpecUtil.java
deleted file mode 100644
index a28cf3e..0000000
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/GcmSpecUtil.java
+++ /dev/null
@@ -1,67 +0,0 @@
-package org.bouncycastle.jcajce.provider.symmetric;
-
-import java.lang.reflect.Constructor;
-import java.lang.reflect.Method;
-import java.security.spec.AlgorithmParameterSpec;
-import java.security.spec.InvalidParameterSpecException;
-
-import org.bouncycastle.asn1.ASN1Primitive;
-import org.bouncycastle.asn1.cms.GCMParameters;
-import org.bouncycastle.jcajce.provider.symmetric.util.ClassUtil;
-import org.bouncycastle.util.Integers;
-
-class GcmSpecUtil
-{
-    static final Class gcmSpecClass = ClassUtil.loadClass(GcmSpecUtil.class, "javax.crypto.spec.GCMParameterSpec");
-
-    static boolean gcmSpecExists()
-    {
-        return gcmSpecClass != null;
-    }
-
-    static boolean isGcmSpec(AlgorithmParameterSpec paramSpec)
-    {
-        return gcmSpecClass != null && gcmSpecClass.isInstance(paramSpec);
-    }
-
-    static boolean isGcmSpec(Class paramSpecClass)
-    {
-        return gcmSpecClass == paramSpecClass;
-    }
-
-    static AlgorithmParameterSpec extractGcmSpec(ASN1Primitive spec)
-        throws InvalidParameterSpecException
-    {
-        try
-        {
-            GCMParameters gcmParams = GCMParameters.getInstance(spec);
-            Constructor constructor = gcmSpecClass.getConstructor(new Class[]{Integer.TYPE, byte[].class});
-
-            return (AlgorithmParameterSpec)constructor.newInstance(new Object[] { Integers.valueOf(gcmParams.getIcvLen() * 8), gcmParams.getNonce() });
-        }
-        catch (NoSuchMethodException e)
-        {
-            throw new InvalidParameterSpecException("No constructor found!");   // should never happen
-        }
-        catch (Exception e)
-        {
-            throw new InvalidParameterSpecException("Construction failed: " + e.getMessage());   // should never happen
-        }
-    }
-
-    static GCMParameters extractGcmParameters(AlgorithmParameterSpec paramSpec)
-        throws InvalidParameterSpecException
-    {
-        try
-        {
-            Method tLen = gcmSpecClass.getDeclaredMethod("getTLen", new Class[0]);
-            Method iv= gcmSpecClass.getDeclaredMethod("getIV", new Class[0]);
-
-            return new GCMParameters((byte[])iv.invoke(paramSpec, new Object[0]), ((Integer)tLen.invoke(paramSpec, new Object[0])).intValue() / 8);
-        }
-        catch (Exception e)
-        {
-            throw new InvalidParameterSpecException("Cannot process GCMParameterSpec");
-        }
-    }
-}
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/PBEPBKDF2.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/PBEPBKDF2.java
index e384134..3e317a2 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/PBEPBKDF2.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/PBEPBKDF2.java
@@ -545,6 +545,15 @@
             super("PBKDF2", PKCS5S2_UTF8, SM3);
         }
     }
+
+    public static class PBKDF2withSM3
+        extends BasePBKDF2
+    {
+        public PBKDF2withSM3()
+        {
+            super("PBKDF2", PKCS5S2_UTF8, SM3);
+        }
+    }
     */
     // END Android-removed: Unsupported algorithms
 
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/util/BCPBEKey.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/util/BCPBEKey.java
index d4756df..240e003 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/util/BCPBEKey.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/util/BCPBEKey.java
@@ -69,19 +69,23 @@
 
     public String getAlgorithm()
     {
+        String rv = this.algorithm;
+
         checkDestroyed(this);
 
-        return algorithm;
+        return rv;
     }
 
     public String getFormat()
     {
+        checkDestroyed(this);
+
         return "RAW";
     }
 
     public byte[] getEncoded()
     {
-        checkDestroyed(this);
+        byte[] enc;
 
         if (param != null)
         {
@@ -96,58 +100,72 @@
                 kParam = (KeyParameter)param;
             }
             
-            return kParam.getKey();
+            enc = kParam.getKey();
         }
         else
         {
             if (type == PBE.PKCS12)
             {
-                return PBEParametersGenerator.PKCS12PasswordToBytes(password);
+                enc = PBEParametersGenerator.PKCS12PasswordToBytes(password);
             }
             else if (type == PBE.PKCS5S2_UTF8)
             {
-                return PBEParametersGenerator.PKCS5PasswordToUTF8Bytes(password);
+                enc = PBEParametersGenerator.PKCS5PasswordToUTF8Bytes(password);
             }
             else
             {   
-                return PBEParametersGenerator.PKCS5PasswordToBytes(password);
+                enc = PBEParametersGenerator.PKCS5PasswordToBytes(password);
             }
         }
+
+        checkDestroyed(this);
+
+        return enc;
     }
     
     int getType()
     {
+        int rv = type;
+
         checkDestroyed(this);
 
-        return type;
+        return rv;
     }
     
     int getDigest()
     {
+        int rv = digest;
+
         checkDestroyed(this);
 
-        return digest;
+        return rv;
     }
     
     int getKeySize()
     {
+        int rv = keySize;
+
         checkDestroyed(this);
 
-        return keySize;
+        return rv;
     }
     
     public int getIvSize()
     {
+        int rv = ivSize;
+
         checkDestroyed(this);
 
-        return ivSize;
+        return rv;
     }
     
     public CipherParameters getParam()
     {
+        CipherParameters rv = param;
+
         checkDestroyed(this);
 
-        return param;
+        return rv;
     }
 
     /* (non-Javadoc)
@@ -155,14 +173,16 @@
      */
     public char[] getPassword()
     {
+        char[] clone = Arrays.clone(password);
+
         checkDestroyed(this);
 
-        if (password == null)
+        if (clone == null)
         {
             throw new IllegalStateException("no password available");
         }
 
-        return Arrays.clone(password);
+        return clone;
     }
 
     /* (non-Javadoc)
@@ -170,9 +190,11 @@
      */
     public byte[] getSalt()
     {
+        byte[] clone = Arrays.clone(salt);
+
         checkDestroyed(this);
 
-        return Arrays.clone(salt);
+        return clone;
     }
 
     /* (non-Javadoc)
@@ -180,16 +202,20 @@
      */
     public int getIterationCount()
     {
+        int rv = iterationCount;
+
         checkDestroyed(this);
 
-        return iterationCount;
+        return rv;
     }
     
     public ASN1ObjectIdentifier getOID()
     {
+        ASN1ObjectIdentifier rv = oid;
+
         checkDestroyed(this);
 
-        return oid;
+        return rv;
     }
     
     public void setTryWrongPKCS12Zero(boolean tryWrong)
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/util/BaseBlockCipher.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/util/BaseBlockCipher.java
index 7dadfa7..4af5297 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/util/BaseBlockCipher.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/util/BaseBlockCipher.java
@@ -28,17 +28,20 @@
 // import javax.crypto.spec.RC5ParameterSpec;
 
 import org.bouncycastle.asn1.DEROctetString;
-import org.bouncycastle.asn1.cms.GCMParameters;
 import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
 import org.bouncycastle.crypto.BlockCipher;
 import org.bouncycastle.crypto.BufferedBlockCipher;
 import org.bouncycastle.crypto.CipherParameters;
 import org.bouncycastle.crypto.CryptoServicesRegistrar;
 import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.DefaultBufferedBlockCipher;
 import org.bouncycastle.crypto.InvalidCipherTextException;
 import org.bouncycastle.crypto.OutputLengthException;
 // Android-removed: Unsupported algorithms
 // import org.bouncycastle.crypto.engines.DSTU7624Engine;
+// import org.bouncycastle.crypto.fpe.FPEEngine;
+// import org.bouncycastle.crypto.fpe.FPEFF1Engine;
+// import org.bouncycastle.crypto.fpe.FPEFF3_1Engine;
 import org.bouncycastle.crypto.modes.AEADBlockCipher;
 import org.bouncycastle.crypto.modes.AEADCipher;
 import org.bouncycastle.crypto.modes.CBCBlockCipher;
@@ -50,6 +53,7 @@
 // import org.bouncycastle.crypto.modes.GCFBBlockCipher;
 import org.bouncycastle.crypto.modes.GCMBlockCipher;
 // Android-removed: Unsupported algorithms
+// import org.bouncycastle.crypto.modes.GCMSIVBlockCipher;
 // import org.bouncycastle.crypto.modes.GOFBBlockCipher;
 // import org.bouncycastle.crypto.modes.KCCMBlockCipher;
 // import org.bouncycastle.crypto.modes.KCTRBlockCipher;
@@ -59,6 +63,8 @@
 // Android-removed: Unsupported algorithms
 // import org.bouncycastle.crypto.modes.OpenPGPCFBBlockCipher;
 // import org.bouncycastle.crypto.modes.PGPCFBBlockCipher;
+import org.bouncycastle.crypto.paddings.PKCS7Padding;
+// import org.bouncycastle.crypto.params.FPEParameters;
 import org.bouncycastle.crypto.modes.SICBlockCipher;
 import org.bouncycastle.crypto.paddings.BlockCipherPadding;
 import org.bouncycastle.crypto.paddings.ISO10126d2Padding;
@@ -84,6 +90,8 @@
 // Android-removed: Unsupported algorithms
 // import org.bouncycastle.jcajce.spec.GOST28147ParameterSpec;
 // import org.bouncycastle.jcajce.spec.RepeatedSecretKeySpec;
+import org.bouncycastle.internal.asn1.cms.GCMParameters;
+// import org.bouncycastle.jcajce.spec.FPEParameterSpec;
 import org.bouncycastle.util.Arrays;
 import org.bouncycastle.util.Strings;
 
@@ -92,7 +100,6 @@
     implements PBE
 {
     private static final int BUF_SIZE = 512;
-    private static final Class gcmSpecClass = ClassUtil.loadClass(BaseBlockCipher.class, "javax.crypto.spec.GCMParameterSpec");
 
     //
     // specs we can handle.
@@ -102,7 +109,7 @@
             // Android-removed: Unsupported alhorithms
             // RC2ParameterSpec.class,
             // RC5ParameterSpec.class,
-            gcmSpecClass,
+            GcmSpecUtil.gcmSpecClass,
             // Android-removed: unsupported algorithms
             // GOST28147ParameterSpec.class,
             IvParameterSpec.class,
@@ -166,7 +173,14 @@
         AEADBlockCipher engine)
     {
         this.baseEngine = engine.getUnderlyingCipher();
-        this.ivLength = baseEngine.getBlockSize();
+        if (engine.getAlgorithmName().indexOf("GCM") >= 0)
+        {
+            this.ivLength = 12;
+        }
+        else
+        {
+            this.ivLength = baseEngine.getBlockSize();
+        }
         this.cipher = new AEADGenericBlockCipher(engine);
     }
 
@@ -348,7 +362,7 @@
         {
             ivLength = baseEngine.getBlockSize();
             cipher = new BufferedGenericBlockCipher(
-                new CBCBlockCipher(baseEngine));
+                CBCBlockCipher.newInstance(baseEngine));
         }
         else if (modeName.startsWith("OFB"))
         {
@@ -374,12 +388,12 @@
                 int wordSize = Integer.parseInt(modeName.substring(3));
 
                 cipher = new BufferedGenericBlockCipher(
-                    new CFBBlockCipher(baseEngine, wordSize));
+                    CFBBlockCipher.newInstance(baseEngine, wordSize));
             }
             else
             {
                 cipher = new BufferedGenericBlockCipher(
-                    new CFBBlockCipher(baseEngine, 8 * baseEngine.getBlockSize()));
+                    CFBBlockCipher.newInstance(baseEngine, 8 * baseEngine.getBlockSize()));
             }
         }
         // BEGIN Android-removed: Unsupported modes
@@ -403,6 +417,18 @@
             cipher = new BufferedGenericBlockCipher(
                 new OpenPGPCFBBlockCipher(baseEngine));
         }
+        else if (modeName.equals("FF1"))
+        {
+            ivLength = 0;
+            cipher = new BufferedFPEBlockCipher(
+                new FPEFF1Engine(baseEngine));
+        }
+        else if (modeName.equals("FF3-1"))
+        {
+            ivLength = 0;
+            cipher = new BufferedFPEBlockCipher(
+                new FPEFF3_1Engine(baseEngine));
+        }
         else if (modeName.equals("SIC"))
         {
             ivLength = baseEngine.getBlockSize();
@@ -411,8 +437,8 @@
                 throw new IllegalArgumentException("Warning: SIC-Mode can become a twotime-pad if the blocksize of the cipher is too small. Use a cipher with a block size of at least 128 bits (e.g. AES)");
             }
             fixedIv = false;
-            cipher = new BufferedGenericBlockCipher(new BufferedBlockCipher(
-                new SICBlockCipher(baseEngine)));
+            cipher = new BufferedGenericBlockCipher(new DefaultBufferedBlockCipher(
+                SICBlockCipher.newInstance(baseEngine)));
         }
         */
         // END Android-removed: Unsupported modes
@@ -424,14 +450,14 @@
             /*
             if (baseEngine instanceof DSTU7624Engine)
             {
-                cipher = new BufferedGenericBlockCipher(new BufferedBlockCipher(
+                cipher = new BufferedGenericBlockCipher(new DefaultBufferedBlockCipher(
                     new KCTRBlockCipher(baseEngine)));
             }
             else
             {
             */
-                cipher = new BufferedGenericBlockCipher(new BufferedBlockCipher(
-                    new SICBlockCipher(baseEngine)));
+                cipher = new BufferedGenericBlockCipher(new DefaultBufferedBlockCipher(
+                    SICBlockCipher.newInstance(baseEngine)));
             /*
             }
             */
@@ -441,13 +467,13 @@
         else if (modeName.equals("GOFB"))
         {
             ivLength = baseEngine.getBlockSize();
-            cipher = new BufferedGenericBlockCipher(new BufferedBlockCipher(
+            cipher = new BufferedGenericBlockCipher(new DefaultBufferedBlockCipher(
                 new GOFBBlockCipher(baseEngine)));
         }
         else if (modeName.equals("GCFB"))
         {
             ivLength = baseEngine.getBlockSize();
-            cipher = new BufferedGenericBlockCipher(new BufferedBlockCipher(
+            cipher = new BufferedGenericBlockCipher(new DefaultBufferedBlockCipher(
                 new GCFBBlockCipher(baseEngine)));
         }
         */
@@ -455,7 +481,7 @@
         else if (modeName.equals("CTS"))
         {
             ivLength = baseEngine.getBlockSize();
-            cipher = new BufferedGenericBlockCipher(new CTSBlockCipher(new CBCBlockCipher(baseEngine)));
+            cipher = new BufferedGenericBlockCipher(new CTSBlockCipher(CBCBlockCipher.newInstance(baseEngine)));
         }
         else if (modeName.equals("CCM"))
         {
@@ -469,7 +495,7 @@
             else
             {
             */
-                cipher = new AEADGenericBlockCipher(new CCMBlockCipher(baseEngine));
+                cipher = new AEADGenericBlockCipher(CCMBlockCipher.newInstance(baseEngine));
             /*
             }
             */
@@ -505,12 +531,13 @@
             /*
             if (baseEngine instanceof DSTU7624Engine)
             {
+                ivLength = baseEngine.getBlockSize();
                 cipher = new AEADGenericBlockCipher(new KGCMBlockCipher(baseEngine));
             }
             else
             {
             */
-                cipher = new AEADGenericBlockCipher(new GCMBlockCipher(baseEngine));
+                cipher = new AEADGenericBlockCipher(GCMBlockCipher.newInstance(baseEngine));
             /*
             }
             */
@@ -536,7 +563,7 @@
         {
             if (cipher.wrapOnNoPadding())
             {
-                cipher = new BufferedGenericBlockCipher(new BufferedBlockCipher(cipher.getUnderlyingCipher()));
+                cipher = new BufferedGenericBlockCipher(new DefaultBufferedBlockCipher(cipher.getUnderlyingCipher()));
             }
         }
         else if (paddingName.equals("WITHCTS") || paddingName.equals("CTSPADDING") || paddingName.equals("CS3PADDING"))
@@ -845,7 +872,7 @@
             GOST28147ParameterSpec gost28147Param = (GOST28147ParameterSpec)params;
 
             param = new ParametersWithSBox(
-                new KeyParameter(key.getEncoded()), ((GOST28147ParameterSpec)params).getSbox());
+                new KeyParameter(key.getEncoded()), ((GOST28147ParameterSpec)params).getSBox());
 
             if (gost28147Param.getIV() != null && ivLength != 0)
             {
@@ -918,9 +945,15 @@
                 ivParam = (ParametersWithIV)param;
             }
         }
+        else if (params instanceof FPEParameterSpec)
+        {
+            FPEParameterSpec spec = (FPEParameterSpec)params;
+
+            param = new FPEParameters((KeyParameter)param, spec.getRadixConverter(), spec.getTweak(), spec.isUsingInverseFunction());
+        }
         */
         // END Android-removed: Unsupported algorithms
-        else if (gcmSpecClass != null && gcmSpecClass.isInstance(params))
+        else if (GcmSpecUtil.isGcmSpec(params))
         {
             if (!isAEADModeName(modeName) && !(cipher instanceof AEADGenericBlockCipher))
             {
@@ -1044,7 +1077,7 @@
                 // need to pick up IV and SBox.
                 GOST28147ParameterSpec gost28147Param = (GOST28147ParameterSpec)params;
 
-                param = new ParametersWithSBox(param, gost28147Param.getSbox());
+                param = new ParametersWithSBox(param, gost28147Param.getSBox());
 
                 if (gost28147Param.getIV() != null && ivLength != 0)
                 {
@@ -1071,7 +1104,7 @@
                 // need to pick up IV and SBox.
                 GOST28147ParameterSpec gost28147Param = (GOST28147ParameterSpec)params;
 
-                param = new ParametersWithSBox(param, gost28147Param.getSbox());
+                param = new ParametersWithSBox(param, gost28147Param.getSBox());
 
                 if (gost28147Param.getIV() != null && ivLength != 0)
                 {
@@ -1347,7 +1380,7 @@
 
         BufferedGenericBlockCipher(org.bouncycastle.crypto.BlockCipher cipher)
         {
-            this.cipher = new PaddedBufferedBlockCipher(cipher);
+            this(cipher, new PKCS7Padding());
         }
 
         BufferedGenericBlockCipher(org.bouncycastle.crypto.BlockCipher cipher, BlockCipherPadding padding)
@@ -1417,6 +1450,85 @@
         }
     }
 
+    // BEGIN Android-removed: unsupported
+    // private static class BufferedFPEBlockCipher
+    //     implements GenericBlockCipher
+    // {
+    //     private FPEEngine cipher;
+    //     private ErasableOutputStream eOut = new ErasableOutputStream();
+
+    //     BufferedFPEBlockCipher(FPEEngine cipher)
+    //     {
+    //         this.cipher = cipher;
+    //     }
+
+    //     public void init(boolean forEncryption, CipherParameters params)
+    //         throws IllegalArgumentException
+    //     {
+    //         cipher.init(forEncryption, params);
+    //     }
+
+    //     public boolean wrapOnNoPadding()
+    //     {
+    //         return false;
+    //     }
+
+    //     public String getAlgorithmName()
+    //     {
+    //         return cipher.getAlgorithmName();
+    //     }
+
+    //     public org.bouncycastle.crypto.BlockCipher getUnderlyingCipher()
+    //     {
+    //         throw new IllegalStateException("not applicable for FPE");
+    //     }
+
+    //     public int getOutputSize(int len)
+    //     {
+    //         return eOut.size() + len;
+    //     }
+
+    //     public int getUpdateOutputSize(int len)
+    //     {
+    //         return 0;
+    //     }
+
+    //     public void updateAAD(byte[] input, int offset, int length)
+    //     {
+    //         throw new UnsupportedOperationException("AAD is not supported in the current mode.");
+    //     }
+
+    //     public int processByte(byte in, byte[] out, int outOff)
+    //         throws DataLengthException
+    //     {
+    //         eOut.write(in);
+
+    //         return 0;
+    //     }
+
+    //     public int processBytes(byte[] in, int inOff, int len, byte[] out, int outOff)
+    //         throws DataLengthException
+    //     {
+    //         eOut.write(in, inOff, len);
+
+    //         return 0;
+    //     }
+
+    //     public int doFinal(byte[] out, int outOff)
+    //         throws IllegalStateException, BadPaddingException
+    //     {
+    //         try
+    //         {
+    //             return cipher.processBlock(eOut.getBuf(), 0, eOut.size(), out, outOff);
+    //         }
+    //         finally
+    //         {
+    //             eOut.erase();
+    //         }
+    //     }
+    // }
+    // END Android-removed: unsupported
+
     private static class AEADGenericBlockCipher
         implements GenericBlockCipher
     {
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/util/BaseMac.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/util/BaseMac.java
index 9f5ac19..757b612 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/util/BaseMac.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/util/BaseMac.java
@@ -32,8 +32,6 @@
 public class BaseMac
     extends MacSpi implements PBE
 {
-    private static final Class gcmSpecClass = ClassUtil.loadClass(BaseMac.class, "javax.crypto.spec.GCMParameterSpec");
-
     private Mac macEngine;
 
     private int scheme = PKCS12;
@@ -211,7 +209,7 @@
         {
             param = new KeyParameter(key.getEncoded());
         }
-        else if (gcmSpecClass != null && gcmSpecClass.isAssignableFrom(params.getClass()))
+        else if (GcmSpecUtil.isGcmSpec(params))
         {
             param = GcmSpecUtil.extractAeadParameters(keyParam, params);
         }
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/util/ClassUtil.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/util/ClassUtil.java
index 37ea08b..71344f8 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/util/ClassUtil.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/util/ClassUtil.java
@@ -23,7 +23,9 @@
                     {
                         try
                         {
-                            return Class.forName(className);
+                            ClassLoader classLoader = ClassLoader.getSystemClassLoader();
+
+                            return classLoader.loadClass(className);
                         }
                         catch (Exception e)
                         {
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/util/GcmSpecUtil.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/util/GcmSpecUtil.java
index 6cf2020..497ebc0 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/util/GcmSpecUtil.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/util/GcmSpecUtil.java
@@ -10,32 +10,55 @@
 import java.security.spec.InvalidParameterSpecException;
 
 import org.bouncycastle.asn1.ASN1Primitive;
-import org.bouncycastle.asn1.cms.GCMParameters;
 import org.bouncycastle.crypto.params.AEADParameters;
 import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.internal.asn1.cms.GCMParameters;
 import org.bouncycastle.util.Integers;
 
 public class GcmSpecUtil
 {
-    static final Class gcmSpecClass = ClassUtil.loadClass(GcmSpecUtil.class, "javax.crypto.spec.GCMParameterSpec");
-
-    static final Method tLen;
-    static final Method iv;
+    static final Class gcmSpecClass;
+    private static final Constructor constructor;
+    private static final Method tLen;
+    private static final Method iv;
 
     static
     {
+        gcmSpecClass = ClassUtil.loadClass(GcmSpecUtil.class, "javax.crypto.spec.GCMParameterSpec");
+
         if (gcmSpecClass != null)
         {
+            constructor = extractConstructor();
             tLen = extractMethod("getTLen");
             iv = extractMethod("getIV");
         }
         else
         {
+            constructor = null;
             tLen = null;
             iv = null;
         }
     }
 
+    private static Constructor extractConstructor()
+    {
+        try
+        {
+            return (Constructor)AccessController.doPrivileged(new PrivilegedExceptionAction()
+            {
+                public Object run()
+                    throws Exception
+                {
+                    return gcmSpecClass.getConstructor(new Class[]{ Integer.TYPE, byte[].class });
+                }
+            });
+        }
+        catch (PrivilegedActionException e)
+        {
+            return null;
+        }
+    }
+
     private static Method extractMethod(final String name)
     {
         try
@@ -60,6 +83,11 @@
         return gcmSpecClass != null;
     }
 
+    public static boolean gcmSpecExtractable()
+    {
+        return constructor != null;
+    }
+
     public static boolean isGcmSpec(AlgorithmParameterSpec paramSpec)
     {
         return gcmSpecClass != null && gcmSpecClass.isInstance(paramSpec);
@@ -76,14 +104,9 @@
         try
         {
             GCMParameters gcmParams = GCMParameters.getInstance(spec);
-            Constructor constructor = gcmSpecClass.getConstructor(new Class[]{Integer.TYPE, byte[].class});
 
             return (AlgorithmParameterSpec)constructor.newInstance(new Object[] { Integers.valueOf(gcmParams.getIcvLen() * 8), gcmParams.getNonce() });
         }
-        catch (NoSuchMethodException e)
-        {
-            throw new InvalidParameterSpecException("No constructor found!");   // should never happen
-        }
         catch (Exception e)
         {
             throw new InvalidParameterSpecException("Construction failed: " + e.getMessage());   // should never happen
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/util/PBE.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/util/PBE.java
index a0621cf..e3c1d50 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/util/PBE.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/util/PBE.java
@@ -14,6 +14,7 @@
 import javax.crypto.spec.PBEParameterSpec;
 
 import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.CryptoServicePurpose;
 import org.bouncycastle.crypto.PBEParametersGenerator;
 // Android-removed: Unsupported algorithms
 // import org.bouncycastle.crypto.digests.GOST3411Digest;
@@ -103,25 +104,25 @@
                 {
                 // Android-removed: Unsupported algorithms
                 // case MD2:
-                //     generator = new PKCS5S2ParametersGenerator(new MD2Digest());
+                    // generator = new PKCS5S2ParametersGenerator(new MD2Digest(CryptoServicePurpose.PRF));
                 //     break;
                 case MD5:
                     // Android-changed: Use Android digests
-                    // generator = new PKCS5S2ParametersGenerator(DigestFactory.createMD5());
+                    // generator = new PKCS5S2ParametersGenerator(DigestFactory.createMD5PRF());
                     generator = new PKCS5S2ParametersGenerator(AndroidDigestFactory.getMD5());
                     break;
                 case SHA1:
                     // Android-changed: Use Android digests
-                    // generator = new PKCS5S2ParametersGenerator(DigestFactory.createSHA1());
+                    // generator = new PKCS5S2ParametersGenerator(DigestFactory.createSHA1PRF());
                     generator = new PKCS5S2ParametersGenerator(AndroidDigestFactory.getSHA1());
                     break;
                 // BEGIN Android-removed: Unsupported algorithms
                 /*
                 case RIPEMD160:
-                    generator = new PKCS5S2ParametersGenerator(new RIPEMD160Digest());
+                    generator = new PKCS5S2ParametersGenerator(new RIPEMD160Digest(CryptoServicePurpose.PRF));
                     break;
                 case TIGER:
-                    generator = new PKCS5S2ParametersGenerator(new TigerDigest());
+                    generator = new PKCS5S2ParametersGenerator(new TigerDigest(CryptoServicePurpose.PRF));
                     break;
                 */
                 // END Android-removed: Unsupported algorithms
@@ -132,36 +133,43 @@
                     break;
                 // Android-removed: Unsupported algorithms
                 // case GOST3411:
+                //     generator = new PKCS5S2ParametersGenerator(new GOST3411Digest(CryptoServicePurpose.PRF));
+                //     break;
+                // Android-removed: Unsupported algorithms
+                // case GOST3411:
                 //     generator = new PKCS5S2ParametersGenerator(new GOST3411Digest());
                 //     break;
                 case SHA224:
                     // Android-changed: Use Android digests
-                    // generator = new PKCS5S2ParametersGenerator(DigestFactory.createSHA224());
+                    // generator = new PKCS5S2ParametersGenerator(DigestFactory.createSHA224PRF());
                     generator = new PKCS5S2ParametersGenerator(AndroidDigestFactory.getSHA224());
                     break;
                 case SHA384:
                     // Android-changed: Use Android digests
-                    // generator = new PKCS5S2ParametersGenerator(DigestFactory.createSHA384());
+                    // generator = new PKCS5S2ParametersGenerator(DigestFactory.createSHA384PRF());
                     generator = new PKCS5S2ParametersGenerator(AndroidDigestFactory.getSHA384());
                     break;
                 case SHA512:
                     // Android-changed: Use Android digests
-                    // generator = new PKCS5S2ParametersGenerator(DigestFactory.createSHA512());
+                    // generator = new PKCS5S2ParametersGenerator(DigestFactory.createSHA512PRF());
                     generator = new PKCS5S2ParametersGenerator(AndroidDigestFactory.getSHA512());
                     break;
                 // BEGIN Android-removed: Unsupported algorithms
                 /*
                 case SHA3_224:
-                    generator = new PKCS5S2ParametersGenerator(DigestFactory.createSHA3_224());
+                    generator = new PKCS5S2ParametersGenerator(DigestFactory.createSHA3_224PRF());
                     break;
                 case SHA3_256:
-                     generator = new PKCS5S2ParametersGenerator(DigestFactory.createSHA3_256());
+                     generator = new PKCS5S2ParametersGenerator(DigestFactory.createSHA3_256PRF());
                      break;
                 case SHA3_384:
-                    generator = new PKCS5S2ParametersGenerator(DigestFactory.createSHA3_384());
+                    generator = new PKCS5S2ParametersGenerator(DigestFactory.createSHA3_384PRF());
                     break;
                 case SHA3_512:
-                    generator = new PKCS5S2ParametersGenerator(DigestFactory.createSHA3_512());
+                    generator = new PKCS5S2ParametersGenerator(DigestFactory.createSHA3_512PRF());
+                    break;
+                case SM3:
+                    generator = new PKCS5S2ParametersGenerator(new SM3Digest(CryptoServicePurpose.PRF));
                     break;
                 case SM3:
                     generator = new PKCS5S2ParametersGenerator(new SM3Digest());
@@ -178,50 +186,54 @@
                 {
                 // Android-removed: Unsupported algorithms
                 // case MD2:
-                //     generator = new PKCS12ParametersGenerator(new MD2Digest());
+                    // generator = new PKCS12ParametersGenerator(new MD2Digest(CryptoServicePurpose.PRF));
                 //     break;
                 case MD5:
                     // Android-changed: Use Android digests
-                    // generator = new PKCS12ParametersGenerator(DigestFactory.createMD5());
+                    // generator = new PKCS12ParametersGenerator(DigestFactory.createMD5PRF());
                     generator = new PKCS12ParametersGenerator(AndroidDigestFactory.getMD5());
                     break;
                 case SHA1:
                     // Android-changed: Use Android digests
-                    // generator = new PKCS12ParametersGenerator(DigestFactory.createSHA1());
+                    // generator = new PKCS12ParametersGenerator(DigestFactory.createSHA1PRF());
                     generator = new PKCS12ParametersGenerator(AndroidDigestFactory.getSHA1());
                     break;
                 // BEGIN Android-removed: Unsupported algorithms
                 /*
                 case RIPEMD160:
-                    generator = new PKCS12ParametersGenerator(new RIPEMD160Digest());
+                    generator = new PKCS12ParametersGenerator(new RIPEMD160Digest(CryptoServicePurpose.PRF));
                     break;
                 case TIGER:
-                    generator = new PKCS12ParametersGenerator(new TigerDigest());
+                    generator = new PKCS12ParametersGenerator(new TigerDigest(CryptoServicePurpose.PRF));
                     break;
                 */
                 // END Android-removed: Unsupported algorithms
                 case SHA256:
                     // Android-changed: Use Android digests
-                    // generator = new PKCS12ParametersGenerator(DigestFactory.createSHA256());
+                    // generator = new PKCS12ParametersGenerator(DigestFactory.createSHA256PRF());
                     generator = new PKCS12ParametersGenerator(AndroidDigestFactory.getSHA256());
                     break;
                 // Android-removed: Unsupported algorithms
                 // case GOST3411:
+                //     generator = new PKCS12ParametersGenerator(new GOST3411Digest(CryptoServicePurpose.PRF));
+                //     break;
+                // Android-removed: Unsupported algorithms
+                // case GOST3411:
                 //     generator = new PKCS12ParametersGenerator(new GOST3411Digest());
                 //     break;
                 case SHA224:
                     // Android-changed: Use Android digests
-                    // generator = new PKCS12ParametersGenerator(DigestFactory.createSHA224());
+                    // generator = new PKCS12ParametersGenerator(DigestFactory.createSHA224PRF());
                     generator = new PKCS12ParametersGenerator(AndroidDigestFactory.getSHA224());
                     break;
                 case SHA384:
                     // Android-changed: Use Android digests
-                    // generator = new PKCS12ParametersGenerator(DigestFactory.createSHA384());
+                    // generator = new PKCS12ParametersGenerator(DigestFactory.createSHA384PRF());
                     generator = new PKCS12ParametersGenerator(AndroidDigestFactory.getSHA384());
                     break;
                 case SHA512:
                     // Android-changed: Use Android digests
-                    // generator = new PKCS12ParametersGenerator(DigestFactory.createSHA512());
+                    // generator = new PKCS12ParametersGenerator(DigestFactory.createSHA512PRF());
                     generator = new PKCS12ParametersGenerator(AndroidDigestFactory.getSHA512());
                     break;
                 default:
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/util/AsymmetricAlgorithmProvider.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/util/AsymmetricAlgorithmProvider.java
index fc4cf4f..4d912ac 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/util/AsymmetricAlgorithmProvider.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/util/AsymmetricAlgorithmProvider.java
@@ -1,5 +1,7 @@
 package org.bouncycastle.jcajce.provider.util;
 
+import java.util.Map;
+
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
 import org.bouncycastle.jcajce.provider.config.ConfigurableProvider;
 
@@ -17,6 +19,24 @@
         provider.addAlgorithm("Alg.Alias.Signature.OID." + oid, algorithm);
     }
 
+    protected void addSignatureAlias(
+        ConfigurableProvider provider,
+        String algorithm,
+        ASN1ObjectIdentifier oid)
+    {
+        provider.addAlgorithm("Alg.Alias.Signature." + oid, algorithm);
+        provider.addAlgorithm("Alg.Alias.Signature.OID." + oid, algorithm);
+    }
+
+    protected void addSignatureAlgorithm(
+        ConfigurableProvider provider,
+        String digest,
+        String algorithm,
+        String className)
+    {
+        addSignatureAlgorithm(provider, digest, algorithm, className, null);
+    }
+
     protected void addSignatureAlgorithm(
         ConfigurableProvider provider,
         String digest,
@@ -33,8 +53,103 @@
         provider.addAlgorithm("Alg.Alias.Signature." + jdk11Variation1, mainName);
         provider.addAlgorithm("Alg.Alias.Signature." + jdk11Variation2, mainName);
         provider.addAlgorithm("Alg.Alias.Signature." + alias, mainName);
-        provider.addAlgorithm("Alg.Alias.Signature." + oid, mainName);
-        provider.addAlgorithm("Alg.Alias.Signature.OID." + oid, mainName);
+        if (oid != null)
+        {
+            provider.addAlgorithm("Alg.Alias.Signature." + oid, mainName);
+            provider.addAlgorithm("Alg.Alias.Signature.OID." + oid, mainName);
+        }
+    }
+
+    protected void addSignatureAlgorithm(
+        ConfigurableProvider provider,
+        String digest,
+        String algorithm,
+        String className,
+        ASN1ObjectIdentifier oid,
+        Map<String, String> attributes)
+    {
+        String mainName = digest + "WITH" + algorithm;
+        String jdk11Variation1 = digest + "with" + algorithm;
+        String jdk11Variation2 = digest + "With" + algorithm;
+        String alias = digest + "/" + algorithm;
+
+        provider.addAlgorithm("Signature." + mainName, className);
+        provider.addAlgorithm("Alg.Alias.Signature." + jdk11Variation1, mainName);
+        provider.addAlgorithm("Alg.Alias.Signature." + jdk11Variation2, mainName);
+        provider.addAlgorithm("Alg.Alias.Signature." + alias, mainName);
+        if (oid != null)
+        {
+            provider.addAlgorithm("Alg.Alias.Signature." + oid, mainName);
+            provider.addAlgorithm("Alg.Alias.Signature.OID." + oid, mainName);
+        }
+        provider.addAttributes("Signature." + mainName, attributes);
+    }
+
+    protected void addKeyPairGeneratorAlgorithm(
+        ConfigurableProvider provider,
+        String algorithm,
+        String className,
+        ASN1ObjectIdentifier oid)
+    {
+        provider.addAlgorithm("KeyPairGenerator." + algorithm, className);
+        if (oid != null)
+        {
+            provider.addAlgorithm("Alg.Alias.KeyPairGenerator." + oid, algorithm);
+            provider.addAlgorithm("Alg.Alias.KeyPairGenerator.OID." + oid, algorithm);
+        }
+    }
+
+    protected void addKeyFactoryAlgorithm(
+        ConfigurableProvider provider,
+        String algorithm,
+        String className,
+        ASN1ObjectIdentifier oid,
+        AsymmetricKeyInfoConverter keyInfoConverter)
+    {
+        provider.addAlgorithm("KeyFactory." + algorithm, className);
+        if (oid != null)
+        {
+            provider.addAlgorithm("Alg.Alias.KeyFactory." + oid, algorithm);
+            provider.addAlgorithm("Alg.Alias.KeyFactory.OID." + oid, algorithm);
+
+            provider.addKeyInfoConverter(oid, keyInfoConverter);
+        }
+    }
+
+    protected void addKeyGeneratorAlgorithm(
+        ConfigurableProvider provider,
+        String algorithm,
+        String className,
+        ASN1ObjectIdentifier oid)
+    {
+        provider.addAlgorithm("KeyGenerator." + algorithm, className);
+        if (oid != null)
+        {
+            provider.addAlgorithm("Alg.Alias.KeyGenerator." + oid, algorithm);
+            provider.addAlgorithm("Alg.Alias.KeyGenerator.OID." + oid, algorithm);
+        }
+    }
+
+    protected void addCipherAlgorithm(
+        ConfigurableProvider provider,
+        String algorithm,
+        String className,
+        ASN1ObjectIdentifier oid)
+    {
+        provider.addAlgorithm("Cipher." + algorithm, className);
+        if (oid != null)
+        {
+            provider.addAlgorithm("Alg.Alias.Cipher." + oid, algorithm);
+            provider.addAlgorithm("Alg.Alias.Cipher.OID." + oid, algorithm);
+        }
+    }
+
+    protected void registerKeyFactoryOid(ConfigurableProvider provider, ASN1ObjectIdentifier oid, String name, AsymmetricKeyInfoConverter keyFactory)
+    {
+        provider.addAlgorithm("Alg.Alias.KeyFactory." + oid, name);
+        provider.addAlgorithm("Alg.Alias.KeyFactory.OID." + oid, name);
+
+        provider.addKeyInfoConverter(oid, keyFactory);
     }
 
     protected void registerOid(ConfigurableProvider provider, ASN1ObjectIdentifier oid, String name, AsymmetricKeyInfoConverter keyFactory)
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/util/DigestFactory.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/util/DigestFactory.java
index 552a6e6..f442aeb 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/util/DigestFactory.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/util/DigestFactory.java
@@ -31,6 +31,8 @@
     private static Set sha3_256 = new HashSet();
     private static Set sha3_384 = new HashSet();
     private static Set sha3_512 = new HashSet();
+    private static Set shake128 = new HashSet();
+    private static Set shake256 = new HashSet();
     */
     // END Android-removed: Unsupported algorithms
 
@@ -82,6 +84,18 @@
 
         sha3_512.add("SHA3-512");
         sha3_512.add(NISTObjectIdentifiers.id_sha3_512.getId());
+
+        shake128.add("SHAKE128");
+        shake128.add(NISTObjectIdentifiers.id_shake128.getId());
+
+        shake256.add("SHAKE256");
+        shake256.add(NISTObjectIdentifiers.id_shake256.getId());
+
+        oids.put("SHAKE128", NISTObjectIdentifiers.id_shake128);
+        oids.put(NISTObjectIdentifiers.id_shake128.getId(), NISTObjectIdentifiers.id_shake128);
+
+        oids.put("SHAKE256", NISTObjectIdentifiers.id_shake256);
+        oids.put(NISTObjectIdentifiers.id_shake256.getId(), NISTObjectIdentifiers.id_shake256);
         */
         // END Android-removed: Unsupported algorithms
 
@@ -198,6 +212,14 @@
         {
             return org.bouncycastle.crypto.util.DigestFactory.createSHA3_512();
         }
+        if (shake128.contains(digestName))
+        {
+            return org.bouncycastle.crypto.util.DigestFactory.createSHAKE128();
+        }
+        if (shake256.contains(digestName))
+        {
+            return org.bouncycastle.crypto.util.DigestFactory.createSHAKE256();
+        }
         */
         // END Android-removed: Unsupported algorithms
 
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/spec/FPEParameterSpec.java b/bcprov/src/main/java/org/bouncycastle/jcajce/spec/FPEParameterSpec.java
new file mode 100644
index 0000000..f24de23
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/spec/FPEParameterSpec.java
@@ -0,0 +1,51 @@
+package org.bouncycastle.jcajce.spec;
+
+import java.security.spec.AlgorithmParameterSpec;
+
+import org.bouncycastle.crypto.util.RadixConverter;
+import org.bouncycastle.util.Arrays;
+
+public class FPEParameterSpec
+    implements AlgorithmParameterSpec
+{
+    private final RadixConverter radixConverter;
+    private final byte[] tweak;
+    private final boolean useInverse;
+
+    public FPEParameterSpec(int radix, byte[] tweak)
+    {
+        this(radix, tweak, false);
+    }
+
+    public FPEParameterSpec(int radix, byte[] tweak, boolean useInverse)
+    {
+        this(new RadixConverter(radix), tweak, useInverse);
+    }
+
+    public FPEParameterSpec(RadixConverter radixConverter, byte[] tweak, boolean useInverse)
+    {
+        this.radixConverter = radixConverter;
+        this.tweak = Arrays.clone(tweak);
+        this.useInverse = useInverse;
+    }
+
+    public int getRadix()
+    {
+        return radixConverter.getRadix();
+    }
+
+    public RadixConverter getRadixConverter()
+    {
+        return radixConverter;
+    }
+
+    public byte[] getTweak()
+    {
+        return Arrays.clone(tweak);
+    }
+
+    public boolean isUsingInverseFunction()
+    {
+        return useInverse;
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/spec/HybridValueParameterSpec.java b/bcprov/src/main/java/org/bouncycastle/jcajce/spec/HybridValueParameterSpec.java
new file mode 100644
index 0000000..6bf3835
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/spec/HybridValueParameterSpec.java
@@ -0,0 +1,97 @@
+package org.bouncycastle.jcajce.spec;
+
+import java.security.spec.AlgorithmParameterSpec;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import javax.security.auth.Destroyable;
+
+import org.bouncycastle.util.Arrays;
+
+/**
+ * SP 800-56C Hybrid Value spec, to allow the secret in a key agreement to be
+ * created as "Z | T" where T is some other secret value as described in Section 2.
+ * <p>
+ * Get methods throw IllegalStateException if destroy() is called.
+ * </p>
+ */
+public class HybridValueParameterSpec
+    implements AlgorithmParameterSpec, Destroyable
+{
+    private final AtomicBoolean hasBeenDestroyed = new AtomicBoolean(false);
+
+    private volatile byte[] t;
+    private volatile AlgorithmParameterSpec baseSpec;
+
+    /**
+     * Create a spec with T set to t and the spec for the KDF in the agreement to baseSpec.
+     * Note: the t value is not copied.
+     *
+     * @param t a shared secret to be concatenated with the agreement's Z value.
+     * @param baseSpec the base spec for the agreements KDF.
+     */
+    public HybridValueParameterSpec(byte[] t, AlgorithmParameterSpec baseSpec)
+    {
+        this.t = t;
+        this.baseSpec = baseSpec;
+    }
+
+    /**
+     * Return a reference to the T value.
+     *
+     * @return a reference to T.
+     */
+    public byte[] getT()
+    {
+        byte[] tVal = t;
+
+        checkDestroyed();
+        
+        return tVal;
+    }
+
+    /**
+     * Return the base parameter spec.
+     *
+     * @return base spec to be applied to the KDF.
+     */
+    public AlgorithmParameterSpec getBaseParameterSpec()
+    {
+        AlgorithmParameterSpec rv = this.baseSpec;
+
+        checkDestroyed();
+
+        return rv;
+    }
+
+    /**
+     * Return true if the destroy() method is called and the contents are
+     * erased.
+     *
+     * @return true if destroyed, false otherwise.
+     */
+    public boolean isDestroyed()
+    {
+        return this.hasBeenDestroyed.get();
+    }
+
+    /**
+     * Destroy this parameter spec, explicitly erasing its contents.
+     */
+    public void destroy()
+    {
+        if (!hasBeenDestroyed.getAndSet(true))
+        {
+            Arrays.clear(t);
+            this.t = null;
+            this.baseSpec = null;
+        }
+    }
+
+    private void checkDestroyed()
+    {
+        if (isDestroyed())
+        {
+            throw new IllegalStateException("spec has been destroyed");
+        }
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/spec/IESKEMParameterSpec.java b/bcprov/src/main/java/org/bouncycastle/jcajce/spec/IESKEMParameterSpec.java
new file mode 100644
index 0000000..11ca348
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/spec/IESKEMParameterSpec.java
@@ -0,0 +1,51 @@
+package org.bouncycastle.jcajce.spec;
+
+import java.security.spec.AlgorithmParameterSpec;
+
+import org.bouncycastle.util.Arrays;
+
+/**
+ * Parameter spec for an integrated encryptor KEM, as in IEEE_Std_1609_2
+ */
+public class IESKEMParameterSpec
+    implements AlgorithmParameterSpec
+{
+    private final byte[] recipientInfo;
+    private final boolean usePointCompression;
+
+
+    /**
+     * Set the IESKEM parameters.
+     *
+     * @param recipientInfo recipient data.
+     */
+    public IESKEMParameterSpec(
+        byte[] recipientInfo)
+    {
+        this(recipientInfo, false);
+    }
+
+    /**
+     * Set the IESKEM parameters - specifying point compression.
+     *
+     * @param recipientInfo recipient data.
+     * @param usePointCompression use point compression on output (ignored on input).
+     */
+    public IESKEMParameterSpec(
+        byte[] recipientInfo,
+        boolean usePointCompression)
+    {
+        this.recipientInfo = Arrays.clone(recipientInfo);
+        this.usePointCompression = usePointCompression;
+    }
+
+    public byte[] getRecipientInfo()
+    {
+        return Arrays.clone(recipientInfo);
+    }
+
+    public boolean hasUsePointCompression()
+    {
+        return usePointCompression;
+    }
+}
\ No newline at end of file
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/spec/KEMExtractSpec.java b/bcprov/src/main/java/org/bouncycastle/jcajce/spec/KEMExtractSpec.java
new file mode 100644
index 0000000..6ece51e
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/spec/KEMExtractSpec.java
@@ -0,0 +1,48 @@
+package org.bouncycastle.jcajce.spec;
+
+import java.security.PrivateKey;
+import java.security.spec.AlgorithmParameterSpec;
+
+import org.bouncycastle.util.Arrays;
+
+public class KEMExtractSpec
+    implements AlgorithmParameterSpec
+{
+    private final PrivateKey privateKey;
+    private final byte[] encapsulation;
+    private final String keyAlgorithmName;
+    private final int keySizeInBits;
+
+    public KEMExtractSpec(PrivateKey privateKey, byte[] encapsulation, String keyAlgorithmName)
+    {
+        this(privateKey, encapsulation, keyAlgorithmName, 256);
+    }
+
+    public KEMExtractSpec(PrivateKey privateKey, byte[] encapsulation, String keyAlgorithmName, int keySizeInBits)
+    {
+        this.privateKey = privateKey;
+        this.encapsulation = Arrays.clone(encapsulation);
+        this.keyAlgorithmName = keyAlgorithmName;
+        this.keySizeInBits = keySizeInBits;
+    }
+
+    public byte[] getEncapsulation()
+    {
+        return Arrays.clone(encapsulation);
+    }
+
+    public PrivateKey getPrivateKey()
+    {
+        return privateKey;
+    }
+
+    public String getKeyAlgorithmName()
+    {
+        return keyAlgorithmName;
+    }
+
+    public int getKeySize()
+    {
+        return keySizeInBits;
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/spec/KEMGenerateSpec.java b/bcprov/src/main/java/org/bouncycastle/jcajce/spec/KEMGenerateSpec.java
new file mode 100644
index 0000000..a5ea3f0
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/spec/KEMGenerateSpec.java
@@ -0,0 +1,39 @@
+package org.bouncycastle.jcajce.spec;
+
+import java.security.PublicKey;
+import java.security.spec.AlgorithmParameterSpec;
+
+public class KEMGenerateSpec
+    implements AlgorithmParameterSpec
+{
+    private final PublicKey publicKey;
+    private final String keyAlgorithmName;
+    private final int keySizeInBits;
+
+    public KEMGenerateSpec(PublicKey publicKey, String keyAlgorithmName)
+    {
+        this(publicKey, keyAlgorithmName, 256);
+    }
+
+    public KEMGenerateSpec(PublicKey publicKey, String keyAlgorithmName, int keySizeInBits)
+    {
+        this.publicKey = publicKey;
+        this.keyAlgorithmName = keyAlgorithmName;
+        this.keySizeInBits = keySizeInBits;
+    }
+
+    public PublicKey getPublicKey()
+    {
+        return publicKey;
+    }
+
+    public String getKeyAlgorithmName()
+    {
+        return keyAlgorithmName;
+    }
+
+    public int getKeySize()
+    {
+        return keySizeInBits;
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/spec/RawEncodedKeySpec.java b/bcprov/src/main/java/org/bouncycastle/jcajce/spec/RawEncodedKeySpec.java
new file mode 100644
index 0000000..27e75e1
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/spec/RawEncodedKeySpec.java
@@ -0,0 +1,25 @@
+package org.bouncycastle.jcajce.spec;
+
+import java.security.spec.EncodedKeySpec;
+
+/**
+ * An encoded key spec that just wraps the minimal data for a public/private key representation.
+ */
+public class RawEncodedKeySpec
+    extends EncodedKeySpec
+{
+    /**
+     * Base constructor - just the minimal data.
+     *
+     * @param bytes the public/private key data.
+     */
+    public RawEncodedKeySpec(byte[] bytes)
+    {
+        super(bytes);
+    }
+
+    public String getFormat()
+    {
+        return "RAW";
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/spec/UserKeyingMaterialSpec.java b/bcprov/src/main/java/org/bouncycastle/jcajce/spec/UserKeyingMaterialSpec.java
index d813531..c63ea02 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/spec/UserKeyingMaterialSpec.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/spec/UserKeyingMaterialSpec.java
@@ -8,14 +8,47 @@
     implements AlgorithmParameterSpec
 {
     private final byte[] userKeyingMaterial;
+    private final byte[] salt;
 
+    /**
+     * Base constructor.
+     *
+     * @param userKeyingMaterial the bytes to be mixed in to the key agreement's KDF.
+     */
     public UserKeyingMaterialSpec(byte[] userKeyingMaterial)
     {
-        this.userKeyingMaterial = Arrays.clone(userKeyingMaterial);
+        this(userKeyingMaterial, null);
     }
 
+    /**
+     * Base constructor.
+     *
+     * @param userKeyingMaterial the bytes to be mixed in to the key agreement's KDF.
+     * @param salt the salt to use with the underlying KDF.
+     */
+    public UserKeyingMaterialSpec(byte[] userKeyingMaterial, byte[] salt)
+    {
+        this.userKeyingMaterial = Arrays.clone(userKeyingMaterial);
+        this.salt = Arrays.clone(salt);
+    }
+
+    /**
+     * Return a copy of the key material in this object.
+     *
+     * @return the user keying material.
+     */
     public byte[] getUserKeyingMaterial()
     {
         return Arrays.clone(userKeyingMaterial);
     }
+
+    /**
+     * Return a copy of the salt in this object.
+     *
+     * @return the KDF salt.
+     */
+    public byte[] getSalt()
+    {
+        return Arrays.clone(salt);
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/util/ECKeyUtil.java b/bcprov/src/main/java/org/bouncycastle/jcajce/util/ECKeyUtil.java
index a37e4e1..3dd71c3 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/util/ECKeyUtil.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/util/ECKeyUtil.java
@@ -11,6 +11,7 @@
 import org.bouncycastle.asn1.x9.ECNamedCurveTable;
 import org.bouncycastle.asn1.x9.X962Parameters;
 import org.bouncycastle.asn1.x9.X9ECParameters;
+import org.bouncycastle.asn1.x9.X9ECParametersHolder;
 import org.bouncycastle.asn1.x9.X9ECPoint;
 import org.bouncycastle.crypto.ec.CustomNamedCurves;
 
@@ -68,10 +69,10 @@
             {
                 ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier)params.getParameters();
 
-                X9ECParameters x9 = CustomNamedCurves.getByOID(oid);
+                X9ECParametersHolder x9 = CustomNamedCurves.getByOIDLazy(oid);
                 if (x9 == null)
                 {
-                    x9 = ECNamedCurveTable.getByOID(oid);
+                    x9 = ECNamedCurveTable.getByOIDLazy(oid);
                 }
                 curve = x9.getCurve();
             }
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/util/MessageDigestUtils.java b/bcprov/src/main/java/org/bouncycastle/jcajce/util/MessageDigestUtils.java
index e20d1f1..5d091cf 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/util/MessageDigestUtils.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/util/MessageDigestUtils.java
@@ -14,11 +14,17 @@
 import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
 // Android-removed: Unsupported algorithms
 // import org.bouncycastle.asn1.teletrust.TeleTrusTObjectIdentifiers;
+// import org.bouncycastle.asn1.DERNull;
+// import org.bouncycastle.asn1.misc.MiscObjectIdentifiers;
+// import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 
 public class MessageDigestUtils
 {
     private static Map<ASN1ObjectIdentifier, String> digestOidMap = new HashMap<ASN1ObjectIdentifier, String>();
 
+    // Android-removed: Unsupported algorithms
+    // private static Map<String, AlgorithmIdentifier> digestAlgIdMap = new HashMap<String, AlgorithmIdentifier>();
+
     static
     {
         // BEGIN Android-removed: Unsupported algorithms
@@ -33,6 +39,8 @@
         digestOidMap.put(NISTObjectIdentifiers.id_sha512, "SHA-512");
         // BEGIN Android-removed: Unsupported algorithms
         /*
+        digestOidMap.put(NISTObjectIdentifiers.id_sha512_224, "SHA-512(224)");
+        digestOidMap.put(NISTObjectIdentifiers.id_sha512_256, "SHA-512(256)");
         digestOidMap.put(TeleTrusTObjectIdentifiers.ripemd128, "RIPEMD-128");
         digestOidMap.put(TeleTrusTObjectIdentifiers.ripemd160, "RIPEMD-160");
         digestOidMap.put(TeleTrusTObjectIdentifiers.ripemd256, "RIPEMD-128");
@@ -45,11 +53,51 @@
         digestOidMap.put(NISTObjectIdentifiers.id_sha3_256, "SHA3-256");
         digestOidMap.put(NISTObjectIdentifiers.id_sha3_384, "SHA3-384");
         digestOidMap.put(NISTObjectIdentifiers.id_sha3_512, "SHA3-512");
+        digestOidMap.put(NISTObjectIdentifiers.id_shake128, "SHAKE128");
+        digestOidMap.put(NISTObjectIdentifiers.id_shake256, "SHAKE256");
         digestOidMap.put(GMObjectIdentifiers.sm3, "SM3");
+        digestOidMap.put(MiscObjectIdentifiers.blake3_256, "BLAKE3-256");
+
+        digestAlgIdMap.put("SHA-1", new AlgorithmIdentifier(OIWObjectIdentifiers.idSHA1, DERNull.INSTANCE));
+        digestAlgIdMap.put("SHA-224", new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha224));
+        digestAlgIdMap.put("SHA224", new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha224));
+        digestAlgIdMap.put("SHA-256", new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha256));
+        digestAlgIdMap.put("SHA256", new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha256));
+        digestAlgIdMap.put("SHA-384", new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha384));
+        digestAlgIdMap.put("SHA384", new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha384));
+        digestAlgIdMap.put("SHA-512", new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha512));
+        digestAlgIdMap.put("SHA512", new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha512));
+        digestAlgIdMap.put("SHA3-224", new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha3_224));
+        digestAlgIdMap.put("SHA3-256", new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha3_256));
+        digestAlgIdMap.put("SHA3-384", new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha3_384));
+        digestAlgIdMap.put("SHA3-512", new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha3_512));
+        digestAlgIdMap.put("BLAKE3-256", new AlgorithmIdentifier(MiscObjectIdentifiers.blake3_256));
         */
         // END Android-removed: Unsupported algorithms
     }
 
+    // BEGIN Android-removed: Unsupported algorithms
+    /*
+     * Attempt to find a standard JCA name for the digest represented by the passed in OID.
+     *
+     * @param digestName name of the digest algorithm of interest.
+     * @return an algorithm identifier representing the digest.
+    public static AlgorithmIdentifier getDigestAlgID(String digestName)
+    {
+        if (digestAlgIdMap.containsKey(digestName))
+        {
+            return (AlgorithmIdentifier)digestAlgIdMap.get(digestName);
+        }
+        throw new IllegalArgumentException("unknown digest: " + digestName);
+    }
+
+    public static AlgorithmIdentifier getDigestAlgID(String digestName)
+    {
+        throw new IllegalArgumentException("unknown digest: " + digestName);
+    }
+    */
+    // END Android-removed: Unsupported algorithms
+
     /**
      * Attempt to find a standard JCA name for the digest represented by the passed in OID.
      *
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/ECNamedCurveTable.java b/bcprov/src/main/java/org/bouncycastle/jce/ECNamedCurveTable.java
index 5ad207a..1165dc5 100644
--- a/bcprov/src/main/java/org/bouncycastle/jce/ECNamedCurveTable.java
+++ b/bcprov/src/main/java/org/bouncycastle/jce/ECNamedCurveTable.java
@@ -21,32 +21,35 @@
     public static ECNamedCurveParameterSpec getParameterSpec(
         String  name)
     {
-        X9ECParameters  ecP = org.bouncycastle.crypto.ec.CustomNamedCurves.getByName(name);
+        ASN1ObjectIdentifier oid;
+        try
+        {
+            oid = possibleOID(name) ? new ASN1ObjectIdentifier(name) : null;
+        }
+        catch (IllegalArgumentException e)
+        {
+            oid = null;
+        }
+
+        X9ECParameters ecP;
+        if (oid != null)
+        {
+            ecP = org.bouncycastle.crypto.ec.CustomNamedCurves.getByOID(oid);
+        }
+        else
+        {
+            ecP = org.bouncycastle.crypto.ec.CustomNamedCurves.getByName(name);
+        }
+
         if (ecP == null)
         {
-            try
+            if (oid != null)
             {
-                ecP = org.bouncycastle.crypto.ec.CustomNamedCurves.getByOID(new ASN1ObjectIdentifier(name));
+                ecP = org.bouncycastle.asn1.x9.ECNamedCurveTable.getByOID(oid);
             }
-            catch (IllegalArgumentException e)
-            {
-                // ignore - not an oid
-            }
-
-            if (ecP == null)
+            else
             {
                 ecP = org.bouncycastle.asn1.x9.ECNamedCurveTable.getByName(name);
-                if (ecP == null)
-                {
-                    try
-                    {
-                        ecP = org.bouncycastle.asn1.x9.ECNamedCurveTable.getByOID(new ASN1ObjectIdentifier(name));
-                    }
-                    catch (IllegalArgumentException e)
-                    {
-                        // ignore - not an oid
-                    }
-                }
             }
         }
 
@@ -73,4 +76,21 @@
     {
         return org.bouncycastle.asn1.x9.ECNamedCurveTable.getNames();
     }
+
+    private static boolean possibleOID(
+        String identifier)
+    {
+        if (identifier.length() < 3 || identifier.charAt(1) != '.')
+        {
+            return false;
+        }
+
+        char first = identifier.charAt(0);
+        if (first < '0' || first > '2')
+        {
+            return false;
+        }
+
+        return true;
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/netscape/NetscapeCertRequest.java b/bcprov/src/main/java/org/bouncycastle/jce/netscape/NetscapeCertRequest.java
index 0c9de93..b284481 100644
--- a/bcprov/src/main/java/org/bouncycastle/jce/netscape/NetscapeCertRequest.java
+++ b/bcprov/src/main/java/org/bouncycastle/jce/netscape/NetscapeCertRequest.java
@@ -17,6 +17,7 @@
 
 import org.bouncycastle.asn1.ASN1EncodableVector;
 import org.bouncycastle.asn1.ASN1Encoding;
+import org.bouncycastle.asn1.ASN1IA5String;
 import org.bouncycastle.asn1.ASN1InputStream;
 import org.bouncycastle.asn1.ASN1Object;
 import org.bouncycastle.asn1.ASN1Primitive;
@@ -103,7 +104,7 @@
                         + pkac.size());
             }
 
-            challenge = ((DERIA5String)pkac.getObjectAt(1)).getString();
+            challenge = ((ASN1IA5String)pkac.getObjectAt(1)).getString();
 
             //this could be dangerous, as ASN.1 decoding/encoding
             //could potentially alter the bytes
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/BouncyCastleProvider.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/BouncyCastleProvider.java
index 7f9285b..a651da6 100644
--- a/bcprov/src/main/java/org/bouncycastle/jce/provider/BouncyCastleProvider.java
+++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/BouncyCastleProvider.java
@@ -12,12 +12,20 @@
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.logging.Level;
+import java.util.logging.Logger;
 
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+// import org.bouncycastle.asn1.bc.BCObjectIdentifiers;
 // import org.bouncycastle.asn1.isara.IsaraObjectIdentifiers;
 // import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
 import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
 import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.bouncycastle.crypto.CryptoServiceConstraintsException;
+import org.bouncycastle.crypto.CryptoServiceProperties;
+import org.bouncycastle.crypto.CryptoServicePurpose;
+import org.bouncycastle.crypto.CryptoServicesRegistrar;
 import org.bouncycastle.jcajce.provider.config.ConfigurableProvider;
 import org.bouncycastle.jcajce.provider.config.ProviderConfiguration;
 import org.bouncycastle.jcajce.provider.symmetric.util.ClassUtil;
@@ -34,6 +42,16 @@
 // import org.bouncycastle.pqc.jcajce.provider.xmss.XMSSKeyFactorySpi;
 // import org.bouncycastle.pqc.jcajce.provider.xmss.XMSSMTKeyFactorySpi;
 // import org.bouncycastle.pqc.jcajce.provider.lms.LMSKeyFactorySpi;
+// import org.bouncycastle.pqc.jcajce.provider.bike.BIKEKeyFactorySpi;
+// import org.bouncycastle.pqc.jcajce.provider.cmce.CMCEKeyFactorySpi;
+// import org.bouncycastle.pqc.jcajce.provider.dilithium.DilithiumKeyFactorySpi;
+// import org.bouncycastle.pqc.jcajce.provider.falcon.FalconKeyFactorySpi;
+// import org.bouncycastle.pqc.jcajce.provider.hqc.HQCKeyFactorySpi;
+// import org.bouncycastle.pqc.jcajce.provider.kyber.KyberKeyFactorySpi;
+// import org.bouncycastle.pqc.jcajce.provider.ntru.NTRUKeyFactorySpi;
+// import org.bouncycastle.pqc.jcajce.provider.picnic.PicnicKeyFactorySpi;
+// import org.bouncycastle.pqc.jcajce.provider.sphincsplus.SPHINCSPlusKeyFactorySpi;
+import org.bouncycastle.util.Strings;
 
 /**
  * To add the provider at runtime use:
@@ -62,7 +80,9 @@
 public final class BouncyCastleProvider extends Provider
     implements ConfigurableProvider
 {
-    private static String info = "BouncyCastle Security Provider v1.68";
+    private static final Logger LOG = Logger.getLogger(BouncyCastleProvider.class.getName());
+
+    private static String info = "BouncyCastle Security Provider v1.77";
 
     public static final String PROVIDER_NAME = "BC";
 
@@ -87,17 +107,27 @@
     private static final String[] SYMMETRIC_MACS =
     {
         // Android-removed: Unsupported algorithms
-        // "SipHash", "Poly1305"
+        // "SipHash", "SipHash128", "Poly1305"
     };
 
-    private static final String[] SYMMETRIC_CIPHERS =
+    private static final CryptoServiceProperties[] SYMMETRIC_CIPHERS =
     {
         // Android-changed: Unsupported algorithms
-        // "AES", "ARC4", "ARIA", "Blowfish", "Camellia", "CAST5", "CAST6", "ChaCha", "DES", "DESede",
-        // "GOST28147", "Grainv1", "Grain128", "HC128", "HC256", "IDEA", "Noekeon", "RC2", "RC5",
-        // "RC6", "Rijndael", "Salsa20", "SEED", "Serpent", "Shacal2", "Skipjack", "SM4", "TEA", "Twofish", "Threefish",
-        // "VMPC", "VMPCKSA3", "XTEA", "XSalsa20", "OpenSSLPBKDF", "DSTU7624", "GOST3412_2015"
-        "AES", "ARC4", "Blowfish", "DES", "DESede", "RC2", "Twofish",
+        // TODO: these numbers need a bit more work, we cap at 256 bits.
+        // service("ARIA", 256), service("Camellia", 256), service("CAST5", 128),
+        // service("CAST6", 256), service("ChaCha", 128), service("GOST28147", 128), 
+        // service("Grainv1", 128), service("Grain128", 128), service("HC128", 128), 
+        // service("HC256", 256), service("IDEA", 128), service("Noekeon", 128), 
+        // service("RC6", 256), service("Rijndael", 256), service("Salsa20", 128), 
+        // service("SEED", 128), service("Serpent", 256), service("Shacal2", 128),
+        // service("Skipjack", 80), service("SM4", 128), service("TEA", 128), 
+        // service("RC5", 128), service("Threefish", 128), service("VMPC", 128), 
+        // service("VMPCKSA3", 128), service("XTEA", 128), service("XSalsa20", 128),
+        // service("OpenSSLPBKDF", 128), service("DSTU7624", 256), service("GOST3412_2015", 256),
+        //  service("Zuc", 128)
+        service("AES", 256), service("ARC4", 20), service("Blowfish", 128),
+        service("DES", 56),  service("DESede", 112), service("RC2", 128), 
+        service("Twofish", 256)
     };
 
      /*
@@ -110,14 +140,14 @@
     private static final String[] ASYMMETRIC_GENERIC =
     {
         // Android-changed: Unsupported algorithms
-        // "X509", "IES", "COMPOSITE"
+        // "X509", "IES", "COMPOSITE", "EXTERNAL"
         "X509"
     };
 
     private static final String[] ASYMMETRIC_CIPHERS =
     {
         // Android-changed: Unsupported algorithms
-        // "DSA", "DH", "EC", "RSA", "GOST", "ECGOST", "ElGamal", "DSTU4145", "GM", "EdEC"
+        // "DSA", "DH", "EC", "RSA", "GOST", "ECGOST", "ElGamal", "DSTU4145", "GM", "EdEC", "LMS", "SPHINCSPlus", "Dilithium", "Falcon", "NTRU"
         "DSA", "DH", "EC", "RSA",
     };
 
@@ -153,6 +183,8 @@
     //     "DRBG"
     // };
 
+    private Map<String, Service> serviceMap = new ConcurrentHashMap<String, Service>();
+
     /**
      * Construct a new provider.  This should only be required when
      * using runtime registration of the provider using the
@@ -160,7 +192,7 @@
      */
     public BouncyCastleProvider()
     {
-        super(PROVIDER_NAME, 1.68, info);
+        super(PROVIDER_NAME, 1.77, info);
 
         AccessController.doPrivileged(new PrivilegedAction()
         {
@@ -226,13 +258,9 @@
         put("Cipher.OLDPBEWITHSHAANDTWOFISH-CBC", "org.bouncycastle.jce.provider.BrokenJCEBlockCipher$OldPBEWithSHAAndTwofish");
 
         // Certification Path API
-        put("CertPathValidator.RFC3281", "org.bouncycastle.jce.provider.PKIXAttrCertPathValidatorSpi");
-        put("CertPathBuilder.RFC3281", "org.bouncycastle.jce.provider.PKIXAttrCertPathBuilderSpi");
-        put("CertPathValidator.RFC3280", "org.bouncycastle.jce.provider.PKIXCertPathValidatorSpi");
-        put("CertPathBuilder.RFC3280", "org.bouncycastle.jce.provider.PKIXCertPathBuilderSpi");
-
         if (revChkClass != null)
         {
+            put("CertPathValidator.RFC3281", "org.bouncycastle.jce.provider.PKIXAttrCertPathValidatorSpi");
             put("CertPathBuilder.RFC3281", "org.bouncycastle.jce.provider.PKIXAttrCertPathBuilderSpi");
             put("CertPathValidator.RFC3280", "org.bouncycastle.jce.provider.PKIXCertPathValidatorSpi_8");
             put("CertPathBuilder.RFC3280", "org.bouncycastle.jce.provider.PKIXCertPathBuilderSpi_8");
@@ -259,33 +287,149 @@
         // put("CertStore.Multi", "org.bouncycastle.jce.provider.MultiCertStoreSpi");
         // put("Alg.Alias.CertStore.X509LDAP", "LDAP");
         // END Android-removed: Unsupported algorithms
+
+        getService("SecureRandom", "DEFAULT");  // prime for new SecureRandom() on 1.8 JVMs.
+    }
+
+    public final Service getService(final String type, final String algorithm)
+    {
+        String upperCaseAlgName = Strings.toUpperCase(algorithm);
+        final String key = type + "." + upperCaseAlgName;
+
+        Service service = serviceMap.get(key);
+
+        if (service == null)
+        {
+            synchronized (this)
+            {
+                if (!serviceMap.containsKey(key))
+                {
+                    service = AccessController.doPrivileged(new PrivilegedAction<Service>()
+                    {
+                        @Override
+                        public Service run()
+                        {
+                            Service service = BouncyCastleProvider.super.getService(type, algorithm);
+                            if (service == null)
+                            {
+                                return null;
+                            }
+                            serviceMap.put(key, service);
+                            // remove legacy entry and swap to service entry
+                            BouncyCastleProvider.super.remove(service.getType() + "." + service.getAlgorithm());
+                            BouncyCastleProvider.super.putService(service);
+
+                            return service;
+                        }
+                    });
+                }
+                else
+                {
+                    service = serviceMap.get(key);
+                }
+            }
+        }
+
+        return service;
     }
 
     private void loadAlgorithms(String packageName, String[] names)
     {
         for (int i = 0; i != names.length; i++)
         {
-            Class clazz = ClassUtil.loadClass(BouncyCastleProvider.class, packageName + names[i] + "$Mappings");
+            loadServiceClass(packageName, names[i]);
+        }
+    }
 
-            if (clazz != null)
+    private void loadAlgorithms(String packageName, CryptoServiceProperties[] services)
+    {
+        for (int i = 0; i != services.length; i++)
+        {
+            CryptoServiceProperties service = services[i];
+            try
             {
-                try
+                CryptoServicesRegistrar.checkConstraints(service);
+
+                loadServiceClass(packageName, service.getServiceName());
+            }
+            catch (CryptoServiceConstraintsException e)
+            {
+                if (LOG.isLoggable(Level.FINE))
                 {
-                    ((AlgorithmProvider)clazz.newInstance()).configure(this);
-                }
-                catch (Exception e)
-                {   // this should never ever happen!!
-                    throw new InternalError("cannot create instance of "
-                        + packageName + names[i] + "$Mappings : " + e);
+                    LOG.fine("service for " + service.getServiceName() + " ignored due to constraints");
                 }
             }
         }
     }
 
+    private void loadServiceClass(String packageName, String serviceName)
+    {
+        Class clazz = ClassUtil.loadClass(BouncyCastleProvider.class, packageName + serviceName + "$Mappings");
+
+        if (clazz != null)
+        {
+            try
+            {
+                ((AlgorithmProvider)clazz.newInstance()).configure(this);
+            }
+            catch (Exception e)
+            {   // this should never ever happen!!
+                throw new InternalError("cannot create instance of "
+                    + packageName + serviceName + "$Mappings : " + e);
+            }
+        }
+    }
     // BEGIN Android-removed: Unsupported algorithms
     /*
+
     private void loadPQCKeys()
     {
+        addKeyInfoConverter(BCObjectIdentifiers.sphincsPlus, new SPHINCSPlusKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.sphincsPlus_sha2_128s_r3, new SPHINCSPlusKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.sphincsPlus_sha2_128f_r3, new SPHINCSPlusKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.sphincsPlus_shake_128s_r3, new SPHINCSPlusKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.sphincsPlus_shake_128f_r3, new SPHINCSPlusKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.sphincsPlus_haraka_128s_r3, new SPHINCSPlusKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.sphincsPlus_haraka_128f_r3, new SPHINCSPlusKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.sphincsPlus_sha2_192s_r3, new SPHINCSPlusKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.sphincsPlus_sha2_192f_r3, new SPHINCSPlusKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.sphincsPlus_shake_192s_r3, new SPHINCSPlusKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.sphincsPlus_shake_192f_r3, new SPHINCSPlusKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.sphincsPlus_haraka_192s_r3, new SPHINCSPlusKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.sphincsPlus_haraka_192f_r3, new SPHINCSPlusKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.sphincsPlus_sha2_256s_r3, new SPHINCSPlusKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.sphincsPlus_sha2_256f_r3, new SPHINCSPlusKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.sphincsPlus_shake_256s_r3, new SPHINCSPlusKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.sphincsPlus_shake_256f_r3, new SPHINCSPlusKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.sphincsPlus_haraka_256s_r3, new SPHINCSPlusKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.sphincsPlus_haraka_256f_r3, new SPHINCSPlusKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.sphincsPlus_sha2_128s_r3_simple, new SPHINCSPlusKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.sphincsPlus_sha2_128f_r3_simple, new SPHINCSPlusKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.sphincsPlus_shake_128s_r3_simple, new SPHINCSPlusKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.sphincsPlus_shake_128f_r3_simple, new SPHINCSPlusKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.sphincsPlus_haraka_128s_r3_simple, new SPHINCSPlusKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.sphincsPlus_haraka_128f_r3_simple, new SPHINCSPlusKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.sphincsPlus_sha2_192s_r3_simple, new SPHINCSPlusKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.sphincsPlus_sha2_192f_r3_simple, new SPHINCSPlusKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.sphincsPlus_shake_192s_r3_simple, new SPHINCSPlusKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.sphincsPlus_shake_192f_r3_simple, new SPHINCSPlusKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.sphincsPlus_haraka_192s_r3_simple, new SPHINCSPlusKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.sphincsPlus_haraka_192f_r3_simple, new SPHINCSPlusKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.sphincsPlus_sha2_256s_r3_simple, new SPHINCSPlusKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.sphincsPlus_sha2_256f_r3_simple, new SPHINCSPlusKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.sphincsPlus_shake_256s_r3_simple, new SPHINCSPlusKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.sphincsPlus_shake_256f_r3_simple, new SPHINCSPlusKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.sphincsPlus_haraka_256s_r3_simple, new SPHINCSPlusKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.sphincsPlus_haraka_256f_r3_simple, new SPHINCSPlusKeyFactorySpi());
+
+        addKeyInfoConverter(BCObjectIdentifiers.sphincsPlus_sha2_128s, new SPHINCSPlusKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.sphincsPlus_sha2_192s, new SPHINCSPlusKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.sphincsPlus_sha2_256s, new SPHINCSPlusKeyFactorySpi());
+        addKeyInfoConverter(new ASN1ObjectIdentifier("1.3.9999.6.4.10"), new SPHINCSPlusKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.sphincsPlus_shake_128f, new SPHINCSPlusKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.sphincsPlus_shake_192f, new SPHINCSPlusKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.sphincsPlus_shake_256f, new SPHINCSPlusKeyFactorySpi());
+
         addKeyInfoConverter(PQCObjectIdentifiers.sphincs256, new Sphincs256KeyFactorySpi());
         addKeyInfoConverter(PQCObjectIdentifiers.newHope, new NHKeyFactorySpi());
         addKeyInfoConverter(PQCObjectIdentifiers.xmss, new XMSSKeyFactorySpi());
@@ -298,6 +442,37 @@
         addKeyInfoConverter(PQCObjectIdentifiers.qTESLA_p_I, new QTESLAKeyFactorySpi());
         addKeyInfoConverter(PQCObjectIdentifiers.qTESLA_p_III, new QTESLAKeyFactorySpi());
         addKeyInfoConverter(PKCSObjectIdentifiers.id_alg_hss_lms_hashsig, new LMSKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.picnic_key, new PicnicKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.falcon_512, new FalconKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.falcon_1024, new FalconKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.dilithium2, new DilithiumKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.dilithium3, new DilithiumKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.dilithium5, new DilithiumKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.dilithium2_aes, new DilithiumKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.dilithium3_aes, new DilithiumKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.dilithium5_aes, new DilithiumKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.kyber512, new KyberKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.kyber768, new KyberKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.kyber1024, new KyberKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.mceliece348864_r3, new CMCEKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.mceliece460896_r3, new CMCEKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.mceliece6688128_r3, new CMCEKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.mceliece6960119_r3, new CMCEKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.mceliece8192128_r3, new CMCEKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.bike128, new BIKEKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.bike192, new BIKEKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.bike256, new BIKEKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.hqc128, new HQCKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.hqc192, new HQCKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.hqc256, new HQCKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.kyber1024, new KyberKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.kyber512_aes, new KyberKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.kyber768_aes, new KyberKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.kyber1024_aes, new KyberKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.ntruhps2048509, new NTRUKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.ntruhps2048677, new NTRUKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.ntruhps4096821, new NTRUKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.ntruhrss701, new NTRUKeyFactorySpi());
     }
     */
     // END Android-removed: Unsupported algorithms
@@ -325,12 +500,25 @@
         put(key, value);
     }
 
+    public void addAlgorithm(String key, String value, Map<String, String> attributes)
+    {
+        addAlgorithm(key, value);
+        addAttributes(key, attributes);
+    }
+
     public void addAlgorithm(String type, ASN1ObjectIdentifier oid, String className)
     {
         addAlgorithm(type + "." + oid, className);
         addAlgorithm(type + ".OID." + oid, className);
     }
 
+    public void addAlgorithm(String type, ASN1ObjectIdentifier oid, String className, Map<String, String> attributes)
+    {
+        addAlgorithm(type, oid, className);
+        addAttributes(type + "." + oid, attributes);
+        addAttributes(type + ".OID." + oid, attributes);
+    }
+    
     public void addKeyInfoConverter(ASN1ObjectIdentifier oid, AsymmetricKeyInfoConverter keyInfoConverter)
     {
         synchronized (keyInfoConverters)
@@ -346,6 +534,8 @@
 
     public void addAttributes(String key, Map<String, String> attributeMap)
     {
+        put(key + " ImplementedIn", "Software");
+
         for (Iterator it = attributeMap.keySet().iterator(); it.hasNext();)
         {
             String attributeName = (String)it.next();
@@ -386,6 +576,11 @@
         }
         // Android-removed: see above
         /*
+        if (publicKeyInfo.getAlgorithm().getAlgorithm().on(BCObjectIdentifiers.picnic_key))
+        {
+            return new PicnicKeyFactorySpi().generatePublic(publicKeyInfo);
+        }
+
         AsymmetricKeyInfoConverter converter = getAsymmetricKeyInfoConverter(publicKeyInfo.getAlgorithm().getAlgorithm());
 
         if (converter == null)
@@ -459,4 +654,42 @@
         return privateProvider;
     }
     // END Android-added: Allow algorithms to be provided privately for BC internals.
+    private static CryptoServiceProperties service(String name, int bitsOfSecurity)
+    {
+        return new JcaCryptoService(name, bitsOfSecurity);
+    }
+
+    private static class JcaCryptoService
+        implements CryptoServiceProperties
+    {
+
+        private final String name;
+        private final int bitsOfSecurity;
+
+        JcaCryptoService(String name, int bitsOfSecurity)
+        {
+            this.name = name;
+            this.bitsOfSecurity = bitsOfSecurity;
+        }
+
+        public int bitsOfSecurity()
+        {
+            return bitsOfSecurity;
+        }
+
+        public String getServiceName()
+        {
+            return name;
+        }
+
+        public CryptoServicePurpose getPurpose()
+        {
+            return CryptoServicePurpose.ANY;
+        }
+
+        public Object getParams()
+        {
+            return null;
+        }
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/CertPathValidatorUtilities.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/CertPathValidatorUtilities.java
index 6f74c72..8fe3807 100644
--- a/bcprov/src/main/java/org/bouncycastle/jce/provider/CertPathValidatorUtilities.java
+++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/CertPathValidatorUtilities.java
@@ -44,7 +44,6 @@
 import org.bouncycastle.asn1.ASN1Encodable;
 import org.bouncycastle.asn1.ASN1Enumerated;
 import org.bouncycastle.asn1.ASN1GeneralizedTime;
-import org.bouncycastle.asn1.ASN1InputStream;
 import org.bouncycastle.asn1.ASN1Integer;
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
 import org.bouncycastle.asn1.ASN1OctetString;
@@ -54,7 +53,6 @@
 import org.bouncycastle.asn1.ASN1String;
 import org.bouncycastle.asn1.DEROctetString;
 import org.bouncycastle.asn1.DERSequence;
-import org.bouncycastle.asn1.isismtt.ISISMTTObjectIdentifiers;
 import org.bouncycastle.asn1.x500.X500Name;
 import org.bouncycastle.asn1.x500.style.RFC4519Style;
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
@@ -68,6 +66,7 @@
 import org.bouncycastle.asn1.x509.GeneralNames;
 import org.bouncycastle.asn1.x509.PolicyInformation;
 import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.bouncycastle.internal.asn1.isismtt.ISISMTTObjectIdentifiers;
 import org.bouncycastle.jcajce.PKIXCRLStore;
 import org.bouncycastle.jcajce.PKIXCRLStoreSelector;
 import org.bouncycastle.jcajce.PKIXCertRevocationCheckerParameters;
@@ -83,7 +82,6 @@
 import org.bouncycastle.util.Store;
 import org.bouncycastle.util.StoreException;
 import org.bouncycastle.x509.X509AttributeCertificate;
-import org.bouncycastle.x509.extension.X509ExtensionUtil;
 
 class CertPathValidatorUtilities
 {
@@ -776,7 +774,7 @@
 
                     for (int j = 0; j < genNames.length; j++)
                     {
-                        GeneralName name = genNames[i];
+                        GeneralName name = genNames[j];
                         if (name.getTagNo() == GeneralName.uniformResourceIdentifier)
                         {
                             try
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/JCEDHPrivateKey.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/JCEDHPrivateKey.java
index d4fa285..32e3043 100644
--- a/bcprov/src/main/java/org/bouncycastle/jce/provider/JCEDHPrivateKey.java
+++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/JCEDHPrivateKey.java
@@ -19,7 +19,7 @@
 import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
 import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
-import org.bouncycastle.asn1.x9.DHDomainParameters;
+import org.bouncycastle.asn1.x9.DomainParameters;
 import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
 import org.bouncycastle.crypto.params.DHPrivateKeyParameters;
 import org.bouncycastle.jcajce.provider.asymmetric.util.PKCS12BagAttributeCarrierImpl;
@@ -81,9 +81,9 @@
         }
         else if (id.equals(X9ObjectIdentifiers.dhpublicnumber))
         {
-            DHDomainParameters params = DHDomainParameters.getInstance(seq);
+            DomainParameters params = DomainParameters.getInstance(seq);
 
-            this.dhSpec = new DHParameterSpec(params.getP().getValue(), params.getG().getValue());
+            this.dhSpec = new DHParameterSpec(params.getP(), params.getG());
         }
         else
         {
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/JCEDHPublicKey.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/JCEDHPublicKey.java
index 3e6a09a..2fdc5fb 100644
--- a/bcprov/src/main/java/org/bouncycastle/jce/provider/JCEDHPublicKey.java
+++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/JCEDHPublicKey.java
@@ -16,7 +16,7 @@
 import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
-import org.bouncycastle.asn1.x9.DHDomainParameters;
+import org.bouncycastle.asn1.x9.DomainParameters;
 import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
 import org.bouncycastle.crypto.params.DHPublicKeyParameters;
 import org.bouncycastle.jcajce.provider.asymmetric.util.KeyUtil;
@@ -76,8 +76,8 @@
 
         this.y = derY.getValue();
 
-        ASN1Sequence seq = ASN1Sequence.getInstance(info.getAlgorithmId().getParameters());
-        ASN1ObjectIdentifier id = info.getAlgorithmId().getAlgorithm();
+        ASN1Sequence seq = ASN1Sequence.getInstance(info.getAlgorithm().getParameters());
+        ASN1ObjectIdentifier id = info.getAlgorithm().getAlgorithm();
 
         // we need the PKCS check to handle older keys marked with the X9 oid.
         if (id.equals(PKCSObjectIdentifiers.dhKeyAgreement) || isPKCSParam(seq))
@@ -95,9 +95,9 @@
         }
         else if (id.equals(X9ObjectIdentifiers.dhpublicnumber))
         {
-            DHDomainParameters params = DHDomainParameters.getInstance(seq);
+            DomainParameters params = DomainParameters.getInstance(seq);
 
-            this.dhSpec = new DHParameterSpec(params.getP().getValue(), params.getG().getValue());
+            this.dhSpec = new DHParameterSpec(params.getP(), params.getG());
         }
         else
         {
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/JCEECPrivateKey.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/JCEECPrivateKey.java
index 26616b4..d4c8b9f 100644
--- a/bcprov/src/main/java/org/bouncycastle/jce/provider/JCEECPrivateKey.java
+++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/JCEECPrivateKey.java
@@ -6,24 +6,21 @@
 import java.math.BigInteger;
 import java.security.interfaces.ECPrivateKey;
 import java.security.spec.ECParameterSpec;
-import java.security.spec.ECPoint;
 import java.security.spec.ECPrivateKeySpec;
 import java.security.spec.EllipticCurve;
 import java.util.Enumeration;
 
+import org.bouncycastle.asn1.ASN1BitString;
 import org.bouncycastle.asn1.ASN1Encodable;
 import org.bouncycastle.asn1.ASN1Encoding;
 import org.bouncycastle.asn1.ASN1Integer;
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
 import org.bouncycastle.asn1.ASN1Primitive;
-import org.bouncycastle.asn1.ASN1Sequence;
-import org.bouncycastle.asn1.DERBitString;
 import org.bouncycastle.asn1.DERNull;
 // Android-removed: Unsupported algorithms
 // import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers;
 // import org.bouncycastle.asn1.cryptopro.ECGOST3410NamedCurves;
 import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
-import org.bouncycastle.asn1.sec.ECPrivateKeyStructure;
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
 import org.bouncycastle.asn1.x9.X962Parameters;
@@ -49,7 +46,7 @@
     private ECParameterSpec ecSpec;
     private boolean         withCompression;
 
-    private DERBitString publicKey;
+    private ASN1BitString publicKey;
 
     private PKCS12BagAttributeCarrierImpl attrCarrier = new PKCS12BagAttributeCarrierImpl();
 
@@ -214,6 +211,7 @@
             else
             */
             // END Android-removed: Unsupported algorithms
+            if (ecP != null)
             {
                 EllipticCurve ellipticCurve = EC5Util.convertCurve(ecP.getCurve(), ecP.getSeed());
 
@@ -250,7 +248,7 @@
         }
         else
         {
-            ECPrivateKeyStructure ec = new ECPrivateKeyStructure((ASN1Sequence)privKey);
+            org.bouncycastle.asn1.sec.ECPrivateKey ec = org.bouncycastle.asn1.sec.ECPrivateKey.getInstance(privKey);
 
             this.d = ec.getKey();
             this.publicKey = ec.getPublicKey();
@@ -310,15 +308,25 @@
         }
         
         PrivateKeyInfo          info;
-        ECPrivateKeyStructure keyStructure;
+        org.bouncycastle.asn1.sec.ECPrivateKey keyStructure;
 
-        if (publicKey != null)
+        int orderBitLength;
+        if (ecSpec == null)
         {
-            keyStructure = new ECPrivateKeyStructure(this.getS(), publicKey, params);
+            orderBitLength = ECUtil.getOrderBitLength(null, null, this.getS());
         }
         else
         {
-            keyStructure = new ECPrivateKeyStructure(this.getS(), params);
+            orderBitLength = ECUtil.getOrderBitLength(null, ecSpec.getOrder(), this.getS());
+        }
+
+        if (publicKey != null)
+        {
+            keyStructure = new org.bouncycastle.asn1.sec.ECPrivateKey(orderBitLength, this.getS(), publicKey, params);
+        }
+        else
+        {
+            keyStructure = new org.bouncycastle.asn1.sec.ECPrivateKey(orderBitLength, this.getS(), params);
         }
 
         try
@@ -430,7 +438,7 @@
 
     }
 
-    private DERBitString getPublicKeyDetails(JCEECPublicKey   pub)
+    private ASN1BitString getPublicKeyDetails(JCEECPublicKey   pub)
     {
         try
         {
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/JCEECPublicKey.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/JCEECPublicKey.java
index 67a8cd7..bad0015 100644
--- a/bcprov/src/main/java/org/bouncycastle/jce/provider/JCEECPublicKey.java
+++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/JCEECPublicKey.java
@@ -10,11 +10,11 @@
 import java.security.spec.ECPublicKeySpec;
 import java.security.spec.EllipticCurve;
 
+import org.bouncycastle.asn1.ASN1BitString;
 import org.bouncycastle.asn1.ASN1Encodable;
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
 import org.bouncycastle.asn1.ASN1OctetString;
 import org.bouncycastle.asn1.ASN1Primitive;
-import org.bouncycastle.asn1.DERBitString;
 import org.bouncycastle.asn1.DERNull;
 import org.bouncycastle.asn1.DEROctetString;
 // Android-removed: Unsupported algorithms
@@ -188,7 +188,7 @@
 
         if (algID.getAlgorithm().equals(CryptoProObjectIdentifiers.gostR3410_2001))
         {
-            DERBitString bits = info.getPublicKeyData();
+            ASN1BitString bits = info.getPublicKeyData();
             ASN1OctetString key;
             this.algorithm = "ECGOST3410";
 
@@ -269,7 +269,7 @@
                     ecP.getH().intValue());
             }
 
-            DERBitString    bits = info.getPublicKeyData();
+            ASN1BitString   bits = info.getPublicKeyData();
             byte[]          data = bits.getBytes();
             ASN1OctetString key = new DEROctetString(data);
 
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/PKIXCRLUtil.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/PKIXCRLUtil.java
index c192532..1599ceb 100644
--- a/bcprov/src/main/java/org/bouncycastle/jce/provider/PKIXCRLUtil.java
+++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/PKIXCRLUtil.java
@@ -40,8 +40,9 @@
         for (Iterator it = initialSet.iterator(); it.hasNext();)
         {
             X509CRL crl = (X509CRL)it.next();
-
-            if (crl.getNextUpdate().after(validityDate))
+            
+            Date nextUpdate = crl.getNextUpdate();
+            if (nextUpdate == null || nextUpdate.after(validityDate))
             {
                 X509Certificate cert = crlselect.getCertificateChecking();
 
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/RFC3280CertPathUtilities.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/RFC3280CertPathUtilities.java
index f3c8d16..52f5ff8 100644
--- a/bcprov/src/main/java/org/bouncycastle/jce/provider/RFC3280CertPathUtilities.java
+++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/RFC3280CertPathUtilities.java
@@ -1,7 +1,6 @@
 package org.bouncycastle.jce.provider;
 
 import java.io.IOException;
-import java.math.BigInteger;
 import java.security.GeneralSecurityException;
 import java.security.PublicKey;
 import java.security.cert.CertPath;
@@ -2098,18 +2097,12 @@
             throw new ExtCertPathValidatorException("Basic constraints extension cannot be decoded.", e, certPath,
                 index);
         }
-        if (bc != null)
+        if (bc != null && bc.isCA())  // if there is a path len constraint and we're not a CA, ignore it! (yes, it happens).
         {
-            BigInteger _pathLengthConstraint = bc.getPathLenConstraint();
-
-            if (_pathLengthConstraint != null)
+            ASN1Integer pathLenConstraint = bc.getPathLenConstraintInteger();
+            if (pathLenConstraint != null)
             {
-                int _plc = _pathLengthConstraint.intValue();
-
-                if (_plc < maxPathLength)
-                {
-                    return _plc;
-                }
+                maxPathLength = Math.min(maxPathLength, pathLenConstraint.intPositiveValueExact());
             }
         }
         return maxPathLength;
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/X509CRLEntryObject.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/X509CRLEntryObject.java
index d5e2338..bbfacab 100644
--- a/bcprov/src/main/java/org/bouncycastle/jce/provider/X509CRLEntryObject.java
+++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/X509CRLEntryObject.java
@@ -23,7 +23,6 @@
 import org.bouncycastle.asn1.x509.GeneralName;
 import org.bouncycastle.asn1.x509.GeneralNames;
 import org.bouncycastle.asn1.x509.TBSCertList;
-import org.bouncycastle.asn1.x509.X509Extension;
 import org.bouncycastle.util.Strings;
 
 /**
@@ -286,11 +285,11 @@
                         buf.append("                       critical(").append(ext.isCritical()).append(") ");
                         try
                         {
-                            if (oid.equals(X509Extension.reasonCode))
+                            if (oid.equals(Extension.reasonCode))
                             {
                                 buf.append(CRLReason.getInstance(ASN1Enumerated.getInstance(dIn.readObject()))).append(nl);
                             }
-                            else if (oid.equals(X509Extension.certificateIssuer))
+                            else if (oid.equals(Extension.certificateIssuer))
                             {
                                 buf.append("Certificate issuer: ").append(GeneralNames.getInstance(dIn.readObject())).append(nl);
                             }
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/X509CertificateObject.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/X509CertificateObject.java
index 4bbd512..7fd1a79 100644
--- a/bcprov/src/main/java/org/bouncycastle/jce/provider/X509CertificateObject.java
+++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/X509CertificateObject.java
@@ -34,13 +34,13 @@
 import org.bouncycastle.asn1.ASN1BitString;
 import org.bouncycastle.asn1.ASN1Encodable;
 import org.bouncycastle.asn1.ASN1Encoding;
+import org.bouncycastle.asn1.ASN1IA5String;
 import org.bouncycastle.asn1.ASN1InputStream;
+import org.bouncycastle.asn1.ASN1Integer;
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
 import org.bouncycastle.asn1.ASN1Primitive;
 import org.bouncycastle.asn1.ASN1Sequence;
 import org.bouncycastle.asn1.ASN1String;
-import org.bouncycastle.asn1.DERBitString;
-import org.bouncycastle.asn1.DERIA5String;
 import org.bouncycastle.asn1.DERNull;
 import org.bouncycastle.asn1.DEROctetString;
 import org.bouncycastle.asn1.misc.MiscObjectIdentifiers;
@@ -107,7 +107,7 @@
             byte[] bytes = this.getExtensionBytes("2.5.29.15");
             if (bytes != null)
             {
-                ASN1BitString bits = DERBitString.getInstance(ASN1Primitive.fromByteArray(bytes));
+                ASN1BitString bits = ASN1BitString.getInstance(ASN1Primitive.fromByteArray(bytes));
 
                 bytes = bits.getBytes();
                 int length = (bytes.length * 8) - bits.getPadBits();
@@ -290,7 +290,7 @@
 
     public boolean[] getIssuerUniqueID()
     {
-        DERBitString    id = c.getTBSCertificate().getIssuerUniqueId();
+        ASN1BitString    id = c.getTBSCertificate().getIssuerUniqueId();
 
         if (id != null)
         {
@@ -310,7 +310,7 @@
 
     public boolean[] getSubjectUniqueID()
     {
-        DERBitString    id = c.getTBSCertificate().getSubjectUniqueId();
+        ASN1BitString    id = c.getTBSCertificate().getSubjectUniqueId();
 
         if (id != null)
         {
@@ -364,26 +364,18 @@
     
     public int getBasicConstraints()
     {
-        if (basicConstraints != null)
+        if (basicConstraints == null || !basicConstraints.isCA())
         {
-            if (basicConstraints.isCA())
-            {
-                if (basicConstraints.getPathLenConstraint() == null)
-                {
-                    return Integer.MAX_VALUE;
-                }
-                else
-                {
-                    return basicConstraints.getPathLenConstraint().intValue();
-                }
-            }
-            else
-            {
-                return -1;
-            }
+            return -1;
         }
 
-        return -1;
+        ASN1Integer pathLenConstraint = basicConstraints.getPathLenConstraintInteger();
+        if (pathLenConstraint == null)
+        {
+            return Integer.MAX_VALUE;
+        }
+
+        return pathLenConstraint.intPositiveValueExact();
     }
 
     public Collection getSubjectAlternativeNames()
@@ -709,15 +701,15 @@
                         }
                         else if (oid.equals(MiscObjectIdentifiers.netscapeCertType))
                         {
-                            buf.append(new NetscapeCertType((DERBitString)dIn.readObject())).append(nl);
+                            buf.append(new NetscapeCertType((ASN1BitString)dIn.readObject())).append(nl);
                         }
                         else if (oid.equals(MiscObjectIdentifiers.netscapeRevocationURL))
                         {
-                            buf.append(new NetscapeRevocationURL((DERIA5String)dIn.readObject())).append(nl);
+                            buf.append(new NetscapeRevocationURL((ASN1IA5String)dIn.readObject())).append(nl);
                         }
                         else if (oid.equals(MiscObjectIdentifiers.verisignCzagExtension))
                         {
-                            buf.append(new VerisignCzagExtension((DERIA5String)dIn.readObject())).append(nl);
+                            buf.append(new VerisignCzagExtension((ASN1IA5String)dIn.readObject())).append(nl);
                         }
                         else 
                         {
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/spec/ECNamedCurveSpec.java b/bcprov/src/main/java/org/bouncycastle/jce/spec/ECNamedCurveSpec.java
index 0be2ca9..6bc0298 100644
--- a/bcprov/src/main/java/org/bouncycastle/jce/spec/ECNamedCurveSpec.java
+++ b/bcprov/src/main/java/org/bouncycastle/jce/spec/ECNamedCurveSpec.java
@@ -43,7 +43,7 @@
         {
             Polynomial poly = ((PolynomialExtensionField)field).getMinimalPolynomial();
             int[] exponents = poly.getExponentsPresent();
-            int[] ks = Arrays.reverse(Arrays.copyOfRange(exponents, 1, exponents.length - 1));
+            int[] ks = Arrays.reverseInPlace(Arrays.copyOfRange(exponents, 1, exponents.length - 1));
             return new ECFieldF2m(poly.getDegree(), ks);
         }
     }
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/ECCurve.java b/bcprov/src/main/java/org/bouncycastle/math/ec/ECCurve.java
index 8f00c6b..04bde03 100644
--- a/bcprov/src/main/java/org/bouncycastle/math/ec/ECCurve.java
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/ECCurve.java
@@ -2,9 +2,14 @@
 
 import java.math.BigInteger;
 import java.security.SecureRandom;
+import java.util.Collections;
+import java.util.HashSet;
 import java.util.Hashtable;
 import java.util.Random;
+import java.util.Set;
 
+import org.bouncycastle.crypto.CryptoServicesRegistrar;
+import org.bouncycastle.math.Primes;
 import org.bouncycastle.math.ec.endo.ECEndomorphism;
 import org.bouncycastle.math.ec.endo.GLVEndomorphism;
 import org.bouncycastle.math.field.FiniteField;
@@ -12,6 +17,7 @@
 import org.bouncycastle.math.raw.Nat;
 import org.bouncycastle.util.BigIntegers;
 import org.bouncycastle.util.Integers;
+import org.bouncycastle.util.Properties;
 
 /**
  * base class for an elliptic curve
@@ -668,6 +674,8 @@
     public static class Fp extends AbstractFp
     {
         private static final int FP_DEFAULT_COORDS = ECCurve.COORD_JACOBIAN_MODIFIED;
+        private static final Set<BigInteger> knownQs = Collections.synchronizedSet(new HashSet<BigInteger>());
+        private static final BigIntegers.Cache validatedQs = new BigIntegers.Cache();
 
         BigInteger q, r;
         ECPoint.Fp infinity;
@@ -682,9 +690,44 @@
 
         public Fp(BigInteger q, BigInteger a, BigInteger b, BigInteger order, BigInteger cofactor)
         {
+            this(q, a, b, order, cofactor, false);
+        }
+
+        public Fp(BigInteger q, BigInteger a, BigInteger b, BigInteger order, BigInteger cofactor, boolean isInternal)
+        {
             super(q);
 
-            this.q = q;
+            if (isInternal)
+            {
+                this.q = q;
+                knownQs.add(q);
+            }
+            else if (knownQs.contains(q) || validatedQs.contains(q))
+            {
+                this.q = q;
+            }
+            else
+            {
+                int maxBitLength = Properties.asInteger("org.bouncycastle.ec.fp_max_size", 1042); // 2 * 521
+                int certainty = Properties.asInteger("org.bouncycastle.ec.fp_certainty", 100);
+
+                int qBitLength = q.bitLength();
+                if (maxBitLength < qBitLength)
+                {
+                    throw new IllegalArgumentException("Fp q value out of range");
+                }
+
+                if (Primes.hasAnySmallFactors(q) || !Primes.isMRProbablePrime(
+                    q, CryptoServicesRegistrar.getSecureRandom(), getNumberOfIterations(qBitLength, certainty)))
+                {
+                    throw new IllegalArgumentException("Fp q value not prime");
+                }
+
+                validatedQs.add(q);
+
+                this.q = q;
+            }
+
             this.r = ECFieldElement.Fp.calculateResidue(q);
             this.infinity = new ECPoint.Fp(this, null, null);
 
@@ -741,6 +784,11 @@
 
         public ECFieldElement fromBigInteger(BigInteger x)
         {
+            if (x == null || x.signum() < 0 || x.compareTo(q) >= 0)
+            {
+                throw new IllegalArgumentException("x value invalid for Fp field element");
+            }
+
             return new ECFieldElement.Fp(this.q, this.r, x);
         }
 
@@ -797,32 +845,11 @@
 
         private static FiniteField buildField(int m, int k1, int k2, int k3)
         {
-            if (k1 == 0)
-            {
-                throw new IllegalArgumentException("k1 must be > 0");
-            }
+            int[] exponents = (k2 | k3) == 0
+                ? new int[]{ 0, k1, m }
+                : new int[]{ 0, k1, k2, k3, m };
 
-            if (k2 == 0)
-            {
-                if (k3 != 0)
-                {
-                    throw new IllegalArgumentException("k3 must be 0 if k2 == 0");
-                }
-
-                return FiniteFields.getBinaryExtensionField(new int[]{ 0, k1, m });
-            }
-
-            if (k2 <= k1)
-            {
-                throw new IllegalArgumentException("k2 must be > k1");
-            }
-
-            if (k3 <= k2)
-            {
-                throw new IllegalArgumentException("k3 must be > k2");
-            }
-
-            return FiniteFields.getBinaryExtensionField(new int[]{ 0, k1, k2, k3, m });
+            return FiniteFields.getBinaryExtensionField(exponents);
         }
 
         protected AbstractF2m(int m, int k1, int k2, int k3)
@@ -915,7 +942,7 @@
                 y = this.getB().sqrt();
             }
             else
-            {
+            { 
                 ECFieldElement beta = x.square().invert().multiply(this.getB()).add(this.getA()).add(x);
                 ECFieldElement z = solveQuadraticEquation(beta);
                 if (z != null)
@@ -1275,7 +1302,16 @@
 
         public ECFieldElement fromBigInteger(BigInteger x)
         {
-            return new ECFieldElement.F2m(this.m, this.k1, this.k2, this.k3, x);
+            if (x == null || x.signum() < 0 || x.bitLength() > m)
+            {
+                throw new IllegalArgumentException("x value invalid in F2m field element");
+            }
+
+            int[] ks = (k2 | k3) == 0
+                ? new int[]{ k1 }
+                : new int[]{ k1, k2, k3 };
+
+            return new ECFieldElement.F2m(m, ks, new LongArray(x));
         }
 
         protected ECPoint createRawPoint(ECFieldElement x, ECFieldElement y)
@@ -1390,4 +1426,36 @@
             };
         }
     }
+
+    private static int getNumberOfIterations(int bits, int certainty)
+    {
+        /*
+         * NOTE: We enforce a minimum 'certainty' of 100 for bits >= 1024 (else 80). Where the
+         * certainty is higher than the FIPS 186-4 tables (C.2/C.3) cater to, extra iterations
+         * are added at the "worst case rate" for the excess.
+         */
+        if (bits >= 1536)
+        {
+            return  certainty <= 100 ? 3
+                :   certainty <= 128 ? 4
+                :   4 + (certainty - 128 + 1) / 2;
+        }
+        else if (bits >= 1024)
+        {
+            return  certainty <= 100 ? 4
+                :   certainty <= 112 ? 5
+                :   5 + (certainty - 112 + 1) / 2;
+        }
+        else if (bits >= 512)
+        {
+            return  certainty <= 80  ? 5
+                :   certainty <= 100 ? 7
+                :   7 + (certainty - 100 + 1) / 2;
+        }
+        else
+        {
+            return  certainty <= 80  ? 40
+                :   40 + (certainty - 80 + 1) / 2;
+        }
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/ECFieldElement.java b/bcprov/src/main/java/org/bouncycastle/math/ec/ECFieldElement.java
index 43c83e0..ef22212 100644
--- a/bcprov/src/main/java/org/bouncycastle/math/ec/ECFieldElement.java
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/ECFieldElement.java
@@ -110,21 +110,8 @@
             return null;
         }
 
-        /**
-         * @deprecated Use ECCurve.fromBigInteger to construct field elements
-         */
-        public Fp(BigInteger q, BigInteger x)
-        {
-            this(q, calculateResidue(q), x);
-        }
-
         Fp(BigInteger q, BigInteger r, BigInteger x)
         {
-            if (x == null || x.signum() < 0 || x.compareTo(q) >= 0)
-            {
-                throw new IllegalArgumentException("x value invalid in Fp field element");
-            }
-
             this.q = q;
             this.r = r;
             this.x = x;
@@ -612,59 +599,6 @@
          */
         LongArray x;
 
-        /**
-         * Constructor for PPB.
-         * @param m  The exponent <code>m</code> of
-         * <code>F<sub>2<sup>m</sup></sub></code>.
-         * @param k1 The integer <code>k1</code> where <code>x<sup>m</sup> +
-         * x<sup>k3</sup> + x<sup>k2</sup> + x<sup>k1</sup> + 1</code>
-         * represents the reduction polynomial <code>f(z)</code>.
-         * @param k2 The integer <code>k2</code> where <code>x<sup>m</sup> +
-         * x<sup>k3</sup> + x<sup>k2</sup> + x<sup>k1</sup> + 1</code>
-         * represents the reduction polynomial <code>f(z)</code>.
-         * @param k3 The integer <code>k3</code> where <code>x<sup>m</sup> +
-         * x<sup>k3</sup> + x<sup>k2</sup> + x<sup>k1</sup> + 1</code>
-         * represents the reduction polynomial <code>f(z)</code>.
-         * @param x The BigInteger representing the value of the field element.
-         * @deprecated Use ECCurve.fromBigInteger to construct field elements
-         */
-        public F2m(
-            int m, 
-            int k1, 
-            int k2, 
-            int k3,
-            BigInteger x)
-        {
-            if (x == null || x.signum() < 0 || x.bitLength() > m)
-            {
-                throw new IllegalArgumentException("x value invalid in F2m field element");
-            }
-
-            if ((k2 == 0) && (k3 == 0))
-            {
-                this.representation = TPB;
-                this.ks = new int[]{ k1 }; 
-            }
-            else
-            {
-                if (k2 >= k3)
-                {
-                    throw new IllegalArgumentException(
-                            "k2 must be smaller than k3");
-                }
-                if (k2 <= 0)
-                {
-                    throw new IllegalArgumentException(
-                            "k2 must be larger than 0");
-                }
-                this.representation = PPB;
-                this.ks = new int[]{ k1, k2, k3 }; 
-            }
-
-            this.m = m;
-            this.x = new LongArray(x);
-        }
-
         F2m(int m, int[] ks, LongArray x)
         {
             this.m = m;
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/Tnaf.java b/bcprov/src/main/java/org/bouncycastle/math/ec/Tnaf.java
index aef0cf7..e2f65cc 100644
--- a/bcprov/src/main/java/org/bouncycastle/math/ec/Tnaf.java
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/Tnaf.java
@@ -2,6 +2,8 @@
 
 import java.math.BigInteger;
 
+import org.bouncycastle.util.BigIntegers;
+
 /**
  * Class holding methods for point multiplication based on the window
  * &tau;-adic nonadjacent form (WTNAF). The algorithms are based on the
@@ -28,20 +30,19 @@
     public static final byte WIDTH = 4;
 
     /**
-     * 2<sup>4</sup>
-     */
-    public static final byte POW_2_WIDTH = 16;
-
-    /**
      * The <code>&alpha;<sub>u</sub></code>'s for <code>a=0</code> as an array
      * of <code>ZTauElement</code>s.
      */
-    public static final ZTauElement[] alpha0 = {
-        null,
-        new ZTauElement(ECConstants.ONE, ECConstants.ZERO), null,
-        new ZTauElement(MINUS_THREE, MINUS_ONE), null,
-        new ZTauElement(MINUS_ONE, MINUS_ONE), null,
-        new ZTauElement(ECConstants.ONE, MINUS_ONE), null
+    public static final ZTauElement[] alpha0 =
+    {
+        null, new ZTauElement(ECConstants.ONE, ECConstants.ZERO),
+        null, new ZTauElement(MINUS_THREE, MINUS_ONE),
+        null, new ZTauElement(MINUS_ONE, MINUS_ONE),
+        null, new ZTauElement(ECConstants.ONE, MINUS_ONE),
+        null, new ZTauElement(MINUS_ONE, ECConstants.ONE),
+        null, new ZTauElement(ECConstants.ONE, ECConstants.ONE),
+        null, new ZTauElement(ECConstants.THREE, ECConstants.ONE),
+        null, new ZTauElement(MINUS_ONE, ECConstants.ZERO),
     };
 
     /**
@@ -56,11 +57,16 @@
      * The <code>&alpha;<sub>u</sub></code>'s for <code>a=1</code> as an array
      * of <code>ZTauElement</code>s.
      */
-    public static final ZTauElement[] alpha1 = {null,
-        new ZTauElement(ECConstants.ONE, ECConstants.ZERO), null,
-        new ZTauElement(MINUS_THREE, ECConstants.ONE), null,
-        new ZTauElement(MINUS_ONE, ECConstants.ONE), null,
-        new ZTauElement(ECConstants.ONE, ECConstants.ONE), null
+    public static final ZTauElement[] alpha1 =
+    {
+        null, new ZTauElement(ECConstants.ONE, ECConstants.ZERO),
+        null, new ZTauElement(MINUS_THREE, ECConstants.ONE),
+        null, new ZTauElement(MINUS_ONE, ECConstants.ONE),
+        null, new ZTauElement(ECConstants.ONE, ECConstants.ONE),
+        null, new ZTauElement(MINUS_ONE, MINUS_ONE),
+        null, new ZTauElement(ECConstants.ONE, MINUS_ONE),
+        null, new ZTauElement(ECConstants.THREE, MINUS_ONE),
+        null, new ZTauElement(MINUS_ONE, ECConstants.ZERO),
     };
 
     /**
@@ -81,31 +87,29 @@
      */
     public static BigInteger norm(final byte mu, ZTauElement lambda)
     {
-        BigInteger norm;
-
         // s1 = u^2
         BigInteger s1 = lambda.u.multiply(lambda.u);
 
         // s2 = u * v
-        BigInteger s2 = lambda.u.multiply(lambda.v);
+//        BigInteger s2 = lambda.u.multiply(lambda.v);
 
         // s3 = 2 * v^2
-        BigInteger s3 = lambda.v.multiply(lambda.v).shiftLeft(1);
+//        BigInteger s3 = lambda.v.multiply(lambda.v).shiftLeft(1);
 
         if (mu == 1)
         {
-            norm = s1.add(s2).add(s3);
+//            return s1.add(s2).add(s3);
+            return lambda.v.shiftLeft(1).add(lambda.u).multiply(lambda.v).add(s1);
         }
         else if (mu == -1)
         {
-            norm = s1.subtract(s2).add(s3);
+//            return s1.subtract(s2).add(s3);
+            return lambda.v.shiftLeft(1).subtract(lambda.u).multiply(lambda.v).add(s1);
         }
         else
         {
             throw new IllegalArgumentException("mu must be 1 or -1");
         }
-
-        return norm;
     }
 
     /**
@@ -451,10 +455,7 @@
             throw new IllegalArgumentException("mu must be 1 or -1");
         }
 
-        BigInteger u0;
-        BigInteger u1;
-        BigInteger u2;
-
+        BigInteger u0, u1, u2;
         if (doV)
         {
             u0 = ECConstants.TWO;
@@ -469,26 +470,18 @@
         for (int i = 1; i < k; i++)
         {
             // u2 = mu*u1 - 2*u0;
-            BigInteger s = null;
-            if (mu == 1)
+            BigInteger s = u1;
+            if (mu < 0)
             {
-                s = u1;
+                s = s.negate();
             }
-            else
-            {
-                // mu == -1
-                s = u1.negate();
-            }
-            
+
             u2 = s.subtract(u0.shiftLeft(1));
             u0 = u1;
             u1 = u2;
-//            System.out.println(i + ": " + u2);
-//            System.out.println();
         }
 
-        BigInteger[] retVal = {u0, u1};
-        return retVal;
+        return new BigInteger[]{ u0, u1 };
     }
 
     /**
@@ -519,11 +512,7 @@
             BigInteger[] us = getLucas(mu, w, false);
             BigInteger twoToW = ECConstants.ZERO.setBit(w);
             BigInteger u1invert = us[1].modInverse(twoToW);
-            BigInteger tw;
-            tw = ECConstants.TWO.multiply(us[0]).multiply(u1invert).mod(twoToW);
-//            System.out.println("mu = " + mu);
-//            System.out.println("tw = " + tw);
-            return tw;
+            return us[0].shiftLeft(1).multiply(u1invert).mod(twoToW);
         }
     }
 
@@ -542,22 +531,7 @@
             throw new IllegalArgumentException("si is defined for Koblitz curves only");
         }
 
-        int m = curve.getFieldSize();
-        int a = curve.getA().toBigInteger().intValue();
-        byte mu = getMu(a);
-        int shifts = getShiftsForCofactor(curve.getCofactor());
-        int index = m + 3 - a;
-        BigInteger[] ui = getLucas(mu, index, false);
-        if (mu == 1)
-        {
-            ui[0] = ui[0].negate();
-            ui[1] = ui[1].negate();
-        }
-
-        BigInteger dividend0 = ECConstants.ONE.add(ui[1]).shiftRight(shifts);
-        BigInteger dividend1 = ECConstants.ONE.add(ui[0]).shiftRight(shifts).negate();
-
-        return new BigInteger[] { dividend0, dividend1 };
+        return getSi(curve.getFieldSize(), curve.getA().toBigInteger().intValue(), curve.getCofactor());
     }
 
     public static BigInteger[] getSi(int fieldSize, int curveA, BigInteger cofactor)
@@ -608,9 +582,11 @@
      * modular reduction.
      * @return <code>&rho; := k partmod (&tau;<sup>m</sup> - 1)/(&tau; - 1)</code>
      */
-    public static ZTauElement partModReduction(BigInteger k, int m, byte a,
-            BigInteger[] s, byte mu, byte c)
+    public static ZTauElement partModReduction(ECCurve.AbstractF2m curve, BigInteger k, byte a, byte mu, byte c)
     {
+        int m = curve.getFieldSize();
+        BigInteger[] s = curve.getSi();
+
         // d0 = s[0] + mu*s[1]; mu is either 1 or -1
         BigInteger d0;
         if (mu == 1)
@@ -622,20 +598,29 @@
             d0 = s[0].subtract(s[1]);
         }
 
-        BigInteger[] v = getLucas(mu, m, true);
-        BigInteger vm = v[1];
+        BigInteger vm;
+        if (curve.isKoblitz())
+        {
+            /*
+             * Jerome A. Solinas, "Improved Algorithms for Arithmetic on Anomalous Binary Curves", (21).
+             */
+            vm = ECConstants.ONE.shiftLeft(m).add(ECConstants.ONE).subtract(
+                curve.getOrder().multiply(curve.getCofactor()));
+        }
+        else
+        {
+            BigInteger[] v = getLucas(mu, m, true);
+            vm = v[1];
+        }
 
-        SimpleBigDecimal lambda0 = approximateDivisionByN(
-                k, s[0], vm, a, m, c);
-        
-        SimpleBigDecimal lambda1 = approximateDivisionByN(
-                k, s[1], vm, a, m, c);
+        SimpleBigDecimal lambda0 = approximateDivisionByN(k, s[0], vm, a, m, c);
+        SimpleBigDecimal lambda1 = approximateDivisionByN(k, s[1], vm, a, m, c);
 
         ZTauElement q = round(lambda0, lambda1, mu);
 
         // r0 = n - d0*q0 - 2*s1*q1
         BigInteger r0 = k.subtract(d0.multiply(q.u)).subtract(
-                BigInteger.valueOf(2).multiply(s[1]).multiply(q.v));
+            s[1].multiply(q.v).shiftLeft(1));
 
         // r1 = s1*q0 - s0*q1
         BigInteger r1 = s[1].multiply(q.u).subtract(s[0].multiply(q.v));
@@ -654,11 +639,10 @@
     public static ECPoint.AbstractF2m multiplyRTnaf(ECPoint.AbstractF2m p, BigInteger k)
     {
         ECCurve.AbstractF2m curve = (ECCurve.AbstractF2m) p.getCurve();
-        int m = curve.getFieldSize();
         int a = curve.getA().toBigInteger().intValue();
         byte mu = getMu(a);
-        BigInteger[] s = curve.getSi();
-        ZTauElement rho = partModReduction(k, m, (byte)a, s, mu, (byte)10);
+
+        ZTauElement rho = partModReduction(curve, k, (byte)a, mu, (byte)10);
 
         return multiplyTnaf(p, rho);
     }
@@ -675,12 +659,11 @@
     public static ECPoint.AbstractF2m multiplyTnaf(ECPoint.AbstractF2m p, ZTauElement lambda)
     {
         ECCurve.AbstractF2m curve = (ECCurve.AbstractF2m)p.getCurve();
+        ECPoint.AbstractF2m pNeg = (ECPoint.AbstractF2m)p.negate();
         byte mu = getMu(curve.getA());
         byte[] u = tauAdicNaf(mu, lambda);
 
-        ECPoint.AbstractF2m q = multiplyFromTnaf(p, u);
-
-        return q;
+        return multiplyFromTnaf(p, pNeg, u);
     }
 
     /**
@@ -692,11 +675,10 @@
     * @param u The the TNAF of <code>&lambda;</code>..
     * @return <code>&lambda; * p</code>
     */
-    public static ECPoint.AbstractF2m multiplyFromTnaf(ECPoint.AbstractF2m p, byte[] u)
+    public static ECPoint.AbstractF2m multiplyFromTnaf(ECPoint.AbstractF2m p, ECPoint.AbstractF2m pNeg, byte[] u)
     {
         ECCurve curve = p.getCurve();
         ECPoint.AbstractF2m q = (ECPoint.AbstractF2m)curve.getInfinity();
-        ECPoint.AbstractF2m pNeg = (ECPoint.AbstractF2m)p.negate();
         int tauCount = 0;
         for (int i = u.length - 1; i >= 0; i--)
         {
@@ -732,10 +714,9 @@
      * @return The <code>[&tau;]</code>-adic window NAF of
      * <code>&lambda;</code>.
      */
-    public static byte[] tauAdicWNaf(byte mu, ZTauElement lambda,
-            byte width, BigInteger pow2w, BigInteger tw, ZTauElement[] alpha)
+    public static byte[] tauAdicWNaf(byte mu, ZTauElement lambda, int width, int tw, ZTauElement[] alpha)
     {
-        if (!((mu == 1) || (mu == -1)))
+        if (!(mu == 1 || mu == -1))
         {
             throw new IllegalArgumentException("mu must be 1 or -1");
         }
@@ -751,75 +732,72 @@
         // The array holding the TNAF
         byte[] u = new byte[maxLength];
 
-        // 2^(width - 1)
-        BigInteger pow2wMin1 = pow2w.shiftRight(1);
+        int pow2Width = 1 << width;
+        int pow2Mask = pow2Width - 1;
+        int s = 32 - width;
 
         // Split lambda into two BigIntegers to simplify calculations
-        BigInteger r0 = lambda.u;
-        BigInteger r1 = lambda.v;
-        int i = 0;
+        BigInteger R0 = lambda.u;
+        BigInteger R1 = lambda.v;
+        int uPos = 0;
 
         // while lambda <> (0, 0)
-        while (!((r0.equals(ECConstants.ZERO))&&(r1.equals(ECConstants.ZERO))))
+        while (R0.bitLength() > 62 || R1.bitLength() > 62)
         {
-            // if r0 is odd
-            if (r0.testBit(0))
+            if (R0.testBit(0))
             {
-                // uUnMod = r0 + r1*tw mod 2^width
-                BigInteger uUnMod
-                    = r0.add(r1.multiply(tw)).mod(pow2w);
-                
-                byte uLocal;
-                // if uUnMod >= 2^(width - 1)
-                if (uUnMod.compareTo(pow2wMin1) >= 0)
-                {
-                    uLocal = (byte) uUnMod.subtract(pow2w).intValue();
-                }
-                else
-                {
-                    uLocal = (byte) uUnMod.intValue();
-                }
-                // uLocal is now in [-2^(width-1), 2^(width-1)-1]
+                int uVal = R0.intValue() + (R1.intValue() * tw);
+                int alphaPos = uVal & pow2Mask;
 
-                u[i] = uLocal;
-                boolean s = true;
-                if (uLocal < 0)
-                {
-                    s = false;
-                    uLocal = (byte)-uLocal;
-                }
-                // uLocal is now >= 0
-
-                if (s)
-                {
-                    r0 = r0.subtract(alpha[uLocal].u);
-                    r1 = r1.subtract(alpha[uLocal].v);
-                }
-                else
-                {
-                    r0 = r0.add(alpha[uLocal].u);
-                    r1 = r1.add(alpha[uLocal].v);
-                }
-            }
-            else
-            {
-                u[i] = 0;
+                u[uPos] = (byte)((uVal << s) >> s);
+                R0 = R0.subtract(alpha[alphaPos].u);
+                R1 = R1.subtract(alpha[alphaPos].v);
             }
 
-            BigInteger t = r0;
+            ++uPos;
 
+            BigInteger t = R0.shiftRight(1);
             if (mu == 1)
             {
-                r0 = r1.add(r0.shiftRight(1));
+                R0 = R1.add(t);
             }
-            else
+            else // mu == -1
             {
-                // mu == -1
-                r0 = r1.subtract(r0.shiftRight(1));
+                R0 = R1.subtract(t);
             }
-            r1 = t.shiftRight(1).negate();
-            i++;
+            R1 = t.negate();
         }
+
+        long r0_64 = BigIntegers.longValueExact(R0);
+        long r1_64 = BigIntegers.longValueExact(R1);
+
+        // while lambda <> (0, 0)
+        while ((r0_64 | r1_64) != 0L)
+        {
+            if ((r0_64 & 1L) != 0L)
+            {
+                int uVal = (int)r0_64 + ((int)r1_64 * tw);
+                int alphaPos = uVal & pow2Mask;
+
+                u[uPos] = (byte)((uVal << s) >> s);
+                r0_64 -= alpha[alphaPos].u.intValue();
+                r1_64 -= alpha[alphaPos].v.intValue();
+            }
+
+            ++uPos;
+
+            long t_64 = r0_64 >> 1;
+            if (mu == 1)
+            {
+                r0_64 = r1_64 + t_64;
+            }
+            else // mu == -1
+            {
+                r0_64 = r1_64 - t_64;
+            }
+            r1_64 = -t_64;
+        }
+        
         return u;
     }
 
@@ -831,6 +809,7 @@
      */
     public static ECPoint.AbstractF2m[] getPreComp(ECPoint.AbstractF2m p, byte a)
     {
+        ECPoint.AbstractF2m pNeg = (ECPoint.AbstractF2m)p.negate();
         byte[][] alphaTnaf = (a == 0) ? Tnaf.alpha0Tnaf : Tnaf.alpha1Tnaf;
 
         ECPoint.AbstractF2m[] pu = new ECPoint.AbstractF2m[(alphaTnaf.length + 1) >>> 1];
@@ -839,7 +818,7 @@
         int precompLen = alphaTnaf.length;
         for (int i = 3; i < precompLen; i += 2)
         {
-            pu[i >>> 1] = Tnaf.multiplyFromTnaf(p, alphaTnaf[i]);
+            pu[i >>> 1] = Tnaf.multiplyFromTnaf(p, pNeg, alphaTnaf[i]);
         }
 
         p.getCurve().normalizeAll(pu);
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/WTauNafMultiplier.java b/bcprov/src/main/java/org/bouncycastle/math/ec/WTauNafMultiplier.java
index 0438e1d..f30a4bd 100644
--- a/bcprov/src/main/java/org/bouncycastle/math/ec/WTauNafMultiplier.java
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/WTauNafMultiplier.java
@@ -29,12 +29,10 @@
 
         ECPoint.AbstractF2m p = (ECPoint.AbstractF2m)point;
         ECCurve.AbstractF2m curve = (ECCurve.AbstractF2m)p.getCurve();
-        int m = curve.getFieldSize();
         byte a = curve.getA().toBigInteger().byteValue();
         byte mu = Tnaf.getMu(a);
-        BigInteger[] s = curve.getSi();
 
-        ZTauElement rho = Tnaf.partModReduction(k, m, a, s, mu, (byte)10);
+        ZTauElement rho = Tnaf.partModReduction(curve, k, a, mu, (byte)10);
 
         return multiplyWTnaf(p, rho, a, mu);
     }
@@ -55,8 +53,7 @@
 
         BigInteger tw = Tnaf.getTw(mu, Tnaf.WIDTH);
 
-        byte[]u = Tnaf.tauAdicWNaf(mu, lambda, Tnaf.WIDTH,
-            BigInteger.valueOf(Tnaf.POW_2_WIDTH), tw, alpha);
+        byte[] u = Tnaf.tauAdicWNaf(mu, lambda, Tnaf.WIDTH, tw.intValue(), alpha);
 
         return multiplyFromWTnaf(p, u);
     }
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP256K1Field.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP256K1Field.java
index 8afbb31..fe41c13 100644
--- a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP256K1Field.java
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP256K1Field.java
@@ -98,6 +98,12 @@
         reduce(tt, z);
     }
 
+    public static void multiply(int[] x, int[] y, int[] z, int[] tt)
+    {
+        Nat256.mul(x, y, tt);
+        reduce(tt, z);
+    }
+
     public static void multiplyAddToExt(int[] x, int[] y, int[] zz)
     {
         int c = Nat256.mulAddTo(x, y, zz);
@@ -171,6 +177,12 @@
         reduce(tt, z);
     }
 
+    public static void square(int[] x, int[] z, int[] tt)
+    {
+        Nat256.square(x, tt);
+        reduce(tt, z);
+    }
+
     public static void squareN(int[] x, int n, int[] z)
     {
 //        assert n > 0;
@@ -186,6 +198,20 @@
         }
     }
 
+    public static void squareN(int[] x, int n, int[] z, int[] tt)
+    {
+//        assert n > 0;
+
+        Nat256.square(x, tt);
+        reduce(tt, z);
+
+        while (--n > 0)
+        {
+            Nat256.square(z, tt);
+            reduce(tt, z);
+        }
+    }
+
     public static void subtract(int[] x, int[] y, int[] z)
     {
         int c = Nat256.sub(x, y, z);
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP256K1FieldElement.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP256K1FieldElement.java
index c04ce87..6809956 100644
--- a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP256K1FieldElement.java
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP256K1FieldElement.java
@@ -146,49 +146,51 @@
             return this;
         }
 
+        int[] tt0 = Nat256.createExt();
+
         int[] x2 = Nat256.create();
-        SecP256K1Field.square(x1, x2);
-        SecP256K1Field.multiply(x2, x1, x2);
+        SecP256K1Field.square(x1, x2, tt0);
+        SecP256K1Field.multiply(x2, x1, x2, tt0);
         int[] x3 = Nat256.create();
-        SecP256K1Field.square(x2, x3);
-        SecP256K1Field.multiply(x3, x1, x3);
+        SecP256K1Field.square(x2, x3, tt0);
+        SecP256K1Field.multiply(x3, x1, x3, tt0);
         int[] x6 = Nat256.create();
-        SecP256K1Field.squareN(x3, 3, x6);
-        SecP256K1Field.multiply(x6, x3, x6);
+        SecP256K1Field.squareN(x3, 3, x6, tt0);
+        SecP256K1Field.multiply(x6, x3, x6, tt0);
         int[] x9 = x6;
-        SecP256K1Field.squareN(x6, 3, x9);
-        SecP256K1Field.multiply(x9, x3, x9);
+        SecP256K1Field.squareN(x6, 3, x9, tt0);
+        SecP256K1Field.multiply(x9, x3, x9, tt0);
         int[] x11 = x9;
-        SecP256K1Field.squareN(x9, 2, x11);
-        SecP256K1Field.multiply(x11, x2, x11);
+        SecP256K1Field.squareN(x9, 2, x11, tt0);
+        SecP256K1Field.multiply(x11, x2, x11, tt0);
         int[] x22 = Nat256.create();
-        SecP256K1Field.squareN(x11, 11, x22);
-        SecP256K1Field.multiply(x22, x11, x22);
+        SecP256K1Field.squareN(x11, 11, x22, tt0);
+        SecP256K1Field.multiply(x22, x11, x22, tt0);
         int[] x44 = x11;
-        SecP256K1Field.squareN(x22, 22, x44);
-        SecP256K1Field.multiply(x44, x22, x44);
+        SecP256K1Field.squareN(x22, 22, x44, tt0);
+        SecP256K1Field.multiply(x44, x22, x44, tt0);
         int[] x88 = Nat256.create();
-        SecP256K1Field.squareN(x44, 44, x88);
-        SecP256K1Field.multiply(x88, x44, x88);
+        SecP256K1Field.squareN(x44, 44, x88, tt0);
+        SecP256K1Field.multiply(x88, x44, x88, tt0);
         int[] x176 = Nat256.create();
-        SecP256K1Field.squareN(x88, 88, x176);
-        SecP256K1Field.multiply(x176, x88, x176);
+        SecP256K1Field.squareN(x88, 88, x176, tt0);
+        SecP256K1Field.multiply(x176, x88, x176, tt0);
         int[] x220 = x88;
-        SecP256K1Field.squareN(x176, 44, x220);
-        SecP256K1Field.multiply(x220, x44, x220);
+        SecP256K1Field.squareN(x176, 44, x220, tt0);
+        SecP256K1Field.multiply(x220, x44, x220, tt0);
         int[] x223 = x44;
-        SecP256K1Field.squareN(x220, 3, x223);
-        SecP256K1Field.multiply(x223, x3, x223);
+        SecP256K1Field.squareN(x220, 3, x223, tt0);
+        SecP256K1Field.multiply(x223, x3, x223, tt0);
 
         int[] t1 = x223;
-        SecP256K1Field.squareN(t1, 23, t1);
-        SecP256K1Field.multiply(t1, x22, t1);
-        SecP256K1Field.squareN(t1, 6, t1);
-        SecP256K1Field.multiply(t1, x2, t1);
-        SecP256K1Field.squareN(t1, 2, t1);
+        SecP256K1Field.squareN(t1, 23, t1, tt0);
+        SecP256K1Field.multiply(t1, x22, t1, tt0);
+        SecP256K1Field.squareN(t1, 6, t1, tt0);
+        SecP256K1Field.multiply(t1, x2, t1, tt0);
+        SecP256K1Field.squareN(t1, 2, t1, tt0);
 
         int[] t2 = x2;
-        SecP256K1Field.square(t1, t2);
+        SecP256K1Field.square(t1, t2, tt0);
 
         return Nat256.eq(x1, t2) ? new SecP256K1FieldElement(t1) : null;
     }
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP256K1Point.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP256K1Point.java
index 0ec55e0..e8cb06e 100644
--- a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP256K1Point.java
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP256K1Point.java
@@ -48,6 +48,7 @@
         SecP256K1FieldElement Z2 = (SecP256K1FieldElement)b.getZCoord(0);
 
         int c;
+        int[] tt0 = Nat256.createExt();
         int[] tt1 = Nat256.createExt();
         int[] t2 = Nat256.create();
         int[] t3 = Nat256.create();
@@ -63,13 +64,13 @@
         else
         {
             S2 = t3;
-            SecP256K1Field.square(Z1.x, S2);
+            SecP256K1Field.square(Z1.x, S2, tt0);
 
             U2 = t2;
-            SecP256K1Field.multiply(S2, X2.x, U2);
+            SecP256K1Field.multiply(S2, X2.x, U2, tt0);
 
-            SecP256K1Field.multiply(S2, Z1.x, S2);
-            SecP256K1Field.multiply(S2, Y2.x, S2);
+            SecP256K1Field.multiply(S2, Z1.x, S2, tt0);
+            SecP256K1Field.multiply(S2, Y2.x, S2, tt0);
         }
 
         boolean Z2IsOne = Z2.isOne();
@@ -82,13 +83,13 @@
         else
         {
             S1 = t4;
-            SecP256K1Field.square(Z2.x, S1);
+            SecP256K1Field.square(Z2.x, S1, tt0);
 
             U1 = tt1;
-            SecP256K1Field.multiply(S1, X1.x, U1);
+            SecP256K1Field.multiply(S1, X1.x, U1, tt0);
 
-            SecP256K1Field.multiply(S1, Z2.x, S1);
-            SecP256K1Field.multiply(S1, Y1.x, S1);
+            SecP256K1Field.multiply(S1, Z2.x, S1, tt0);
+            SecP256K1Field.multiply(S1, Y1.x, S1, tt0);
         }
 
         int[] H = Nat256.create();
@@ -111,13 +112,13 @@
         }
 
         int[] HSquared = t3;
-        SecP256K1Field.square(H, HSquared);
+        SecP256K1Field.square(H, HSquared, tt0);
 
         int[] G = Nat256.create();
-        SecP256K1Field.multiply(HSquared, H, G);
+        SecP256K1Field.multiply(HSquared, H, G, tt0);
 
         int[] V = t3;
-        SecP256K1Field.multiply(HSquared, U1, V);
+        SecP256K1Field.multiply(HSquared, U1, V, tt0);
 
         SecP256K1Field.negate(G, G);
         Nat256.mul(S1, G, tt1);
@@ -126,7 +127,7 @@
         SecP256K1Field.reduce32(c, G);
 
         SecP256K1FieldElement X3 = new SecP256K1FieldElement(t4);
-        SecP256K1Field.square(R, X3.x);
+        SecP256K1Field.square(R, X3.x, tt0);
         SecP256K1Field.subtract(X3.x, G, X3.x);
 
         SecP256K1FieldElement Y3 = new SecP256K1FieldElement(G);
@@ -137,11 +138,11 @@
         SecP256K1FieldElement Z3 = new SecP256K1FieldElement(H);
         if (!Z1IsOne)
         {
-            SecP256K1Field.multiply(Z3.x, Z1.x, Z3.x);
+            SecP256K1Field.multiply(Z3.x, Z1.x, Z3.x, tt0);
         }
         if (!Z2IsOne)
         {
-            SecP256K1Field.multiply(Z3.x, Z2.x, Z3.x);
+            SecP256K1Field.multiply(Z3.x, Z2.x, Z3.x, tt0);
         }
 
         ECFieldElement[] zs = new ECFieldElement[] { Z3 };
@@ -168,20 +169,21 @@
         SecP256K1FieldElement X1 = (SecP256K1FieldElement)this.x, Z1 = (SecP256K1FieldElement)this.zs[0];
 
         int c;
+        int[] tt0 = Nat256.createExt();
 
         int[] Y1Squared = Nat256.create();
-        SecP256K1Field.square(Y1.x, Y1Squared);
+        SecP256K1Field.square(Y1.x, Y1Squared, tt0);
 
         int[] T = Nat256.create();
-        SecP256K1Field.square(Y1Squared, T);
+        SecP256K1Field.square(Y1Squared, T, tt0);
 
         int[] M = Nat256.create();
-        SecP256K1Field.square(X1.x, M);
+        SecP256K1Field.square(X1.x, M, tt0);
         c = Nat256.addBothTo(M, M, M);
         SecP256K1Field.reduce32(c, M);
 
         int[] S = Y1Squared;
-        SecP256K1Field.multiply(Y1Squared, X1.x, S);
+        SecP256K1Field.multiply(Y1Squared, X1.x, S, tt0);
         c = Nat.shiftUpBits(8, S, 2, 0);
         SecP256K1Field.reduce32(c, S);
 
@@ -190,20 +192,20 @@
         SecP256K1Field.reduce32(c, t1);
 
         SecP256K1FieldElement X3 = new SecP256K1FieldElement(T);
-        SecP256K1Field.square(M, X3.x);
+        SecP256K1Field.square(M, X3.x, tt0);
         SecP256K1Field.subtract(X3.x, S, X3.x);
         SecP256K1Field.subtract(X3.x, S, X3.x);
 
         SecP256K1FieldElement Y3 = new SecP256K1FieldElement(S);
         SecP256K1Field.subtract(S, X3.x, Y3.x);
-        SecP256K1Field.multiply(Y3.x, M, Y3.x);
+        SecP256K1Field.multiply(Y3.x, M, Y3.x, tt0);
         SecP256K1Field.subtract(Y3.x, t1, Y3.x);
 
         SecP256K1FieldElement Z3 = new SecP256K1FieldElement(M);
         SecP256K1Field.twice(Y1.x, Z3.x);
         if (!Z1.isOne())
         {
-            SecP256K1Field.multiply(Z3.x, Z1.x, Z3.x);
+            SecP256K1Field.multiply(Z3.x, Z1.x, Z3.x, tt0);
         }
 
         return new SecP256K1Point(curve, X3, Y3, new ECFieldElement[] { Z3 });
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP256R1Field.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP256R1Field.java
index 6b780fd..298bdd4 100644
--- a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP256R1Field.java
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP256R1Field.java
@@ -94,6 +94,12 @@
         reduce(tt, z);
     }
 
+    public static void multiply(int[] x, int[] y, int[] z, int[] tt)
+    {
+        Nat256.mul(x, y, tt);
+        reduce(tt, z);
+    }
+
     public static void multiplyAddToExt(int[] x, int[] y, int[] zz)
     {
         int c = Nat256.mulAddTo(x, y, zz);
@@ -240,6 +246,12 @@
         reduce(tt, z);
     }
 
+    public static void square(int[] x, int[] z, int[] tt)
+    {
+        Nat256.square(x, tt);
+        reduce(tt, z);
+    }
+
     public static void squareN(int[] x, int n, int[] z)
     {
 //        assert n > 0;
@@ -255,6 +267,20 @@
         }
     }
 
+    public static void squareN(int[] x, int n, int[] z, int[] tt)
+    {
+//        assert n > 0;
+
+        Nat256.square(x, tt);
+        reduce(tt, z);
+
+        while (--n > 0)
+        {
+            Nat256.square(z, tt);
+            reduce(tt, z);
+        }
+    }
+
     public static void subtract(int[] x, int[] y, int[] z)
     {
         int c = Nat256.sub(x, y, z);
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP256R1FieldElement.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP256R1FieldElement.java
index f2ad785..50cff26 100644
--- a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP256R1FieldElement.java
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP256R1FieldElement.java
@@ -137,32 +137,33 @@
             return this;
         }
 
+        int[] tt0 = Nat256.createExt();
         int[] t1 = Nat256.create();
         int[] t2 = Nat256.create();
 
-        SecP256R1Field.square(x1, t1);
-        SecP256R1Field.multiply(t1, x1, t1);
+        SecP256R1Field.square(x1, t1, tt0);
+        SecP256R1Field.multiply(t1, x1, t1, tt0);
 
-        SecP256R1Field.squareN(t1, 2, t2);
-        SecP256R1Field.multiply(t2, t1, t2);
+        SecP256R1Field.squareN(t1, 2, t2, tt0);
+        SecP256R1Field.multiply(t2, t1, t2, tt0);
 
-        SecP256R1Field.squareN(t2, 4, t1);
-        SecP256R1Field.multiply(t1, t2, t1);
+        SecP256R1Field.squareN(t2, 4, t1, tt0);
+        SecP256R1Field.multiply(t1, t2, t1, tt0);
 
-        SecP256R1Field.squareN(t1, 8, t2);
-        SecP256R1Field.multiply(t2, t1, t2);
+        SecP256R1Field.squareN(t1, 8, t2, tt0);
+        SecP256R1Field.multiply(t2, t1, t2, tt0);
 
-        SecP256R1Field.squareN(t2, 16, t1);
-        SecP256R1Field.multiply(t1, t2, t1);
+        SecP256R1Field.squareN(t2, 16, t1, tt0);
+        SecP256R1Field.multiply(t1, t2, t1, tt0);
 
-        SecP256R1Field.squareN(t1, 32, t1);
-        SecP256R1Field.multiply(t1, x1, t1);
+        SecP256R1Field.squareN(t1, 32, t1, tt0);
+        SecP256R1Field.multiply(t1, x1, t1, tt0);
 
-        SecP256R1Field.squareN(t1, 96, t1);
-        SecP256R1Field.multiply(t1, x1, t1);
+        SecP256R1Field.squareN(t1, 96, t1, tt0);
+        SecP256R1Field.multiply(t1, x1, t1, tt0);
 
-        SecP256R1Field.squareN(t1, 94, t1);
-        SecP256R1Field.square(t1, t2);
+        SecP256R1Field.squareN(t1, 94, t1, tt0);
+        SecP256R1Field.square(t1, t2, tt0);
 
         return Nat256.eq(x1, t2) ? new SecP256R1FieldElement(t1) : null;
     }
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP256R1Point.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP256R1Point.java
index 7f39a4e..1c38eab 100644
--- a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP256R1Point.java
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP256R1Point.java
@@ -47,6 +47,7 @@
         SecP256R1FieldElement Z2 = (SecP256R1FieldElement)b.getZCoord(0);
 
         int c;
+        int[] tt0 = Nat256.createExt();
         int[] tt1 = Nat256.createExt();
         int[] t2 = Nat256.create();
         int[] t3 = Nat256.create();
@@ -62,13 +63,13 @@
         else
         {
             S2 = t3;
-            SecP256R1Field.square(Z1.x, S2);
+            SecP256R1Field.square(Z1.x, S2, tt0);
 
             U2 = t2;
-            SecP256R1Field.multiply(S2, X2.x, U2);
+            SecP256R1Field.multiply(S2, X2.x, U2, tt0);
 
-            SecP256R1Field.multiply(S2, Z1.x, S2);
-            SecP256R1Field.multiply(S2, Y2.x, S2);
+            SecP256R1Field.multiply(S2, Z1.x, S2, tt0);
+            SecP256R1Field.multiply(S2, Y2.x, S2, tt0);
         }
 
         boolean Z2IsOne = Z2.isOne();
@@ -81,13 +82,13 @@
         else
         {
             S1 = t4;
-            SecP256R1Field.square(Z2.x, S1);
+            SecP256R1Field.square(Z2.x, S1, tt0);
 
             U1 = tt1;
-            SecP256R1Field.multiply(S1, X1.x, U1);
+            SecP256R1Field.multiply(S1, X1.x, U1, tt0);
 
-            SecP256R1Field.multiply(S1, Z2.x, S1);
-            SecP256R1Field.multiply(S1, Y1.x, S1);
+            SecP256R1Field.multiply(S1, Z2.x, S1, tt0);
+            SecP256R1Field.multiply(S1, Y1.x, S1, tt0);
         }
 
         int[] H = Nat256.create();
@@ -110,13 +111,13 @@
         }
 
         int[] HSquared = t3;
-        SecP256R1Field.square(H, HSquared);
+        SecP256R1Field.square(H, HSquared, tt0);
 
         int[] G = Nat256.create();
-        SecP256R1Field.multiply(HSquared, H, G);
+        SecP256R1Field.multiply(HSquared, H, G, tt0);
 
         int[] V = t3;
-        SecP256R1Field.multiply(HSquared, U1, V);
+        SecP256R1Field.multiply(HSquared, U1, V, tt0);
 
         SecP256R1Field.negate(G, G);
         Nat256.mul(S1, G, tt1);
@@ -125,7 +126,7 @@
         SecP256R1Field.reduce32(c, G);
 
         SecP256R1FieldElement X3 = new SecP256R1FieldElement(t4);
-        SecP256R1Field.square(R, X3.x);
+        SecP256R1Field.square(R, X3.x, tt0);
         SecP256R1Field.subtract(X3.x, G, X3.x);
 
         SecP256R1FieldElement Y3 = new SecP256R1FieldElement(G);
@@ -136,11 +137,11 @@
         SecP256R1FieldElement Z3 = new SecP256R1FieldElement(H);
         if (!Z1IsOne)
         {
-            SecP256R1Field.multiply(Z3.x, Z1.x, Z3.x);
+            SecP256R1Field.multiply(Z3.x, Z1.x, Z3.x, tt0);
         }
         if (!Z2IsOne)
         {
-            SecP256R1Field.multiply(Z3.x, Z2.x, Z3.x);
+            SecP256R1Field.multiply(Z3.x, Z2.x, Z3.x, tt0);
         }
 
         ECFieldElement[] zs = new ECFieldElement[]{ Z3 };
@@ -166,14 +167,15 @@
         SecP256R1FieldElement X1 = (SecP256R1FieldElement)this.x, Z1 = (SecP256R1FieldElement)this.zs[0];
 
         int c;
+        int[] tt0 = Nat256.createExt();
         int[] t1 = Nat256.create();
         int[] t2 = Nat256.create();
 
         int[] Y1Squared = Nat256.create();
-        SecP256R1Field.square(Y1.x, Y1Squared);
+        SecP256R1Field.square(Y1.x, Y1Squared, tt0);
 
         int[] T = Nat256.create();
-        SecP256R1Field.square(Y1Squared, T);
+        SecP256R1Field.square(Y1Squared, T, tt0);
 
         boolean Z1IsOne = Z1.isOne();
 
@@ -181,19 +183,19 @@
         if (!Z1IsOne)
         {
             Z1Squared = t2;
-            SecP256R1Field.square(Z1.x, Z1Squared);
+            SecP256R1Field.square(Z1.x, Z1Squared, tt0);
         }
 
         SecP256R1Field.subtract(X1.x, Z1Squared, t1);
 
         int[] M = t2;
         SecP256R1Field.add(X1.x, Z1Squared, M);
-        SecP256R1Field.multiply(M, t1, M);
+        SecP256R1Field.multiply(M, t1, M, tt0);
         c = Nat256.addBothTo(M, M, M);
         SecP256R1Field.reduce32(c, M);
 
         int[] S = Y1Squared;
-        SecP256R1Field.multiply(Y1Squared, X1.x, S);
+        SecP256R1Field.multiply(Y1Squared, X1.x, S, tt0);
         c = Nat.shiftUpBits(8, S, 2, 0);
         SecP256R1Field.reduce32(c, S);
 
@@ -201,20 +203,20 @@
         SecP256R1Field.reduce32(c, t1);
 
         SecP256R1FieldElement X3 = new SecP256R1FieldElement(T);
-        SecP256R1Field.square(M, X3.x);
+        SecP256R1Field.square(M, X3.x, tt0);
         SecP256R1Field.subtract(X3.x, S, X3.x);
         SecP256R1Field.subtract(X3.x, S, X3.x);
 
         SecP256R1FieldElement Y3 = new SecP256R1FieldElement(S);
         SecP256R1Field.subtract(S, X3.x, Y3.x);
-        SecP256R1Field.multiply(Y3.x, M, Y3.x);
+        SecP256R1Field.multiply(Y3.x, M, Y3.x, tt0);
         SecP256R1Field.subtract(Y3.x, t1, Y3.x);
 
         SecP256R1FieldElement Z3 = new SecP256R1FieldElement(M);
         SecP256R1Field.twice(Y1.x, Z3.x);
         if (!Z1IsOne)
         {
-            SecP256R1Field.multiply(Z3.x, Z1.x, Z3.x);
+            SecP256R1Field.multiply(Z3.x, Z1.x, Z3.x, tt0);
         }
 
         return new SecP256R1Point(curve, X3, Y3, new ECFieldElement[]{ Z3 });
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP384R1Field.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP384R1Field.java
index 83852e8..4afab65 100644
--- a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP384R1Field.java
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP384R1Field.java
@@ -100,6 +100,12 @@
         reduce(tt, z);
     }
 
+    public static void multiply(int[] x, int[] y, int[] z, int[] tt)
+    {
+        Nat384.mul(x, y, tt);
+        reduce(tt, z);
+    }
+
     public static void negate(int[] x, int[] z)
     {
         if (0 != isZero(x))
@@ -236,6 +242,12 @@
         reduce(tt, z);
     }
 
+    public static void square(int[] x, int[] z, int[] tt)
+    {
+        Nat384.square(x, tt);
+        reduce(tt, z);
+    }
+
     public static void squareN(int[] x, int n, int[] z)
     {
 //        assert n > 0;
@@ -251,6 +263,20 @@
         }
     }
 
+    public static void squareN(int[] x, int n, int[] z, int[] tt)
+    {
+//        assert n > 0;
+
+        Nat384.square(x, tt);
+        reduce(tt, z);
+
+        while (--n > 0)
+        {
+            Nat384.square(z, tt);
+            reduce(tt, z);
+        }
+    }
+
     public static void subtract(int[] x, int[] y, int[] z)
     {
         int c = Nat.sub(12, x, y, z);
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP384R1FieldElement.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP384R1FieldElement.java
index 77623c1..7cfa2e3 100644
--- a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP384R1FieldElement.java
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP384R1FieldElement.java
@@ -137,54 +137,55 @@
             return this;
         }
 
+        int[] tt0 = Nat.create(24);
         int[] t1 = Nat.create(12);
         int[] t2 = Nat.create(12);
         int[] t3 = Nat.create(12);
         int[] t4 = Nat.create(12);
 
-        SecP384R1Field.square(x1, t1);
-        SecP384R1Field.multiply(t1, x1, t1);
+        SecP384R1Field.square(x1, t1, tt0);
+        SecP384R1Field.multiply(t1, x1, t1, tt0);
 
-        SecP384R1Field.squareN(t1, 2, t2);
-        SecP384R1Field.multiply(t2, t1, t2);
+        SecP384R1Field.squareN(t1, 2, t2, tt0);
+        SecP384R1Field.multiply(t2, t1, t2, tt0);
 
-        SecP384R1Field.square(t2, t2);
-        SecP384R1Field.multiply(t2, x1, t2);
+        SecP384R1Field.square(t2, t2, tt0);
+        SecP384R1Field.multiply(t2, x1, t2, tt0);
 
-        SecP384R1Field.squareN(t2, 5, t3);
-        SecP384R1Field.multiply(t3, t2, t3);
+        SecP384R1Field.squareN(t2, 5, t3, tt0);
+        SecP384R1Field.multiply(t3, t2, t3, tt0);
 
-        SecP384R1Field.squareN(t3, 5, t4);
-        SecP384R1Field.multiply(t4, t2, t4);
+        SecP384R1Field.squareN(t3, 5, t4, tt0);
+        SecP384R1Field.multiply(t4, t2, t4, tt0);
 
-        SecP384R1Field.squareN(t4, 15, t2);
-        SecP384R1Field.multiply(t2, t4, t2);
+        SecP384R1Field.squareN(t4, 15, t2, tt0);
+        SecP384R1Field.multiply(t2, t4, t2, tt0);
 
-        SecP384R1Field.squareN(t2, 2, t3);
-        SecP384R1Field.multiply(t1, t3, t1);
+        SecP384R1Field.squareN(t2, 2, t3, tt0);
+        SecP384R1Field.multiply(t1, t3, t1, tt0);
 
-        SecP384R1Field.squareN(t3, 28, t3);
-        SecP384R1Field.multiply(t2, t3, t2);
+        SecP384R1Field.squareN(t3, 28, t3, tt0);
+        SecP384R1Field.multiply(t2, t3, t2, tt0);
 
-        SecP384R1Field.squareN(t2, 60, t3);
-        SecP384R1Field.multiply(t3, t2, t3);
+        SecP384R1Field.squareN(t2, 60, t3, tt0);
+        SecP384R1Field.multiply(t3, t2, t3, tt0);
 
         int[] r = t2;
 
-        SecP384R1Field.squareN(t3, 120, r);
-        SecP384R1Field.multiply(r, t3, r);
+        SecP384R1Field.squareN(t3, 120, r, tt0);
+        SecP384R1Field.multiply(r, t3, r, tt0);
 
-        SecP384R1Field.squareN(r, 15, r);
-        SecP384R1Field.multiply(r, t4, r);
+        SecP384R1Field.squareN(r, 15, r, tt0);
+        SecP384R1Field.multiply(r, t4, r, tt0);
 
-        SecP384R1Field.squareN(r, 33, r);
-        SecP384R1Field.multiply(r, t1, r);
+        SecP384R1Field.squareN(r, 33, r, tt0);
+        SecP384R1Field.multiply(r, t1, r, tt0);
 
-        SecP384R1Field.squareN(r, 64, r);
-        SecP384R1Field.multiply(r, x1, r);
+        SecP384R1Field.squareN(r, 64, r, tt0);
+        SecP384R1Field.multiply(r, x1, r, tt0);
 
-        SecP384R1Field.squareN(r, 30, t1);
-        SecP384R1Field.square(t1, t2);
+        SecP384R1Field.squareN(r, 30, t1, tt0);
+        SecP384R1Field.square(t1, t2, tt0);
 
         return Nat.eq(12, x1, t2) ? new SecP384R1FieldElement(t1) : null;
     }
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP384R1Point.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP384R1Point.java
index d234cb5..e54029d 100644
--- a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP384R1Point.java
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP384R1Point.java
@@ -47,6 +47,7 @@
         SecP384R1FieldElement Z2 = (SecP384R1FieldElement)b.getZCoord(0);
 
         int c;
+        int[] tt0 = Nat.create(24);
         int[] tt1 = Nat.create(24);
         int[] tt2 = Nat.create(24);
         int[] t3 = Nat.create(12);
@@ -62,13 +63,13 @@
         else
         {
             S2 = t3;
-            SecP384R1Field.square(Z1.x, S2);
+            SecP384R1Field.square(Z1.x, S2, tt0);
 
             U2 = tt2;
-            SecP384R1Field.multiply(S2, X2.x, U2);
+            SecP384R1Field.multiply(S2, X2.x, U2, tt0);
 
-            SecP384R1Field.multiply(S2, Z1.x, S2);
-            SecP384R1Field.multiply(S2, Y2.x, S2);
+            SecP384R1Field.multiply(S2, Z1.x, S2, tt0);
+            SecP384R1Field.multiply(S2, Y2.x, S2, tt0);
         }
 
         boolean Z2IsOne = Z2.isOne();
@@ -81,13 +82,13 @@
         else
         {
             S1 = t4;
-            SecP384R1Field.square(Z2.x, S1);
+            SecP384R1Field.square(Z2.x, S1, tt0);
 
             U1 = tt1;
-            SecP384R1Field.multiply(S1, X1.x, U1);
+            SecP384R1Field.multiply(S1, X1.x, U1, tt0);
 
-            SecP384R1Field.multiply(S1, Z2.x, S1);
-            SecP384R1Field.multiply(S1, Y1.x, S1);
+            SecP384R1Field.multiply(S1, Z2.x, S1, tt0);
+            SecP384R1Field.multiply(S1, Y1.x, S1, tt0);
         }
 
         int[] H = Nat.create(12);
@@ -110,13 +111,13 @@
         }
 
         int[] HSquared = t3;
-        SecP384R1Field.square(H, HSquared);
+        SecP384R1Field.square(H, HSquared, tt0);
 
         int[] G = Nat.create(12);
-        SecP384R1Field.multiply(HSquared, H, G);
+        SecP384R1Field.multiply(HSquared, H, G, tt0);
 
         int[] V = t3;
-        SecP384R1Field.multiply(HSquared, U1, V);
+        SecP384R1Field.multiply(HSquared, U1, V, tt0);
 
         SecP384R1Field.negate(G, G);
         Nat384.mul(S1, G, tt1);
@@ -125,7 +126,7 @@
         SecP384R1Field.reduce32(c, G);
 
         SecP384R1FieldElement X3 = new SecP384R1FieldElement(t4);
-        SecP384R1Field.square(R, X3.x);
+        SecP384R1Field.square(R, X3.x, tt0);
         SecP384R1Field.subtract(X3.x, G, X3.x);
 
         SecP384R1FieldElement Y3 = new SecP384R1FieldElement(G);
@@ -137,11 +138,11 @@
         SecP384R1FieldElement Z3 = new SecP384R1FieldElement(H);
         if (!Z1IsOne)
         {
-            SecP384R1Field.multiply(Z3.x, Z1.x, Z3.x);
+            SecP384R1Field.multiply(Z3.x, Z1.x, Z3.x, tt0);
         }
         if (!Z2IsOne)
         {
-            SecP384R1Field.multiply(Z3.x, Z2.x, Z3.x);
+            SecP384R1Field.multiply(Z3.x, Z2.x, Z3.x, tt0);
         }
 
         ECFieldElement[] zs = new ECFieldElement[]{ Z3 };
@@ -167,14 +168,15 @@
         SecP384R1FieldElement X1 = (SecP384R1FieldElement)this.x, Z1 = (SecP384R1FieldElement)this.zs[0];
 
         int c;
+        int[] tt0 = Nat.create(24);
         int[] t1 = Nat.create(12);
         int[] t2 = Nat.create(12);
 
         int[] Y1Squared = Nat.create(12);
-        SecP384R1Field.square(Y1.x, Y1Squared);
+        SecP384R1Field.square(Y1.x, Y1Squared, tt0);
 
         int[] T = Nat.create(12);
-        SecP384R1Field.square(Y1Squared, T);
+        SecP384R1Field.square(Y1Squared, T, tt0);
 
         boolean Z1IsOne = Z1.isOne();
 
@@ -182,19 +184,19 @@
         if (!Z1IsOne)
         {
             Z1Squared = t2;
-            SecP384R1Field.square(Z1.x, Z1Squared);
+            SecP384R1Field.square(Z1.x, Z1Squared, tt0);
         }
 
         SecP384R1Field.subtract(X1.x, Z1Squared, t1);
 
         int[] M = t2;
         SecP384R1Field.add(X1.x, Z1Squared, M);
-        SecP384R1Field.multiply(M, t1, M);
+        SecP384R1Field.multiply(M, t1, M, tt0);
         c = Nat.addBothTo(12, M, M, M);
         SecP384R1Field.reduce32(c, M);
 
         int[] S = Y1Squared;
-        SecP384R1Field.multiply(Y1Squared, X1.x, S);
+        SecP384R1Field.multiply(Y1Squared, X1.x, S, tt0);
         c = Nat.shiftUpBits(12, S, 2, 0);
         SecP384R1Field.reduce32(c, S);
 
@@ -202,20 +204,20 @@
         SecP384R1Field.reduce32(c, t1);
 
         SecP384R1FieldElement X3 = new SecP384R1FieldElement(T);
-        SecP384R1Field.square(M, X3.x);
+        SecP384R1Field.square(M, X3.x, tt0);
         SecP384R1Field.subtract(X3.x, S, X3.x);
         SecP384R1Field.subtract(X3.x, S, X3.x);
 
         SecP384R1FieldElement Y3 = new SecP384R1FieldElement(S);
         SecP384R1Field.subtract(S, X3.x, Y3.x);
-        SecP384R1Field.multiply(Y3.x, M, Y3.x);
+        SecP384R1Field.multiply(Y3.x, M, Y3.x, tt0);
         SecP384R1Field.subtract(Y3.x, t1, Y3.x);
 
         SecP384R1FieldElement Z3 = new SecP384R1FieldElement(M);
         SecP384R1Field.twice(Y1.x, Z3.x);
         if (!Z1IsOne)
         {
-            SecP384R1Field.multiply(Z3.x, Z1.x, Z3.x);
+            SecP384R1Field.multiply(Z3.x, Z1.x, Z3.x, tt0);
         }
 
         return new SecP384R1Point(curve, X3, Y3, new ECFieldElement[]{ Z3 });
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP521R1Field.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP521R1Field.java
index 62f2591..1f39f3d 100644
--- a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP521R1Field.java
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP521R1Field.java
@@ -77,6 +77,12 @@
         reduce(tt, z);
     }
 
+    public static void multiply(int[] x, int[] y, int[] z, int[] tt)
+    {
+        implMultiply(x, y, tt);
+        reduce(tt, z);
+    }
+
     public static void negate(int[] x, int[] z)
     {
         if (0 != isZero(x))
@@ -145,6 +151,12 @@
         reduce(tt, z);
     }
 
+    public static void square(int[] x, int[] z, int[] tt)
+    {
+        implSquare(x, tt);
+        reduce(tt, z);
+    }
+
     public static void squareN(int[] x, int n, int[] z)
     {
 //        assert n > 0;
@@ -160,6 +172,20 @@
         }
     }
 
+    public static void squareN(int[] x, int n, int[] z, int[] tt)
+    {
+//        assert n > 0;
+
+        implSquare(x, tt);
+        reduce(tt, z);
+
+        while (--n > 0)
+        {
+            implSquare(z, tt);
+            reduce(tt, z);
+        }
+    }
+
     public static void subtract(int[] x, int[] y, int[] z)
     {
         int c = Nat.sub(16, x, y, z) + x[16] - y[16];
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP521R1FieldElement.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP521R1FieldElement.java
index be52b34..3a91fc7 100644
--- a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP521R1FieldElement.java
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP521R1FieldElement.java
@@ -138,11 +138,12 @@
             return this;
         }
 
+        int[] tt0 = Nat.create(33);
         int[] t1 = Nat.create(17);
         int[] t2 = Nat.create(17);
 
-        SecP521R1Field.squareN(x1, 519, t1);
-        SecP521R1Field.square(t1, t2);
+        SecP521R1Field.squareN(x1, 519, t1, tt0);
+        SecP521R1Field.square(t1, t2, tt0);
 
         return Nat.eq(17, x1, t2) ? new SecP521R1FieldElement(t1) : null;
     }
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP521R1Point.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP521R1Point.java
index d695cda..025e137 100644
--- a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP521R1Point.java
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP521R1Point.java
@@ -45,6 +45,7 @@
         SecP521R1FieldElement Z1 = (SecP521R1FieldElement)this.zs[0];
         SecP521R1FieldElement Z2 = (SecP521R1FieldElement)b.getZCoord(0);
 
+        int[] tt0 = Nat.create(33);
         int[] t1 = Nat.create(17);
         int[] t2 = Nat.create(17);
         int[] t3 = Nat.create(17);
@@ -60,13 +61,13 @@
         else
         {
             S2 = t3;
-            SecP521R1Field.square(Z1.x, S2);
+            SecP521R1Field.square(Z1.x, S2, tt0);
 
             U2 = t2;
-            SecP521R1Field.multiply(S2, X2.x, U2);
+            SecP521R1Field.multiply(S2, X2.x, U2, tt0);
 
-            SecP521R1Field.multiply(S2, Z1.x, S2);
-            SecP521R1Field.multiply(S2, Y2.x, S2);
+            SecP521R1Field.multiply(S2, Z1.x, S2, tt0);
+            SecP521R1Field.multiply(S2, Y2.x, S2, tt0);
         }
 
         boolean Z2IsOne = Z2.isOne();
@@ -79,13 +80,13 @@
         else
         {
             S1 = t4;
-            SecP521R1Field.square(Z2.x, S1);
+            SecP521R1Field.square(Z2.x, S1, tt0);
 
             U1 = t1;
-            SecP521R1Field.multiply(S1, X1.x, U1);
+            SecP521R1Field.multiply(S1, X1.x, U1, tt0);
 
-            SecP521R1Field.multiply(S1, Z2.x, S1);
-            SecP521R1Field.multiply(S1, Y1.x, S1);
+            SecP521R1Field.multiply(S1, Z2.x, S1, tt0);
+            SecP521R1Field.multiply(S1, Y1.x, S1, tt0);
         }
 
         int[] H = Nat.create(17);
@@ -108,35 +109,35 @@
         }
 
         int[] HSquared = t3;
-        SecP521R1Field.square(H, HSquared);
+        SecP521R1Field.square(H, HSquared, tt0);
 
         int[] G = Nat.create(17);
-        SecP521R1Field.multiply(HSquared, H, G);
+        SecP521R1Field.multiply(HSquared, H, G, tt0);
 
         int[] V = t3;
-        SecP521R1Field.multiply(HSquared, U1, V);
+        SecP521R1Field.multiply(HSquared, U1, V, tt0);
 
-        SecP521R1Field.multiply(S1, G, t1);
+        SecP521R1Field.multiply(S1, G, t1, tt0);
 
         SecP521R1FieldElement X3 = new SecP521R1FieldElement(t4);
-        SecP521R1Field.square(R, X3.x);
+        SecP521R1Field.square(R, X3.x, tt0);
         SecP521R1Field.add(X3.x, G, X3.x);
         SecP521R1Field.subtract(X3.x, V, X3.x);
         SecP521R1Field.subtract(X3.x, V, X3.x);
 
         SecP521R1FieldElement Y3 = new SecP521R1FieldElement(G);
         SecP521R1Field.subtract(V, X3.x, Y3.x);
-        SecP521R1Field.multiply(Y3.x, R, t2);
+        SecP521R1Field.multiply(Y3.x, R, t2, tt0);
         SecP521R1Field.subtract(t2, t1, Y3.x);
 
         SecP521R1FieldElement Z3 = new SecP521R1FieldElement(H);
         if (!Z1IsOne)
         {
-            SecP521R1Field.multiply(Z3.x, Z1.x, Z3.x);
+            SecP521R1Field.multiply(Z3.x, Z1.x, Z3.x, tt0);
         }
         if (!Z2IsOne)
         {
-            SecP521R1Field.multiply(Z3.x, Z2.x, Z3.x);
+            SecP521R1Field.multiply(Z3.x, Z2.x, Z3.x, tt0);
         }
 
         ECFieldElement[] zs = new ECFieldElement[]{ Z3 };
@@ -161,14 +162,15 @@
 
         SecP521R1FieldElement X1 = (SecP521R1FieldElement)this.x, Z1 = (SecP521R1FieldElement)this.zs[0];
 
+        int[] tt0 = Nat.create(33);
         int[] t1 = Nat.create(17);
         int[] t2 = Nat.create(17);
 
         int[] Y1Squared = Nat.create(17);
-        SecP521R1Field.square(Y1.x, Y1Squared);
+        SecP521R1Field.square(Y1.x, Y1Squared, tt0);
 
         int[] T = Nat.create(17);
-        SecP521R1Field.square(Y1Squared, T);
+        SecP521R1Field.square(Y1Squared, T, tt0);
 
         boolean Z1IsOne = Z1.isOne();
 
@@ -176,19 +178,19 @@
         if (!Z1IsOne)
         {
             Z1Squared = t2;
-            SecP521R1Field.square(Z1.x, Z1Squared);
+            SecP521R1Field.square(Z1.x, Z1Squared, tt0);
         }
 
         SecP521R1Field.subtract(X1.x, Z1Squared, t1);
 
         int[] M = t2;
         SecP521R1Field.add(X1.x, Z1Squared, M);
-        SecP521R1Field.multiply(M, t1, M);
+        SecP521R1Field.multiply(M, t1, M, tt0);
         Nat.addBothTo(17, M, M, M);
         SecP521R1Field.reduce23(M);
 
         int[] S = Y1Squared;
-        SecP521R1Field.multiply(Y1Squared, X1.x, S);
+        SecP521R1Field.multiply(Y1Squared, X1.x, S, tt0);
         Nat.shiftUpBits(17, S, 2, 0);
         SecP521R1Field.reduce23(S);
 
@@ -196,20 +198,20 @@
         SecP521R1Field.reduce23(t1);
 
         SecP521R1FieldElement X3 = new SecP521R1FieldElement(T);
-        SecP521R1Field.square(M, X3.x);
+        SecP521R1Field.square(M, X3.x, tt0);
         SecP521R1Field.subtract(X3.x, S, X3.x);
         SecP521R1Field.subtract(X3.x, S, X3.x);
 
         SecP521R1FieldElement Y3 = new SecP521R1FieldElement(S);
         SecP521R1Field.subtract(S, X3.x, Y3.x);
-        SecP521R1Field.multiply(Y3.x, M, Y3.x);
+        SecP521R1Field.multiply(Y3.x, M, Y3.x, tt0);
         SecP521R1Field.subtract(Y3.x, t1, Y3.x);
 
         SecP521R1FieldElement Z3 = new SecP521R1FieldElement(M);
         SecP521R1Field.twice(Y1.x, Z3.x);
         if (!Z1IsOne)
         {
-            SecP521R1Field.multiply(Z3.x, Z1.x, Z3.x);
+            SecP521R1Field.multiply(Z3.x, Z1.x, Z3.x, tt0);
         }
 
         return new SecP521R1Point(curve, X3, Y3, new ECFieldElement[]{ Z3 });
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/endo/GLVTypeBParameters.java b/bcprov/src/main/java/org/bouncycastle/math/ec/endo/GLVTypeBParameters.java
index 55cc4c8..7367a31 100644
--- a/bcprov/src/main/java/org/bouncycastle/math/ec/endo/GLVTypeBParameters.java
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/endo/GLVTypeBParameters.java
@@ -7,17 +7,6 @@
     protected final BigInteger beta, lambda;
     protected final ScalarSplitParameters splitParams;
 
-    /**
-     * @deprecated Use constructor taking a {@link ScalarSplitParameters} instead.
-     */
-    public GLVTypeBParameters(BigInteger beta, BigInteger lambda, BigInteger[] v1, BigInteger[] v2, BigInteger g1,
-        BigInteger g2, int bits)
-    {
-        this.beta = beta;
-        this.lambda = lambda;
-        this.splitParams = new ScalarSplitParameters(v1, v2, g1, g2, bits);
-    }
-
     public GLVTypeBParameters(BigInteger beta, BigInteger lambda, ScalarSplitParameters splitParams)
     {
         this.beta = beta;
@@ -39,60 +28,4 @@
     {
         return splitParams;
     }
-
-    /**
-     * @deprecated Access via {@link #getSplitParams()} instead.
-     */
-    public BigInteger getV1A()
-    {
-        return getSplitParams().getV1A();
-    }
-
-    /**
-     * @deprecated Access via {@link #getSplitParams()} instead.
-     */
-    public BigInteger getV1B()
-    {
-        return getSplitParams().getV1B();
-    }
-
-    /**
-     * @deprecated Access via {@link #getSplitParams()} instead.
-     */
-    public BigInteger getV2A()
-    {
-        return getSplitParams().getV2A();
-    }
-
-    /**
-     * @deprecated Access via {@link #getSplitParams()} instead.
-     */
-    public BigInteger getV2B()
-    {
-        return getSplitParams().getV2B();
-    }
-
-    /**
-     * @deprecated Access via {@link #getSplitParams()} instead.
-     */
-    public BigInteger getG1()
-    {
-        return getSplitParams().getG1();
-    }
-
-    /**
-     * @deprecated Access via {@link #getSplitParams()} instead.
-     */
-    public BigInteger getG2()
-    {
-        return getSplitParams().getG2();
-    }
-    
-    /**
-     * @deprecated Access via {@link #getSplitParams()} instead.
-     */
-    public int getBits()
-    {
-        return getSplitParams().getBits();
-    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/math/field/FiniteFields.java b/bcprov/src/main/java/org/bouncycastle/math/field/FiniteFields.java
index c71eda7..e324885 100644
--- a/bcprov/src/main/java/org/bouncycastle/math/field/FiniteFields.java
+++ b/bcprov/src/main/java/org/bouncycastle/math/field/FiniteFields.java
@@ -2,6 +2,8 @@
 
 import java.math.BigInteger;
 
+import org.bouncycastle.util.BigIntegers;
+
 public abstract class FiniteFields
 {
     static final FiniteField GF_2 = new PrimeField(BigInteger.valueOf(2));
@@ -39,7 +41,7 @@
 
         if (bitLength < 3)
         {
-            switch (characteristic.intValue())
+            switch (BigIntegers.intValueExact(characteristic))
             {
             case 2:
                 return GF_2;
diff --git a/bcprov/src/main/java/org/bouncycastle/math/raw/Mod.java b/bcprov/src/main/java/org/bouncycastle/math/raw/Mod.java
index fc8f3c8..c956aea 100644
--- a/bcprov/src/main/java/org/bouncycastle/math/raw/Mod.java
+++ b/bcprov/src/main/java/org/bouncycastle/math/raw/Mod.java
@@ -14,17 +14,6 @@
     private static final int M30 = 0x3FFFFFFF;
     private static final long M32L = 0xFFFFFFFFL;
 
-    /** @deprecated Will be removed. */
-    public static void add(int[] p, int[] x, int[] y, int[] z)
-    {
-        int len = p.length;
-        int c = Nat.add(len, x, y, z);
-        if (c != 0)
-        {
-            Nat.subFrom(len, p, z);
-        }
-    }
-
     public static void checkedModOddInverse(int[] m, int[] x, int[] z)
     {
         if (0 == modOddInverse(m, x, z))
@@ -54,12 +43,6 @@
         return  x;
     }
 
-    /** @deprecated Use {@link #checkedModOddInverseVar(int[], int[], int[])} instead. */
-    public static void invert(int[] m, int[] x, int[] z)
-    {
-        checkedModOddInverseVar(m,  x,  z);
-    }
-
     public static int modOddInverse(int[] m, int[] x, int[] z)
     {
         int len32 = m.length;
@@ -82,13 +65,13 @@
         encode30(bits, m, 0, M, 0);
         System.arraycopy(M, 0, F, 0, len30);
 
-        int eta = -1;
+        int delta = 0;
         int m0Inv32 = inverse32(M[0]);
         int maxDivsteps = getMaximumDivsteps(bits);
 
         for (int divSteps = 0; divSteps < maxDivsteps; divSteps += 30)
         {
-            eta = divsteps30(eta, F[0], G[0], t);
+            delta = divsteps30(delta, F[0], G[0], t);
             updateDE30(len30, D, E, t, m0Inv32, M);
             updateFG30(len30, F, G, t);
         }
@@ -228,17 +211,6 @@
         return s;
     }
 
-    /** @deprecated Will be removed. */
-    public static void subtract(int[] p, int[] x, int[] y, int[] z)
-    {
-        int len = p.length;
-        int c = Nat.sub(len, x, y, z);
-        if (c != 0)
-        {
-            Nat.addTo(len, p, z);
-        }
-    }
-
     private static int add30(int len30, int[] D, int[] M)
     {
 //        assert len30 > 0;
@@ -276,7 +248,6 @@
 //        assert len30 > 0;
 //        assert D.length >= len30;
 //        assert M.length >= len30;
-
         int last = len30 - 1;
 
         {
@@ -331,38 +302,38 @@
         }
     }
 
-    private static int divsteps30(int eta, int f0, int g0, int[] t)
+    private static int divsteps30(int delta, int f0, int g0, int[] t)
     {
-        int u = 1, v = 0, q = 0, r = 1;
+        int u = 1 << 30, v = 0, q = 0, r = 1 << 30;
         int f = f0, g = g0;
 
         for (int i = 0; i < 30; ++i)
         {
 //            assert (f & 1) == 1;
-//            assert (u * f0 + v * g0) == f << i;
-//            assert (q * f0 + r * g0) == g << i;
+//            assert ((u >> (30 - i)) * f0 + (v >> (30 - i)) * g0) == f << i;
+//            assert ((q >> (30 - i)) * f0 + (r >> (30 - i)) * g0) == g << i;
 
-            int c1 = eta >> 31;
+            int c1 = delta >> 31;
             int c2 = -(g & 1);
 
-            int x = (f ^ c1) - c1;
-            int y = (u ^ c1) - c1;
-            int z = (v ^ c1) - c1;
+            int x = f ^ c1;
+            int y = u ^ c1;
+            int z = v ^ c1;
 
-            g += x & c2;
-            q += y & c2;
-            r += z & c2;
+            g -= x & c2;
+            q -= y & c2;
+            r -= z & c2;
 
-            c1 &= c2;
-            eta = (eta ^ c1) - (c1 + 1);
+            c2 &= ~c1;
+            delta = (delta ^ c2) - (c2 - 1);
 
-            f += g & c1;
-            u += q & c1;
-            v += r & c1;
+            f += g & c2;
+            u += q & c2;
+            v += r & c2;
 
             g >>= 1;
-            u <<= 1;
-            v <<= 1;
+            q >>= 1;
+            r >>= 1;
         }
 
         t[0] = u;
@@ -370,7 +341,7 @@
         t[2] = q;
         t[3] = r;
 
-        return eta;
+        return delta;
     }
 
     private static int divsteps30Var(int eta, int f0, int g0, int[] t)
diff --git a/bcprov/src/main/java/org/bouncycastle/math/raw/Nat.java b/bcprov/src/main/java/org/bouncycastle/math/raw/Nat.java
index 28745df..592dd61 100644
--- a/bcprov/src/main/java/org/bouncycastle/math/raw/Nat.java
+++ b/bcprov/src/main/java/org/bouncycastle/math/raw/Nat.java
@@ -232,6 +232,19 @@
         return (int)c;
     }
 
+    public static int caddTo(int len, int mask, int[] x, int[] z)
+    {
+        long MASK = -(mask & 1) & M;
+        long c = 0;
+        for (int i = 0; i < len; ++i)
+        {
+            c += (z[i] & M) + (x[i] & MASK);
+            z[i] = (int)c;
+            c >>>= 32;
+        }
+        return (int)c;
+    }
+
     public static void cmov(int len, int mask, int[] x, int xOff, int[] z, int zOff)
     {
         mask = -(mask & 1);
@@ -1125,41 +1138,6 @@
         shiftUpBit(extLen, zz, zzOff, x[xOff] << 31);
     }
 
-    /**
-     * @deprecated Use {@link #squareWordAddTo(int[], int, int[])} instead.
-     */
-    public static int squareWordAdd(int[] x, int xPos, int[] z)
-    {
-        long c = 0, xVal = x[xPos] & M;
-        int i = 0;
-        do
-        {
-            c += xVal * (x[i] & M) + (z[xPos + i] & M);
-            z[xPos + i] = (int)c;
-            c >>>= 32;
-        }
-        while (++i < xPos);
-        return (int)c;
-    }
-
-    /**
-     * @deprecated Use {@link #squareWordAddTo(int[], int, int, int[], int)} instead.
-     */
-    public static int squareWordAdd(int[] x, int xOff, int xPos, int[] z, int zOff)
-    {
-        long c = 0, xVal = x[xOff + xPos] & M;
-        int i = 0;
-        do
-        {
-            c += xVal * (x[xOff + i] & M) + (z[xPos + zOff] & M);
-            z[xPos + zOff] = (int)c;
-            c >>>= 32;
-            ++zOff;
-        }
-        while (++i < xPos);
-        return (int)c;
-    }
-
     public static int squareWordAddTo(int[] x, int xPos, int[] z)
     {
         long c = 0, xVal = x[xPos] & M;
diff --git a/bcprov/src/main/java/org/bouncycastle/math/raw/Nat224.java b/bcprov/src/main/java/org/bouncycastle/math/raw/Nat224.java
index e4b7d5e..3610442 100644
--- a/bcprov/src/main/java/org/bouncycastle/math/raw/Nat224.java
+++ b/bcprov/src/main/java/org/bouncycastle/math/raw/Nat224.java
@@ -143,6 +143,33 @@
         return (int)c;
     }
 
+    public static int addTo(int[] x, int[] z, int cIn)
+    {
+        long c = cIn & M;
+        c += (x[0] & M) + (z[0] & M);
+        z[0] = (int)c;
+        c >>>= 32;
+        c += (x[1] & M) + (z[1] & M);
+        z[1] = (int)c;
+        c >>>= 32;
+        c += (x[2] & M) + (z[2] & M);
+        z[2] = (int)c;
+        c >>>= 32;
+        c += (x[3] & M) + (z[3] & M);
+        z[3] = (int)c;
+        c >>>= 32;
+        c += (x[4] & M) + (z[4] & M);
+        z[4] = (int)c;
+        c >>>= 32;
+        c += (x[5] & M) + (z[5] & M);
+        z[5] = (int)c;
+        c >>>= 32;
+        c += (x[6] & M) + (z[6] & M);
+        z[6] = (int)c;
+        c >>>= 32;
+        return (int)c;
+    }
+
     public static int addTo(int[] x, int xOff, int[] z, int zOff, int cIn)
     {
         long c = cIn & M;
diff --git a/bcprov/src/main/java/org/bouncycastle/math/raw/Nat256.java b/bcprov/src/main/java/org/bouncycastle/math/raw/Nat256.java
index 20b17cd..ed46d05 100644
--- a/bcprov/src/main/java/org/bouncycastle/math/raw/Nat256.java
+++ b/bcprov/src/main/java/org/bouncycastle/math/raw/Nat256.java
@@ -158,6 +158,36 @@
         return (int)c;
     }
 
+    public static int addTo(int[] x, int[] z, int cIn)
+    {
+        long c = cIn & M;
+        c += (x[0] & M) + (z[0] & M);
+        z[0] = (int)c;
+        c >>>= 32;
+        c += (x[1] & M) + (z[1] & M);
+        z[1] = (int)c;
+        c >>>= 32;
+        c += (x[2] & M) + (z[2] & M);
+        z[2] = (int)c;
+        c >>>= 32;
+        c += (x[3] & M) + (z[3] & M);
+        z[3] = (int)c;
+        c >>>= 32;
+        c += (x[4] & M) + (z[4] & M);
+        z[4] = (int)c;
+        c >>>= 32;
+        c += (x[5] & M) + (z[5] & M);
+        z[5] = (int)c;
+        c >>>= 32;
+        c += (x[6] & M) + (z[6] & M);
+        z[6] = (int)c;
+        c >>>= 32;
+        c += (x[7] & M) + (z[7] & M);
+        z[7] = (int)c;
+        c >>>= 32;
+        return (int)c;
+    }
+
     public static int addTo(int[] x, int xOff, int[] z, int zOff, int cIn)
     {
         long c = cIn & M;
@@ -602,6 +632,77 @@
         }
     }
 
+    public static void mul128(int[] x, int[] y128, int[] zz)
+    {
+        long x_0 = x[0] & M;
+        long x_1 = x[1] & M;
+        long x_2 = x[2] & M;
+        long x_3 = x[3] & M;
+        long x_4 = x[4] & M;
+        long x_5 = x[5] & M;
+        long x_6 = x[6] & M;
+        long x_7 = x[7] & M;
+
+        {
+            long c = 0, y_0 = y128[0] & M;
+            c += y_0 * x_0;
+            zz[0] = (int)c;
+            c >>>= 32;
+            c += y_0 * x_1;
+            zz[1] = (int)c;
+            c >>>= 32;
+            c += y_0 * x_2;
+            zz[2] = (int)c;
+            c >>>= 32;
+            c += y_0 * x_3;
+            zz[3] = (int)c;
+            c >>>= 32;
+            c += y_0 * x_4;
+            zz[4] = (int)c;
+            c >>>= 32;
+            c += y_0 * x_5;
+            zz[5] = (int)c;
+            c >>>= 32;
+            c += y_0 * x_6;
+            zz[6] = (int)c;
+            c >>>= 32;
+            c += y_0 * x_7;
+            zz[7] = (int)c;
+            c >>>= 32;
+            zz[8] = (int)c;
+        }
+
+        for (int i = 1; i < 4; ++i)
+        {
+            long c = 0, y_i = y128[i] & M;
+            c += y_i * x_0 + (zz[i + 0] & M);
+            zz[i + 0] = (int)c;
+            c >>>= 32;
+            c += y_i * x_1 + (zz[i + 1] & M);
+            zz[i + 1] = (int)c;
+            c >>>= 32;
+            c += y_i * x_2 + (zz[i + 2] & M);
+            zz[i + 2] = (int)c;
+            c >>>= 32;
+            c += y_i * x_3 + (zz[i + 3] & M);
+            zz[i + 3] = (int)c;
+            c >>>= 32;
+            c += y_i * x_4 + (zz[i + 4] & M);
+            zz[i + 4] = (int)c;
+            c >>>= 32;
+            c += y_i * x_5 + (zz[i + 5] & M);
+            zz[i + 5] = (int)c;
+            c >>>= 32;
+            c += y_i * x_6 + (zz[i + 6] & M);
+            zz[i + 6] = (int)c;
+            c >>>= 32;
+            c += y_i * x_7 + (zz[i + 7] & M);
+            zz[i + 7] = (int)c;
+            c >>>= 32;
+            zz[i + 8] = (int)c;
+        }
+    }
+
     public static int mulAddTo(int[] x, int[] y, int[] zz)
     {
         long y_0 = y[0] & M;
@@ -1347,6 +1448,36 @@
         return (int)c;
     }
 
+    public static int subFrom(int[] x, int[] z, int cIn)
+    {
+        long c = cIn & M;
+        c += (z[0] & M) - (x[0] & M);
+        z[0] = (int)c;
+        c >>= 32;
+        c += (z[1] & M) - (x[1] & M);
+        z[1] = (int)c;
+        c >>= 32;
+        c += (z[2] & M) - (x[2] & M);
+        z[2] = (int)c;
+        c >>= 32;
+        c += (z[3] & M) - (x[3] & M);
+        z[3] = (int)c;
+        c >>= 32;
+        c += (z[4] & M) - (x[4] & M);
+        z[4] = (int)c;
+        c >>= 32;
+        c += (z[5] & M) - (x[5] & M);
+        z[5] = (int)c;
+        c >>= 32;
+        c += (z[6] & M) - (x[6] & M);
+        z[6] = (int)c;
+        c >>= 32;
+        c += (z[7] & M) - (x[7] & M);
+        z[7] = (int)c;
+        c >>= 32;
+        return (int)c;
+    }
+
     public static int subFrom(int[] x, int xOff, int[] z, int zOff)
     {
         long c = 0;
@@ -1377,6 +1508,36 @@
         return (int)c;
     }
 
+    public static int subFrom(int[] x, int xOff, int[] z, int zOff, int cIn)
+    {
+        long c = cIn & M;
+        c += (z[zOff + 0] & M) - (x[xOff + 0] & M);
+        z[zOff + 0] = (int)c;
+        c >>= 32;
+        c += (z[zOff + 1] & M) - (x[xOff + 1] & M);
+        z[zOff + 1] = (int)c;
+        c >>= 32;
+        c += (z[zOff + 2] & M) - (x[xOff + 2] & M);
+        z[zOff + 2] = (int)c;
+        c >>= 32;
+        c += (z[zOff + 3] & M) - (x[xOff + 3] & M);
+        z[zOff + 3] = (int)c;
+        c >>= 32;
+        c += (z[zOff + 4] & M) - (x[xOff + 4] & M);
+        z[zOff + 4] = (int)c;
+        c >>= 32;
+        c += (z[zOff + 5] & M) - (x[xOff + 5] & M);
+        z[zOff + 5] = (int)c;
+        c >>= 32;
+        c += (z[zOff + 6] & M) - (x[xOff + 6] & M);
+        z[zOff + 6] = (int)c;
+        c >>= 32;
+        c += (z[zOff + 7] & M) - (x[xOff + 7] & M);
+        z[zOff + 7] = (int)c;
+        c >>= 32;
+        return (int)c;
+    }
+
     public static BigInteger toBigInteger(int[] x)
     {
         byte[] bs = new byte[32];
diff --git a/bcprov/src/main/java/org/bouncycastle/util/Arrays.java b/bcprov/src/main/java/org/bouncycastle/util/Arrays.java
index 6e52c36..27371dd 100644
--- a/bcprov/src/main/java/org/bouncycastle/util/Arrays.java
+++ b/bcprov/src/main/java/org/bouncycastle/util/Arrays.java
@@ -149,6 +149,47 @@
         return 0 == d;
     }
 
+    /**
+     * A constant time equals comparison - does not terminate early if
+     * comparison fails. For best results always pass the expected value
+     * as the first parameter.
+     *
+     * @param expected first array
+     * @param supplied second array
+     * @return true if arrays equal, false otherwise.
+     */
+    public static boolean constantTimeAreEqual(
+        char[] expected,
+        char[] supplied)
+    {
+        if (expected == null || supplied == null)
+        {
+            return false;
+        }
+
+        if (expected == supplied)
+        {
+            return true;
+        }
+
+        int len = Math.min(expected.length, supplied.length);
+
+        int nonEqual = expected.length ^ supplied.length;
+
+        // do the char-wise comparison
+        for (int i = 0; i != len; i++)
+        {
+            nonEqual |= (expected[i] ^ supplied[i]);
+        }
+        // If supplied is longer than expected, iterate over rest of supplied with NOPs
+        for (int i = len; i < supplied.length; i++)
+        {
+            nonEqual |= ((byte)supplied[i] ^ (byte)~supplied[i]);
+        }
+
+        return nonEqual == 0;
+    }
+
     public static int compareUnsigned(byte[] a, byte[] b)
     {
         if (a == b)
@@ -274,14 +315,6 @@
         java.util.Arrays.fill(a, val);
     }
 
-    /**
-     * @deprecated Use {@link #fill(byte[], int, int, byte)} instead.
-     */
-    public static void fill(byte[] a, int fromIndex, byte val)
-    {
-        fill(a, fromIndex, a.length, val);
-    }
-
     public static void fill(byte[] a, int fromIndex, int toIndex, byte val)
     {
         java.util.Arrays.fill(a, fromIndex, toIndex, val);
@@ -302,14 +335,6 @@
         java.util.Arrays.fill(a, val);
     }
 
-    /**
-     * @deprecated Use {@link #fill(int[], int, int, int)} instead.
-     */
-    public static void fill(int[] a, int fromIndex, int val)
-    {
-        java.util.Arrays.fill(a, fromIndex, a.length, val);
-    }
-
     public static void fill(int[] a, int fromIndex, int toIndex, int val)
     {
         java.util.Arrays.fill(a, fromIndex, toIndex, val);
@@ -320,14 +345,6 @@
         java.util.Arrays.fill(a, val);
     }
 
-    /**
-     * @deprecated Use {@link #fill(long[], int, int, long)} instead.
-     */
-    public static void fill(long[] a, int fromIndex, long val)
-    {
-        java.util.Arrays.fill(a, fromIndex, a.length, val);
-    }
-
     public static void fill(long[] a, int fromIndex, int toIndex, long val)
     {
         java.util.Arrays.fill(a, fromIndex, toIndex, val);
@@ -348,14 +365,6 @@
         java.util.Arrays.fill(a, val);
     }
 
-    /**
-     * @deprecated Use {@link #fill(short[], int, int, short)} instead.
-     */
-    public static void fill(short[] a, int fromIndex, short val)
-    {
-        java.util.Arrays.fill(a, fromIndex, a.length, val);
-    }
-
     public static void fill(short[] a, int fromIndex, int toIndex, short val)
     {
         java.util.Arrays.fill(a, fromIndex, toIndex, val);
@@ -794,9 +803,7 @@
         int newLength = to - from;
         if (newLength < 0)
         {
-            StringBuffer sb = new StringBuffer(from);
-            sb.append(" > ").append(to);
-            throw new IllegalArgumentException(sb.toString());
+            throw new IllegalArgumentException(from + " > " + to);
         }
         return newLength;
     }
@@ -1063,6 +1070,80 @@
         return result;
     }
 
+    public static void reverse(byte[] input, byte[] output)
+    {
+        int last = input.length - 1;
+        for (int i = 0; i <= last; ++i)
+        {
+            output[i] = input[last - i];
+        }
+    }
+
+    public static byte[] reverseInPlace(byte[] a)
+    {
+        if (null == a)
+        {
+            return null;
+        }
+
+        int p1 = 0, p2 = a.length - 1;
+        while (p1 < p2)
+        {
+            byte t1 = a[p1], t2 = a[p2];
+            a[p1++] = t2;
+            a[p2--] = t1;
+        }
+
+        return a;
+    }
+
+    public static void reverseInPlace(byte[] a, int aOff, int aLen)
+    {
+        int p1 = aOff, p2 = aOff + aLen - 1;
+        while (p1 < p2)
+        {
+            byte t1 = a[p1], t2 = a[p2];
+            a[p1++] = t2;
+            a[p2--] = t1;
+        }
+    }
+
+    public static short[] reverseInPlace(short[] a)
+    {
+        if (null == a)
+        {
+            return null;
+        }
+
+        int p1 = 0, p2 = a.length - 1;
+        while (p1 < p2)
+        {
+            short t1 = a[p1], t2 = a[p2];
+            a[p1++] = t2;
+            a[p2--] = t1;
+        }
+
+        return a;
+    }
+
+    public static int[] reverseInPlace(int[] a)
+    {
+        if (null == a)
+        {
+            return null;
+        }
+
+        int p1 = 0, p2 = a.length - 1;
+        while (p1 < p2)
+        {
+            int t1 = a[p1], t2 = a[p2];
+            a[p1++] = t2;
+            a[p2--] = t1;
+        }
+
+        return a;
+    }
+
     /**
      * Iterator backed by a specific array.
      */
diff --git a/bcprov/src/main/java/org/bouncycastle/util/BigIntegers.java b/bcprov/src/main/java/org/bouncycastle/util/BigIntegers.java
index 46e2bb0..fe0891b 100644
--- a/bcprov/src/main/java/org/bouncycastle/util/BigIntegers.java
+++ b/bcprov/src/main/java/org/bouncycastle/util/BigIntegers.java
@@ -2,6 +2,11 @@
 
 import java.math.BigInteger;
 import java.security.SecureRandom;
+import java.util.Map;
+import java.util.WeakHashMap;
+
+import org.bouncycastle.math.raw.Mod;
+import org.bouncycastle.math.raw.Nat;
 
 import org.bouncycastle.math.raw.Mod;
 import org.bouncycastle.math.raw.Nat;
@@ -21,7 +26,7 @@
 
     /**
      * Return the passed in value as an unsigned byte array.
-     * 
+     *
      * @param value the value to be converted.
      * @return a byte array without a leading zero byte if present in the signed encoding.
      */
@@ -29,16 +34,15 @@
         BigInteger value)
     {
         byte[] bytes = value.toByteArray();
-        
         if (bytes[0] == 0 && bytes.length != 1)
         {
             byte[] tmp = new byte[bytes.length - 1];
-            
+
             System.arraycopy(bytes, 1, tmp, 0, tmp.length);
-            
+
             return tmp;
         }
-        
+
         return bytes;
     }
 
@@ -46,10 +50,8 @@
      * Return the passed in value as an unsigned byte array of the specified length, padded with
      * leading zeros as necessary..
      *
-     * @param length
-     *            the fixed length of the result
-     * @param value
-     *            the value to be converted.
+     * @param length the fixed length of the result
+     * @param value  the value to be converted.
      * @return a byte array padded to a fixed length with leading zeros.
      */
     public static byte[] asUnsignedByteArray(int length, BigInteger value)
@@ -77,14 +79,10 @@
      * Write the passed in value as unsigned bytes to the specified buffer range, padded with
      * leading zeros as necessary.
      *
-     * @param value
-     *            the value to be converted.
-     * @param buf
-     *            the buffer to which the value is written.
-     * @param off
-     *            the start offset in array <code>buf</code> at which the data is written.
-     * @param len
-     *            the fixed length of data written (possibly padded with leading zeros).
+     * @param value the value to be converted.
+     * @param buf   the buffer to which the value is written.
+     * @param off   the start offset in array <code>buf</code> at which the data is written.
+     * @param len   the fixed length of data written (possibly padded with leading zeros).
      */
     public static void asUnsignedByteArray(BigInteger value, byte[] buf, int off, int len)
     {
@@ -104,22 +102,23 @@
         }
 
         int padLen = len - count;
-        Arrays.fill(buf,  off, off + padLen, (byte)0x00);
+        Arrays.fill(buf, off, off + padLen, (byte)0x00);
         System.arraycopy(bytes, start, buf, off + padLen, count);
     }
 
+
     /**
      * Return a random BigInteger not less than 'min' and not greater than 'max'
-     * 
-     * @param min the least value that may be generated
-     * @param max the greatest value that may be generated
+     *
+     * @param min    the least value that may be generated
+     * @param max    the greatest value that may be generated
      * @param random the source of randomness
      * @return a random BigInteger value in the range [min,max]
      */
     public static BigInteger createRandomInRange(
-        BigInteger      min,
-        BigInteger      max,
-        SecureRandom    random)
+        BigInteger min,
+        BigInteger max,
+        SecureRandom random)
     {
         int cmp = min.compareTo(max);
         if (cmp >= 0)
@@ -150,6 +149,7 @@
         return createRandomBigInteger(max.subtract(min).bitLength() - 1, random).add(min);
     }
 
+
     public static BigInteger fromUnsignedByteArray(byte[] buf)
     {
         return new BigInteger(1, buf);
@@ -166,6 +166,28 @@
         return new BigInteger(1, mag);
     }
 
+    public static byte byteValueExact(BigInteger x)
+    {
+        // Since Java 1.8 could use BigInteger.byteValueExact instead
+        if (x.bitLength() > 7)
+        {
+            throw new ArithmeticException("BigInteger out of int range");
+        }
+
+        return x.byteValue();
+    }
+
+    public static short shortValueExact(BigInteger x)
+    {
+        // Since Java 1.8 could use BigInteger.shortValueExact instead
+        if (x.bitLength() > 15)
+        {
+            throw new ArithmeticException("BigInteger out of int range");
+        }
+
+        return x.shortValue();
+    }
+
     public static int intValueExact(BigInteger x)
     {
         // Since Java 1.8 could use BigInteger.intValueExact instead
@@ -174,7 +196,7 @@
             throw new ArithmeticException("BigInteger out of int range");
         }
 
-        return x.intValue(); 
+        return x.intValue();
     }
 
     public static long longValueExact(BigInteger x)
@@ -185,7 +207,7 @@
             throw new ArithmeticException("BigInteger out of long range");
         }
 
-        return x.longValue(); 
+        return x.longValue();
     }
 
     public static BigInteger modOddInverse(BigInteger M, BigInteger X)
@@ -264,7 +286,7 @@
      * Return a positive BigInteger in the range of 0 to 2**bitLength - 1.
      *
      * @param bitLength maximum bit length for the generated BigInteger.
-     * @param random a source of randomness.
+     * @param random    a source of randomness.
      * @return a positive BigInteger
      */
     public static BigInteger createRandomBigInteger(int bitLength, SecureRandom random)
@@ -274,7 +296,7 @@
 
     // Hexadecimal value of the product of the 131 smallest odd primes from 3 to 743
     private static final BigInteger SMALL_PRIMES_PRODUCT = new BigInteger(
-              "8138e8a0fcf3a4e84a771d40fd305d7f4aa59306d7251de54d98af8fe95729a1f"
+        "8138e8a0fcf3a4e84a771d40fd305d7f4aa59306d7251de54d98af8fe95729a1f"
             + "73d893fa424cd2edc8636a6c3285e022b0e3866a565ae8108eed8591cd4fe8d2"
             + "ce86165a978d719ebf647f362d33fca29cd179fb42401cbaf3df0c614056f9c8"
             + "f3cfd51e474afb6bc6974f78db8aba8e9e517fded658591ab7502bd41849462f",
@@ -285,7 +307,7 @@
      * Return a prime number candidate of the specified bit length.
      *
      * @param bitLength bit length for the generated BigInteger.
-     * @param random a source of randomness.
+     * @param random    a source of randomness.
      * @return a positive BigInteger of numBits length
      */
     public static BigInteger createRandomPrime(int bitLength, int certainty, SecureRandom random)
@@ -347,4 +369,38 @@
 
         return rv;
     }
+
+    public static class Cache
+    {
+        private final Map<BigInteger, Boolean> values = new WeakHashMap<BigInteger, Boolean>();
+        private final BigInteger[] preserve = new BigInteger[8];
+
+        private int preserveCounter = 0;
+
+        public synchronized void add(BigInteger value)
+        {
+            values.put(value, Boolean.TRUE);
+            preserve[preserveCounter] = value;
+            preserveCounter = (preserveCounter + 1) % preserve.length;
+        }
+
+        public synchronized boolean contains(BigInteger value)
+        {
+            return values.containsKey(value);
+        }
+
+        public synchronized int size()
+        {
+            return values.size();
+        }
+
+        public synchronized void clear()
+        {
+            values.clear();
+            for (int i = 0; i != preserve.length; i++)
+            {
+                preserve[i] = null;
+            }
+        }
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/util/Bytes.java b/bcprov/src/main/java/org/bouncycastle/util/Bytes.java
new file mode 100644
index 0000000..4db8575
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/util/Bytes.java
@@ -0,0 +1,42 @@
+package org.bouncycastle.util;
+
+/**
+ * Utility methods and constants for bytes.
+ */
+public class Bytes
+{
+    public static final int BYTES = 1;
+    public static final int SIZE = Byte.SIZE;
+
+    public static void xor(int len, byte[] x, byte[] y, byte[] z)
+    {
+        for (int i = 0; i < len; ++i)
+        {
+            z[i] = (byte)(x[i] ^ y[i]);
+        }
+    }
+
+    public static void xor(int len, byte[] x, int xOff, byte[] y, int yOff, byte[] z, int zOff)
+    {
+        for (int i = 0; i < len; ++i)
+        {
+            z[zOff + i] = (byte)(x[xOff + i] ^ y[yOff + i]);
+        }
+    }
+
+    public static void xorTo(int len, byte[] x, byte[] z)
+    {
+        for (int i = 0; i < len; ++i)
+        {
+            z[i] ^= x[i];
+        }
+    }
+
+    public static void xorTo(int len, byte[] x, int xOff, byte[] z, int zOff)
+    {
+        for (int i = 0; i < len; ++i)
+        {
+            z[zOff + i] ^= x[xOff + i];
+        }
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/util/Characters.java b/bcprov/src/main/java/org/bouncycastle/util/Characters.java
new file mode 100644
index 0000000..828c464
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/util/Characters.java
@@ -0,0 +1,9 @@
+package org.bouncycastle.util;
+
+public class Characters
+{
+    public static Character valueOf(char c)
+    {
+        return Character.valueOf(c);
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/util/Exceptions.java b/bcprov/src/main/java/org/bouncycastle/util/Exceptions.java
new file mode 100644
index 0000000..74ef192
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/util/Exceptions.java
@@ -0,0 +1,22 @@
+package org.bouncycastle.util;
+
+import java.io.IOException;
+
+public class Exceptions
+{
+    public static IllegalArgumentException illegalArgumentException(String message, Throwable cause)
+    {
+        return new IllegalArgumentException(message, cause);
+    }
+
+    public static IllegalStateException illegalStateException(String message, Throwable cause)
+    {
+        return new IllegalStateException(message, cause);
+    }
+
+    public static IOException ioException(String message, Throwable cause)
+    {
+        return new IOException(message, cause);
+    }
+
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/util/IPAddress.java b/bcprov/src/main/java/org/bouncycastle/util/IPAddress.java
index 8f57263..5dbf1d0 100644
--- a/bcprov/src/main/java/org/bouncycastle/util/IPAddress.java
+++ b/bcprov/src/main/java/org/bouncycastle/util/IPAddress.java
@@ -12,8 +12,7 @@
      *
      * @return true if a valid address, false otherwise
      */
-    public static boolean isValid(
-        String address)
+    public static boolean isValid(String address)
     {
         return isValidIPv4(address) || isValidIPv6(address);
     }
@@ -25,8 +24,7 @@
      *
      * @return true if a valid address with netmask, false otherwise
      */
-    public static boolean isValidWithNetMask(
-        String address)
+    public static boolean isValidWithNetMask(String address)
     {
         return isValidIPv4WithNetmask(address) || isValidIPv6WithNetmask(address);
     }
@@ -38,79 +36,42 @@
      *
      * @return true if a valid IPv4 address, false otherwise
      */
-    public static boolean isValidIPv4(
-        String address)
+    public static boolean isValidIPv4(String address)
     {
-        if (address.length() == 0)
+        int length = address.length();
+        if (length < 7 || length > 15)
         {
             return false;
         }
 
-        int octet;
-        int octets = 0;
-        
-        String temp = address+".";
-
-        int pos;
-        int start = 0;
-        while (start < temp.length()
-            && (pos = temp.indexOf('.', start)) > start)
+        int pos = 0;
+        for (int octetIndex = 0; octetIndex < 3; ++octetIndex)
         {
-            if (octets == 4)
+            int end = address.indexOf('.', pos);
+
+            if (!isParseableIPv4Octet(address, pos, end))
             {
                 return false;
             }
-            try
-            {
-                octet = Integer.parseInt(temp.substring(start, pos));
-            }
-            catch (NumberFormatException ex)
-            {
-                return false;
-            }
-            if (octet < 0 || octet > 255)
-            {
-                return false;
-            }
-            start = pos + 1;
-            octets++;
+
+            pos = end + 1;
         }
 
-        return octets == 4;
+        return isParseableIPv4Octet(address, pos, length);
     }
 
-    public static boolean isValidIPv4WithNetmask(
-        String address)
+    public static boolean isValidIPv4WithNetmask(String address)
     {
         int index = address.indexOf("/");
-        String mask = address.substring(index + 1);
-
-        return (index > 0) && isValidIPv4(address.substring(0, index))
-                           && (isValidIPv4(mask) || isMaskValue(mask, 32));
-    }
-
-    public static boolean isValidIPv6WithNetmask(
-        String address)
-    {
-        int index = address.indexOf("/");
-        String mask = address.substring(index + 1);
-
-        return (index > 0) && (isValidIPv6(address.substring(0, index))
-                           && (isValidIPv6(mask) || isMaskValue(mask, 128)));
-    }
-
-    private static boolean isMaskValue(String component, int size)
-    {
-        try
-        {
-            int value = Integer.parseInt(component);
-
-            return value >= 0 && value <= size;
-        }
-        catch (NumberFormatException e)
+        if (index < 1)
         {
             return false;
         }
+
+        String before = address.substring(0, index);
+        String after = address.substring(index + 1);
+
+        return isValidIPv4(before) && (isValidIPv4(after) || isParseableIPv4Mask(after));
     }
 
     /**
@@ -120,72 +81,131 @@
      *
      * @return true if a valid IPv6 address, false otherwise
      */
-    public static boolean isValidIPv6(
-        String address)
+    public static boolean isValidIPv6(String address)
     {
         if (address.length() == 0)
         {
             return false;
         }
 
-        int octet;
-        int octets = 0;
+        char firstChar = address.charAt(0);
+        if (firstChar != ':' && Character.digit(firstChar, 16) < 0)
+        {
+            return false;
+        }        
 
+        int segmentCount = 0;
         String temp = address + ":";
         boolean doubleColonFound = false;
-        int pos;
-        int start = 0;
-        while (start < temp.length()
-            && (pos = temp.indexOf(':', start)) >= start)
+
+        int pos = 0, end;
+        while (pos < temp.length() && (end = temp.indexOf(':', pos)) >= pos)
         {
-            if (octets == 8)
+            if (segmentCount == 8)
             {
                 return false;
             }
 
-            if (start != pos)
+            if (pos != end)
             {
-                String value = temp.substring(start, pos);
+                String value = temp.substring(pos, end);
 
-                if (pos == (temp.length() - 1) && value.indexOf('.') > 0)
+                if (end == temp.length() - 1 && value.indexOf('.') > 0)
                 {
+                    // add an extra one as address covers 2 words.
+                    if (++segmentCount == 8)
+                    {
+                        return false;
+                    }
                     if (!isValidIPv4(value))
                     {
                         return false;
                     }
-
-                    octets++; // add an extra one as address covers 2 words.
                 }
-                else
+                else if (!isParseableIPv6Segment(temp, pos, end))
                 {
-                    try
-                    {
-                        octet = Integer.parseInt(temp.substring(start, pos), 16);
-                    }
-                    catch (NumberFormatException ex)
-                    {
-                        return false;
-                    }
-                    if (octet < 0 || octet > 0xffff)
-                    {
-                        return false;
-                    }
+                    return false;
                 }
             }
             else
             {
-                if (pos != 1 && pos != temp.length() - 1 && doubleColonFound)
+                if (end != 1 && end != temp.length() - 1 && doubleColonFound)
                 {
                     return false;
                 }
                 doubleColonFound = true;
             }
-            start = pos + 1;
-            octets++;
+
+            pos = end + 1;
+            ++segmentCount;
         }
 
-        return octets == 8 || doubleColonFound;
+        return segmentCount == 8 || doubleColonFound;
+    }
+
+    public static boolean isValidIPv6WithNetmask(String address)
+    {
+        int index = address.indexOf("/");
+        if (index < 1)
+        {
+            return false;
+        }
+
+        String before = address.substring(0, index);
+        String after = address.substring(index + 1);
+
+        return isValidIPv6(before) && (isValidIPv6(after) || isParseableIPv6Mask(after));
+    }
+
+    private static boolean isParseableIPv4Mask(String s)
+    {
+        return isParseable(s, 0, s.length(), 10, 2, false, 0, 32);
+    }
+
+    private static boolean isParseableIPv4Octet(String s, int pos, int end)
+    {
+        return isParseable(s, pos, end, 10, 3, true, 0, 255);
+    }
+
+    private static boolean isParseableIPv6Mask(String s)
+    {
+        return isParseable(s, 0, s.length(), 10, 3, false, 1, 128);
+    }
+
+    private static boolean isParseableIPv6Segment(String s, int pos, int end)
+    {
+        return isParseable(s, pos, end, 16, 4, true, 0x0000, 0xFFFF);
+    }
+
+    private static boolean isParseable(String s, int pos, int end, int radix, int maxLength, boolean allowLeadingZero,
+        int minValue, int maxValue)
+    {
+        int length = end - pos;
+        if (length < 1 | length > maxLength)
+        {
+            return false;
+        }
+
+        boolean checkLeadingZero = length > 1 & !allowLeadingZero; 
+        if (checkLeadingZero && Character.digit(s.charAt(pos), radix) <= 0)
+        {
+            return false;
+        }
+
+        int value = 0;
+        while (pos < end)
+        {
+            char c = s.charAt(pos++);
+            int d = Character.digit(c, radix);
+            if (d < 0)
+            {
+                return false;
+            }
+
+            value *= radix;
+            value += d;
+        }
+
+        return value >= minValue & value <= maxValue;
     }
 }
-
-
diff --git a/bcprov/src/main/java/org/bouncycastle/util/Integers.java b/bcprov/src/main/java/org/bouncycastle/util/Integers.java
index fdacb51..9af524b 100644
--- a/bcprov/src/main/java/org/bouncycastle/util/Integers.java
+++ b/bcprov/src/main/java/org/bouncycastle/util/Integers.java
@@ -1,10 +1,28 @@
 package org.bouncycastle.util;
 
 /**
- * Utility methods for ints.
+ * Utility methods and constants for ints.
  */
 public class Integers
 {
+    public static final int BYTES = 4;
+    public static final int SIZE = Integer.SIZE;
+
+    public static int bitCount(int i)
+    {
+        return Integer.bitCount(i);
+    }
+
+    public static int highestOneBit(int i)
+    {
+        return Integer.highestOneBit(i);
+    }
+
+    public static int lowestOneBit(int i)
+    {
+        return Integer.lowestOneBit(i);
+    }
+
     public static int numberOfLeadingZeros(int i)
     {
         return Integer.numberOfLeadingZeros(i);
diff --git a/bcprov/src/main/java/org/bouncycastle/util/Longs.java b/bcprov/src/main/java/org/bouncycastle/util/Longs.java
index 2e50210..443e310 100644
--- a/bcprov/src/main/java/org/bouncycastle/util/Longs.java
+++ b/bcprov/src/main/java/org/bouncycastle/util/Longs.java
@@ -1,7 +1,33 @@
 package org.bouncycastle.util;
 
+/**
+ * Utility methods and constants for longs.
+ */
 public class Longs
 {
+    public static final int BYTES = 8;
+    public static final int SIZE = Long.SIZE;
+
+    public static long highestOneBit(long i)
+    {
+        return Long.highestOneBit(i);
+    }
+
+    public static long lowestOneBit(long i)
+    {
+        return Long.lowestOneBit(i);
+    }
+
+    public static int numberOfLeadingZeros(long i)
+    {
+        return Long.numberOfLeadingZeros(i);
+    }
+
+    public static int numberOfTrailingZeros(long i)
+    {
+        return Long.numberOfTrailingZeros(i);
+    }
+
     public static long reverse(long i)
     {
         return Long.reverse(i);
diff --git a/bcprov/src/main/java/org/bouncycastle/util/Memoable.java b/bcprov/src/main/java/org/bouncycastle/util/Memoable.java
index bf40f4e..a6eede9 100644
--- a/bcprov/src/main/java/org/bouncycastle/util/Memoable.java
+++ b/bcprov/src/main/java/org/bouncycastle/util/Memoable.java
@@ -2,7 +2,7 @@
 
 /**
  * Interface for Memoable objects. Memoable objects allow the taking of a snapshot of their internal state
- * via the copy() method and then reseting the object back to that state later using the reset() method.
+ * via the copy() method and then resetting the object back to that state later using the reset() method.
  */
 public interface Memoable
 {
diff --git a/bcprov/src/main/java/org/bouncycastle/util/Pack.java b/bcprov/src/main/java/org/bouncycastle/util/Pack.java
index d0eeef4..a2ce48f 100644
--- a/bcprov/src/main/java/org/bouncycastle/util/Pack.java
+++ b/bcprov/src/main/java/org/bouncycastle/util/Pack.java
@@ -147,7 +147,6 @@
      * @param bs    The target.
      * @param off   Position in target to start.
      * @param bytes number of bytes to write.
-     * 
      * @deprecated Will be removed
      */
     public static void longToBigEndian(long value, byte[] bs, int off, int bytes)
@@ -175,6 +174,25 @@
         return n;
     }
 
+    public static int littleEndianToInt_High(byte[] bs, int off, int len)
+    {
+        return littleEndianToInt_Low(bs, off, len) << ((4 - len) << 3);
+    }
+
+    public static int littleEndianToInt_Low(byte[] bs, int off, int len)
+    {
+//        assert 1 <= len && len <= 4;
+
+        int result = bs[off] & 0xff;
+        int pos = 0;
+        for (int i = 1; i < len; ++i)
+        {
+            pos += 8;
+            result |= (bs[off + i] & 0xff) << pos;
+        }
+        return result;
+    }
+
     public static void littleEndianToInt(byte[] bs, int off, int[] ns)
     {
         for (int i = 0; i < ns.length; ++i)
@@ -297,6 +315,40 @@
         }
     }
 
+    public static void longToLittleEndian_High(long n, byte[] bs, int off, int len)
+    {
+        //Debug.Assert(1 <= len && len <= 8);
+        int pos = 56;
+        bs[off] = (byte)(n >>> pos);
+        for (int i = 1; i < len; ++i)
+        {
+            pos -= 8;
+            bs[off + i] = (byte)(n >>> pos);
+        }
+    }
+
+//    public static void longToLittleEndian_Low(long n, byte[] bs, int off, int len)
+//    {
+//        longToLittleEndian_High(n << ((8 - len) << 3), bs, off, len);
+//    }
+
+    public static long littleEndianToLong_High(byte[] bs, int off, int len)
+    {
+        return littleEndianToLong_Low(bs, off, len) << ((8 - len) << 3);
+    }
+
+    public static long littleEndianToLong_Low(byte[] bs, int off, int len)
+    {
+        //Debug.Assert(1 <= len && len <= 8);
+        long result = bs[off] & 0xFF;
+        for (int i = 1; i < len; ++i)
+        {
+            result <<= 8;
+            result |= bs[off + i] & 0xFF;
+        }
+        return result;
+    }
+
     public static byte[] longToLittleEndian(long n)
     {
         byte[] bs = new byte[8];
diff --git a/bcprov/src/main/java/org/bouncycastle/util/Properties.java b/bcprov/src/main/java/org/bouncycastle/util/Properties.java
index b87d3ac..6630de9 100644
--- a/bcprov/src/main/java/org/bouncycastle/util/Properties.java
+++ b/bcprov/src/main/java/org/bouncycastle/util/Properties.java
@@ -13,10 +13,17 @@
 import java.util.StringTokenizer;
 
 /**
- * Utility method for accessing system properties.
+ * Utility method for accessing properties values - properties can be set in java.security,
+ * thread local, and system properties. They are checked for in the same order with
+ * checking stopped as soon as a value is found.
  */
 public class Properties
 {
+    /**
+     * If set the provider will attempt, where possible, to behave the same way as the oracle one.
+     */
+    public static final String EMULATE_ORACLE = "org.bouncycastle.emulate.oracle";
+
     private Properties()
     {
     }
@@ -115,6 +122,31 @@
         return false;
     }
 
+    /**
+     * Return propertyName as an integer, defaultValue used if not defined.
+     *
+     * @param propertyName name of property.
+     * @param defaultValue integer to return if property not defined.
+     * @return value of property, or default if not found, as an int.
+     */
+    public static int asInteger(String propertyName, int defaultValue)
+    {
+        String p = getPropertyValue(propertyName);
+
+        if (p != null)
+        {
+            return Integer.parseInt(p);
+        }
+
+        return defaultValue;
+    }
+
+    /**
+     * Return propertyName as a BigInteger.
+     *
+     * @param propertyName name of property.
+     * @return value of property as a BigInteger, null if not defined.
+     */
     public static BigInteger asBigInteger(String propertyName)
     {
         String p = getPropertyValue(propertyName);
@@ -145,6 +177,13 @@
         return Collections.unmodifiableSet(set);
     }
 
+    /**
+     * Return the String value of the property propertyName. Property valuation
+     * starts with java.security, then thread local, then system properties.
+     *
+     * @param propertyName name of property.
+     * @return value of property as a String, null if not defined.
+     */
     public static String getPropertyValue(final String propertyName)
     {
         String val = (String)AccessController.doPrivileged(new PrivilegedAction()
@@ -178,6 +217,18 @@
         });
     }
 
+    public static String getPropertyValue(final String propertyName,  String defValue)
+    {
+        String rv = getPropertyValue(propertyName);
+
+        if (rv == null)
+        {
+            return defValue;
+        }
+
+        return rv;
+    }
+
     private static boolean isSetFalse(String p)
     {
         if (p == null || p.length() != 5)
diff --git a/bcprov/src/main/java/org/bouncycastle/util/Strings.java b/bcprov/src/main/java/org/bouncycastle/util/Strings.java
index bda9d9a..a568149 100644
--- a/bcprov/src/main/java/org/bouncycastle/util/Strings.java
+++ b/bcprov/src/main/java/org/bouncycastle/util/Strings.java
@@ -56,6 +56,17 @@
         return new String(chars, 0, len);
     }
 
+    public static String fromUTF8ByteArray(byte[] bytes, int off, int length)
+    {
+        char[] chars = new char[length];
+        int len = UTF8.transcodeToUTF16(bytes, off, length, chars);
+        if (len < 0)
+        {
+            throw new IllegalArgumentException("Invalid UTF-8 input");
+        }
+        return new String(chars, 0, len);
+    }
+
     public static byte[] toUTF8ByteArray(String string)
     {
         return toUTF8ByteArray(string.toCharArray());
@@ -228,6 +239,37 @@
     }
 
     /**
+     * Constant time string comparison.
+     *
+     * @param a a string.
+     * @param b another string to compare to a.
+     *
+     * @return true if a and b represent the same string, false otherwise.
+     */
+    public static boolean constantTimeAreEqual(String a, String b)
+    {
+        boolean isEqual = a.length() == b.length();
+        int     len = a.length();
+
+        if (isEqual)
+        {
+            for (int i = 0; i != len; i++)
+            {
+                isEqual &= (a.charAt(i) == b.charAt(i));
+            }
+        }
+        else
+        {
+            for (int i = 0; i != len; i++)
+            {
+                isEqual &= (a.charAt(i) == ' ');
+            }
+        }
+
+        return isEqual;
+    }
+
+    /**
      * Convert an array of 8 bit characters into a string.
      *
      * @param bytes 8 bit characters.
diff --git a/bcprov/src/main/java/org/bouncycastle/util/encoders/Base32.java b/bcprov/src/main/java/org/bouncycastle/util/encoders/Base32.java
new file mode 100644
index 0000000..a64502a
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/util/encoders/Base32.java
@@ -0,0 +1,174 @@
+package org.bouncycastle.util.encoders;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+import org.bouncycastle.util.Strings;
+
+/**
+ * Utility class for converting Base32 data to bytes and back again.
+ */
+public class Base32
+{
+    private static final Encoder encoder = new Base32Encoder();
+
+    public static String toBase32String(
+        byte[] data)
+    {
+        return toBase32String(data, 0, data.length);
+    }
+
+    public static String toBase32String(
+        byte[] data,
+        int off,
+        int length)
+    {
+        byte[] encoded = encode(data, off, length);
+        return Strings.fromByteArray(encoded);
+    }
+
+    /**
+     * encode the input data producing a base 32 encoded byte array.
+     *
+     * @return a byte array containing the base 32 encoded data.
+     */
+    public static byte[] encode(
+        byte[] data)
+    {
+        return encode(data, 0, data.length);
+    }
+
+    /**
+     * encode the input data producing a base 32 encoded byte array.
+     *
+     * @return a byte array containing the base 32 encoded data.
+     */
+    public static byte[] encode(
+        byte[] data,
+        int off,
+        int length)
+    {
+        int len = encoder.getEncodedLength(length);
+        ByteArrayOutputStream bOut = new ByteArrayOutputStream(len);
+
+        try
+        {
+            encoder.encode(data, off, length, bOut);
+        }
+        catch (Exception e)
+        {
+            throw new EncoderException("exception encoding base32 string: " + e.getMessage(), e);
+        }
+
+        return bOut.toByteArray();
+    }
+
+    /**
+     * Encode the byte data to base 32 writing it to the given output stream.
+     *
+     * @return the number of bytes produced.
+     */
+    public static int encode(
+        byte[] data,
+        OutputStream out)
+        throws IOException
+    {
+        return encoder.encode(data, 0, data.length, out);
+    }
+
+    /**
+     * Encode the byte data to base 32 writing it to the given output stream.
+     *
+     * @return the number of bytes produced.
+     */
+    public static int encode(
+        byte[] data,
+        int off,
+        int length,
+        OutputStream out)
+        throws IOException
+    {
+        return encoder.encode(data, off, length, out);
+    }
+
+    /**
+     * decode the base 32 encoded input data. It is assumed the input data is valid.
+     *
+     * @return a byte array representing the decoded data.
+     */
+    public static byte[] decode(
+        byte[] data)
+    {
+        int len = data.length / 8 * 5;
+        ByteArrayOutputStream bOut = new ByteArrayOutputStream(len);
+
+        try
+        {
+            encoder.decode(data, 0, data.length, bOut);
+        }
+        catch (Exception e)
+        {
+            throw new DecoderException("unable to decode base32 data: " + e.getMessage(), e);
+        }
+
+        return bOut.toByteArray();
+    }
+
+    /**
+     * decode the base 32 encoded String data - whitespace will be ignored.
+     *
+     * @return a byte array representing the decoded data.
+     */
+    public static byte[] decode(
+        String data)
+    {
+        int len = data.length() / 8 * 5;
+        ByteArrayOutputStream bOut = new ByteArrayOutputStream(len);
+
+        try
+        {
+            encoder.decode(data, bOut);
+        }
+        catch (Exception e)
+        {
+            throw new DecoderException("unable to decode base32 string: " + e.getMessage(), e);
+        }
+
+        return bOut.toByteArray();
+    }
+
+    /**
+     * decode the base 32 encoded String data writing it to the given output stream,
+     * whitespace characters will be ignored.
+     *
+     * @return the number of bytes produced.
+     */
+    public static int decode(
+        String data,
+        OutputStream out)
+        throws IOException
+    {
+        return encoder.decode(data, out);
+    }
+
+    /**
+     * Decode to an output stream;
+     *
+     * @param base32Data       The source data.
+     * @param start            Start position.
+     * @param length           the length.
+     * @param out The output stream to write to.
+     */
+    public static int decode(byte[] base32Data, int start, int length, OutputStream out)
+    {
+        try
+        {
+           return encoder.decode(base32Data, start, length, out);
+        }
+        catch (Exception e)
+        {
+            throw new DecoderException("unable to decode base32 data: " + e.getMessage(), e);
+        }
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/util/encoders/Base32Encoder.java b/bcprov/src/main/java/org/bouncycastle/util/encoders/Base32Encoder.java
new file mode 100644
index 0000000..d17ed98
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/util/encoders/Base32Encoder.java
@@ -0,0 +1,453 @@
+package org.bouncycastle.util.encoders;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.Strings;
+
+/**
+ * A streaming Base32 encoder.
+ */
+public class Base32Encoder
+    implements Encoder
+{
+    private static final byte[] DEAULT_ENCODING_TABLE =
+    {
+        (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F', (byte)'G',
+        (byte)'H', (byte)'I', (byte)'J', (byte)'K', (byte)'L', (byte)'M', (byte)'N',
+        (byte)'O', (byte)'P', (byte)'Q', (byte)'R', (byte)'S', (byte)'T', (byte)'U',
+        (byte)'V', (byte)'W', (byte)'X', (byte)'Y', (byte)'Z',
+        (byte)'2', (byte)'3', (byte)'4', (byte)'5', (byte)'6', (byte)'7'
+    };
+
+    private static final byte DEFAULT_PADDING = (byte)'=';
+
+    /*
+     * set up the decoding table.
+     */
+    private final byte[] encodingTable;
+    private final byte   padding;
+    private final byte[] decodingTable = new byte[128];
+
+    protected void initialiseDecodingTable()
+    {
+        for (int i = 0; i < decodingTable.length; i++)
+        {
+            decodingTable[i] = (byte)0xff;
+        }
+
+        for (int i = 0; i < encodingTable.length; i++)
+        {
+            decodingTable[encodingTable[i]] = (byte)i;
+        }
+    }
+
+    /**
+     * Base constructor for RFC 4648, Section 6.
+     */
+    public Base32Encoder()
+    {
+        this.encodingTable = DEAULT_ENCODING_TABLE;
+        this.padding = DEFAULT_PADDING;
+
+        initialiseDecodingTable();
+    }
+
+    /**
+     * Constructor allowing the setting of an alternative alphabet.
+     *
+     * @param encodingTable a 32 entry encoding table to do the mapping.
+     * @param padding the padding value to use.
+     */
+    public Base32Encoder(byte[] encodingTable, byte padding)
+    {
+        if (encodingTable.length != 32)
+        {
+            throw new IllegalArgumentException("encoding table needs to be length 32");
+        }
+
+        this.encodingTable = Arrays.clone(encodingTable);
+        this.padding = padding;
+        
+        initialiseDecodingTable();
+    }
+
+    public int encode(byte[] inBuf, int inOff, int inLen, byte[] outBuf, int outOff) throws IOException
+    {
+        int inPos = inOff;
+        int inEnd = inOff + inLen - 4;
+        int outPos = outOff;
+
+        while (inPos < inEnd)
+        {
+             encodeBlock(inBuf, inPos, outBuf, outPos);
+             inPos += 5;
+             outPos += 8;
+        }
+
+        int extra = inLen - (inPos - inOff);
+        if (extra > 0)
+        {
+            byte[] in = new byte[5];
+            System.arraycopy(inBuf, inPos, in, 0, extra);
+            encodeBlock(in, 0, outBuf, outPos);
+            switch (extra)
+            {
+            case 1:
+                outBuf[outPos + 2] = padding;
+                outBuf[outPos + 3] = padding;
+                outBuf[outPos + 4] = padding;
+                outBuf[outPos + 5] = padding;
+                outBuf[outPos + 6] = padding;
+                outBuf[outPos + 7] = padding;
+                break;
+            case 2:
+                outBuf[outPos + 4] = padding;
+                outBuf[outPos + 5] = padding;
+                outBuf[outPos + 6] = padding;
+                outBuf[outPos + 7] = padding;
+                break;
+            case 3:
+                outBuf[outPos + 5] = padding;
+                outBuf[outPos + 6] = padding;
+                outBuf[outPos + 7] = padding;
+                break;
+            case 4:
+                outBuf[outPos + 7] = padding;
+                break;
+            }
+
+            outPos += 8;
+        }
+
+        return outPos - outOff;
+    }
+
+    private void encodeBlock(byte[] inBuf, int inPos, byte[] outBuf, int outPos)
+    {
+        int a1 = inBuf[inPos++];
+        int a2 = inBuf[inPos++] & 0xFF;
+        int a3 = inBuf[inPos++] & 0xFF;
+        int a4 = inBuf[inPos++] & 0xFF;
+        int a5 = inBuf[inPos] & 0xFF;
+
+        outBuf[outPos++] = encodingTable[(a1 >>> 3) & 0x1F];
+        outBuf[outPos++] = encodingTable[((a1 << 2) | (a2 >>> 6)) & 0x1F];
+        outBuf[outPos++] = encodingTable[(a2 >>> 1) & 0x1F];
+        outBuf[outPos++] = encodingTable[((a2 << 4) | (a3 >>> 4)) & 0x1F];
+        outBuf[outPos++] = encodingTable[((a3 << 1) | (a4 >>> 7)) & 0x1F];
+        outBuf[outPos++] = encodingTable[(a4 >>> 2) & 0x1F];
+        outBuf[outPos++] = encodingTable[((a4 << 3) | (a5 >>> 5)) & 0x1F];
+        outBuf[outPos] = encodingTable[a5 & 0x1F];
+    }
+
+    public int getEncodedLength(int inputLength)
+    {
+        return (inputLength + 4) / 5 * 8;
+    }
+
+    public int getMaxDecodedLength(int inputLength)
+    {
+        return inputLength / 8 * 5;
+    }
+
+    /**
+     * encode the input data producing a base 32 output stream.
+     *
+     * @return the number of bytes produced.
+     */
+    public int encode(byte[] buf, int off, int len, OutputStream out) 
+        throws IOException
+    {
+        if (len < 0)
+        {
+            return 0;
+        }
+
+        byte[] tmp = new byte[72];
+        int remaining = len;
+        while (remaining > 0)
+        {
+            int inLen = Math.min(45, remaining);
+            int outLen = encode(buf, off, inLen, tmp, 0);
+            out.write(tmp, 0, outLen);
+            off += inLen;
+            remaining -= inLen;
+        }
+        return (len + 2) / 3 * 4;
+    }
+
+    private boolean ignore(
+        char    c)
+    {
+        return (c == '\n' || c =='\r' || c == '\t' || c == ' ');
+    }
+    
+    /**
+     * decode the base 32 encoded byte data writing it to the given output stream,
+     * whitespace characters will be ignored.
+     *
+     * @return the number of bytes produced.
+     */
+    public int decode(
+        byte[]          data,
+        int             off,
+        int             length,
+        OutputStream    out)
+        throws IOException
+    {
+        byte    b1, b2, b3, b4, b5, b6, b7, b8;
+        byte[]  outBuffer = new byte[55];
+        int     bufOff = 0;
+        int     outLen = 0;
+        
+        int     end = off + length;
+        
+        while (end > off)
+        {
+            if (!ignore((char)data[end - 1]))
+            {
+                break;
+            }
+            
+            end--;
+        }
+
+        // empty data!
+        if (end == 0)
+        {
+            return 0;
+        }
+        
+        int  i = 0;
+        int  finish = end;
+
+        while (finish > off && i != 8)
+        {
+            if (!ignore((char)data[finish - 1]))
+            {
+                i++;
+            }
+
+            finish--;
+        }
+
+        i = nextI(data, off, finish);
+
+        while (i < finish)
+        {
+            b1 = decodingTable[data[i++]];
+            
+            i = nextI(data, i, finish);
+            
+            b2 = decodingTable[data[i++]];
+            
+            i = nextI(data, i, finish);
+            
+            b3 = decodingTable[data[i++]];
+            
+            i = nextI(data, i, finish);
+            
+            b4 = decodingTable[data[i++]];
+
+            i = nextI(data, i, finish);
+
+            b5 = decodingTable[data[i++]];
+
+            i = nextI(data, i, finish);
+
+            b6 = decodingTable[data[i++]];
+
+            i = nextI(data, i, finish);
+
+            b7 = decodingTable[data[i++]];
+
+            i = nextI(data, i, finish);
+
+            b8 = decodingTable[data[i++]];
+
+            if ((b1 | b2 | b3 | b4 | b5 | b6 | b7 | b8) < 0)
+            {
+                throw new IOException("invalid characters encountered in base32 data");
+            }
+
+            outBuffer[bufOff++] = (byte)((b1 << 3) | (b2 >> 2));
+            outBuffer[bufOff++] = (byte)((b2 << 6) | (b3 << 1) | (b4 >> 4));
+            outBuffer[bufOff++] = (byte)((b4 << 4) | (b5 >> 1));
+            outBuffer[bufOff++] = (byte)((b5 << 7) | (b6 << 2) | (b7 >> 3));
+            outBuffer[bufOff++] = (byte)((b7 << 5) | b8);
+
+            if (bufOff == outBuffer.length)
+            {
+                out.write(outBuffer);
+                bufOff = 0;
+            }
+            
+            outLen += 5;
+            
+            i = nextI(data, i, finish);
+        }
+
+        if (bufOff > 0)
+        {
+            out.write(outBuffer, 0, bufOff);
+        }
+
+        int e0 = nextI(data, i, end);
+        int e1 = nextI(data, e0 + 1, end);
+        int e2 = nextI(data, e1 + 1, end);
+        int e3 = nextI(data, e2 + 1, end);
+        int e4 = nextI(data, e3 + 1, end);
+        int e5 = nextI(data, e4 + 1, end);
+        int e6 = nextI(data, e5 + 1, end);
+        int e7 = nextI(data, e6 + 1, end);
+
+        outLen += decodeLastBlock(out,
+            (char)data[e0], (char)data[e1], (char)data[e2], (char)data[e3],
+            (char)data[e4], (char)data[e5], (char)data[e6], (char)data[e7]);
+
+        return outLen;
+    }
+
+    private int nextI(byte[] data, int i, int finish)
+    {
+        while ((i < finish) && ignore((char)data[i]))
+        {
+            i++;
+        }
+        return i;
+    }
+    
+    /**
+     * decode the base 32 encoded String data writing it to the given output stream,
+     * whitespace characters will be ignored.
+     *
+     * @return the number of bytes produced.
+     */
+    public int decode(
+        String          data,
+        OutputStream    out)
+        throws IOException
+    {
+        byte[] bytes = Strings.toByteArray(data);
+        return decode(bytes, 0, bytes.length, out);
+    }
+
+    private int decodeLastBlock(OutputStream out,
+                                char c1, char c2, char c3, char c4,
+                                char c5, char c6, char c7, char c8)
+        throws IOException
+    {
+        byte    b1, b2, b3, b4, b5, b6, b7, b8;
+        
+        if (c8 == padding)
+        {
+            if (c7 != padding)
+            {
+                b1 = decodingTable[c1];
+                b2 = decodingTable[c2];
+                b3 = decodingTable[c3];
+                b4 = decodingTable[c4];
+                b5 = decodingTable[c5];
+                b6 = decodingTable[c6];
+                b7 = decodingTable[c7];
+
+                if ((b1 | b2 | b3 | b4 | b5 | b6 | b7) < 0)
+                {
+                    throw new IOException("invalid characters encountered at end of base32 data");
+                }
+
+                out.write((b1 << 3) | (b2 >> 2));
+                out.write((b2 << 6) | (b3 << 1) | (b4 >> 4));
+                out.write((b4 << 4) | (b5 >> 1));
+                out.write((b5 << 7) | (b6 << 2) | (b7 >> 3));
+
+                return 4;
+            }
+            if (c6 != padding)
+            {
+                throw new IOException("invalid characters encountered at end of base32 data");
+            }
+
+            if (c5 != padding)
+            {
+                b1 = decodingTable[c1];
+                b2 = decodingTable[c2];
+                b3 = decodingTable[c3];
+                b4 = decodingTable[c4];
+                b5 = decodingTable[c5];
+
+                if ((b1 | b2 | b3 | b4 | b5) < 0)
+                {
+                    throw new IOException("invalid characters encountered at end of base32 data");
+                }
+
+                out.write((b1 << 3) | (b2 >> 2));
+                out.write((b2 << 6) | (b3 << 1) | (b4 >> 4));
+                out.write((b4 << 4) | (b5 >> 1));
+
+                return 3;
+            }
+
+            if (c4 != padding)
+            {
+                b1 = decodingTable[c1];
+                b2 = decodingTable[c2];
+                b3 = decodingTable[c3];
+                b4 = decodingTable[c4];
+
+                if ((b1 | b2 | b3 | b4) < 0)
+                {
+                    throw new IOException("invalid characters encountered at end of base32 data");
+                }
+
+                out.write((b1 << 3) | (b2 >> 2));
+                out.write((b2 << 6) | (b3 << 1) | (b4 >> 4));
+
+                return 2;
+            }
+            
+            if (c3 != padding)
+            {
+                throw new IOException("invalid characters encountered at end of base32 data");
+            }
+
+            b1 = decodingTable[c1];
+            b2 = decodingTable[c2];
+
+            if ((b1 | b2) < 0)
+            {
+                throw new IOException("invalid characters encountered at end of base32 data");
+            }
+
+            out.write((b1 << 3) | (b2 >> 2));
+            
+            return 1;
+        }
+        else
+        {
+            b1 = decodingTable[c1];
+            b2 = decodingTable[c2];
+            b3 = decodingTable[c3];
+            b4 = decodingTable[c4];
+            b5 = decodingTable[c5];
+            b6 = decodingTable[c6];
+            b7 = decodingTable[c7];
+            b8 = decodingTable[c8];
+
+            if ((b1 | b2 | b3 | b4 | b5 | b6 | b7 | b8) < 0)
+            {
+                throw new IOException("invalid characters encountered at end of base32 data");
+            }
+            
+            out.write((b1 << 3) | (b2 >> 2));
+            out.write((b2 << 6) | (b3 << 1) | (b4 >> 4));
+            out.write((b4 << 4) | (b5 >> 1));
+            out.write((b5 << 7) | (b6 << 2) | (b7 >> 3));
+            out.write((b7 << 5) | b8);
+            
+            return 5;
+        } 
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/util/encoders/Base64.java b/bcprov/src/main/java/org/bouncycastle/util/encoders/Base64.java
index e3129f0..72e0a71 100644
--- a/bcprov/src/main/java/org/bouncycastle/util/encoders/Base64.java
+++ b/bcprov/src/main/java/org/bouncycastle/util/encoders/Base64.java
@@ -49,7 +49,7 @@
         int off,
         int length)
     {
-        int len = (length + 2) / 3 * 4;
+        int len = encoder.getEncodedLength(length);
         ByteArrayOutputStream bOut = new ByteArrayOutputStream(len);
 
         try
diff --git a/bcprov/src/main/java/org/bouncycastle/util/encoders/Base64Encoder.java b/bcprov/src/main/java/org/bouncycastle/util/encoders/Base64Encoder.java
index 1af4860..82c2343 100644
--- a/bcprov/src/main/java/org/bouncycastle/util/encoders/Base64Encoder.java
+++ b/bcprov/src/main/java/org/bouncycastle/util/encoders/Base64Encoder.java
@@ -96,24 +96,40 @@
         return outPos - outOff;
     }
 
+    public int getEncodedLength(int inputLength)
+    {
+        return (inputLength + 2) / 3 * 4;
+    }
+
+    public int getMaxDecodedLength(int inputLength)
+    {
+        return inputLength / 4 * 3;
+    }
+
     /**
      * encode the input data producing a base 64 output stream.
      *
      * @return the number of bytes produced.
      */
-    public int encode(byte[] buf, int off, int len, OutputStream out) 
+    public int encode(byte[] buf, int off, int len, OutputStream out)
         throws IOException
     {
-        byte[] tmp = new byte[72];
-        while (len > 0)
+        if (len < 0)
         {
-            int inLen = Math.min(54, len);
+            return 0;
+        }
+
+        byte[] tmp = new byte[72];
+        int remaining = len;
+        while (remaining > 0)
+        {
+            int inLen = Math.min(54, remaining);
             int outLen = encode(buf, off, inLen, tmp, 0);
             out.write(tmp, 0, outLen);
             off += inLen;
-            len -= inLen;
+            remaining -= inLen;
         }
-        return ((len + 2) / 3) * 4;
+        return (len + 2) / 3 * 4;
     }
 
     private boolean ignore(
diff --git a/bcprov/src/main/java/org/bouncycastle/util/encoders/Encoder.java b/bcprov/src/main/java/org/bouncycastle/util/encoders/Encoder.java
index b066121..bb63b61 100644
--- a/bcprov/src/main/java/org/bouncycastle/util/encoders/Encoder.java
+++ b/bcprov/src/main/java/org/bouncycastle/util/encoders/Encoder.java
@@ -9,6 +9,23 @@
  */
 public interface Encoder
 {
+    /**
+     * Return the expected output length of the encoding.
+     *
+     * @param inputLength the input length of the data.
+     * @return the output length of an encoding.
+     */
+    int getEncodedLength(int inputLength);
+
+    /**
+     * Return the maximum expected output length of a decoding. If padding
+     * is present the value returned will be greater than the decoded data length.
+     *
+     * @param inputLength the input length of the encoded data.
+     * @return the upper bound of the output length of a decoding.
+     */
+    int getMaxDecodedLength(int inputLength);
+
     int encode(byte[] data, int off, int length, OutputStream out) throws IOException;
     
     int decode(byte[] data, int off, int length, OutputStream out) throws IOException;
diff --git a/bcprov/src/main/java/org/bouncycastle/util/encoders/HexEncoder.java b/bcprov/src/main/java/org/bouncycastle/util/encoders/HexEncoder.java
index 6bdafaf..8823189 100644
--- a/bcprov/src/main/java/org/bouncycastle/util/encoders/HexEncoder.java
+++ b/bcprov/src/main/java/org/bouncycastle/util/encoders/HexEncoder.java
@@ -62,6 +62,16 @@
         return outPos - outOff;
     }
 
+    public int getEncodedLength(int inputLength)
+    {
+        return inputLength * 2;
+    }
+
+    public int getMaxDecodedLength(int inputLength)
+    {
+        return inputLength / 2;
+    }
+
     /**
      * encode the input data producing a Hex output stream.
      *
@@ -70,14 +80,20 @@
     public int encode(byte[] buf, int off, int len, OutputStream out) 
         throws IOException
     {
-        byte[] tmp = new byte[72];
-        while (len > 0)
+        if (len < 0)
         {
-            int inLen = Math.min(36, len);
+            return 0;
+        }
+
+        byte[] tmp = new byte[72];
+        int remaining = len;
+        while (remaining > 0)
+        {
+            int inLen = Math.min(36, remaining);
             int outLen = encode(buf, off, inLen, tmp, 0);
             out.write(tmp, 0, outLen);
             off += inLen;
-            len -= inLen;
+            remaining -= inLen;
         }
         return len * 2;
     }
diff --git a/bcprov/src/main/java/org/bouncycastle/util/encoders/UTF8.java b/bcprov/src/main/java/org/bouncycastle/util/encoders/UTF8.java
index e64e443..cd9073d 100644
--- a/bcprov/src/main/java/org/bouncycastle/util/encoders/UTF8.java
+++ b/bcprov/src/main/java/org/bouncycastle/util/encoders/UTF8.java
@@ -2,7 +2,7 @@
 
 /**
  * Utilities for working with UTF-8 encodings.
- * 
+ * <p>
  * Decoding of UTF-8 is based on a presentation by Bob Steagall at CppCon2018 (see
  * https://github.com/BobSteagall/CppCon2018). It uses a Deterministic Finite Automaton (DFA) to
  * recognize and decode multi-byte code points.
@@ -71,8 +71,8 @@
         fill(transitionTable, S_P4A + 0x9, S_P4A + 0xB, S_CS2);
         fill(transitionTable, S_P4B + 0x8, S_P4B + 0x8, S_CS2);
 
-        byte[] firstUnitMasks = { 0x00, 0x00, 0x00, 0x00, 0x1F, 0x0F, 0x0F, 0x0F, 0x07, 0x07, 0x07 };
-        byte[] firstUnitTransitions = { S_ERR, S_ERR, S_ERR, S_ERR, S_CS1, S_P3A, S_CS2, S_P3B, S_P4A, S_CS3, S_P4B };
+        byte[] firstUnitMasks = {0x00, 0x00, 0x00, 0x00, 0x1F, 0x0F, 0x0F, 0x0F, 0x07, 0x07, 0x07};
+        byte[] firstUnitTransitions = {S_ERR, S_ERR, S_ERR, S_ERR, S_CS1, S_P3A, S_CS2, S_P3B, S_P4A, S_CS3, S_P4B};
 
         for (int i = 0x00; i < 0x80; ++i)
         {
@@ -94,26 +94,52 @@
      * "overlong" encodings, and unmappable code points. In particular, no unmatched surrogates will
      * be produced. An error will also result if {@code utf16} is found to be too small to store the
      * complete output.
-     * 
-     * @param utf8
-     *            A non-null array containing a well-formed UTF-8 encoding.
-     * @param utf16
-     *            A non-null array, at least as long as the {@code utf8} array in order to ensure
-     *            the output will fit.
+     *
+     * @param utf8  A non-null array containing a well-formed UTF-8 encoding.
+     * @param utf16 A non-null array, at least as long as the {@code utf8} array in order to ensure
+     *              the output will fit.
      * @return The number of UTF-16 code units written to {@code utf16} (beginning from index 0), or
-     *         else -1 if the input was either malformed or encoded any unmappable characters, or if
-     *         the {@code utf16} is too small.
+     * else -1 if the input was either malformed or encoded any unmappable characters, or if
+     * the {@code utf16} is too small.
      */
     public static int transcodeToUTF16(byte[] utf8, char[] utf16)
     {
-        int i = 0, j = 0;
+        return transcodeToUTF16(utf8, 0, utf8.length, utf16);
+    }
 
-        while (i < utf8.length)
+    /**
+     * Transcode a UTF-8 encoding into a UTF-16 representation. In the general case the output
+     * {@code utf16} array should be at least as long as the input length from {@code utf8} to handle
+     * arbitrary inputs. The number of output UTF-16 code units is returned, or -1 if any errors are
+     * encountered (in which case an arbitrary amount of data may have been written into the output
+     * array). Errors that will be detected are malformed UTF-8, including incomplete, truncated or
+     * "overlong" encodings, and unmappable code points. In particular, no unmatched surrogates will
+     * be produced. An error will also result if {@code utf16} is found to be too small to store the
+     * complete output.
+     *
+     * @param utf8  A non-null array containing a well-formed UTF-8 encoding.
+     * @param utf8Off start position in the array for the well-formed encoding.
+     * @param utf8Length length in bytes of the well-formed encoding.
+     * @param utf16 A non-null array, at least as long as the {@code utf8} array in order to ensure
+     *              the output will fit.
+     * @return The number of UTF-16 code units written to {@code utf16} (beginning from index 0), or
+     * else -1 if the input was either malformed or encoded any unmappable characters, or if
+     * the {@code utf16} is too small.
+     */
+    public static int transcodeToUTF16(byte[] utf8, int utf8Off, int utf8Length, char[] utf16)
+    {
+        int i = utf8Off, j = 0;
+        int maxI = utf8Off + utf8Length;
+
+        while (i < maxI)
         {
             byte codeUnit = utf8[i++];
             if (codeUnit >= 0)
             {
-                if (j >= utf16.length) { return -1; }
+                if (j >= utf16.length)
+                {
+                    return -1;
+                }
 
                 utf16[j++] = (char)codeUnit;
                 continue;
@@ -125,25 +151,37 @@
 
             while (state >= 0)
             {
-                if (i >= utf8.length) { return -1; }
+                if (i >= maxI)
+                {
+                    return -1;
+                }
 
                 codeUnit = utf8[i++];
                 codePoint = (codePoint << 6) | (codeUnit & 0x3F);
                 state = transitionTable[state + ((codeUnit & 0xFF) >>> 4)];
             }
 
-            if (state == S_ERR) { return -1; }
+            if (state == S_ERR)
+            {
+                return -1;
+            }
 
             if (codePoint <= 0xFFFF)
             {
-                if (j >= utf16.length) { return -1; }
+                if (j >= utf16.length)
+                {
+                    return -1;
+                }
 
                 // Code points from U+D800 to U+DFFF are caught by the DFA
                 utf16[j++] = (char)codePoint;
             }
             else
             {
-                if (j >= utf16.length - 1) { return -1; }
+                if (j >= utf16.length - 1)
+                {
+                    return -1;
+                }
 
                 // Code points above U+10FFFF are caught by the DFA
                 utf16[j++] = (char)(0xD7C0 + (codePoint >>> 10));
diff --git a/bcprov/src/main/java/org/bouncycastle/util/io/Streams.java b/bcprov/src/main/java/org/bouncycastle/util/io/Streams.java
index adb158f..c72a476 100644
--- a/bcprov/src/main/java/org/bouncycastle/util/io/Streams.java
+++ b/bcprov/src/main/java/org/bouncycastle/util/io/Streams.java
@@ -28,6 +28,64 @@
     }
 
     /**
+     * Write the full contents of inStr to the destination stream outStr.
+     *
+     * @param inStr source input stream.
+     * @param outStr destination output stream.
+     * @throws IOException in case of underlying IOException.
+     */
+    public static void pipeAll(InputStream inStr, OutputStream outStr)
+        throws IOException
+    {
+        pipeAll(inStr, outStr, BUFFER_SIZE);
+    }
+
+    /**
+     * Write the full contents of inStr to the destination stream outStr.
+     *
+     * @param inStr source input stream.
+     * @param outStr destination output stream.
+     * @param bufferSize the size of temporary buffer to use.
+     * @throws IOException in case of underlying IOException.
+     */
+    public static void pipeAll(InputStream inStr, OutputStream outStr, int bufferSize)
+        throws IOException
+    {
+        byte[] bs = new byte[bufferSize];
+        int numRead;
+        while ((numRead = inStr.read(bs, 0, bs.length)) >= 0)
+        {
+            outStr.write(bs, 0, numRead);
+        }
+    }
+
+    /**
+     * Write up to limit bytes of data from inStr to the destination stream outStr.
+     *
+     * @param inStr source input stream.
+     * @param limit the maximum number of bytes allowed to be read.
+     * @param outStr destination output stream.
+     * @throws IOException in case of underlying IOException, or if limit is reached on inStr still has data in it.
+     */
+    public static long pipeAllLimited(InputStream inStr, long limit, OutputStream outStr)
+        throws IOException
+    {
+        long total = 0;
+        byte[] bs = new byte[BUFFER_SIZE];
+        int numRead;
+        while ((numRead = inStr.read(bs, 0, bs.length)) >= 0)
+        {
+            if ((limit - total) < numRead)
+            {
+                throw new StreamOverflowException("Data Overflow");
+            }
+            total += numRead;
+            outStr.write(bs, 0, numRead);
+        }
+        return total;
+    }
+
+    /**
      * Read stream fully, returning contents in a byte array.
      *
      * @param inStr stream to be read.
@@ -96,51 +154,22 @@
             }
             totalRead += numRead;
         }
+        
         return totalRead;
     }
 
-    /**
-     * Write the full contents of inStr to the destination stream outStr.
-     *
-     * @param inStr source input stream.
-     * @param outStr destination output stream.
-     * @throws IOException in case of underlying IOException.
-     */
-    public static void pipeAll(InputStream inStr, OutputStream outStr)
-        throws IOException
+    public static void validateBufferArguments(byte[] buf, int off, int len)
     {
-        byte[] bs = new byte[BUFFER_SIZE];
-        int numRead;
-        while ((numRead = inStr.read(bs, 0, bs.length)) >= 0)
+        if (buf == null)
         {
-            outStr.write(bs, 0, numRead);
+            throw new NullPointerException();
         }
-    }
-
-    /**
-     * Write up to limit bytes of data from inStr to the destination stream outStr.
-     *
-     * @param inStr source input stream.
-     * @param limit the maximum number of bytes allowed to be read.
-     * @param outStr destination output stream.
-     * @throws IOException in case of underlying IOException, or if limit is reached on inStr still has data in it.
-     */
-    public static long pipeAllLimited(InputStream inStr, long limit, OutputStream outStr)
-        throws IOException
-    {
-        long total = 0;
-        byte[] bs = new byte[BUFFER_SIZE];
-        int numRead;
-        while ((numRead = inStr.read(bs, 0, bs.length)) >= 0)
+        int available = buf.length - off;
+        int remaining = available - len;
+        if ((off | len | available | remaining) < 0)
         {
-            if ((limit - total) < numRead)
-            {
-                throw new StreamOverflowException("Data Overflow");
-            }
-            total += numRead;
-            outStr.write(bs, 0, numRead);
+            throw new IndexOutOfBoundsException();
         }
-        return total;
     }
 
     public static void writeBufTo(ByteArrayOutputStream buf, OutputStream output)
diff --git a/bcprov/src/main/java/org/bouncycastle/util/io/pem/PemReader.java b/bcprov/src/main/java/org/bouncycastle/util/io/pem/PemReader.java
index 6616fec..7156818 100644
--- a/bcprov/src/main/java/org/bouncycastle/util/io/pem/PemReader.java
+++ b/bcprov/src/main/java/org/bouncycastle/util/io/pem/PemReader.java
@@ -64,9 +64,9 @@
 
         while ((line = readLine()) != null)
         {
-            if (line.indexOf(":") >= 0)
+            int index = line.indexOf(':');
+            if (index >= 0)
             {
-                int index = line.indexOf(':');
                 String hdr = line.substring(0, index);
                 String value = line.substring(index + 1).trim();
 
diff --git a/bcprov/src/main/java/org/bouncycastle/x509/X509V2AttributeCertificate.java b/bcprov/src/main/java/org/bouncycastle/x509/X509V2AttributeCertificate.java
index 96a899b..8eb5e98 100644
--- a/bcprov/src/main/java/org/bouncycastle/x509/X509V2AttributeCertificate.java
+++ b/bcprov/src/main/java/org/bouncycastle/x509/X509V2AttributeCertificate.java
@@ -21,12 +21,12 @@
 import java.util.List;
 import java.util.Set;
 
+import org.bouncycastle.asn1.ASN1BitString;
 import org.bouncycastle.asn1.ASN1Encodable;
 import org.bouncycastle.asn1.ASN1Encoding;
 import org.bouncycastle.asn1.ASN1InputStream;
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
 import org.bouncycastle.asn1.ASN1Sequence;
-import org.bouncycastle.asn1.DERBitString;
 import org.bouncycastle.asn1.x509.AttributeCertificate;
 import org.bouncycastle.asn1.x509.Extension;
 import org.bouncycastle.asn1.x509.Extensions;
@@ -123,7 +123,7 @@
     
     public boolean[] getIssuerUniqueID()
     {
-        DERBitString    id = cert.getAcinfo().getIssuerUniqueID();
+        ASN1BitString id = cert.getAcinfo().getIssuerUniqueID();
 
         if (id != null)
         {
diff --git a/bcprov/src/test/java/org/bouncycastle/math/ec/test/ECPointTest.java b/bcprov/src/test/java/org/bouncycastle/math/ec/test/ECPointTest.java
index aaf2f5b..8f243ae 100644
--- a/bcprov/src/test/java/org/bouncycastle/math/ec/test/ECPointTest.java
+++ b/bcprov/src/test/java/org/bouncycastle/math/ec/test/ECPointTest.java
@@ -1,5 +1,5 @@
-package org.bouncycastle.math.ec.test;
 
+package org.bouncycastle.math.ec.test;
 import java.math.BigInteger;
 import java.security.SecureRandom;
 import java.util.ArrayList;
@@ -9,7 +9,6 @@
 import java.util.List;
 import java.util.Random;
 import java.util.Set;
-
 import junit.framework.Test;
 import junit.framework.TestCase;
 import junit.framework.TestSuite;
@@ -27,9 +26,7 @@
 import org.bouncycastle.util.BigIntegers;
 import org.bouncycastle.util.Integers;
 import org.bouncycastle.util.encoders.Hex;
-
 import android.platform.test.annotations.LargeTest;
-
 /**
  * Test class for {@link org.bouncycastle.math.ec.ECPoint ECPoint}. All
  * literature values are taken from "Guide to elliptic curve cryptography",
@@ -42,34 +39,22 @@
      * Random source used to generate random points
      */
     private SecureRandom secRand = new SecureRandom();
-
     private ECPointTest.Fp fp = null;
-
     private ECPointTest.F2m f2m = null;
-
     /**
      * Nested class containing sample literature values for <code>Fp</code>.
      */
     public static class Fp
     {
         private final BigInteger q = new BigInteger("1063");
-
         private final BigInteger a = new BigInteger("4");
-
         private final BigInteger b = new BigInteger("20");
-
         private final BigInteger n = new BigInteger("38");
-
         private final BigInteger h = new BigInteger("1");
-
         private final ECCurve curve = new ECCurve.Fp(q, a, b, n, h);
-
         private final ECPoint infinity = curve.getInfinity();
-
         private final int[] pointSource = { 1, 5, 4, 10, 234, 1024, 817, 912 };
-
         private ECPoint[] p = new ECPoint[pointSource.length / 2];
-
         /**
          * Creates the points on the curve with literature values.
          */
@@ -83,7 +68,6 @@
             }
         }
     }
-
     /**
      * Nested class containing sample literature values for <code>F2m</code>.
      */
@@ -91,28 +75,18 @@
     {
         // Irreducible polynomial for TPB z^4 + z + 1
         private final int m = 4;
-
         private final int k1 = 1;
-
         // a = z^3
         private final BigInteger aTpb = new BigInteger("1000", 2);
-
         // b = z^3 + 1
         private final BigInteger bTpb = new BigInteger("1001", 2);
-
         private final BigInteger n = new BigInteger("23");
-
         private final BigInteger h = new BigInteger("1");
-
         private final ECCurve.F2m curve = new ECCurve.F2m(m, k1, aTpb, bTpb, n, h);
-
         private final ECPoint.F2m infinity = (ECPoint.F2m) curve.getInfinity();
-
         private final String[] pointSource = { "0010", "1111", "1100", "1100",
                 "0001", "0001", "1011", "0010" };
-
         private ECPoint[] p = new ECPoint[pointSource.length / 2];
-
         /**
          * Creates the points on the curve with literature values.
          */
@@ -126,16 +100,13 @@
             }
         }
     }
-
     public void setUp()
     {
         fp = new ECPointTest.Fp();
         fp.createPoints();
-
         f2m = new ECPointTest.F2m();
         f2m.createPoints();
     }
-
     /**
      * Tests, if inconsistent points can be created, i.e. points with exactly
      * one null coordinate (not permitted).
@@ -150,7 +121,6 @@
         catch (IllegalArgumentException expected)
         {
         }
-
         try
         {
             ECPoint bad = fp.curve.createPoint(null, new BigInteger("12"));
@@ -159,7 +129,6 @@
         catch (IllegalArgumentException expected)
         {
         }
-
         try
         {
             ECPoint bad = f2m.curve.createPoint(new BigInteger("1011"), null);
@@ -168,7 +137,6 @@
         catch (IllegalArgumentException expected)
         {
         }
-
         try
         {
             ECPoint bad = f2m.curve.createPoint(null, new BigInteger("1011"));
@@ -178,7 +146,6 @@
         {
         }
     }
-
     /**
      * Tests <code>ECPoint.add()</code> against literature values.
      * 
@@ -197,7 +164,6 @@
             assertPointsEqual("Adding to infinity failed", p[i], infinity.add(p[i]));
         }
     }
-
     /**
      * Calls <code>implTestAdd()</code> for <code>Fp</code> and
      * <code>F2m</code>.
@@ -207,7 +173,6 @@
         implTestAdd(fp.p, fp.infinity);
         implTestAdd(f2m.p, f2m.infinity);
     }
-
     /**
      * Tests <code>ECPoint.twice()</code> against literature values.
      * 
@@ -219,7 +184,6 @@
         assertPointsEqual("Twice incorrect", p[3], p[0].twice());
         assertPointsEqual("Add same point incorrect", p[3], p[0].add(p[0]));
     }
-
     /**
      * Calls <code>implTestTwice()</code> for <code>Fp</code> and
      * <code>F2m</code>.
@@ -229,7 +193,6 @@
         implTestTwice(fp.p);
         implTestTwice(f2m.p);
     }
-
     private void implTestThreeTimes(ECPoint[] p)
     {
         ECPoint P = p[0];
@@ -237,7 +200,6 @@
         assertPointsEqual("ThreeTimes incorrect", _3P, P.threeTimes());
         assertPointsEqual("TwicePlus incorrect", _3P, P.twicePlus(P));
     }
-
     /**
      * Calls <code>implTestThreeTimes()</code> for <code>Fp</code> and
      * <code>F2m</code>.
@@ -247,7 +209,6 @@
         implTestThreeTimes(fp.p);
         implTestThreeTimes(f2m.p);
     }
-
     /**
      * Goes through all points on an elliptic curve and checks, if adding a
      * point <code>k</code>-times is the same as multiplying the point by
@@ -263,7 +224,6 @@
     {
         ECPoint adder = infinity;
         ECPoint multiplier = infinity;
-
         BigInteger i = BigInteger.valueOf(1);
         do
         {
@@ -275,7 +235,6 @@
         }
         while (!(adder.equals(infinity)));
     }
-
     /**
      * Calls <code>implTestAllPoints()</code> for the small literature curves,
      * both for <code>Fp</code> and <code>F2m</code>.
@@ -286,13 +245,11 @@
         {
             implTestAllPoints(fp.p[i], fp.infinity);
         }
-
         for (int i = 0; i < f2m.p.length; i++)
         {
             implTestAllPoints(f2m.p[i], f2m.infinity);
         }
     }
-
     /**
      * Checks, if the point multiplication algorithm of the given point yields
      * the same result as point multiplication done by the reference
@@ -312,7 +269,6 @@
         ECPoint q = p.multiply(k);
         assertPointsEqual("ECPoint.multiply is incorrect", ref, q);
     }
-
     /**
      * Checks, if the point multiplication algorithm of the given point yields
      * the same result as point multiplication done by the reference
@@ -329,7 +285,6 @@
     {
         BigInteger bound = BigInteger.ONE.shiftLeft(numBits);
         BigInteger k = BigInteger.ZERO;
-
         do
         {
             ECPoint ref = ECAlgorithms.referenceMultiply(p, k);
@@ -339,7 +294,6 @@
         }
         while (k.compareTo(bound) < 0);
     }
-
     /**
      * Tests <code>ECPoint.add()</code> and <code>ECPoint.subtract()</code>
      * for the given point and the given point at infinity.
@@ -360,7 +314,6 @@
         assertPointsEqual("infinity plus infinity is not infinity ", infinity, infinity.add(infinity));
         assertPointsEqual("Twice infinity is not infinity ", infinity, infinity.twice());
     }
-
     /**
      * Calls <code>implTestAddSubtract()</code> for literature values, both
      * for <code>Fp</code> and <code>F2m</code>.
@@ -371,21 +324,17 @@
         for (int iFp = 0; iFp < fp.pointSource.length / 2; iFp++)
         {
             implTestAddSubtract(fp.p[iFp], fp.infinity);
-
             implTestMultiplyAll(fp.p[iFp], fpBits);
             implTestMultiplyAll(fp.infinity, fpBits);
         }
-
         int f2mBits = f2m.curve.getOrder().bitLength();
         for (int iF2m = 0; iF2m < f2m.pointSource.length / 2; iF2m++)
         {
             implTestAddSubtract(f2m.p[iF2m], f2m.infinity);
-
             implTestMultiplyAll(f2m.p[iF2m], f2mBits);
             implTestMultiplyAll(f2m.infinity, f2mBits);
         }
     }
-
     /**
      * Test encoding with and without point compression.
      * 
@@ -398,25 +347,20 @@
         byte[] unCompBarr = p.getEncoded(false);
         ECPoint decUnComp = p.getCurve().decodePoint(unCompBarr);
         assertPointsEqual("Error decoding uncompressed point", p, decUnComp);
-
         // Point compression
         byte[] compBarr = p.getEncoded(true);
         ECPoint decComp = p.getCurve().decodePoint(compBarr);
         assertPointsEqual("Error decoding compressed point", p, decComp);
     }
-
     private void implAddSubtractMultiplyTwiceEncodingTest(ECCurve curve, ECPoint q, BigInteger n)
     {
         // Get point at infinity on the curve
         ECPoint infinity = curve.getInfinity();
-
         implTestAddSubtract(q, infinity);
         implTestMultiply(q, n.bitLength());
         implTestMultiply(infinity, n.bitLength());
-
         int logSize = 32 - Integers.numberOfLeadingZeros(curve.getFieldSize() - 1);
         int rounds = Math.max(2, Math.min(10, 32 - 3 * logSize));
-
         ECPoint p = q;
         for (int i = 0; i < rounds; ++i)
         {
@@ -424,7 +368,6 @@
             p = p.twice();
         }
     }
-
     private void implSqrtTest(ECCurve c)
     {
         if (ECAlgorithms.isFpCurve(c))
@@ -432,19 +375,15 @@
             BigInteger p = c.getField().getCharacteristic();
             BigInteger pMinusOne = p.subtract(ECConstants.ONE);
             BigInteger legendreExponent = p.shiftRight(1);
-
             ECFieldElement zero = c.fromBigInteger(BigInteger.ZERO);
             assertEquals(zero, zero.sqrt());
-
             ECFieldElement one = c.fromBigInteger(BigInteger.ONE);
             assertEquals(one, one.sqrt());
-
             for (int i = 0; i < 20; ++i)
             {
                 BigInteger x = BigIntegers.createRandomInRange(ECConstants.TWO, pMinusOne, secRand);
                 ECFieldElement fe = c.fromBigInteger(x);
                 ECFieldElement root = fe.sqrt();
-
                 if (root == null)
                 {
                     assertEquals(pMinusOne, x.modPow(legendreExponent, p));
@@ -469,11 +408,9 @@
             }
         }
     }
-
     private void implValidityTest(ECCurve c, ECPoint g)
     {
         assertTrue(g.isValid());
-
         if (ECAlgorithms.isF2mCurve(c))
         {
             BigInteger h = c.getCofactor();
@@ -489,7 +426,6 @@
                     assertFalse(bad2.isValid());
                     ECPoint good2 = bad2.add(order2);
                     assertTrue(good2.isValid());
-
                     if (!h.testBit(1))
                     {
                         ECFieldElement L = solveQuadraticEquation(c, c.getA());
@@ -513,13 +449,11 @@
             }
         }
     }
-
     private void implAddSubtractMultiplyTwiceEncodingTestAllCoords(X9ECParameters x9ECParameters)
     {
         BigInteger n = x9ECParameters.getN();
         ECPoint G = x9ECParameters.getG();
         ECCurve C = x9ECParameters.getCurve();
-
         int[] coords = ECCurve.getAllCoordinateSystems();
         for (int i = 0; i < coords.length; ++i)
         {
@@ -528,26 +462,20 @@
             {
                 ECCurve c = C;
                 ECPoint g = G;
-
                 if (c.getCoordinateSystem() != coord)
                 {
                     c = C.configure().setCoordinateSystem(coord).create();
                     g = c.importPoint(G);
                 }
-
                 // The generator is multiplied by random b to get random q
                 BigInteger b = new BigInteger(n.bitLength(), secRand);
                 ECPoint q = g.multiply(b).normalize();
-
                 implAddSubtractMultiplyTwiceEncodingTest(c, q, n);
-
                 implSqrtTest(c);
-
                 implValidityTest(c, g);
             }
         }
     }
-
     /**
      * Calls <code>implTestAddSubtract()</code>,
      * <code>implTestMultiply</code> and <code>implTestEncoding</code> for
@@ -558,15 +486,12 @@
     {
         Set names = new HashSet(enumToList(ECNamedCurveTable.getNames()));
         names.addAll(enumToList(CustomNamedCurves.getNames()));
-
         Iterator it = names.iterator();
         while (it.hasNext())
         {
             String name = (String)it.next();
-
             X9ECParameters x9A = ECNamedCurveTable.getByName(name);
             X9ECParameters x9B = CustomNamedCurves.getByName(name);
-
             if (x9A != null && x9B != null)
             {
                 assertEquals(x9A.getCurve().getField(), x9B.getCurve().getField());
@@ -574,31 +499,25 @@
                 assertEquals(x9A.getCurve().getB().toBigInteger(), x9B.getCurve().getB().toBigInteger());
                 assertOptionalValuesAgree(x9A.getCurve().getCofactor(), x9B.getCurve().getCofactor());
                 assertOptionalValuesAgree(x9A.getCurve().getOrder(), x9B.getCurve().getOrder());
-
                 assertPointsEqual("Custom curve base-point inconsistency", x9A.getG(), x9B.getG());
-
                 assertEquals(x9A.getH(), x9B.getH());
                 assertEquals(x9A.getN(), x9B.getN());
                 assertOptionalValuesAgree(x9A.getSeed(), x9B.getSeed());
-
                 BigInteger k = new BigInteger(x9A.getN().bitLength(), secRand);
                 ECPoint pA = x9A.getG().multiply(k);
                 ECPoint pB = x9B.getG().multiply(k);
                 assertPointsEqual("Custom curve multiplication inconsistency", pA, pB);
             }
-
             if (x9A != null)
             {
                 implAddSubtractMultiplyTwiceEncodingTestAllCoords(x9A);
             }
-
             if (x9B != null)
             {
                 implAddSubtractMultiplyTwiceEncodingTestAllCoords(x9B);
             }
         }
     }
-
     public void testExampleFpB0() throws Exception
     {
         /*
@@ -619,9 +538,7 @@
         byte[] S = null;
         BigInteger n = p.add(BigInteger.valueOf(1)).shiftRight(2);
         BigInteger h = BigInteger.valueOf(4);
-
         ECCurve curve = configureCurve(new ECCurve.Fp(p, a, b, n, h));
-
         X9ECPoint G = configureBasepoint(curve, "04"
             // Px
             + "53FC09EE332C29AD0A7990053ED9B52A"
@@ -641,19 +558,15 @@
             + "AC6F1E80164AA989492D979FC5A4D5F2"
             + "13515AD7E9CB99A980BDAD5AD5BB4636"
             + "ADB9B5706A67DCDE75573FD71BEF16D7");
-
         X9ECParameters x9 = new X9ECParameters(curve, G, n, h, S);
-
         implAddSubtractMultiplyTwiceEncodingTestAllCoords(x9);
     }
-
     private void assertPointsEqual(String message, ECPoint a, ECPoint b)
     {
         // NOTE: We intentionally test points for equality in both directions
         assertEquals(message, a, b);
         assertEquals(message, b, a);
     }
-
     private void assertOptionalValuesAgree(Object a, Object b)
     {
         if (a != null && b != null)
@@ -661,7 +574,6 @@
             assertEquals(a, b);
         }
     }
-
     private void assertOptionalValuesAgree(byte[] a, byte[] b)
     {
         if (a != null && b != null)
@@ -669,46 +581,37 @@
             assertTrue(Arrays.areEqual(a, b));
         }
     }
-
     private static X9ECPoint configureBasepoint(ECCurve curve, String encoding)
     {
         X9ECPoint G = new X9ECPoint(curve, Hex.decode(encoding));
         WNafUtil.configureBasepoint(G.getPoint());
         return G;
     }
-
     private static ECCurve configureCurve(ECCurve curve)
     {
         return curve;
     }
-
     private List enumToList(Enumeration en)
     {
         List rv = new ArrayList();
-
         while (en.hasMoreElements())
         {
             rv.add(en.nextElement());
         }
-
         return rv;
     }
-
     private static BigInteger fromHex(
         String hex)
     {
         return new BigInteger(1, Hex.decode(hex));
     }
-
     private static ECFieldElement solveQuadraticEquation(ECCurve c, ECFieldElement rhs)
     {
         if (rhs.isZero())
         {
             return rhs;
         }
-
         ECFieldElement gamma, z, zeroElement = c.fromBigInteger(ECConstants.ZERO);
-
         int m = c.getFieldSize();
         Random rand = new Random();
         do
@@ -729,12 +632,11 @@
             gamma = z.square().add(z);
         }
         while (gamma.isZero());
-
         return z;
     }
-
     public static Test suite()
     {
         return new TestSuite(ECPointTest.class);
     }
 }
+
diff --git a/bouncycastle.version b/bouncycastle.version
index bab63ae..b2db47f 100644
--- a/bouncycastle.version
+++ b/bouncycastle.version
@@ -1,2 +1,2 @@
-BOUNCYCASTLE_JDK=15on
-BOUNCYCASTLE_VERSION=168
+BOUNCYCASTLE_JDK=18on
+BOUNCYCASTLE_VERSION=177
diff --git a/proguard.flags b/proguard.flags
index 4a4ff37..1dae437 100644
--- a/proguard.flags
+++ b/proguard.flags
@@ -167,6 +167,7 @@
 -keep class com.android.org.bouncycastle.math.ec.ECAlgorithms { public *; }
 -keep class com.android.org.bouncycastle.math.ec.ECCurve { public *; }
 -keep class com.android.org.bouncycastle.math.ec.ECCurve$Config { public *; }
+-keep class com.android.org.bouncycastle.math.ec.ECCurve$Fp { public *; }
 -keep class com.android.org.bouncycastle.math.ec.ECPoint { public *; }
 -keep class com.android.org.bouncycastle.math.ec.FixedPointCombMultiplier { public *; }
 -keep class com.android.org.bouncycastle.math.raw.Interleave { public *; }
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1Absent.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1Absent.java
new file mode 100644
index 0000000..d6054e2
--- /dev/null
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1Absent.java
@@ -0,0 +1,47 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.org.bouncycastle.asn1;
+
+import java.io.IOException;
+
+/**
+ * An ASN1 class that encodes to nothing, used in the OER library to deal with the Optional type.
+ * @hide This class is not part of the Android public SDK API
+ */
+public class ASN1Absent
+    extends ASN1Primitive
+{
+
+    public static final ASN1Absent INSTANCE = new ASN1Absent();
+
+    private ASN1Absent()
+    {
+
+    }
+
+    public int hashCode()
+    {
+        return 0;
+    }
+
+    boolean encodeConstructed()
+    {
+        return false;
+    }
+
+    int encodedLength(boolean withTag)
+        throws IOException
+    {
+        return 0;
+    }
+
+    void encode(ASN1OutputStream out, boolean withTag)
+        throws IOException
+    {
+
+    }
+
+    boolean asn1Equals(ASN1Primitive o)
+    {
+        return o == this;
+    }
+}
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1ApplicationSpecific.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1ApplicationSpecific.java
deleted file mode 100644
index 60cc233..0000000
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1ApplicationSpecific.java
+++ /dev/null
@@ -1,248 +0,0 @@
-/* GENERATED SOURCE. DO NOT MODIFY. */
-package com.android.org.bouncycastle.asn1;
-
-import java.io.IOException;
-
-import com.android.org.bouncycastle.util.Arrays;
-import com.android.org.bouncycastle.util.encoders.Hex;
-
-/**
- * Base class for an ASN.1 ApplicationSpecific object
- * @hide This class is not part of the Android public SDK API
- */
-public abstract class ASN1ApplicationSpecific
-    extends ASN1Primitive
-{
-    protected final boolean   isConstructed;
-    protected final int       tag;
-    protected final byte[]    octets;
-
-    ASN1ApplicationSpecific(
-        boolean isConstructed,
-        int tag,
-        byte[] octets)
-    {
-        this.isConstructed = isConstructed;
-        this.tag = tag;
-        this.octets = Arrays.clone(octets);
-    }
-
-    /**
-     * Return an ASN1ApplicationSpecific from the passed in object, which may be a byte array, or null.
-     *
-     * @param obj the object to be converted.
-     * @return obj's representation as an ASN1ApplicationSpecific object.
-     */
-    public static ASN1ApplicationSpecific getInstance(Object obj)
-    {
-        if (obj == null || obj instanceof ASN1ApplicationSpecific)
-        {
-            return (ASN1ApplicationSpecific)obj;
-        }
-        else if (obj instanceof byte[])
-        {
-            try
-            {
-                return ASN1ApplicationSpecific.getInstance(ASN1Primitive.fromByteArray((byte[])obj));
-            }
-            catch (IOException e)
-            {
-                throw new IllegalArgumentException("Failed to construct object from byte[]: " + e.getMessage());
-            }
-        }
-
-        throw new IllegalArgumentException("unknown object in getInstance: " + obj.getClass().getName());
-    }
-
-    protected static int getLengthOfHeader(byte[] data)
-    {
-        int length = data[1] & 0xff; // TODO: assumes 1 byte tag
-
-        if (length == 0x80)
-        {
-            return 2;      // indefinite-length encoding
-        }
-
-        if (length > 127)
-        {
-            int size = length & 0x7f;
-
-            // Note: The invalid long form "0xff" (see X.690 8.1.3.5c) will be caught here
-            if (size > 4)
-            {
-                throw new IllegalStateException("DER length more than 4 bytes: " + size);
-            }
-
-            return size + 2;
-        }
-
-        return 2;
-    }
-
-    /**
-     * Return true if the object is marked as constructed, false otherwise.
-     *
-     * @return true if constructed, otherwise false.
-     */
-    public boolean isConstructed()
-    {
-        return isConstructed;
-    }
-
-    /**
-     * Return the contents of this object as a byte[]
-     *
-     * @return the encoded contents of the object.
-     */
-    public byte[] getContents()
-    {
-        return Arrays.clone(octets);
-    }
-
-    /**
-     * Return the tag number associated with this object,
-     *
-     * @return the application tag number.
-     */
-    public int getApplicationTag() 
-    {
-        return tag;
-    }
-
-    /**
-     * Return the enclosed object assuming explicit tagging.
-     *
-     * @return  the resulting object
-     * @throws IOException if reconstruction fails.
-     */
-    public ASN1Primitive getObject()
-        throws IOException 
-    {
-        return ASN1Primitive.fromByteArray(getContents());
-    }
-
-    /**
-     * Return the enclosed object assuming implicit tagging.
-     *
-     * @param derTagNo the type tag that should be applied to the object's contents.
-     * @return  the resulting object
-     * @throws IOException if reconstruction fails.
-     */
-    public ASN1Primitive getObject(int derTagNo)
-        throws IOException
-    {
-        if (derTagNo >= 0x1f)
-        {
-            throw new IOException("unsupported tag number");
-        }
-
-        byte[] orig = this.getEncoded();
-        byte[] tmp = replaceTagNumber(derTagNo, orig);
-
-        if ((orig[0] & BERTags.CONSTRUCTED) != 0)
-        {
-            tmp[0] |= BERTags.CONSTRUCTED;
-        }
-
-        return ASN1Primitive.fromByteArray(tmp);
-    }
-
-    int encodedLength()
-        throws IOException
-    {
-        return StreamUtil.calculateTagLength(tag) + StreamUtil.calculateBodyLength(octets.length) + octets.length;
-    }
-
-    /* (non-Javadoc)
-     * @see org.bouncycastle.asn1.ASN1Primitive#encode(org.bouncycastle.asn1.DEROutputStream)
-     */
-    void encode(ASN1OutputStream out, boolean withTag) throws IOException
-    {
-        int flags = BERTags.APPLICATION;
-        if (isConstructed)
-        {
-            flags |= BERTags.CONSTRUCTED;
-        }
-
-        out.writeEncoded(withTag, flags, tag, octets);
-    }
-
-    boolean asn1Equals(
-        ASN1Primitive o)
-    {
-        if (!(o instanceof ASN1ApplicationSpecific))
-        {
-            return false;
-        }
-
-        ASN1ApplicationSpecific other = (ASN1ApplicationSpecific)o;
-
-        return isConstructed == other.isConstructed
-            && tag == other.tag
-            && Arrays.areEqual(octets, other.octets);
-    }
-
-    public int hashCode()
-    {
-        return (isConstructed ? 1 : 0) ^ tag ^ Arrays.hashCode(octets);
-    }
-
-    private byte[] replaceTagNumber(int newTag, byte[] input)
-        throws IOException
-    {
-        int tagNo = input[0] & 0x1f;
-        int index = 1;
-        //
-        // with tagged object tag number is bottom 5 bits, or stored at the start of the content
-        //
-        if (tagNo == 0x1f)
-        {
-            int b = input[index++] & 0xff;
-
-            // X.690-0207 8.1.2.4.2
-            // "c) bits 7 to 1 of the first subsequent octet shall not all be zero."
-            if ((b & 0x7f) == 0) // Note: -1 will pass
-            {
-                throw new IOException("corrupted stream - invalid high tag number found");
-            }
-
-            while ((b & 0x80) != 0)
-            {
-                b = input[index++] & 0xff;
-            }
-        }
-
-        byte[] tmp = new byte[input.length - index + 1];
-
-        System.arraycopy(input, index, tmp, 1, tmp.length - 1);
-
-        tmp[0] = (byte)newTag;
-
-        return tmp;
-    }
-
-    public String toString()
-    {
-        StringBuffer sb = new StringBuffer();
-        sb.append("[");
-        if (isConstructed())
-        {
-            sb.append("CONSTRUCTED ");
-        }
-        sb.append("APPLICATION ");
-        sb.append(Integer.toString(getApplicationTag()));
-        sb.append("]");
-        // @todo content encoding somehow?
-        if (this.octets != null)
-        {
-            sb.append(" #");
-            sb.append(Hex.toHexString(this.octets));
-        }
-        else
-        {
-            sb.append(" #null");
-        }
-        sb.append(" ");
-        return sb.toString();
-    }
-}
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1BMPString.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1BMPString.java
new file mode 100644
index 0000000..24bc2e4
--- /dev/null
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1BMPString.java
@@ -0,0 +1,215 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.org.bouncycastle.asn1;
+
+import java.io.IOException;
+
+import com.android.org.bouncycastle.util.Arrays;
+
+/**
+ * ASN.1 BMPString object encodes BMP (<i>Basic Multilingual Plane</i>) subset
+ * (aka UCS-2) of UNICODE (ISO 10646) characters in codepoints 0 to 65535.
+ * <p>
+ * At ISO-10646:2011 the term "BMP" has been withdrawn, and replaced by
+ * term "UCS-2".
+ * </p>
+ * @hide This class is not part of the Android public SDK API
+ */
+public abstract class ASN1BMPString
+    extends ASN1Primitive
+    implements ASN1String
+{
+    static final ASN1UniversalType TYPE = new ASN1UniversalType(ASN1BMPString.class, BERTags.BMP_STRING)
+    {
+        ASN1Primitive fromImplicitPrimitive(DEROctetString octetString)
+        {
+            return createPrimitive(octetString.getOctets());
+        }
+    };
+
+    /**
+     * Return a BMP String from the given object.
+     *
+     * @param obj the object we want converted.
+     * @exception IllegalArgumentException if the object cannot be converted.
+     * @return an ASN1BMPString instance, or null.
+     */
+    public static ASN1BMPString getInstance(Object obj)
+    {
+        if (obj == null || obj instanceof ASN1BMPString)
+        {
+            return (ASN1BMPString)obj;
+        }
+        if (obj instanceof ASN1Encodable)
+        {
+            ASN1Primitive primitive = ((ASN1Encodable)obj).toASN1Primitive();
+            if (primitive instanceof ASN1BMPString)
+            {
+                return (ASN1BMPString)primitive;
+            }
+        }
+        if (obj instanceof byte[])
+        {
+            try
+            {
+                return (ASN1BMPString)TYPE.fromByteArray((byte[])obj);
+            }
+            catch (Exception e)
+            {
+                throw new IllegalArgumentException("encoding error in getInstance: " + e.toString());
+            }
+        }
+
+        throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
+    }
+
+    /**
+     * Return a BMP String from a tagged object.
+     *
+     * @param taggedObject      the tagged object holding the object we want
+     * @param explicit true if the object is meant to be explicitly tagged false
+     *                 otherwise.
+     * @exception IllegalArgumentException if the tagged object cannot be converted.
+     * @return an ASN1BMPString instance.
+     */
+    public static ASN1BMPString getInstance(ASN1TaggedObject taggedObject, boolean explicit)
+    {
+        return (ASN1BMPString)TYPE.getContextInstance(taggedObject, explicit);
+    }
+
+    final char[] string;
+
+    ASN1BMPString(String string)
+    {
+        if (string == null)
+        {
+            throw new NullPointerException("'string' cannot be null");
+        }
+
+        this.string = string.toCharArray();
+    }
+
+    ASN1BMPString(byte[] string)
+    {
+        if (string == null)
+        {
+            throw new NullPointerException("'string' cannot be null");
+        }
+
+        int byteLen = string.length;
+        if (0 != (byteLen & 1))
+        {
+            throw new IllegalArgumentException("malformed BMPString encoding encountered");
+        }
+
+        int charLen = byteLen / 2;
+        char[] cs = new char[charLen];
+
+        for (int i = 0; i != charLen; i++)
+        {
+            cs[i] = (char)((string[2 * i] << 8) | (string[2 * i + 1] & 0xff));
+        }
+
+        this.string = cs;
+    }
+
+    ASN1BMPString(char[] string)
+    {
+        if (string == null)
+        {
+            throw new NullPointerException("'string' cannot be null");
+        }
+
+        this.string = string;
+    }
+
+    public final String getString()
+    {
+        return new String(string);
+    }
+
+    public String toString()
+    {
+        return getString();
+    }
+
+    final boolean asn1Equals(ASN1Primitive other)
+    {
+        if (!(other instanceof ASN1BMPString))
+        {
+            return false;
+        }
+
+        ASN1BMPString that = (ASN1BMPString)other;
+
+        return Arrays.areEqual(this.string, that.string);
+    }
+
+    public final int hashCode()
+    {
+        return Arrays.hashCode(string);
+    }
+
+    final boolean encodeConstructed()
+    {
+        return false;
+    }
+
+    final int encodedLength(boolean withTag)
+    {
+        return ASN1OutputStream.getLengthOfEncodingDL(withTag, string.length * 2);
+    }
+
+    final void encode(ASN1OutputStream out, boolean withTag) throws IOException
+    {
+        int count = string.length;
+
+        out.writeIdentifier(withTag, BERTags.BMP_STRING);
+        out.writeDL(count * 2);
+
+        byte[] buf = new byte[8];
+
+        int i = 0, limit = count & -4;
+        while (i < limit)
+        {
+            char c0 = string[i], c1 = string[i + 1], c2 = string[i + 2], c3 = string[i + 3];
+            i += 4;
+
+            buf[0] = (byte)(c0 >> 8);
+            buf[1] = (byte)c0;
+            buf[2] = (byte)(c1 >> 8);
+            buf[3] = (byte)c1;
+            buf[4] = (byte)(c2 >> 8);
+            buf[5] = (byte)c2;
+            buf[6] = (byte)(c3 >> 8);
+            buf[7] = (byte)c3;
+
+            out.write(buf, 0, 8);
+        }
+        if (i < count)
+        {
+            int bufPos = 0;
+            do
+            {
+                char c0 = string[i];
+                i += 1;
+
+                buf[bufPos++] = (byte)(c0 >> 8);
+                buf[bufPos++] = (byte)c0;
+            }
+            while (i < count);
+
+            out.write(buf, 0, bufPos);
+        }
+    }
+
+    static ASN1BMPString createPrimitive(byte[] contents)
+    {
+        return new DERBMPString(contents);
+    }
+
+    static ASN1BMPString createPrimitive(char[] string)
+    {
+        // TODO ASN1InputStream has a validator/converter that should be unified in this class somehow
+        return new DERBMPString(string);
+    }
+}
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1BitString.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1BitString.java
index 38b6b46..fd02759 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1BitString.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1BitString.java
@@ -1,12 +1,11 @@
 /* GENERATED SOURCE. DO NOT MODIFY. */
 package com.android.org.bouncycastle.asn1;
 
-import java.io.EOFException;
+import java.io.ByteArrayInputStream;
 import java.io.IOException;
 import java.io.InputStream;
 
 import com.android.org.bouncycastle.util.Arrays;
-import com.android.org.bouncycastle.util.io.Streams;
 
 /**
  * Base class for BIT STRING objects
@@ -14,12 +13,57 @@
  */
 public abstract class ASN1BitString
     extends ASN1Primitive
-    implements ASN1String
+    implements ASN1String, ASN1BitStringParser
 {
-    private static final char[]  table = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
+    static final ASN1UniversalType TYPE = new ASN1UniversalType(ASN1BitString.class, BERTags.BIT_STRING)
+    {
+        ASN1Primitive fromImplicitPrimitive(DEROctetString octetString)
+        {
+            return createPrimitive(octetString.getOctets());
+        }
 
-    protected final byte[]      data;
-    protected final int         padBits;
+        ASN1Primitive fromImplicitConstructed(ASN1Sequence sequence)
+        {
+            return sequence.toASN1BitString();
+        }
+    };
+
+    public static ASN1BitString getInstance(Object obj)
+    {
+        if (obj == null || obj instanceof ASN1BitString)
+        {
+            return (ASN1BitString)obj;
+        }
+//      else if (obj instanceof ASN1BitStringParser)
+        else if (obj instanceof ASN1Encodable)
+        {
+            ASN1Primitive primitive = ((ASN1Encodable)obj).toASN1Primitive();
+            if (primitive instanceof ASN1BitString)
+            {
+                return (ASN1BitString)primitive;
+            }
+        }
+        else if (obj instanceof byte[])
+        {
+            try
+            {
+                return (ASN1BitString)TYPE.fromByteArray((byte[])obj);
+            }
+            catch (IOException e)
+            {
+                throw new IllegalArgumentException("failed to construct BIT STRING from byte[]: " + e.getMessage());
+            }
+        }
+
+        throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
+    }
+
+    public static ASN1BitString getInstance(ASN1TaggedObject taggedObject, boolean explicit)
+    {
+        return (ASN1BitString)TYPE.getContextInstance(taggedObject, explicit);
+    }
+
+    private static final char[]  table = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
 
     /**
      * @param bitString an int containing the BIT STRING
@@ -101,15 +145,16 @@
         return result;
     }
 
-    protected ASN1BitString(byte data, int padBits)
+    final byte[] contents;
+
+    ASN1BitString(byte data, int padBits)
     {
         if (padBits > 7 || padBits < 0)
         {
             throw new IllegalArgumentException("pad bits cannot be greater than 7 or less than 0");
         }
 
-        this.data = new byte[]{ data };
-        this.padBits = padBits;
+        this.contents = new byte[]{ (byte)padBits, data };
     }
 
     /**
@@ -118,9 +163,7 @@
      * @param data the octets making up the bit string.
      * @param padBits the number of extra bits at the end of the string.
      */
-    public ASN1BitString(
-        byte[]  data,
-        int     padBits)
+    ASN1BitString(byte[] data, int padBits)
     {
         if (data == null)
         {
@@ -135,8 +178,58 @@
             throw new IllegalArgumentException("pad bits cannot be greater than 7 or less than 0");
         }
 
-        this.data = Arrays.clone(data);
-        this.padBits = padBits;
+        this.contents = Arrays.prepend(data, (byte)padBits);
+    }
+
+    ASN1BitString(byte[] contents, boolean check)
+    {
+        if (check)
+        {
+            if (null == contents)
+            {
+                throw new NullPointerException("'contents' cannot be null");
+            }
+            if (contents.length < 1)
+            {
+                throw new IllegalArgumentException("'contents' cannot be empty");
+            }
+
+            int padBits = contents[0] & 0xFF;
+            if (padBits > 0)
+            {
+                if (contents.length < 2)
+                {
+                    throw new IllegalArgumentException("zero length data with non-zero pad bits");
+                }
+                if (padBits > 7)
+                {
+                    throw new IllegalArgumentException("pad bits cannot be greater than 7 or less than 0");
+                }
+            }
+        }
+
+        this.contents = contents;
+    }
+
+    public InputStream getBitStream() throws IOException
+    {
+        return new ByteArrayInputStream(contents, 1, contents.length - 1);
+    }
+
+    public InputStream getOctetStream() throws IOException
+    {
+        int padBits = contents[0] & 0xFF;
+        if (0 != padBits)
+        {
+            throw new IOException("expected octet-aligned bitstring, but found padBits: " + padBits);
+        }
+
+        return getBitStream();
+    }
+
+    public ASN1BitStringParser parser()
+    {
+        return this;
     }
 
     /**
@@ -146,8 +239,6 @@
      */
     public String getString()
     {
-        StringBuffer buf = new StringBuffer("#");
-
         byte[] string;
         try
         {
@@ -158,10 +249,14 @@
             throw new ASN1ParsingException("Internal error encoding BitString: " + e.getMessage(), e);
         }
 
+        StringBuffer buf = new StringBuffer(1 + string.length * 2);
+        buf.append('#');
+
         for (int i = 0; i != string.length; i++)
         {
-            buf.append(table[(string[i] >>> 4) & 0xf]);
-            buf.append(table[string[i] & 0xf]);
+            byte b = string[i];
+            buf.append(table[(b >>> 4) & 0xf]);
+            buf.append(table[b & 0xf]);
         }
 
         return buf.toString();
@@ -173,15 +268,16 @@
     public int intValue()
     {
         int value = 0;
-        int end = Math.min(4, data.length - 1);
-        for (int i = 0; i < end; ++i)
+        int end = Math.min(5, contents.length - 1);
+        for (int i = 1; i < end; ++i)
         {
-            value |= (data[i] & 0xFF) << (8 * i);
+            value |= (contents[i] & 0xFF) << (8 * (i - 1));
         }
-        if (0 <= end && end < 4)
+        if (1 <= end && end < 5)
         {
-            byte der = (byte)(data[end] & (0xFF << padBits));
-            value |= (der & 0xFF) << (8 * end);
+            int padBits = contents[0] & 0xFF;
+            byte der = (byte)(contents[end] & (0xFF << padBits));
+            value |= (der & 0xFF) << (8 * (end - 1));
         }
         return value;
     }
@@ -195,30 +291,31 @@
      */
     public byte[] getOctets()
     {
-        if (padBits != 0)
+        if (contents[0] != 0)
         {
             throw new IllegalStateException("attempt to get non-octet aligned data from BIT STRING");
         }
 
-        return Arrays.clone(data);
+        return Arrays.copyOfRange(contents, 1, contents.length);
     }
 
     public byte[] getBytes()
     {
-        if (0 == data.length)
+        if (contents.length == 1)
         {
-            return data;
+            return ASN1OctetString.EMPTY_OCTETS;
         }
 
-        byte[] rv = Arrays.clone(data);
+        int padBits = contents[0] & 0xFF;
+        byte[] rv = Arrays.copyOfRange(contents, 1, contents.length);
         // DER requires pad bits be zero
-        rv[data.length - 1] &= (0xFF << padBits);
+        rv[rv.length - 1] &= (byte)(0xFF << padBits);
         return rv;
     }
 
     public int getPadBits()
     {
-        return padBits;
+        return contents[0] & 0xFF;
     }
 
     public String toString()
@@ -228,85 +325,56 @@
 
     public int hashCode()
     {
-        int end = data.length;
-        if (--end < 0)
+        if (contents.length < 2)
         {
             return 1;
         }
 
-        byte der = (byte)(data[end] & (0xFF << padBits));
+        int padBits = contents[0] & 0xFF;
+        int last = contents.length - 1;
 
-        int hc = Arrays.hashCode(data, 0, end);
+        byte lastOctetDER = (byte)(contents[last] & (0xFF << padBits));
+
+        int hc = Arrays.hashCode(contents, 0, last);
         hc *= 257;
-        hc ^= der;
-        return hc ^ padBits;
+        hc ^= lastOctetDER;
+        return hc;
     }
 
-    boolean asn1Equals(
-        ASN1Primitive o)
+    boolean asn1Equals(ASN1Primitive other)
     {
-        if (!(o instanceof ASN1BitString))
+        if (!(other instanceof ASN1BitString))
         {
             return false;
         }
 
-        ASN1BitString other = (ASN1BitString)o;
-        if (padBits != other.padBits)
+        ASN1BitString that = (ASN1BitString)other;
+        byte[] thisContents = this.contents, thatContents = that.contents;
+
+        int length = thisContents.length;
+        if (thatContents.length != length)
         {
             return false;
         }
-        byte[] a = data, b = other.data;
-        int end = a.length;
-        if (end != b.length)
-        {
-            return false;
-        }
-        if (--end < 0)
+        if (length == 1)
         {
             return true;
         }
-        for (int i = 0; i < end; ++i)
+
+        int last = length - 1;
+        for (int i = 0; i < last; ++i)
         {
-            if (a[i] != b[i])
+            if (thisContents[i] != thatContents[i])
             {
                 return false;
             }
         }
 
-        byte derA = (byte)(a[end] & (0xFF << padBits));
-        byte derB = (byte)(b[end] & (0xFF << padBits));
+        int padBits = thisContents[0] & 0xFF;
+        byte thisLastOctetDER = (byte)(thisContents[last] & (0xFF << padBits));
+        byte thatLastOctetDER = (byte)(thatContents[last] & (0xFF << padBits));
 
-        return derA == derB;
-    }
-
-    static ASN1BitString fromInputStream(int length, InputStream stream)
-        throws IOException
-    {
-        if (length < 1)
-        {
-            throw new IllegalArgumentException("truncated BIT STRING detected");
-        }
-
-        int padBits = stream.read();
-        byte[] data = new byte[length - 1];
-
-        if (data.length != 0)
-        {
-            if (Streams.readFully(stream, data) != data.length)
-            {
-                throw new EOFException("EOF encountered in middle of BIT STRING");
-            }
-
-            if (padBits > 0 && padBits < 8)
-            {
-                if (data[data.length - 1] != (byte)(data[data.length - 1] & (0xFF << padBits)))
-                {
-                    return new DLBitString(data, padBits);
-                }
-            }
-        }
-
-        return new DERBitString(data, padBits);
+        return thisLastOctetDER == thatLastOctetDER;
     }
 
     public ASN1Primitive getLoadedObject()
@@ -316,13 +384,37 @@
 
     ASN1Primitive toDERObject()
     {
-        return new DERBitString(data, padBits);
+        return new DERBitString(contents, false);
     }
 
     ASN1Primitive toDLObject()
     {
-        return new DLBitString(data, padBits);
+        return new DLBitString(contents, false);
     }
 
-    abstract void encode(ASN1OutputStream out, boolean withTag) throws IOException;
+    static ASN1BitString createPrimitive(byte[] contents)
+    {
+        int length = contents.length;
+        if (length < 1)
+        {
+            throw new IllegalArgumentException("truncated BIT STRING detected");
+        }
+
+        int padBits = contents[0] & 0xFF;
+        if (padBits > 0)
+        {
+            if (padBits > 7 || length < 2)
+            {
+                throw new IllegalArgumentException("invalid pad bits detected");
+            }
+
+            byte finalOctet = contents[length - 1];
+            if (finalOctet != (byte)(finalOctet & (0xFF << padBits)))
+            {
+                return new DLBitString(contents, false);
+            }
+        }
+
+        return new DERBitString(contents, false);
+    }
 }
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1BitStringParser.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1BitStringParser.java
new file mode 100644
index 0000000..5305b19
--- /dev/null
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1BitStringParser.java
@@ -0,0 +1,42 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.org.bouncycastle.asn1;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * A basic parser for a BIT STRING object
+ * @hide This class is not part of the Android public SDK API
+ */
+public interface ASN1BitStringParser
+    extends ASN1Encodable, InMemoryRepresentable
+{
+    /**
+     * Return an InputStream representing the contents of the BIT STRING. The final
+     * byte, if any, may include pad bits. See {@link #getPadBits()}.
+     *
+     * @return an InputStream with its source as the BIT STRING content.
+     */
+    public InputStream getBitStream() throws IOException;
+
+    /**
+     * Return an InputStream representing the contents of the BIT STRING, where the
+     * content is expected to be octet-aligned (this will be automatically checked
+     * during parsing).
+     *
+     * @return an InputStream with its source as the BIT STRING content.
+     */
+    public InputStream getOctetStream() throws IOException;
+
+    /**
+     * Return the number of pad bits, if any, in the final byte, if any, read from
+     * {@link #getBitStream()}. This number is in the range zero to seven. That
+     * number of the least significant bits of the final byte, if any, are not part
+     * of the contents and should be ignored. NOTE: Must be called AFTER the stream
+     * has been fully processed. (Does not need to be called if
+     * {@link #getOctetStream()} was used instead of {@link #getBitStream()}).
+     *
+     * @return the number of pad bits. In the range zero to seven.
+     */
+    public int getPadBits();
+}
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1Boolean.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1Boolean.java
index afb0cad..d6b5cef 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1Boolean.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1Boolean.java
@@ -18,6 +18,14 @@
 public class ASN1Boolean
     extends ASN1Primitive
 {
+    static final ASN1UniversalType TYPE = new ASN1UniversalType(ASN1Boolean.class, BERTags.BOOLEAN)
+    {
+        ASN1Primitive fromImplicitPrimitive(DEROctetString octetString)
+        {
+            return createPrimitive(octetString.getOctets());
+        }
+    };
+
     private static final byte FALSE_VALUE = 0x00;
     private static final byte TRUE_VALUE = (byte)0xFF;
 
@@ -46,7 +54,7 @@
             byte[] enc = (byte[])obj;
             try
             {
-                return (ASN1Boolean)fromByteArray(enc);
+                return (ASN1Boolean)TYPE.fromByteArray(enc);
             }
             catch (IOException e)
             {
@@ -91,25 +99,16 @@
     /**
      * Return a Boolean from a tagged object.
      *
-     * @param obj the tagged object holding the object we want
+     * @param taggedObject the tagged object holding the object we want
      * @param explicit true if the object is meant to be explicitly
      *              tagged false otherwise.
      * @exception IllegalArgumentException if the tagged object cannot
      *               be converted.
      * @return an ASN1Boolean instance.
      */
-    public static ASN1Boolean getInstance(ASN1TaggedObject obj, boolean explicit)
+    public static ASN1Boolean getInstance(ASN1TaggedObject taggedObject, boolean explicit)
     {
-        ASN1Primitive o = obj.getObject();
-
-        if (explicit || o instanceof ASN1Boolean)
-        {
-            return getInstance(o);
-        }
-        else
-        {
-            return ASN1Boolean.fromOctetString(ASN1OctetString.getInstance(o).getOctets());
-        }
+        return (ASN1Boolean)TYPE.getContextInstance(taggedObject, explicit);
     }
 
     private ASN1Boolean(byte value)
@@ -122,19 +121,19 @@
         return value != FALSE_VALUE;
     }
 
-    boolean isConstructed()
+    boolean encodeConstructed()
     {
         return false;
     }
 
-    int encodedLength()
+    int encodedLength(boolean withTag)
     {
-        return 3;
+        return ASN1OutputStream.getLengthOfEncodingDL(withTag, 1);
     }
 
     void encode(ASN1OutputStream out, boolean withTag) throws IOException
     {
-        out.writeEncoded(withTag, BERTags.BOOLEAN, value);
+        out.writeEncodingDL(withTag, BERTags.BOOLEAN, value);
     }
 
     boolean asn1Equals(ASN1Primitive other)
@@ -164,14 +163,14 @@
       return isTrue() ? "TRUE" : "FALSE";
     }
 
-    static ASN1Boolean fromOctetString(byte[] value)
+    static ASN1Boolean createPrimitive(byte[] contents)
     {
-        if (value.length != 1)
+        if (contents.length != 1)
         {
             throw new IllegalArgumentException("BOOLEAN value should have 1 byte in it");
         }
 
-        byte b = value[0];
+        byte b = contents[0];
         switch (b)
         {
         case FALSE_VALUE:   return FALSE;
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1EncodableVector.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1EncodableVector.java
index 7dbd82d..6a885b7 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1EncodableVector.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1EncodableVector.java
@@ -52,6 +52,16 @@
         this.elementCount = minCapacity;
     }
 
+    public void addAll(ASN1Encodable[] others)
+    {
+        if (null == others)
+        {
+            throw new NullPointerException("'others' cannot be null");
+        }
+
+        doAddAll(others, "'others' elements cannot be null");
+    }
+
     public void addAll(ASN1EncodableVector other)
     {
         if (null == other)
@@ -59,7 +69,12 @@
             throw new NullPointerException("'other' cannot be null");
         }
 
-        int otherElementCount = other.size();
+        doAddAll(other.elements, "'other' elements cannot be null");
+    }
+
+    private void doAddAll(ASN1Encodable[] others, String nullMsg)
+    {
+        int otherElementCount = others.length;
         if (otherElementCount < 1)
         {
             return;
@@ -75,10 +90,10 @@
         int i = 0;
         do
         {
-            ASN1Encodable otherElement = other.get(i);
+            ASN1Encodable otherElement = others[i];
             if (null == otherElement)
             {
-                throw new NullPointerException("'other' elements cannot be null");
+                throw new NullPointerException(nullMsg);
             }
 
             this.elements[elementCount + i] = otherElement;
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1Enumerated.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1Enumerated.java
index e67e174..6d96fb6 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1Enumerated.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1Enumerated.java
@@ -13,8 +13,13 @@
 public class ASN1Enumerated
     extends ASN1Primitive
 {
-    private final byte[] bytes;
-    private final int start;
+    static final ASN1UniversalType TYPE = new ASN1UniversalType(ASN1Enumerated.class, BERTags.ENUMERATED)
+    {
+        ASN1Primitive fromImplicitPrimitive(DEROctetString octetString)
+        {
+            return createPrimitive(octetString.getOctets(), false);
+        }
+    };
 
     /**
      * return an enumerated from the passed in object
@@ -35,7 +40,7 @@
         {
             try
             {
-                return (ASN1Enumerated)fromByteArray((byte[])obj);
+                return (ASN1Enumerated)TYPE.fromByteArray((byte[])obj);
             }
             catch (Exception e)
             {
@@ -49,29 +54,21 @@
     /**
      * return an Enumerated from a tagged object.
      *
-     * @param obj the tagged object holding the object we want
+     * @param taggedObject the tagged object holding the object we want
      * @param explicit true if the object is meant to be explicitly
      *              tagged false otherwise.
      * @exception IllegalArgumentException if the tagged object cannot
      *               be converted.
      * @return an ASN1Enumerated instance, or null.
      */
-    public static ASN1Enumerated getInstance(
-        ASN1TaggedObject obj,
-        boolean          explicit)
+    public static ASN1Enumerated getInstance(ASN1TaggedObject taggedObject, boolean explicit)
     {
-        ASN1Primitive o = obj.getObject();
-
-        if (explicit || o instanceof ASN1Enumerated)
-        {
-            return getInstance(o);
-        }
-        else
-        {
-            return fromOctetString(ASN1OctetString.getInstance(o).getOctets());
-        }
+        return (ASN1Enumerated)TYPE.getContextInstance(taggedObject, explicit);
     }
 
+    private final byte[] contents;
+    private final int start;
+
     /**
      * Constructor from int.
      *
@@ -84,7 +81,7 @@
             throw new IllegalArgumentException("enumerated must be non-negative");
         }
 
-        this.bytes = BigInteger.valueOf(value).toByteArray();
+        this.contents = BigInteger.valueOf(value).toByteArray();
         this.start = 0;
     }
 
@@ -100,67 +97,78 @@
             throw new IllegalArgumentException("enumerated must be non-negative");
         }
 
-        this.bytes = value.toByteArray();
+        this.contents = value.toByteArray();
         this.start = 0;
     }
 
     /**
      * Constructor from encoded BigInteger.
      *
-     * @param bytes the value of this enumerated as an encoded BigInteger (signed).
+     * @param contents the value of this enumerated as an encoded BigInteger (signed).
      */
-    public ASN1Enumerated(byte[] bytes)
+    public ASN1Enumerated(byte[] contents)
     {
-        if (ASN1Integer.isMalformed(bytes))
+        this(contents, true);
+    }
+
+    ASN1Enumerated(byte[] contents, boolean clone)
+    {
+        if (ASN1Integer.isMalformed(contents))
         {
             throw new IllegalArgumentException("malformed enumerated");
         }
-        if (0 != (bytes[0] & 0x80))
+        if (0 != (contents[0] & 0x80))
         {
             throw new IllegalArgumentException("enumerated must be non-negative");
         }
 
-        this.bytes = Arrays.clone(bytes);
-        this.start = ASN1Integer.signBytesToSkip(bytes); 
+        this.contents = clone ? Arrays.clone(contents) : contents;
+        this.start = ASN1Integer.signBytesToSkip(contents); 
     }
 
     public BigInteger getValue()
     {
-        return new BigInteger(bytes);
+        return new BigInteger(contents);
+    }
+
+    public boolean hasValue(int x)
+    {
+        return (contents.length - start) <= 4
+            && ASN1Integer.intValue(contents, start, ASN1Integer.SIGN_EXT_SIGNED) == x;
     }
 
     public boolean hasValue(BigInteger x)
     {
         return null != x
             // Fast check to avoid allocation
-            && ASN1Integer.intValue(bytes, start, ASN1Integer.SIGN_EXT_SIGNED) == x.intValue()
+            && ASN1Integer.intValue(contents, start, ASN1Integer.SIGN_EXT_SIGNED) == x.intValue()
             && getValue().equals(x);
     }
 
     public int intValueExact()
     {
-        int count = bytes.length - start;
+        int count = contents.length - start;
         if (count > 4)
         {
             throw new ArithmeticException("ASN.1 Enumerated out of int range");
         }
 
-        return ASN1Integer.intValue(bytes, start, ASN1Integer.SIGN_EXT_SIGNED); 
+        return ASN1Integer.intValue(contents, start, ASN1Integer.SIGN_EXT_SIGNED); 
     }
 
-    boolean isConstructed()
+    boolean encodeConstructed()
     {
         return false;
     }
 
-    int encodedLength()
+    int encodedLength(boolean withTag)
     {
-        return 1 + StreamUtil.calculateBodyLength(bytes.length) + bytes.length;
+        return ASN1OutputStream.getLengthOfEncodingDL(withTag, contents.length);
     }
 
     void encode(ASN1OutputStream out, boolean withTag) throws IOException
     {
-        out.writeEncoded(withTag, BERTags.ENUMERATED, bytes);
+        out.writeEncodingDL(withTag, BERTags.ENUMERATED, contents);
     }
 
     boolean asn1Equals(
@@ -173,39 +181,39 @@
 
         ASN1Enumerated other = (ASN1Enumerated)o;
 
-        return Arrays.areEqual(this.bytes, other.bytes);
+        return Arrays.areEqual(this.contents, other.contents);
     }
 
     public int hashCode()
     {
-        return Arrays.hashCode(bytes);
+        return Arrays.hashCode(contents);
     }
 
-    private static ASN1Enumerated[] cache = new ASN1Enumerated[12];
+    private static final ASN1Enumerated[] cache = new ASN1Enumerated[12];
 
-    static ASN1Enumerated fromOctetString(byte[] enc)
+    static ASN1Enumerated createPrimitive(byte[] contents, boolean clone)
     {
-        if (enc.length > 1)
+        if (contents.length > 1)
         {
-            return new ASN1Enumerated(enc);
+            return new ASN1Enumerated(contents, clone);
         }
 
-        if (enc.length == 0)
+        if (contents.length == 0)
         {
             throw new IllegalArgumentException("ENUMERATED has zero length");
         }
-        int value = enc[0] & 0xff;
+        int value = contents[0] & 0xff;
 
         if (value >= cache.length)
         {
-            return new ASN1Enumerated(enc);
+            return new ASN1Enumerated(contents, clone);
         }
 
         ASN1Enumerated possibleMatch = cache[value];
 
         if (possibleMatch == null)
         {
-            possibleMatch = cache[value] = new ASN1Enumerated(enc);
+            possibleMatch = cache[value] = new ASN1Enumerated(contents, clone);
         }
 
         return possibleMatch;
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1External.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1External.java
index dee1198..345858b 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1External.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1External.java
@@ -3,6 +3,8 @@
 
 import java.io.IOException;
 
+import com.android.org.bouncycastle.util.Objects;
+
 /**
  * Class representing the DER-type External
  * @hide This class is not part of the Android public SDK API
@@ -10,101 +12,124 @@
 public abstract class ASN1External
     extends ASN1Primitive
 {
-    protected ASN1ObjectIdentifier directReference;
-    protected ASN1Integer indirectReference;
-    protected ASN1Primitive dataValueDescriptor;
-    protected int encoding;
-    protected ASN1Primitive externalContent;
+    static final ASN1UniversalType TYPE = new ASN1UniversalType(ASN1External.class, BERTags.EXTERNAL)
+    {
+        ASN1Primitive fromImplicitConstructed(ASN1Sequence sequence)
+        {
+            // TODO Ideally ASN1External would have no subclasses and just hold the sequence
+            return sequence.toASN1External();
+        }
+    };
 
-    /**
-     * Construct an EXTERNAL object, the input encoding vector must have exactly two elements on it.
-     * <p>
-     * Acceptable input formats are:
-     * <ul>
-     * <li> {@link ASN1ObjectIdentifier} + data {@link DERTaggedObject} (direct reference form)</li>
-     * <li> {@link ASN1Integer} + data {@link DERTaggedObject} (indirect reference form)</li>
-     * <li> Anything but {@link DERTaggedObject} + data {@link DERTaggedObject} (data value form)</li>
-     * </ul>
-     *
-     * @throws IllegalArgumentException if input size is wrong, or
-     */
-    public ASN1External(ASN1EncodableVector vector)
+    public static ASN1External getInstance(Object obj)
+    {
+        if (obj == null || obj instanceof ASN1External)
+        {
+            return (ASN1External)obj;
+        }
+        else if (obj instanceof ASN1Encodable)
+        {
+            ASN1Primitive primitive = ((ASN1Encodable)obj).toASN1Primitive();
+            if (primitive instanceof ASN1External)
+            {
+                return (ASN1External)primitive;
+            }
+        }
+        else if (obj instanceof byte[])
+        {
+            try
+            {
+                return (ASN1External)TYPE.fromByteArray((byte[])obj);
+            }
+            catch (IOException e)
+            {
+                throw new IllegalArgumentException("failed to construct external from byte[]: " + e.getMessage());
+            }
+        }
+
+        throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
+    }
+
+    public static ASN1External getInstance(ASN1TaggedObject taggedObject, boolean explicit)
+    {
+        return (ASN1External)TYPE.getContextInstance(taggedObject, explicit);
+    }
+
+    ASN1ObjectIdentifier directReference;
+    ASN1Integer indirectReference;
+    // TODO Actually use ASN1ObjectDescriptor for this
+    ASN1Primitive dataValueDescriptor;
+    int encoding;
+    ASN1Primitive externalContent;
+
+    ASN1External(ASN1Sequence sequence)
     {
         int offset = 0;
 
-        ASN1Primitive enc = getObjFromVector(vector, offset);
-        if (enc instanceof ASN1ObjectIdentifier)
+        ASN1Primitive asn1 = getObjFromSequence(sequence, offset);
+        if (asn1 instanceof ASN1ObjectIdentifier)
         {
-            directReference = (ASN1ObjectIdentifier)enc;
-            offset++;
-            enc = getObjFromVector(vector, offset);
+            directReference = (ASN1ObjectIdentifier)asn1;
+            asn1 = getObjFromSequence(sequence, ++offset);
         }
-        if (enc instanceof ASN1Integer)
+        if (asn1 instanceof ASN1Integer)
         {
-            indirectReference = (ASN1Integer) enc;
-            offset++;
-            enc = getObjFromVector(vector, offset);
+            indirectReference = (ASN1Integer)asn1;
+            asn1 = getObjFromSequence(sequence, ++offset);
         }
-        if (!(enc instanceof ASN1TaggedObject))
+        if (!(asn1 instanceof ASN1TaggedObject))
         {
-            dataValueDescriptor = (ASN1Primitive) enc;
-            offset++;
-            enc = getObjFromVector(vector, offset);
+            dataValueDescriptor = asn1;
+            asn1 = getObjFromSequence(sequence, ++offset);
         }
 
-        if (vector.size() != offset + 1)
+        if (sequence.size() != offset + 1)
         {
-            throw new IllegalArgumentException("input vector too large");
+            throw new IllegalArgumentException("input sequence too large");
         }
 
-        if (!(enc instanceof ASN1TaggedObject))
+        if (!(asn1 instanceof ASN1TaggedObject))
         {
-            throw new IllegalArgumentException("No tagged object found in vector. Structure doesn't seem to be of type External");
+            throw new IllegalArgumentException(
+                "No tagged object found in sequence. Structure doesn't seem to be of type External");
         }
-        ASN1TaggedObject obj = (ASN1TaggedObject)enc;
-        setEncoding(obj.getTagNo());
-        externalContent = obj.getObject();
+
+        ASN1TaggedObject obj = (ASN1TaggedObject)asn1;
+        this.encoding = checkEncoding(obj.getTagNo());
+        this.externalContent = getExternalContent(obj);
     }
 
-    private ASN1Primitive getObjFromVector(ASN1EncodableVector v, int index)
+    ASN1External(ASN1ObjectIdentifier directReference, ASN1Integer indirectReference, ASN1Primitive dataValueDescriptor,
+        DERTaggedObject externalData)
     {
-        if (v.size() <= index)
-        {
-            throw new IllegalArgumentException("too few objects in input vector");
-        }
-
-        return v.get(index).toASN1Primitive();
+        this.directReference = directReference;
+        this.indirectReference = indirectReference;
+        this.dataValueDescriptor = dataValueDescriptor;
+        this.encoding = checkEncoding(externalData.getTagNo());
+        this.externalContent = getExternalContent(externalData);
     }
 
-    /**
-     * Creates a new instance of External
-     * See X.690 for more informations about the meaning of these parameters
-     * @param directReference The direct reference or <code>null</code> if not set.
-     * @param indirectReference The indirect reference or <code>null</code> if not set.
-     * @param dataValueDescriptor The data value descriptor or <code>null</code> if not set.
-     * @param externalData The external data in its encoded form.
-     */
-    public ASN1External(ASN1ObjectIdentifier directReference, ASN1Integer indirectReference, ASN1Primitive dataValueDescriptor, DERTaggedObject externalData)
+    ASN1External(ASN1ObjectIdentifier directReference, ASN1Integer indirectReference, ASN1Primitive dataValueDescriptor,
+        int encoding, ASN1Primitive externalData)
     {
-        this(directReference, indirectReference, dataValueDescriptor, externalData.getTagNo(), externalData.toASN1Primitive());
+        this.directReference = directReference;
+        this.indirectReference = indirectReference;
+        this.dataValueDescriptor = dataValueDescriptor;
+        this.encoding = checkEncoding(encoding);
+        this.externalContent = checkExternalContent(encoding, externalData);
     }
 
-    /**
-     * Creates a new instance of External.
-     * See X.690 for more informations about the meaning of these parameters
-     * @param directReference The direct reference or <code>null</code> if not set.
-     * @param indirectReference The indirect reference or <code>null</code> if not set.
-     * @param dataValueDescriptor The data value descriptor or <code>null</code> if not set.
-     * @param encoding The encoding to be used for the external data
-     * @param externalData The external data
-     */
-    public ASN1External(ASN1ObjectIdentifier directReference, ASN1Integer indirectReference, ASN1Primitive dataValueDescriptor, int encoding, ASN1Primitive externalData)
+    abstract ASN1Sequence buildSequence();
+
+    int encodedLength(boolean withTag) throws IOException
     {
-        setDirectReference(directReference);
-        setIndirectReference(indirectReference);
-        setDataValueDescriptor(dataValueDescriptor);
-        setEncoding(encoding);
-        setExternalContent(externalData.toASN1Primitive());
+        return buildSequence().encodedLength(withTag);
+    }
+
+    void encode(ASN1OutputStream out, boolean withTag) throws IOException
+    {
+        out.writeIdentifier(withTag, BERTags.CONSTRUCTED | BERTags.EXTERNAL);
+        buildSequence().encode(out, false);
     }
 
     ASN1Primitive toDERObject()
@@ -117,75 +142,38 @@
         return new DLExternal(directReference, indirectReference, dataValueDescriptor, encoding, externalContent);
     }
 
-    /* (non-Javadoc)
-     * @see java.lang.Object#hashCode()
-     */
     public int hashCode()
     {
-        int ret = 0;
-        if (directReference != null)
-        {
-            ret = directReference.hashCode();
-        }
-        if (indirectReference != null)
-        {
-            ret ^= indirectReference.hashCode();
-        }
-        if (dataValueDescriptor != null)
-        {
-            ret ^= dataValueDescriptor.hashCode();
-        }
-        ret ^= externalContent.hashCode();
-        return ret;
+        return Objects.hashCode(this.directReference)
+            ^  Objects.hashCode(this.indirectReference)
+            ^  Objects.hashCode(this.dataValueDescriptor)
+            ^  this.encoding
+            ^  this.externalContent.hashCode();
     }
 
-    boolean isConstructed()
+    boolean encodeConstructed()
     {
         return true;
     }
 
-    int encodedLength()
-        throws IOException
+    boolean asn1Equals(ASN1Primitive primitive)
     {
-        return this.getEncoded().length;
-    }
-
-    /* (non-Javadoc)
-     * @see org.bouncycastle.asn1.ASN1Primitive#asn1Equals(org.bouncycastle.asn1.ASN1Primitive)
-     */
-    boolean asn1Equals(ASN1Primitive o)
-    {
-        if (!(o instanceof ASN1External))
-        {
-            return false;
-        }
-        if (this == o)
+        if (this == primitive)
         {
             return true;
         }
-        ASN1External other = (ASN1External)o;
-        if (directReference != null)
+        if (!(primitive instanceof ASN1External))
         {
-            if (other.directReference == null || !other.directReference.equals(directReference))  
-            {
-                return false;
-            }
+            return false;
         }
-        if (indirectReference != null)
-        {
-            if (other.indirectReference == null || !other.indirectReference.equals(indirectReference))
-            {
-                return false;
-            }
-        }
-        if (dataValueDescriptor != null)
-        {
-            if (other.dataValueDescriptor == null || !other.dataValueDescriptor.equals(dataValueDescriptor))
-            {
-                return false;
-            }
-        }
-        return externalContent.equals(other.externalContent);
+
+        ASN1External that = (ASN1External)primitive;
+
+        return Objects.areEqual(this.directReference, that.directReference)
+            && Objects.areEqual(this.indirectReference, that.indirectReference)
+            && Objects.areEqual(this.dataValueDescriptor, that.dataValueDescriptor)
+            && this.encoding == that.encoding
+            && this.externalContent.equals(that.externalContent);
     }
 
     /**
@@ -219,7 +207,7 @@
     {
         return encoding;
     }
-    
+
     /**
      * Returns the content of this element
      * @return The content
@@ -228,7 +216,7 @@
     {
         return externalContent;
     }
-    
+
     /**
      * Returns the indirect reference of this element
      * @return The reference
@@ -237,27 +225,9 @@
     {
         return indirectReference;
     }
-    
-    /**
-     * Sets the data value descriptor
-     * @param dataValueDescriptor The descriptor
-     */
-    private void setDataValueDescriptor(ASN1Primitive dataValueDescriptor)
-    {
-        this.dataValueDescriptor = dataValueDescriptor;
-    }
 
     /**
-     * Sets the direct reference of the external element
-     * @param directReferemce The reference
-     */
-    private void setDirectReference(ASN1ObjectIdentifier directReferemce)
-    {
-        this.directReference = directReferemce;
-    }
-    
-    /**
-     * Sets the encoding of the content. Valid values are
+     * Checks the encoding of the content. Valid values are
      * <ul>
      * <li><code>0</code> single-ASN1-type</li>
      * <li><code>1</code> OCTET STRING</li>
@@ -265,30 +235,57 @@
      * </ul>
      * @param encoding The encoding
      */
-    private void setEncoding(int encoding)
+    private static int checkEncoding(int encoding)
     {
         if (encoding < 0 || encoding > 2)
         {
             throw new IllegalArgumentException("invalid encoding value: " + encoding);
         }
-        this.encoding = encoding;
+
+        return encoding;
     }
-    
-    /**
-     * Sets the content of this element
-     * @param externalContent The content
-     */
-    private void setExternalContent(ASN1Primitive externalContent)
+
+    private static ASN1Primitive checkExternalContent(int tagNo, ASN1Primitive externalContent)
     {
-        this.externalContent = externalContent;
+        switch (tagNo)
+        {
+        case 1:
+            return ASN1OctetString.TYPE.checkedCast(externalContent);
+        case 2:
+            return ASN1BitString.TYPE.checkedCast(externalContent);
+        default:
+            return externalContent;
+        }
     }
-    
-    /**
-     * Sets the indirect reference of this element
-     * @param indirectReference The reference
-     */
-    private void setIndirectReference(ASN1Integer indirectReference)
+
+    private static ASN1Primitive getExternalContent(ASN1TaggedObject encoding)
     {
-        this.indirectReference = indirectReference;
+        int tagClass = encoding.getTagClass(), tagNo = encoding.getTagNo();
+        if (BERTags.CONTEXT_SPECIFIC != tagClass)
+        {
+            throw new IllegalArgumentException("invalid tag: " + ASN1Util.getTagText(tagClass, tagNo));
+        }
+
+        switch (tagNo)
+        {
+        case 0:
+            return encoding.getExplicitBaseObject().toASN1Primitive();
+        case 1:
+            return ASN1OctetString.getInstance(encoding, false);
+        case 2:
+            return ASN1BitString.getInstance(encoding, false);
+        default:
+            throw new IllegalArgumentException("invalid tag: " + ASN1Util.getTagText(tagClass, tagNo));
+        }
+    }
+
+    private static ASN1Primitive getObjFromSequence(ASN1Sequence sequence, int index)
+    {
+        if (sequence.size() <= index)
+        {
+            throw new IllegalArgumentException("too few objects in input sequence");
+        }
+
+        return sequence.getObjectAt(index).toASN1Primitive();
     }
 }
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1ApplicationSpecificParser.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1ExternalParser.java
similarity index 81%
rename from repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1ApplicationSpecificParser.java
rename to repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1ExternalParser.java
index ff1d48c..7d1300e 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1ApplicationSpecificParser.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1ExternalParser.java
@@ -4,10 +4,10 @@
 import java.io.IOException;
 
 /**
- * Interface to parse ASN.1 ApplicationSpecific objects.
+ * Parser DER EXTERNAL tagged objects.
  * @hide This class is not part of the Android public SDK API
  */
-public interface ASN1ApplicationSpecificParser
+public interface ASN1ExternalParser
     extends ASN1Encodable, InMemoryRepresentable
 {
     /**
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1GeneralString.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1GeneralString.java
new file mode 100644
index 0000000..61bcd1f
--- /dev/null
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1GeneralString.java
@@ -0,0 +1,153 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.org.bouncycastle.asn1;
+
+import java.io.IOException;
+
+import com.android.org.bouncycastle.util.Arrays;
+import com.android.org.bouncycastle.util.Strings;
+
+/**
+ * ASN.1 GENERAL-STRING data type.
+ * <p>
+ * This is an 8-bit encoded ISO 646 (ASCII) character set
+ * with optional escapes to other character sets.
+ * </p>
+ * @hide This class is not part of the Android public SDK API
+ */
+public abstract class ASN1GeneralString 
+    extends ASN1Primitive
+    implements ASN1String
+{
+    static final ASN1UniversalType TYPE = new ASN1UniversalType(ASN1GeneralString.class, BERTags.GENERAL_STRING)
+    {
+        ASN1Primitive fromImplicitPrimitive(DEROctetString octetString)
+        {
+            return createPrimitive(octetString.getOctets());
+        }
+    };
+
+    /**
+     * Return a GeneralString from the given object.
+     *
+     * @param obj the object we want converted.
+     * @exception IllegalArgumentException if the object cannot be converted.
+     * @return an ASN1GeneralString instance, or null.
+     */
+    public static ASN1GeneralString getInstance(Object obj)
+    {
+        if (obj == null || obj instanceof ASN1GeneralString) 
+        {
+            return (ASN1GeneralString) obj;
+        }
+        if (obj instanceof ASN1Encodable)
+        {
+            ASN1Primitive primitive = ((ASN1Encodable)obj).toASN1Primitive();
+            if (primitive instanceof ASN1GeneralString)
+            {
+                return (ASN1GeneralString)primitive;
+            }
+        }
+        if (obj instanceof byte[])
+        {
+            try
+            {
+                return (ASN1GeneralString)TYPE.fromByteArray((byte[])obj);
+            }
+            catch (Exception e)
+            {
+                throw new IllegalArgumentException("encoding error in getInstance: " + e.toString());
+            }
+        }
+
+        throw new IllegalArgumentException("illegal object in getInstance: "
+                + obj.getClass().getName());
+    }
+
+    /**
+     * Return a GeneralString from a tagged object.
+     *
+     * @param taggedObject      the tagged object holding the object we want
+     * @param explicit true if the object is meant to be explicitly tagged false
+     *                 otherwise.
+     * @exception IllegalArgumentException if the tagged object cannot be converted.
+     * @return an ASN1GeneralString instance.
+     */
+    public static ASN1GeneralString getInstance(ASN1TaggedObject taggedObject, boolean explicit)
+    {
+        return (ASN1GeneralString)TYPE.getContextInstance(taggedObject, explicit);
+    }
+
+    final byte[] contents;
+
+    ASN1GeneralString(String string) 
+    {
+        this.contents = Strings.toByteArray(string);
+    }
+
+    ASN1GeneralString(byte[] contents, boolean clone)
+    {
+        this.contents = clone ? Arrays.clone(contents) : contents;
+    }
+
+    /**
+     * Return a Java String representation of our contained String.
+     *
+     * @return a Java String representing our contents.
+     */
+    public final String getString() 
+    {
+        return Strings.fromByteArray(contents);
+    }
+
+    public String toString()
+    {
+        return getString();
+    }
+
+    /**
+     * Return a byte array representation of our contained String.
+     *
+     * @return a byte array representing our contents.
+     */
+    public final byte[] getOctets() 
+    {
+        return Arrays.clone(contents);
+    }
+
+    final boolean encodeConstructed()
+    {
+        return false;
+    }
+
+    final int encodedLength(boolean withTag)
+    {
+        return ASN1OutputStream.getLengthOfEncodingDL(withTag, contents.length);
+    }
+
+    final void encode(ASN1OutputStream out, boolean withTag) throws IOException
+    {
+        out.writeEncodingDL(withTag, BERTags.GENERAL_STRING, contents);
+    }
+
+    final boolean asn1Equals(ASN1Primitive other)
+    {
+        if (!(other instanceof ASN1GeneralString))
+        {
+            return false;
+        }
+
+        ASN1GeneralString that = (ASN1GeneralString)other;
+
+        return Arrays.areEqual(this.contents, that.contents);
+    }
+
+    public final int hashCode() 
+    {
+        return Arrays.hashCode(contents);
+    }
+
+    static ASN1GeneralString createPrimitive(byte[] contents)
+    {
+        return new DERGeneralString(contents, false);
+    }
+}
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1GeneralizedTime.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1GeneralizedTime.java
index 319c9bc..50b9795 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1GeneralizedTime.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1GeneralizedTime.java
@@ -48,7 +48,13 @@
 public class ASN1GeneralizedTime
     extends ASN1Primitive
 {
-    protected byte[] time;
+    static final ASN1UniversalType TYPE = new ASN1UniversalType(ASN1GeneralizedTime.class, BERTags.GENERALIZED_TIME)
+    {
+        ASN1Primitive fromImplicitPrimitive(DEROctetString octetString)
+        {
+            return createPrimitive(octetString.getOctets());
+        }
+    };
 
     /**
      * return a generalized time from the passed in object
@@ -64,12 +70,19 @@
         {
             return (ASN1GeneralizedTime)obj;
         }
-
+        if (obj instanceof ASN1Encodable)
+        {
+            ASN1Primitive primitive = ((ASN1Encodable)obj).toASN1Primitive();
+            if (primitive instanceof ASN1GeneralizedTime)
+            {
+                return (ASN1GeneralizedTime)primitive;
+            }
+        }
         if (obj instanceof byte[])
         {
             try
             {
-                return (ASN1GeneralizedTime)fromByteArray((byte[])obj);
+                return (ASN1GeneralizedTime)TYPE.fromByteArray((byte[])obj);
             }
             catch (Exception e)
             {
@@ -83,29 +96,19 @@
     /**
      * return a Generalized Time object from a tagged object.
      *
-     * @param obj      the tagged object holding the object we want
-     * @param explicit true if the object is meant to be explicitly
-     *                 tagged false otherwise.
+     * @param taggedObject the tagged object holding the object we want
+     * @param explicit     true if the object is meant to be explicitly tagged false
+     *                     otherwise.
      * @return an ASN1GeneralizedTime instance.
-     * @throws IllegalArgumentException if the tagged object cannot
-     * be converted.
+     * @throws IllegalArgumentException if the tagged object cannot be converted.
      */
-    public static ASN1GeneralizedTime getInstance(
-        ASN1TaggedObject obj,
-        boolean explicit)
+    public static ASN1GeneralizedTime getInstance(ASN1TaggedObject taggedObject, boolean explicit)
     {
-        ASN1Primitive o = obj.getObject();
-
-        if (explicit || o instanceof ASN1GeneralizedTime)
-        {
-            return getInstance(o);
-        }
-        else
-        {
-            return new ASN1GeneralizedTime(ASN1OctetString.getInstance(o).getOctets());
-        }
+        return (ASN1GeneralizedTime)TYPE.getContextInstance(taggedObject, explicit);
     }
 
+    final byte[] contents;
+
     /**
      * The correct format for this is YYYYMMDDHHMMSS[.f]Z, or without the Z
      * for local time, or Z+-HHMM on the end, for difference between local
@@ -118,7 +121,7 @@
     public ASN1GeneralizedTime(
         String time)
     {
-        this.time = Strings.toByteArray(time);
+        this.contents = Strings.toByteArray(time);
         try
         {
             this.getDate();
@@ -143,7 +146,7 @@
 
         dateF.setTimeZone(new SimpleTimeZone(0, "Z"));
 
-        this.time = Strings.toByteArray(dateF.format(time));
+        this.contents = Strings.toByteArray(dateF.format(time));
     }
 
     /**
@@ -165,7 +168,7 @@
 
         dateF.setTimeZone(new SimpleTimeZone(0, "Z"));
 
-        this.time = Strings.toByteArray(dateF.format(time));
+        this.contents = Strings.toByteArray(dateF.format(time));
     }
 
     ASN1GeneralizedTime(
@@ -175,7 +178,7 @@
         {
             throw new IllegalArgumentException("GeneralizedTime string too short");
         }
-        this.time = bytes;
+        this.contents = bytes;
 
         if (!(isDigit(0) && isDigit(1) && isDigit(2) && isDigit(3)))
         {
@@ -190,7 +193,7 @@
      */
     public String getTimeString()
     {
-        return Strings.fromByteArray(time);
+        return Strings.fromByteArray(contents);
     }
 
     /**
@@ -208,7 +211,7 @@
      */
     public String getTime()
     {
-        String stime = Strings.fromByteArray(time);
+        String stime = Strings.fromByteArray(contents);
 
         //
         // standardise the format.
@@ -360,34 +363,26 @@
         throws ParseException
     {
         SimpleDateFormat dateF;
-        String stime = Strings.fromByteArray(time);
+        String stime = Strings.fromByteArray(contents);
         String d = stime;
 
         if (stime.endsWith("Z"))
         {
             if (hasFractionalSeconds())
             {
-                // Android-changed: Use localized version
-                // dateF = new SimpleDateFormat("yyyyMMddHHmmss.SSS'Z'");
-                dateF = new SimpleDateFormat("yyyyMMddHHmmss.SSS'Z'", Locale.US);
+                dateF = new SimpleDateFormat("yyyyMMddHHmmss.SSS'Z'", LocaleUtil.EN_Locale);
             }
             else if (hasSeconds())
             {
-                // Android-changed: Use localized version
-                // dateF = new SimpleDateFormat("yyyyMMddHHmmss'Z'");
-                dateF = new SimpleDateFormat("yyyyMMddHHmmss'Z'", Locale.US);
+                dateF = new SimpleDateFormat("yyyyMMddHHmmss'Z'", LocaleUtil.EN_Locale);
             }
             else if (hasMinutes())
             {
-                // Android-changed: Use localized version
-                // dateF = new SimpleDateFormat("yyyyMMddHHmm'Z'");
-                dateF = new SimpleDateFormat("yyyyMMddHHmm'Z'", Locale.US);
+                dateF = new SimpleDateFormat("yyyyMMddHHmm'Z'", LocaleUtil.EN_Locale);
             }
             else
             {
-                // Android-changed: Use localized version
-                // dateF = new SimpleDateFormat("yyyyMMddHH'Z'");
-                dateF = new SimpleDateFormat("yyyyMMddHH'Z'", Locale.US);
+                dateF = new SimpleDateFormat("yyyyMMddHH'Z'", LocaleUtil.EN_Locale);
             }
 
             dateF.setTimeZone(new SimpleTimeZone(0, "Z"));
@@ -432,14 +427,14 @@
             d = pruneFractionalSeconds(d);
         }
         
-        return DateUtil.epochAdjust(dateF.parse(d));
+        return dateF.parse(d);
     }
 
     protected boolean hasFractionalSeconds()
     {
-        for (int i = 0; i != time.length; i++)
+        for (int i = 0; i != contents.length; i++)
         {
-            if (time[i] == '.')
+            if (contents[i] == '.')
             {
                 if (i == 14)
                 {
@@ -462,49 +457,46 @@
 
     private boolean isDigit(int pos)
     {
-        return time.length > pos && time[pos] >= '0' && time[pos] <= '9';
+        return contents.length > pos && contents[pos] >= '0' && contents[pos] <= '9';
     }
 
-    boolean isConstructed()
+    final boolean encodeConstructed()
     {
         return false;
     }
 
-    int encodedLength()
+    int encodedLength(boolean withTag)
     {
-        int length = time.length;
-
-        return 1 + StreamUtil.calculateBodyLength(length) + length;
+        return ASN1OutputStream.getLengthOfEncodingDL(withTag, contents.length);
     }
 
     void encode(ASN1OutputStream out, boolean withTag) throws IOException
     {
-        out.writeEncoded(withTag, BERTags.GENERALIZED_TIME, time);
+        out.writeEncodingDL(withTag, BERTags.GENERALIZED_TIME, contents);
     }
 
     ASN1Primitive toDERObject()
     {
-        return new DERGeneralizedTime(time);
+        return new DERGeneralizedTime(contents);
     }
 
-    ASN1Primitive toDLObject()
-    {
-        return new DERGeneralizedTime(time);
-    }
-
-    boolean asn1Equals(
-        ASN1Primitive o)
+    boolean asn1Equals(ASN1Primitive o)
     {
         if (!(o instanceof ASN1GeneralizedTime))
         {
             return false;
         }
 
-        return Arrays.areEqual(time, ((ASN1GeneralizedTime)o).time);
+        return Arrays.areEqual(contents, ((ASN1GeneralizedTime)o).contents);
     }
 
     public int hashCode()
     {
-        return Arrays.hashCode(time);
+        return Arrays.hashCode(contents);
+    }
+
+    static ASN1GeneralizedTime createPrimitive(byte[] contents)
+    {
+        return new ASN1GeneralizedTime(contents);
     }
 }
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1GraphicString.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1GraphicString.java
new file mode 100644
index 0000000..a768336
--- /dev/null
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1GraphicString.java
@@ -0,0 +1,132 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.org.bouncycastle.asn1;
+
+import java.io.IOException;
+
+import com.android.org.bouncycastle.util.Arrays;
+import com.android.org.bouncycastle.util.Strings;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+public abstract class ASN1GraphicString
+    extends ASN1Primitive
+    implements ASN1String
+{
+    static final ASN1UniversalType TYPE = new ASN1UniversalType(ASN1GraphicString.class, BERTags.GRAPHIC_STRING)
+    {
+        ASN1Primitive fromImplicitPrimitive(DEROctetString octetString)
+        {
+            return createPrimitive(octetString.getOctets());
+        }
+    };
+
+    /**
+     * Return a GraphicString from the passed in object.
+     *
+     * @param obj an ASN1GraphicString or an object that can be converted into one.
+     * @exception IllegalArgumentException if the object cannot be converted.
+     * @return an ASN1GraphicString instance, or null.
+     */
+    public static ASN1GraphicString getInstance(Object obj)
+    {
+        if (obj == null || obj instanceof ASN1GraphicString)
+        {
+            return (ASN1GraphicString)obj;
+        }
+        if (obj instanceof ASN1Encodable)
+        {
+            ASN1Primitive primitive = ((ASN1Encodable)obj).toASN1Primitive();
+            if (primitive instanceof ASN1GraphicString)
+            {
+                return (ASN1GraphicString)primitive;
+            }
+        }
+        if (obj instanceof byte[])
+        {
+            try
+            {
+                return (ASN1GraphicString)TYPE.fromByteArray((byte[])obj);
+            }
+            catch (Exception e)
+            {
+                throw new IllegalArgumentException("encoding error in getInstance: " + e.toString());
+            }
+        }
+
+        throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
+    }
+
+    /**
+     * Return a GraphicString from a tagged object.
+     *
+     * @param taggedObject the tagged object holding the object we want.
+     * @param explicit     true if the object is meant to be explicitly tagged,
+     *                     false otherwise.
+     * @exception IllegalArgumentException if the tagged object cannot be converted.
+     * @return an ASN1GraphicString instance, or null.
+     */
+    public static ASN1GraphicString getInstance(ASN1TaggedObject taggedObject, boolean explicit)
+    {
+        return (ASN1GraphicString)TYPE.getContextInstance(taggedObject, explicit);
+    }
+
+    final byte[] contents;
+
+    ASN1GraphicString(byte[] contents, boolean clone)
+    {
+        if (null == contents)
+        {
+            throw new NullPointerException("'contents' cannot be null");
+        }
+
+        this.contents = clone ? Arrays.clone(contents) : contents;
+    }
+
+    public final byte[] getOctets()
+    {
+        return Arrays.clone(contents);
+    }
+
+    final boolean encodeConstructed()
+    {
+        return false;
+    }
+
+    final int encodedLength(boolean withTag)
+    {
+        return ASN1OutputStream.getLengthOfEncodingDL(withTag, contents.length);
+    }
+
+    final void encode(ASN1OutputStream out, boolean withTag) throws IOException
+    {
+        out.writeEncodingDL(withTag, BERTags.GRAPHIC_STRING, contents);
+    }
+
+    final boolean asn1Equals(ASN1Primitive other)
+    {
+        if (!(other instanceof ASN1GraphicString))
+        {
+            return false;
+        }
+
+        ASN1GraphicString that = (ASN1GraphicString)other;
+
+        return Arrays.areEqual(this.contents, that.contents);
+    }
+
+    public final int hashCode()
+    {
+        return Arrays.hashCode(contents);
+    }
+
+    public final String getString()
+    {
+        return Strings.fromByteArray(contents);
+    }
+
+    static ASN1GraphicString createPrimitive(byte[] contents)
+    {
+        return new DERGraphicString(contents, false);
+    }
+}
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1IA5String.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1IA5String.java
new file mode 100644
index 0000000..6dd9904
--- /dev/null
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1IA5String.java
@@ -0,0 +1,172 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.org.bouncycastle.asn1;
+
+import java.io.IOException;
+
+import com.android.org.bouncycastle.util.Arrays;
+import com.android.org.bouncycastle.util.Strings;
+
+/**
+ * ASN.1 IA5String object - this is a ISO 646 (ASCII) string encoding code points 0 to 127.
+ * <p>
+ * Explicit character set escape sequences are not allowed.
+ * </p>
+ * @hide This class is not part of the Android public SDK API
+ */
+public abstract class ASN1IA5String
+    extends ASN1Primitive
+    implements ASN1String
+{
+    static final ASN1UniversalType TYPE = new ASN1UniversalType(ASN1IA5String.class, BERTags.IA5_STRING)
+    {
+        ASN1Primitive fromImplicitPrimitive(DEROctetString octetString)
+        {
+            return createPrimitive(octetString.getOctets());
+        }
+    };
+
+    /**
+     * Return an IA5 string from the passed in object
+     *
+     * @param obj an ASN1IA5String or an object that can be converted into one.
+     * @exception IllegalArgumentException if the object cannot be converted.
+     * @return a ASN1IA5String instance, or null.
+     */
+    public static ASN1IA5String getInstance(Object obj)
+    {
+        if (obj == null || obj instanceof ASN1IA5String)
+        {
+            return (ASN1IA5String)obj;
+        }
+        if (obj instanceof ASN1Encodable)
+        {
+            ASN1Primitive primitive = ((ASN1Encodable)obj).toASN1Primitive();
+            if (primitive instanceof ASN1IA5String)
+            {
+                return (ASN1IA5String)primitive;
+            }
+        }
+        if (obj instanceof byte[])
+        {
+            try
+            {
+                return (ASN1IA5String)TYPE.fromByteArray((byte[])obj);
+            }
+            catch (Exception e)
+            {
+                throw new IllegalArgumentException("encoding error in getInstance: " + e.toString());
+            }
+        }
+
+        throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
+    }
+
+    /**
+     * Return an IA5 String from a tagged object.
+     *
+     * @param taggedObject the tagged object holding the object we want
+     * @param explicit true if the object is meant to be explicitly
+     *              tagged false otherwise.
+     * @exception IllegalArgumentException if the tagged object cannot
+     *               be converted.
+     * @return an ASN1IA5String instance, or null.
+     */
+    public static ASN1IA5String getInstance(ASN1TaggedObject taggedObject, boolean explicit)
+    {
+        return (ASN1IA5String)TYPE.getContextInstance(taggedObject, explicit);
+    }
+
+    final byte[] contents;
+
+    ASN1IA5String(String string, boolean validate)
+    {
+        if (string == null)
+        {
+            throw new NullPointerException("'string' cannot be null");
+        }
+        if (validate && !isIA5String(string))
+        {
+            throw new IllegalArgumentException("'string' contains illegal characters");
+        }
+
+        this.contents = Strings.toByteArray(string);
+    }
+
+    ASN1IA5String(byte[] contents, boolean clone)
+    {
+        this.contents = clone ? Arrays.clone(contents) : contents;
+    }
+
+    public final String getString()
+    {
+        return Strings.fromByteArray(contents);
+    }
+
+    public String toString()
+    {
+        return getString();
+    }
+
+    public final byte[] getOctets()
+    {
+        return Arrays.clone(contents);
+    }
+
+    final boolean encodeConstructed()
+    {
+        return false;
+    }
+
+    final int encodedLength(boolean withTag)
+    {
+        return ASN1OutputStream.getLengthOfEncodingDL(withTag, contents.length);
+    }
+
+    final void encode(ASN1OutputStream out, boolean withTag) throws IOException
+    {
+        out.writeEncodingDL(withTag, BERTags.IA5_STRING, contents);
+    }
+
+    final boolean asn1Equals(ASN1Primitive other)
+    {
+        if (!(other instanceof ASN1IA5String))
+        {
+            return false;
+        }
+
+        ASN1IA5String that = (ASN1IA5String)other;
+
+        return Arrays.areEqual(this.contents, that.contents);
+    }
+
+    public final int hashCode()
+    {
+        return Arrays.hashCode(contents);
+    }
+
+    /**
+     * return true if the passed in String can be represented without
+     * loss as an IA5String, false otherwise.
+     *
+     * @param str the string to check.
+     * @return true if character set in IA5String set, false otherwise.
+     */
+    public static boolean isIA5String(String str)
+    {
+        for (int i = str.length() - 1; i >= 0; i--)
+        {
+            char ch = str.charAt(i);
+            if (ch > 0x007f)
+            {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    static ASN1IA5String createPrimitive(byte[] contents)
+    {
+        return new DERIA5String(contents, false);
+    }
+}
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1InputStream.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1InputStream.java
index dbe0e1a..4cda186 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1InputStream.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1InputStream.java
@@ -22,12 +22,10 @@
 {
     private final int limit;
     private final boolean lazyEvaluate;
-
     private final byte[][] tmpBuffers;
 
     @android.compat.annotation.UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
-    public ASN1InputStream(
-        InputStream is)
+    public ASN1InputStream(InputStream is)
     {
         this(is, StreamUtil.findLimit(is));
     }
@@ -39,8 +37,7 @@
      * @param input array containing ASN.1 encoded data.
      */
     @android.compat.annotation.UnsupportedAppUsage
-    public ASN1InputStream(
-        byte[] input)
+    public ASN1InputStream(byte[] input)
     {
         this(new ByteArrayInputStream(input), input.length);
     }
@@ -52,22 +49,18 @@
      * @param input array containing ASN.1 encoded data.
      * @param lazyEvaluate true if parsing inside constructed objects can be delayed.
      */
-    public ASN1InputStream(
-        byte[] input,
-        boolean lazyEvaluate)
+    public ASN1InputStream(byte[] input, boolean lazyEvaluate)
     {
         this(new ByteArrayInputStream(input), input.length, lazyEvaluate);
     }
-    
+
     /**
      * Create an ASN1InputStream where no DER object will be longer than limit.
      * 
      * @param input stream containing ASN.1 encoded data.
      * @param limit maximum size of a DER encoded object.
      */
-    public ASN1InputStream(
-        InputStream input,
-        int         limit)
+    public ASN1InputStream(InputStream input, int limit)
     {
         this(input, limit, false);
     }
@@ -79,9 +72,7 @@
      * @param input stream containing ASN.1 encoded data.
      * @param lazyEvaluate true if parsing inside constructed objects can be delayed.
      */
-    public ASN1InputStream(
-        InputStream input,
-        boolean     lazyEvaluate)
+    public ASN1InputStream(InputStream input, boolean lazyEvaluate)
     {
         this(input, StreamUtil.findLimit(input), lazyEvaluate);
     }
@@ -94,15 +85,17 @@
      * @param limit maximum size of a DER encoded object.
      * @param lazyEvaluate true if parsing inside constructed objects can be delayed.
      */
-    public ASN1InputStream(
-        InputStream input,
-        int         limit,
-        boolean     lazyEvaluate)
+    public ASN1InputStream(InputStream input, int limit, boolean lazyEvaluate)
+    {
+        this(input, limit, lazyEvaluate, new byte[11][]);
+    }
+
+    private ASN1InputStream(InputStream input, int limit, boolean lazyEvaluate, byte[][] tmpBuffers)
     {
         super(input);
         this.limit = limit;
         this.lazyEvaluate = lazyEvaluate;
-        this.tmpBuffers = new byte[11][];
+        this.tmpBuffers = tmpBuffers;
     }
 
     int getLimit()
@@ -120,7 +113,7 @@
         byte[]  bytes)
         throws IOException
     {
-        if (Streams.readFully(this, bytes) != bytes.length)
+        if (Streams.readFully(this, bytes, 0, bytes.length) != bytes.length)
         {
             throw new EOFException("EOF encountered in middle of object");
         }
@@ -141,82 +134,57 @@
         int       length)
         throws IOException
     {
-        boolean isConstructed = (tag & CONSTRUCTED) != 0;
+        // TODO[asn1] Special-case zero length first?
 
         DefiniteLengthInputStream defIn = new DefiniteLengthInputStream(this, length, limit);
 
-        if ((tag & APPLICATION) != 0)
+        if (0 == (tag & FLAGS))
         {
-            return new DLApplicationSpecific(isConstructed, tagNo, defIn.toByteArray());
+            return createPrimitiveDERObject(tagNo, defIn, tmpBuffers);
         }
 
-        if ((tag & TAGGED) != 0)
+        int tagClass = tag & PRIVATE;
+        if (0 != tagClass)
         {
-            return new ASN1StreamParser(defIn).readTaggedObject(isConstructed, tagNo);
+            boolean isConstructed = (tag & CONSTRUCTED) != 0;
+            return readTaggedObjectDL(tagClass, tagNo, isConstructed, defIn);
         }
 
-        if (isConstructed)
+        switch (tagNo)
         {
-            // TODO There are other tags that may be constructed (e.g. BIT_STRING)
-            switch (tagNo)
+        case BIT_STRING:
+        {
+            return buildConstructedBitString(readVector(defIn));
+        }
+        case OCTET_STRING:
+        {
+            //
+            // yes, people actually do this...
+            //
+            return buildConstructedOctetString(readVector(defIn));
+        }
+        case SEQUENCE:
+        {
+            if (defIn.getRemaining() < 1)
             {
-                case OCTET_STRING:
-                    //
-                    // yes, people actually do this...
-                    //
-                    ASN1EncodableVector v = readVector(defIn);
-                    ASN1OctetString[] strings = new ASN1OctetString[v.size()];
-
-                    for (int i = 0; i != strings.length; i++)
-                    {
-                        ASN1Encodable asn1Obj = v.get(i);
-                        if (asn1Obj instanceof ASN1OctetString)
-                        {
-                            strings[i] = (ASN1OctetString)asn1Obj;
-                        }
-                        else
-                        {
-                            throw new ASN1Exception("unknown object encountered in constructed OCTET STRING: " + asn1Obj.getClass());
-                        }
-                    }
-
-                    return new BEROctetString(strings);
-                case SEQUENCE:
-                    if (lazyEvaluate)
-                    {
-                        return new LazyEncodedSequence(defIn.toByteArray());
-                    }
-                    else
-                    {
-                        return DLFactory.createSequence(readVector(defIn));   
-                    }
-                case SET:
-                    return DLFactory.createSet(readVector(defIn));
-                case EXTERNAL:
-                    return new DLExternal(readVector(defIn));
-                default:
-                    throw new IOException("unknown tag " + tagNo + " encountered");
+                return DLFactory.EMPTY_SEQUENCE;
+            }
+            else if (lazyEvaluate)
+            {
+                return new LazyEncodedSequence(defIn.toByteArray());
+            }
+            else
+            {
+                return DLFactory.createSequence(readVector(defIn));
             }
         }
-
-        return createPrimitiveDERObject(tagNo, defIn, tmpBuffers);
-    }
-
-    ASN1EncodableVector readVector(DefiniteLengthInputStream dIn) throws IOException
-    {
-        if (dIn.getRemaining() < 1)
-        {
-            return new ASN1EncodableVector(0);
+        case SET:
+            return DLFactory.createSet(readVector(defIn));
+        case EXTERNAL:
+            return DLFactory.createSequence(readVector(defIn)).toASN1External();
+        default:
+            throw new IOException("unknown tag " + tagNo + " encountered");
         }
-
-        ASN1InputStream subStream = new ASN1InputStream(dIn);
-        ASN1EncodableVector v = new ASN1EncodableVector();
-        ASN1Primitive p;
-        while ((p = subStream.readObject()) != null)
-        {
-            v.add(p);
-        }
-        return v;
     }
 
     @android.compat.annotation.UnsupportedAppUsage
@@ -234,55 +202,12 @@
             return null;
         }
 
-        //
-        // calculate tag number
-        //
         int tagNo = readTagNumber(this, tag);
-
-        boolean isConstructed = (tag & CONSTRUCTED) != 0;
-
-        //
-        // calculate length
-        //
         int length = readLength();
 
-        if (length < 0) // indefinite-length method
+        if (length >= 0)
         {
-            if (!isConstructed)
-            {
-                throw new IOException("indefinite-length primitive encoding encountered");
-            }
-
-            IndefiniteLengthInputStream indIn = new IndefiniteLengthInputStream(this, limit);
-            ASN1StreamParser sp = new ASN1StreamParser(indIn, limit);
-
-            if ((tag & APPLICATION) != 0)
-            {
-                return new BERApplicationSpecificParser(tagNo, sp).getLoadedObject();
-            }
-
-            if ((tag & TAGGED) != 0)
-            {
-                return new BERTaggedObjectParser(true, tagNo, sp).getLoadedObject();
-            }
-
-            // TODO There are other tags that may be constructed (e.g. BIT_STRING)
-            switch (tagNo)
-            {
-                case OCTET_STRING:
-                    return new BEROctetStringParser(sp).getLoadedObject();
-                case SEQUENCE:
-                    return new BERSequenceParser(sp).getLoadedObject();
-                case SET:
-                    return new BERSetParser(sp).getLoadedObject();
-                case EXTERNAL:
-                    return new DERExternalParser(sp).getLoadedObject();
-                default:
-                    throw new IOException("unknown BER object encountered");
-            }
-        }
-        else
-        {
+            // definite-length
             try
             {
                 return buildObject(tag, tagNo, length);
@@ -292,6 +217,124 @@
                 throw new ASN1Exception("corrupted stream detected", e);
             }
         }
+
+        // indefinite-length
+
+        if (0 == (tag & CONSTRUCTED))
+        {
+            throw new IOException("indefinite-length primitive encoding encountered");
+        }
+
+        IndefiniteLengthInputStream indIn = new IndefiniteLengthInputStream(this, limit);
+        ASN1StreamParser sp = new ASN1StreamParser(indIn, limit, tmpBuffers);
+
+        int tagClass = tag & PRIVATE;
+        if (0 != tagClass)
+        {
+            return sp.loadTaggedIL(tagClass, tagNo);
+        }
+
+        switch (tagNo)
+        {
+        case BIT_STRING:
+            return BERBitStringParser.parse(sp);
+        case OCTET_STRING:
+            return BEROctetStringParser.parse(sp);
+        case EXTERNAL:
+            // TODO[asn1] BERExternalParser
+            return DERExternalParser.parse(sp);
+        case SEQUENCE:
+            return BERSequenceParser.parse(sp);
+        case SET:
+            return BERSetParser.parse(sp);
+        default:
+            throw new IOException("unknown BER object encountered");
+        }
+    }
+
+    ASN1BitString buildConstructedBitString(ASN1EncodableVector contentsElements) throws IOException
+    {
+        ASN1BitString[] strings = new ASN1BitString[contentsElements.size()];
+
+        for (int i = 0; i != strings.length; i++)
+        {
+            ASN1Encodable asn1Obj = contentsElements.get(i);
+            if (asn1Obj instanceof ASN1BitString)
+            {
+                strings[i] = (ASN1BitString)asn1Obj;
+            }
+            else
+            {
+                throw new ASN1Exception(
+                    "unknown object encountered in constructed BIT STRING: " + asn1Obj.getClass());
+            }
+        }
+
+        // TODO Probably ought to be DLBitString
+        return new BERBitString(strings);
+    }
+
+    ASN1OctetString buildConstructedOctetString(ASN1EncodableVector contentsElements) throws IOException
+    {
+        ASN1OctetString[] strings = new ASN1OctetString[contentsElements.size()];
+
+        for (int i = 0; i != strings.length; i++)
+        {
+            ASN1Encodable asn1Obj = contentsElements.get(i);
+            if (asn1Obj instanceof ASN1OctetString)
+            {
+                strings[i] = (ASN1OctetString)asn1Obj;
+            }
+            else
+            {
+                throw new ASN1Exception(
+                    "unknown object encountered in constructed OCTET STRING: " + asn1Obj.getClass());
+            }
+        }
+
+        // TODO Probably ought to be DEROctetString (no DLOctetString available)
+        return new BEROctetString(strings);
+    }
+
+    ASN1Primitive readTaggedObjectDL(int tagClass, int tagNo, boolean constructed, DefiniteLengthInputStream defIn)
+        throws IOException
+    {
+        if (!constructed)
+        {
+            byte[] contentsOctets = defIn.toByteArray();
+            return ASN1TaggedObject.createPrimitive(tagClass, tagNo, contentsOctets);
+        }
+
+        ASN1EncodableVector contentsElements = readVector(defIn);
+        return ASN1TaggedObject.createConstructedDL(tagClass, tagNo, contentsElements);
+    }
+
+    ASN1EncodableVector readVector() throws IOException
+    {
+        ASN1Primitive p = readObject();
+        if (null == p)
+        {
+            return new ASN1EncodableVector(0);
+        }
+
+        ASN1EncodableVector v = new ASN1EncodableVector();
+        do
+        {
+            v.add(p);
+        }
+        while ((p = readObject()) != null);
+        return v;
+    }
+
+    ASN1EncodableVector readVector(DefiniteLengthInputStream defIn) throws IOException
+    {
+        int remaining = defIn.getRemaining();
+        if (remaining < 1)
+        {
+            return new ASN1EncodableVector(0);
+        }
+
+        return new ASN1InputStream(defIn, remaining, lazyEvaluate, tmpBuffers).readVector();
     }
 
     static int readTagNumber(InputStream s, int tag) 
@@ -304,32 +347,44 @@
         //
         if (tagNo == 0x1f)
         {
-            tagNo = 0;
-
             int b = s.read();
+            if (b < 31)
+            {
+                if (b < 0)
+                {
+                    throw new EOFException("EOF found inside tag value.");
+                }
+                throw new IOException("corrupted stream - high tag number < 31 found");
+            }
+
+            tagNo = b & 0x7f;
 
             // X.690-0207 8.1.2.4.2
             // "c) bits 7 to 1 of the first subsequent octet shall not all be zero."
-            if ((b & 0x7f) == 0) // Note: -1 will pass
+            if (0 == tagNo)
             {
                 throw new IOException("corrupted stream - invalid high tag number found");
             }
 
-            while ((b >= 0) && ((b & 0x80) != 0))
+            while ((b & 0x80) != 0)
             {
-                tagNo |= (b & 0x7f);
-                tagNo <<= 7;
-                b = s.read();
-            }
+                if ((tagNo >>> 24) != 0)
+                {
+                    throw new IOException("Tag number more than 31 bits");
+                }
 
-            if (b < 0)
-            {
-                throw new EOFException("EOF found inside tag value.");
+                tagNo <<= 7;
+
+                b = s.read();
+                if (b < 0)
+                {
+                    throw new EOFException("EOF found inside tag value.");
+                }
+
+                tagNo |= (b & 0x7f);
             }
-            
-            tagNo |= (b & 0x7f);
         }
-        
+
         return tagNo;
     }
 
@@ -337,48 +392,48 @@
         throws IOException
     {
         int length = s.read();
+        if (0 == (length >>> 7))
+        {
+            // definite-length short form 
+            return length;
+        }
+        if (0x80 == length)
+        {
+            // indefinite-length
+            return -1;
+        }
         if (length < 0)
         {
             throw new EOFException("EOF found when length expected");
         }
-
-        if (length == 0x80)
+        if (0xFF == length)
         {
-            return -1;      // indefinite-length encoding
+            throw new IOException("invalid long form definite-length 0xFF");
         }
 
-        if (length > 127)
+        int octetsCount = length & 0x7F, octetsPos = 0;
+
+        length = 0;
+        do
         {
-            int size = length & 0x7f;
-
-            // Note: The invalid long form "0xff" (see X.690 8.1.3.5c) will be caught here
-            if (size > 4)
+            int octet = s.read();
+            if (octet < 0)
             {
-                throw new IOException("DER length more than 4 bytes: " + size);
+                throw new EOFException("EOF found reading length");
             }
 
-            length = 0;
-            for (int i = 0; i < size; i++)
+            if ((length >>> 23) != 0)
             {
-                int next = s.read();
-
-                if (next < 0)
-                {
-                    throw new EOFException("EOF found reading length");
-                }
-
-                length = (length << 8) + next;
+                throw new IOException("long form definite-length more than 31 bits");
             }
 
-            if (length < 0)
-            {
-                throw new IOException("corrupted stream - negative length found");
-            }
+            length = (length << 8) + octet;
+        }
+        while (++octetsPos < octetsCount);
 
-            if (length >= limit && !isParsing)   // after all we must have read at least 1 byte
-            {
-                throw new IOException("corrupted stream - out of bounds length found: " + length + " >= " + limit);
-            }
+        if (length >= limit && !isParsing)   // after all we must have read at least 1 byte
+        {
+            throw new IOException("corrupted stream - out of bounds length found: " + length + " >= " + limit);
         }
 
         return length;
@@ -462,50 +517,79 @@
         byte[][] tmpBuffers)
         throws IOException
     {
-        switch (tagNo)
+        /*
+         * TODO[asn1] Lookup the universal type object and get it to parse the stream directly (possibly with
+         * access to a single temporary buffer replacing tmpBuffers).
+         */
+        try
         {
+            switch (tagNo)
+            {
             case BIT_STRING:
-                return ASN1BitString.fromInputStream(defIn.getRemaining(), defIn);
+                return ASN1BitString.createPrimitive(defIn.toByteArray());
             case BMP_STRING:
-                return new DERBMPString(getBMPCharBuffer(defIn));
+                return ASN1BMPString.createPrimitive(getBMPCharBuffer(defIn));
             case BOOLEAN:
-                return ASN1Boolean.fromOctetString(getBuffer(defIn, tmpBuffers));
+                return ASN1Boolean.createPrimitive(getBuffer(defIn, tmpBuffers));
             case ENUMERATED:
-                return ASN1Enumerated.fromOctetString(getBuffer(defIn, tmpBuffers));
-            case GENERALIZED_TIME:
-                return new ASN1GeneralizedTime(defIn.toByteArray());
+                // TODO Ideally only clone if we used a buffer
+                return ASN1Enumerated.createPrimitive(getBuffer(defIn, tmpBuffers), true);
             case GENERAL_STRING:
-                return new DERGeneralString(defIn.toByteArray());
-            case IA5_STRING:
-                return new DERIA5String(defIn.toByteArray());
-            case INTEGER:
-                return new ASN1Integer(defIn.toByteArray(), false);
-            case NULL:
-                return DERNull.INSTANCE;   // actual content is ignored (enforce 0 length?)
-            case NUMERIC_STRING:
-                return new DERNumericString(defIn.toByteArray());
-            case OBJECT_IDENTIFIER:
-                return ASN1ObjectIdentifier.fromOctetString(getBuffer(defIn, tmpBuffers));
-            case OCTET_STRING:
-                return new DEROctetString(defIn.toByteArray());
-            case PRINTABLE_STRING:
-                return new DERPrintableString(defIn.toByteArray());
-            case T61_STRING:
-                return new DERT61String(defIn.toByteArray());
-            case UNIVERSAL_STRING:
-                return new DERUniversalString(defIn.toByteArray());
-            case UTC_TIME:
-                return new ASN1UTCTime(defIn.toByteArray());
-            case UTF8_STRING:
-                return new DERUTF8String(defIn.toByteArray());
-            case VISIBLE_STRING:
-                return new DERVisibleString(defIn.toByteArray());
+                return ASN1GeneralString.createPrimitive(defIn.toByteArray());
+            case GENERALIZED_TIME:
+                return ASN1GeneralizedTime.createPrimitive(defIn.toByteArray());
             case GRAPHIC_STRING:
-                return new DERGraphicString(defIn.toByteArray());
+                return ASN1GraphicString.createPrimitive(defIn.toByteArray());
+            case IA5_STRING:
+                return ASN1IA5String.createPrimitive(defIn.toByteArray());
+            case INTEGER:
+                return ASN1Integer.createPrimitive(defIn.toByteArray());
+            case NULL:
+                return ASN1Null.createPrimitive(defIn.toByteArray());
+            case NUMERIC_STRING:
+                return ASN1NumericString.createPrimitive(defIn.toByteArray());
+            case OBJECT_DESCRIPTOR:
+                return ASN1ObjectDescriptor.createPrimitive(defIn.toByteArray());
+            case OBJECT_IDENTIFIER:
+                // TODO Ideally only clone if we used a buffer
+                return ASN1ObjectIdentifier.createPrimitive(getBuffer(defIn, tmpBuffers), true);
+            case OCTET_STRING:
+                return ASN1OctetString.createPrimitive(defIn.toByteArray());
+            case PRINTABLE_STRING:
+                return ASN1PrintableString.createPrimitive(defIn.toByteArray());
+            case RELATIVE_OID:
+                return ASN1RelativeOID.createPrimitive(defIn.toByteArray(), false);
+            case T61_STRING:
+                return ASN1T61String.createPrimitive(defIn.toByteArray());
+            case UNIVERSAL_STRING:
+                return ASN1UniversalString.createPrimitive(defIn.toByteArray());
+            case UTC_TIME:
+                return ASN1UTCTime.createPrimitive(defIn.toByteArray());
+            case UTF8_STRING:
+                return ASN1UTF8String.createPrimitive(defIn.toByteArray());
             case VIDEOTEX_STRING:
-                return new DERVideotexString(defIn.toByteArray());
+                return ASN1VideotexString.createPrimitive(defIn.toByteArray());
+            case VISIBLE_STRING:
+                return ASN1VisibleString.createPrimitive(defIn.toByteArray());
+            case TIME:
+            case DATE:
+            case TIME_OF_DAY:
+            case DATE_TIME:
+            case DURATION:
+            case OBJECT_IDENTIFIER_IRI:
+            case RELATIVE_OID_IRI:
+                throw new IOException("unsupported tag " + tagNo + " encountered");
             default:
                 throw new IOException("unknown tag " + tagNo + " encountered");
+            }
+        }
+        catch (IllegalArgumentException e)
+        {
+            throw new ASN1Exception(e.getMessage(), e);
+        }
+        catch (IllegalStateException e)
+        {
+            throw new ASN1Exception(e.getMessage(), e);
         }
     }
 }
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1Integer.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1Integer.java
index 4e2d68e..22fc045 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1Integer.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1Integer.java
@@ -14,6 +14,14 @@
 public class ASN1Integer
     extends ASN1Primitive
 {
+    static final ASN1UniversalType TYPE = new ASN1UniversalType(ASN1Integer.class, BERTags.INTEGER)
+    {
+        ASN1Primitive fromImplicitPrimitive(DEROctetString octetString)
+        {
+            return createPrimitive(octetString.getOctets());
+        }
+    };
+
     static final int SIGN_EXT_SIGNED = 0xFFFFFFFF;
     static final int SIGN_EXT_UNSIGNED = 0xFF;
 
@@ -39,7 +47,7 @@
         {
             try
             {
-                return (ASN1Integer)fromByteArray((byte[])obj);
+                return (ASN1Integer)TYPE.fromByteArray((byte[])obj);
             }
             catch (Exception e)
             {
@@ -53,27 +61,16 @@
     /**
      * Return an Integer from a tagged object.
      *
-     * @param obj      the tagged object holding the object we want
+     * @param taggedObject the tagged object holding the object we want
      * @param explicit true if the object is meant to be explicitly
      *                 tagged false otherwise.
      * @return an ASN1Integer instance.
      * @throws IllegalArgumentException if the tagged object cannot
      * be converted.
      */
-    public static ASN1Integer getInstance(
-        ASN1TaggedObject obj,
-        boolean explicit)
+    public static ASN1Integer getInstance(ASN1TaggedObject taggedObject, boolean explicit)
     {
-        ASN1Primitive o = obj.getObject();
-
-        if (explicit || o instanceof ASN1Integer)
-        {
-            return getInstance(o);
-        }
-        else
-        {
-            return new ASN1Integer(ASN1OctetString.getInstance(o).getOctets());
-        }
+        return (ASN1Integer)TYPE.getContextInstance(taggedObject, explicit);
     }
 
     /**
@@ -153,6 +150,18 @@
         return new BigInteger(bytes);
     }
 
+    public boolean hasValue(int x)
+    {
+        return (bytes.length - start) <= 4
+            && intValue(bytes, start, SIGN_EXT_SIGNED) == x;
+    }
+
+    public boolean hasValue(long x)
+    {
+        return (bytes.length - start) <= 8
+            && longValue(bytes, start, SIGN_EXT_SIGNED) == x;
+    }
+
     public boolean hasValue(BigInteger x)
     {
         return null != x
@@ -194,19 +203,19 @@
         return longValue(bytes, start, SIGN_EXT_SIGNED);
     }
 
-    boolean isConstructed()
+    boolean encodeConstructed()
     {
         return false;
     }
 
-    int encodedLength()
+    int encodedLength(boolean withTag)
     {
-        return 1 + StreamUtil.calculateBodyLength(bytes.length) + bytes.length;
+        return ASN1OutputStream.getLengthOfEncodingDL(withTag, bytes.length);
     }
 
     void encode(ASN1OutputStream out, boolean withTag) throws IOException
     {
-        out.writeEncoded(withTag, BERTags.INTEGER, bytes);
+        out.writeEncodingDL(withTag, BERTags.INTEGER, bytes);
     }
 
     public int hashCode()
@@ -231,6 +240,11 @@
         return getValue().toString();
     }
 
+    static ASN1Integer createPrimitive(byte[] contents)
+    {
+        return new ASN1Integer(contents, false);
+    }
+
     static int intValue(byte[] bytes, int start, int signExt)
     {
         int length = bytes.length;
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1Null.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1Null.java
index 6737fd5..6545545 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1Null.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1Null.java
@@ -1,7 +1,4 @@
 /* GENERATED SOURCE. DO NOT MODIFY. */
-/***************************************************************/
-/******    DO NOT EDIT THIS CLASS bc-java SOURCE FILE     ******/
-/***************************************************************/
 package com.android.org.bouncycastle.asn1;
 
 import java.io.IOException;
@@ -13,10 +10,13 @@
 public abstract class ASN1Null
     extends ASN1Primitive
 {
-    ASN1Null()
+    static final ASN1UniversalType TYPE = new ASN1UniversalType(ASN1Null.class, BERTags.NULL)
     {
-
-    }
+        ASN1Primitive fromImplicitPrimitive(DEROctetString octetString)
+        {
+            return createPrimitive(octetString.getOctets());
+        }
+    };
 
     /**
      * Return an instance of ASN.1 NULL from the passed in object.
@@ -44,21 +44,26 @@
         {
             try
             {
-                return ASN1Null.getInstance(ASN1Primitive.fromByteArray((byte[])o));
+                return (ASN1Null)TYPE.fromByteArray((byte[])o);
             }
             catch (IOException e)
             {
                 throw new IllegalArgumentException("failed to construct NULL from byte[]: " + e.getMessage());
             }
-            catch (ClassCastException e)
-            {
-                throw new IllegalArgumentException("unknown object in getInstance(): " + o.getClass().getName());
-            }
         }
 
         return null;
     }
 
+    public static ASN1Null getInstance(ASN1TaggedObject taggedObject, boolean explicit)
+    {
+        return (ASN1Null)TYPE.getContextInstance(taggedObject, explicit);
+    }
+
+    ASN1Null()
+    {
+    }
+
     public int hashCode()
     {
         return -1;
@@ -75,10 +80,17 @@
         return true;
     }
 
-    abstract void encode(ASN1OutputStream out, boolean withTag) throws IOException;
-
     public String toString()
     {
          return "NULL";
     }
+
+    static ASN1Null createPrimitive(byte[] contents)
+    {
+        if (0 != contents.length)
+        {
+            throw new IllegalStateException("malformed NULL encoding encountered");
+        }
+        return DERNull.INSTANCE;
+    }
 }
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1NumericString.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1NumericString.java
new file mode 100644
index 0000000..c384bb3
--- /dev/null
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1NumericString.java
@@ -0,0 +1,215 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.org.bouncycastle.asn1;
+
+import java.io.IOException;
+
+import com.android.org.bouncycastle.util.Arrays;
+import com.android.org.bouncycastle.util.Strings;
+
+/**
+ * NumericString object - this is an ascii string of characters {0,1,2,3,4,5,6,7,8,9, }.
+ * ASN.1 NUMERIC-STRING object.
+ * <p>
+ * This is an ASCII string of characters {0,1,2,3,4,5,6,7,8,9} + space.
+ * <p>
+ * See X.680 section 37.2.
+ * <p>
+ * Explicit character set escape sequences are not allowed.
+ * @hide This class is not part of the Android public SDK API
+ */
+public abstract class ASN1NumericString
+    extends ASN1Primitive
+    implements ASN1String
+{
+    static final ASN1UniversalType TYPE = new ASN1UniversalType(ASN1NumericString.class, BERTags.NUMERIC_STRING)
+    {
+        ASN1Primitive fromImplicitPrimitive(DEROctetString octetString)
+        {
+            return createPrimitive(octetString.getOctets());
+        }
+    };
+
+    /**
+     * Return a Numeric string from the passed in object
+     *
+     * @param obj an ASN1NumericString or an object that can be converted into one.
+     * @exception IllegalArgumentException if the object cannot be converted.
+     * @return an ASN1NumericString instance, or null
+     */
+    public static ASN1NumericString getInstance(Object obj)
+    {
+        if (obj == null || obj instanceof ASN1NumericString)
+        {
+            return (ASN1NumericString)obj;
+        }
+        if (obj instanceof ASN1Encodable)
+        {
+            ASN1Primitive primitive = ((ASN1Encodable)obj).toASN1Primitive();
+            if (primitive instanceof ASN1NumericString)
+            {
+                return (ASN1NumericString)primitive;
+            }
+        }
+        if (obj instanceof byte[])
+        {
+            try
+            {
+                return (ASN1NumericString)TYPE.fromByteArray((byte[])obj);
+            }
+            catch (Exception e)
+            {
+                throw new IllegalArgumentException("encoding error in getInstance: " + e.toString());
+            }
+        }
+
+        throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
+    }
+
+    /**
+     * Return an Numeric String from a tagged object.
+     *
+     * @param taggedObject the tagged object holding the object we want
+     * @param explicit     true if the object is meant to be explicitly tagged false
+     *                     otherwise.
+     * @exception IllegalArgumentException if the tagged object cannot be converted.
+     * @return an ASN1NumericString instance, or null.
+     */
+    public static ASN1NumericString getInstance(ASN1TaggedObject taggedObject, boolean explicit)
+    {
+        return (ASN1NumericString)TYPE.getContextInstance(taggedObject, explicit);
+    }
+
+    final byte[] contents;
+
+    /**
+     * Constructor with optional validation.
+     *
+     * @param string the base string to wrap.
+     * @param validate whether or not to check the string.
+     * @throws IllegalArgumentException if validate is true and the string
+     * contains characters that should not be in a NumericString.
+     */
+    ASN1NumericString(String string, boolean validate)
+    {
+        if (validate && !isNumericString(string))
+        {
+            throw new IllegalArgumentException("string contains illegal characters");
+        }
+
+        this.contents = Strings.toByteArray(string);
+    }
+
+    ASN1NumericString(byte[] contents, boolean clone)
+    {
+        this.contents = clone ? Arrays.clone(contents) : contents;
+    }
+
+    public final String getString()
+    {
+        return Strings.fromByteArray(contents);
+    }
+
+    public String toString()
+    {
+        return getString();
+    }
+
+    public final byte[] getOctets()
+    {
+        return Arrays.clone(contents);
+    }
+
+    final boolean encodeConstructed()
+    {
+        return false;
+    }
+
+    final int encodedLength(boolean withTag)
+    {
+        return ASN1OutputStream.getLengthOfEncodingDL(withTag, contents.length);
+    }
+
+    final void encode(ASN1OutputStream out, boolean withTag) throws IOException
+    {
+        out.writeEncodingDL(withTag, BERTags.NUMERIC_STRING, contents);
+    }
+
+    public final int hashCode()
+    {
+        return Arrays.hashCode(contents);
+    }
+
+    final boolean asn1Equals(ASN1Primitive other)
+    {
+        if (!(other instanceof ASN1NumericString))
+        {
+            return false;
+        }
+
+        ASN1NumericString that = (ASN1NumericString)other;
+
+        return Arrays.areEqual(this.contents, that.contents);
+    }
+
+    /**
+     * Return true if the string can be represented as a NumericString ('0'..'9', ' ')
+     *
+     * @param str string to validate.
+     * @return true if numeric, false otherwise.
+     */
+    public static boolean isNumericString(String str)
+    {
+        for (int i = str.length() - 1; i >= 0; i--)
+        {
+            char ch = str.charAt(i);
+
+            if (ch > 0x007f)
+            {
+                return false;
+            }
+
+            if (('0' <= ch && ch <= '9') || ch == ' ')
+            {
+                continue;
+            }
+
+            return false;
+        }
+
+        return true;
+    }
+
+    static boolean isNumericString(byte[] contents)
+    {
+        for (int i = 0; i < contents.length; ++i)
+        {
+            switch (contents[i])
+            {
+            case 0x20:
+            case 0x30:
+            case 0x31:
+            case 0x32:
+            case 0x33:
+            case 0x34:
+            case 0x35:
+            case 0x36:
+            case 0x37:
+            case 0x38:
+            case 0x39:
+                break;
+            default:
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    static ASN1NumericString createPrimitive(byte[] contents)
+    {
+        // TODO Validation - sort out exception types
+//        if (!isNumericString(contents))
+
+        return new DERNumericString(contents, false);
+    }
+}
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1Object.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1Object.java
index d582aa8..eca3120 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1Object.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1Object.java
@@ -19,12 +19,12 @@
 
     public void encodeTo(OutputStream output) throws IOException
     {
-        ASN1OutputStream.create(output).writeObject(this);
+        toASN1Primitive().encodeTo(output);
     }
 
     public void encodeTo(OutputStream output, String encoding) throws IOException
     {
-        ASN1OutputStream.create(output, encoding).writeObject(this);
+        toASN1Primitive().encodeTo(output, encoding);
     }
 
     /**
@@ -36,7 +36,7 @@
     public byte[] getEncoded() throws IOException
     {
         ByteArrayOutputStream bOut = new ByteArrayOutputStream();
-        encodeTo(bOut);
+        toASN1Primitive().encodeTo(bOut);
         return bOut.toByteArray();
     }
 
@@ -50,7 +50,7 @@
     public byte[] getEncoded(String encoding) throws IOException
     {
         ByteArrayOutputStream bOut = new ByteArrayOutputStream();
-        encodeTo(bOut, encoding);
+        toASN1Primitive().encodeTo(bOut, encoding);
         return bOut.toByteArray();
     }
 
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1ObjectDescriptor.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1ObjectDescriptor.java
new file mode 100644
index 0000000..2b02186
--- /dev/null
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1ObjectDescriptor.java
@@ -0,0 +1,145 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.org.bouncycastle.asn1;
+
+import java.io.IOException;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+public final class ASN1ObjectDescriptor
+    extends ASN1Primitive
+{
+    static final ASN1UniversalType TYPE = new ASN1UniversalType(ASN1ObjectDescriptor.class, BERTags.OBJECT_DESCRIPTOR)
+    {
+        ASN1Primitive fromImplicitPrimitive(DEROctetString octetString)
+        {
+            return new ASN1ObjectDescriptor(
+                (ASN1GraphicString)ASN1GraphicString.TYPE.fromImplicitPrimitive(octetString));
+        }
+
+        ASN1Primitive fromImplicitConstructed(ASN1Sequence sequence)
+        {
+            return new ASN1ObjectDescriptor(
+                (ASN1GraphicString)ASN1GraphicString.TYPE.fromImplicitConstructed(sequence));
+        }
+    };
+
+    /**
+     * Return an ObjectDescriptor from the passed in object.
+     *
+     * @param obj an ASN1ObjectDescriptor or an object that can be converted into one.
+     * @exception IllegalArgumentException if the object cannot be converted.
+     * @return an ASN1ObjectDescriptor instance, or null.
+     */
+    public static ASN1ObjectDescriptor getInstance(Object obj)
+    {
+        if (obj == null || obj instanceof ASN1ObjectDescriptor)
+        {
+            return (ASN1ObjectDescriptor)obj;
+        }
+        else if (obj instanceof ASN1Encodable)
+        {
+            ASN1Primitive primitive = ((ASN1Encodable)obj).toASN1Primitive();
+            if (primitive instanceof ASN1ObjectDescriptor)
+            {
+                return (ASN1ObjectDescriptor)primitive;
+            }
+        }
+        else if (obj instanceof byte[])
+        {
+            try
+            {
+                return (ASN1ObjectDescriptor)TYPE.fromByteArray((byte[])obj);
+            }
+            catch (IOException e)
+            {
+                throw new IllegalArgumentException("failed to construct object descriptor from byte[]: " + e.getMessage());
+            }
+        }
+
+        throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
+    }
+
+    /**
+     * Return an ObjectDescriptor from a tagged object.
+     *
+     * @param taggedObject the tagged object holding the object we want.
+     * @param explicit     true if the object is meant to be explicitly tagged,
+     *                     false otherwise.
+     * @exception IllegalArgumentException if the tagged object cannot be converted.
+     * @return an ASN1ObjectDescriptor instance, or null.
+     */
+    public static ASN1ObjectDescriptor getInstance(ASN1TaggedObject taggedObject, boolean explicit)
+    {
+        return (ASN1ObjectDescriptor)TYPE.getContextInstance(taggedObject, explicit);
+    }
+
+    private final ASN1GraphicString baseGraphicString;
+
+    public ASN1ObjectDescriptor(ASN1GraphicString baseGraphicString)
+    {
+        if (null == baseGraphicString)
+        {
+            throw new NullPointerException("'baseGraphicString' cannot be null");
+        }
+
+        this.baseGraphicString = baseGraphicString;
+    }
+
+    public ASN1GraphicString getBaseGraphicString()
+    {
+        return baseGraphicString;
+    }
+
+    boolean encodeConstructed()
+    {
+        return false;
+    }
+
+    int encodedLength(boolean withTag)
+    {
+        return baseGraphicString.encodedLength(withTag);
+    }
+
+    void encode(ASN1OutputStream out, boolean withTag) throws IOException
+    {
+        out.writeIdentifier(withTag, BERTags.OBJECT_DESCRIPTOR);
+        baseGraphicString.encode(out, false);
+    }
+
+    ASN1Primitive toDERObject()
+    {
+        ASN1GraphicString der = (ASN1GraphicString)baseGraphicString.toDERObject();
+
+        return der == baseGraphicString ? this : new ASN1ObjectDescriptor(der);
+    }
+
+    ASN1Primitive toDLObject()
+    {
+        ASN1GraphicString dl = (ASN1GraphicString)baseGraphicString.toDLObject();
+
+        return dl == baseGraphicString ? this : new ASN1ObjectDescriptor(dl);
+    }
+
+    boolean asn1Equals(ASN1Primitive other)
+    {
+        if (!(other instanceof ASN1ObjectDescriptor))
+        {
+            return false;
+        }
+
+        ASN1ObjectDescriptor that = (ASN1ObjectDescriptor)other;
+
+        return this.baseGraphicString.asn1Equals(that.baseGraphicString);
+    }
+
+    public int hashCode()
+    {
+        return ~baseGraphicString.hashCode();
+    }
+
+    static ASN1ObjectDescriptor createPrimitive(byte[] contents)
+    {
+        return new ASN1ObjectDescriptor(ASN1GraphicString.createPrimitive(contents));
+    }
+}
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1ObjectIdentifier.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1ObjectIdentifier.java
index 452a3a0..f6903dd 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1ObjectIdentifier.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1ObjectIdentifier.java
@@ -16,9 +16,18 @@
 public class ASN1ObjectIdentifier
     extends ASN1Primitive
 {
-    private final String identifier;
+    static final ASN1UniversalType TYPE = new ASN1UniversalType(ASN1ObjectIdentifier.class, BERTags.OBJECT_IDENTIFIER)
+    {
+        ASN1Primitive fromImplicitPrimitive(DEROctetString octetString)
+        {
+            return createPrimitive(octetString.getOctets(), false);
+        }
+    };
 
-    private byte[] body;
+    public static ASN1ObjectIdentifier fromContents(byte[] contents)
+    {
+        return createPrimitive(contents, true);
+    }
 
     /**
      * Return an OID from the passed in object
@@ -27,30 +36,25 @@
      * @return an ASN1ObjectIdentifier instance, or null.
      * @throws IllegalArgumentException if the object cannot be converted.
      */
-    public static ASN1ObjectIdentifier getInstance(
-        Object obj)
+    public static ASN1ObjectIdentifier getInstance(Object obj)
     {
         if (obj == null || obj instanceof ASN1ObjectIdentifier)
         {
             return (ASN1ObjectIdentifier)obj;
         }
-
-        if (obj instanceof ASN1Encodable)
+        else if (obj instanceof ASN1Encodable)
         {
             ASN1Primitive primitive = ((ASN1Encodable)obj).toASN1Primitive();
-
             if (primitive instanceof ASN1ObjectIdentifier)
             {
                 return (ASN1ObjectIdentifier)primitive;
             }
         }
-
-        if (obj instanceof byte[])
+        else if (obj instanceof byte[])
         {
-            byte[] enc = (byte[])obj;
             try
             {
-                return (ASN1ObjectIdentifier)fromByteArray(enc);
+                return (ASN1ObjectIdentifier)TYPE.fromByteArray((byte[])obj);
             }
             catch (IOException e)
             {
@@ -64,47 +68,60 @@
     /**
      * Return an OBJECT IDENTIFIER from a tagged object.
      *
-     * @param obj      the tagged object holding the object we want
+     * @param taggedObject      the tagged object holding the object we want
      * @param explicit true if the object is meant to be explicitly
      *                 tagged false otherwise.
      * @return an ASN1ObjectIdentifier instance, or null.
      * @throws IllegalArgumentException if the tagged object cannot
      * be converted.
      */
-    public static ASN1ObjectIdentifier getInstance(
-        ASN1TaggedObject obj,
-        boolean explicit)
+    public static ASN1ObjectIdentifier getInstance(ASN1TaggedObject taggedObject, boolean explicit)
     {
-        ASN1Primitive o = obj.getObject();
+        /*
+         * TODO[asn1] This block here is for backward compatibility, but should eventually be removed.
+         * 
+         * - see https://github.com/bcgit/bc-java/issues/1015
+         */
+        if (!explicit && !taggedObject.isParsed() && BERTags.CONTEXT_SPECIFIC == taggedObject.getTagClass())
+        {
+            ASN1Primitive base = taggedObject.getBaseObject().toASN1Primitive();
+            if (!(base instanceof ASN1ObjectIdentifier))
+            {
+                return fromContents(ASN1OctetString.getInstance(base).getOctets());
+            }
+        }
 
-        if (explicit || o instanceof ASN1ObjectIdentifier)
-        {
-            return getInstance(o);
-        }
-        else
-        {
-            return ASN1ObjectIdentifier.fromOctetString(ASN1OctetString.getInstance(o).getOctets());
-        }
+        return (ASN1ObjectIdentifier)TYPE.getContextInstance(taggedObject, explicit);
     }
 
-    private static final long LONG_LIMIT = (Long.MAX_VALUE >> 7) - 0x7f;
+    private static final long LONG_LIMIT = (Long.MAX_VALUE >> 7) - 0x7F;
 
-    ASN1ObjectIdentifier(
-        byte[] bytes)
+    private static final ConcurrentMap<OidHandle, ASN1ObjectIdentifier> pool =
+        new ConcurrentHashMap<OidHandle, ASN1ObjectIdentifier>();
+
+    private final String identifier;
+    private byte[] contents;
+
+    ASN1ObjectIdentifier(byte[] contents, boolean clone)
     {
-        StringBuffer objId = new StringBuffer();
+        if (contents.length == 0)
+        {
+            throw new IllegalArgumentException("empty OBJECT IDENTIFIER with no sub-identifiers");
+        }
+
+        StringBuilder objId = new StringBuilder();
         long value = 0;
         BigInteger bigValue = null;
         boolean first = true;
 
-        for (int i = 0; i != bytes.length; i++)
+        for (int i = 0; i != contents.length; i++)
         {
-            int b = bytes[i] & 0xff;
+            int b = contents[i] & 0xff;
 
             if (value <= LONG_LIMIT)
             {
-                value += (b & 0x7f);
-                if ((b & 0x80) == 0)             // end of number reached
+                value += b & 0x7F;
+                if ((b & 0x80) == 0)
                 {
                     if (first)
                     {
@@ -140,7 +157,7 @@
                 {
                     bigValue = BigInteger.valueOf(value);
                 }
-                bigValue = bigValue.or(BigInteger.valueOf(b & 0x7f));
+                bigValue = bigValue.or(BigInteger.valueOf(b & 0x7F));
                 if ((b & 0x80) == 0)
                 {
                     if (first)
@@ -164,7 +181,7 @@
 
         // Android-changed: Intern the identifier so there aren't hundreds of duplicates in practice.
         this.identifier = objId.toString().intern();
-        this.body = Arrays.clone(bytes);
+        this.contents = clone ? Arrays.clone(contents) : contents;
     }
 
     /**
@@ -196,7 +213,7 @@
      */
     ASN1ObjectIdentifier(ASN1ObjectIdentifier oid, String branchID)
     {
-        if (!isValidBranchID(branchID, 0))
+        if (!ASN1RelativeOID.isValidIdentifier(branchID, 0))
         {
             throw new IllegalArgumentException("string " + branchID + " not a valid OID branch");
         }
@@ -237,44 +254,6 @@
         return id.length() > stemId.length() && id.charAt(stemId.length()) == '.' && id.startsWith(stemId);
     }
 
-    private void writeField(
-        ByteArrayOutputStream out,
-        long fieldValue)
-    {
-        byte[] result = new byte[9];
-        int pos = 8;
-        result[pos] = (byte)((int)fieldValue & 0x7f);
-        while (fieldValue >= (1L << 7))
-        {
-            fieldValue >>= 7;
-            result[--pos] = (byte)((int)fieldValue & 0x7f | 0x80);
-        }
-        out.write(result, pos, 9 - pos);
-    }
-
-    private void writeField(
-        ByteArrayOutputStream out,
-        BigInteger fieldValue)
-    {
-        int byteCount = (fieldValue.bitLength() + 6) / 7;
-        if (byteCount == 0)
-        {
-            out.write(0);
-        }
-        else
-        {
-            BigInteger tmpValue = fieldValue;
-            byte[] tmp = new byte[byteCount];
-            for (int i = byteCount - 1; i >= 0; i--)
-            {
-                tmp[i] = (byte)((tmpValue.intValue() & 0x7f) | 0x80);
-                tmpValue = tmpValue.shiftRight(7);
-            }
-            tmp[byteCount - 1] &= 0x7f;
-            out.write(tmp, 0, tmp.length);
-        }
-    }
-
     private void doOutput(ByteArrayOutputStream aOut)
     {
         OIDTokenizer tok = new OIDTokenizer(identifier);
@@ -283,11 +262,11 @@
         String secondToken = tok.nextToken();
         if (secondToken.length() <= 18)
         {
-            writeField(aOut, first + Long.parseLong(secondToken));
+            ASN1RelativeOID.writeField(aOut, first + Long.parseLong(secondToken));
         }
         else
         {
-            writeField(aOut, new BigInteger(secondToken).add(BigInteger.valueOf(first)));
+            ASN1RelativeOID.writeField(aOut, new BigInteger(secondToken).add(BigInteger.valueOf(first)));
         }
 
         while (tok.hasMoreTokens())
@@ -295,45 +274,42 @@
             String token = tok.nextToken();
             if (token.length() <= 18)
             {
-                writeField(aOut, Long.parseLong(token));
+                ASN1RelativeOID.writeField(aOut, Long.parseLong(token));
             }
             else
             {
-                writeField(aOut, new BigInteger(token));
+                ASN1RelativeOID.writeField(aOut, new BigInteger(token));
             }
         }
     }
 
-    private synchronized byte[] getBody()
+    private synchronized byte[] getContents()
     {
-        if (body == null)
+        if (contents == null)
         {
             ByteArrayOutputStream bOut = new ByteArrayOutputStream();
 
             doOutput(bOut);
 
-            body = bOut.toByteArray();
+            contents = bOut.toByteArray();
         }
 
-        return body;
+        return contents;
     }
 
-    boolean isConstructed()
+    boolean encodeConstructed()
     {
         return false;
     }
 
-    int encodedLength()
-        throws IOException
+    int encodedLength(boolean withTag)
     {
-        int length = getBody().length;
-
-        return 1 + StreamUtil.calculateBodyLength(length) + length;
+        return ASN1OutputStream.getLengthOfEncodingDL(withTag, getContents().length);
     }
 
     void encode(ASN1OutputStream out, boolean withTag) throws IOException
     {
-        out.writeEncoded(withTag, BERTags.OBJECT_IDENTIFIER, getBody());
+        out.writeEncodingDL(withTag, BERTags.OBJECT_IDENTIFIER, getContents());
     }
 
     public int hashCode()
@@ -362,45 +338,6 @@
         return getId();
     }
 
-    private static boolean isValidBranchID(
-        String branchID, int start)
-    {
-        int digitCount = 0;
-
-        int pos = branchID.length();
-        while (--pos >= start)
-        {
-            char ch = branchID.charAt(pos);
-
-            if (ch == '.')
-            {
-                if (0 == digitCount
-                    || (digitCount > 1 && branchID.charAt(pos + 1) == '0'))
-                {
-                    return false;
-                }
-
-                digitCount = 0;
-            }
-            else if ('0' <= ch && ch <= '9')
-            {
-                ++digitCount;
-            }
-            else
-            {
-                return false;
-            }
-        }
-
-        if (0 == digitCount
-            || (digitCount > 1 && branchID.charAt(pos + 1) == '0'))
-        {
-            return false;
-        }
-
-        return true;
-    }
-
     private static boolean isValidIdentifier(
         String identifier)
     {
@@ -415,7 +352,7 @@
             return false;
         }
 
-        return isValidBranchID(identifier, 2);
+        return ASN1RelativeOID.isValidIdentifier(identifier, 2);
     }
 
     /**
@@ -430,30 +367,35 @@
      */
     public ASN1ObjectIdentifier intern()
     {
-        final OidHandle hdl = new OidHandle(getBody());
+        final OidHandle hdl = new OidHandle(getContents());
         ASN1ObjectIdentifier oid = pool.get(hdl);
         if (oid == null)
         {
-            oid = pool.putIfAbsent(hdl, this);
-            if (oid == null)
+            synchronized (pool)
             {
-                oid = this;
+                if (!pool.containsKey(hdl))
+                {
+                    pool.put(hdl, this);
+                    return this;
+                }
+                else
+                {
+                    return pool.get(hdl);
+                }
             }
         }
         return oid;
     }
 
-    private static final ConcurrentMap<OidHandle, ASN1ObjectIdentifier> pool = new ConcurrentHashMap<OidHandle, ASN1ObjectIdentifier>();
-
     private static class OidHandle
     {
         private final int key;
-        private final byte[] enc;
+        private final byte[] contents;
 
-        OidHandle(byte[] enc)
+        OidHandle(byte[] contents)
         {
-            this.key = Arrays.hashCode(enc);
-            this.enc = enc;
+            this.key = Arrays.hashCode(contents);
+            this.contents = contents;
         }
 
         public int hashCode()
@@ -465,20 +407,20 @@
         {
             if (o instanceof OidHandle)
             {
-                return Arrays.areEqual(enc, ((OidHandle)o).enc);
+                return Arrays.areEqual(contents, ((OidHandle)o).contents);
             }
 
             return false;
         }
     }
 
-    static ASN1ObjectIdentifier fromOctetString(byte[] enc)
+    static ASN1ObjectIdentifier createPrimitive(byte[] contents, boolean clone)
     {
-        final OidHandle hdl = new OidHandle(enc);
+        final OidHandle hdl = new OidHandle(contents);
         ASN1ObjectIdentifier oid = pool.get(hdl);
         if (oid == null)
         {
-            return new ASN1ObjectIdentifier(enc);
+            return new ASN1ObjectIdentifier(contents, clone);
         }
         return oid;
     }
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1OctetString.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1OctetString.java
index 30351c2..831b780 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1OctetString.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1OctetString.java
@@ -102,7 +102,18 @@
     extends ASN1Primitive
     implements ASN1OctetStringParser
 {
-    byte[]  string;
+    static final ASN1UniversalType TYPE = new ASN1UniversalType(ASN1OctetString.class, BERTags.OCTET_STRING)
+    {
+        ASN1Primitive fromImplicitPrimitive(DEROctetString octetString)
+        {
+            return octetString;
+        }
+
+        ASN1Primitive fromImplicitConstructed(ASN1Sequence sequence)
+        {
+            return sequence.toASN1OctetString();
+        }
+    };
 
     /**
      * return an Octet String from a tagged object.
@@ -113,70 +124,9 @@
      * @exception IllegalArgumentException if the tagged object cannot
      *              be converted.
      */
-    public static ASN1OctetString getInstance(
-        ASN1TaggedObject    taggedObject,
-        boolean             explicit)
+    public static ASN1OctetString getInstance(ASN1TaggedObject taggedObject, boolean explicit)
     {
-        if (explicit)
-        {
-            if (!taggedObject.isExplicit())
-            {
-                throw new IllegalArgumentException("object implicit - explicit expected.");
-            }
-
-            return getInstance(taggedObject.getObject());
-        }
-
-        ASN1Primitive o = taggedObject.getObject();
-
-        /*
-         * constructed object which appears to be explicitly tagged and it's really implicit means
-         * we have to add the surrounding octet string.
-         */
-        if (taggedObject.isExplicit())
-        {
-            ASN1OctetString singleSegment = getInstance(o);
-
-            if (taggedObject instanceof BERTaggedObject)
-            {
-                return new BEROctetString(new ASN1OctetString[]{ singleSegment });
-            }
-
-            // TODO Should really be similar to the BERTaggedObject case above:
-//            return new DLOctetString(new ASN1OctetString[]{ singleSegment });
-            return (ASN1OctetString)new BEROctetString(new ASN1OctetString[]{ singleSegment }).toDLObject();
-        }
-
-        if (o instanceof ASN1OctetString)
-        {
-            ASN1OctetString s = (ASN1OctetString)o;
-
-            if (taggedObject instanceof BERTaggedObject)
-            {
-                return s;
-            }
-
-            return (ASN1OctetString)s.toDLObject();
-        }
-
-        /*
-         * in this case the parser returns a sequence, convert it into an octet string.
-         */
-        if (o instanceof ASN1Sequence)
-        {
-            ASN1Sequence s = (ASN1Sequence)o;
-
-            if (taggedObject instanceof BERTaggedObject)
-            {
-                return BEROctetString.fromSequence(s);
-            }
-
-            // TODO Should really be similar to the BERTaggedObject case above:
-//            return DLOctetString.fromSequence(s);
-            return (ASN1OctetString)BEROctetString.fromSequence(s).toDLObject();
-        }
-
-        throw new IllegalArgumentException("unknown object in getInstance: " + taggedObject.getClass().getName());
+        return (ASN1OctetString)TYPE.getContextInstance(taggedObject, explicit);
     }
 
     /**
@@ -185,37 +135,40 @@
      * @param obj the object we want converted.
      * @exception IllegalArgumentException if the object cannot be converted.
      */
-    public static ASN1OctetString getInstance(
-        Object  obj)
+    public static ASN1OctetString getInstance(Object obj)
     {
         if (obj == null || obj instanceof ASN1OctetString)
         {
             return (ASN1OctetString)obj;
         }
+//      else if (obj instanceof ASN1OctetStringParser)
+        else if (obj instanceof ASN1Encodable)
+        {
+            ASN1Primitive primitive = ((ASN1Encodable)obj).toASN1Primitive();
+            if (primitive instanceof ASN1OctetString)
+            {
+                return (ASN1OctetString)primitive;
+            }
+        }
         else if (obj instanceof byte[])
         {
             try
             {
-                return getInstance(fromByteArray((byte[])obj));
+                return (ASN1OctetString)TYPE.fromByteArray((byte[])obj);
             }
             catch (IOException e)
             {
                 throw new IllegalArgumentException("failed to construct OCTET STRING from byte[]: " + e.getMessage());
             }
         }
-        else if (obj instanceof ASN1Encodable)
-        {
-            ASN1Primitive primitive = ((ASN1Encodable)obj).toASN1Primitive();
-
-            if (primitive instanceof ASN1OctetString)
-            {
-                return (ASN1OctetString)primitive;
-            }
-        }
 
         throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
     }
 
+    static final byte[] EMPTY_OCTETS = new byte[0];
+
+    byte[] string;
+
     /**
      * Base constructor.
      *
@@ -261,6 +214,11 @@
         return string;
     }
 
+    public int getOctetsLength()
+    {
+        return getOctets().length;
+    }
+
     public int hashCode()
     {
         return Arrays.hashCode(this.getOctets());
@@ -294,10 +252,13 @@
         return new DEROctetString(string);
     }
 
-    abstract void encode(ASN1OutputStream out, boolean withTag) throws IOException;
-
     public String toString()
     {
       return "#" + Strings.fromByteArray(Hex.encode(string));
     }
+
+    static ASN1OctetString createPrimitive(byte[] contents)
+    {
+        return new DEROctetString(contents);
+    }
 }
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1OutputStream.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1OutputStream.java
index 0c2c931..b84a84a 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1OutputStream.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1OutputStream.java
@@ -34,250 +34,33 @@
 
     private OutputStream os;
 
-    /**
-     * @deprecated Use {@link ASN1OutputStream#create(OutputStream)} instead.
-     */
-    public ASN1OutputStream(OutputStream os)
+    ASN1OutputStream(OutputStream os)
     {
         this.os = os;
     }
 
-    final void writeLength(
-        int length)
-        throws IOException
+    public void close() throws IOException
     {
-        if (length > 127)
-        {
-            int size = 1;
-            int val = length;
-
-            while ((val >>>= 8) != 0)
-            {
-                size++;
-            }
-
-            write((byte)(size | 0x80));
-
-            for (int i = (size - 1) * 8; i >= 0; i -= 8)
-            {
-                write((byte)(length >> i));
-            }
-        }
-        else
-        {
-            write((byte)length);
-        }
+        os.close();
     }
 
-    final void write(int b)
-        throws IOException
+    public void flush() throws IOException
     {
-        os.write(b);
+        os.flush();
     }
 
-    final void write(byte[] bytes, int off, int len)
-        throws IOException
+    public final void writeObject(ASN1Encodable encodable) throws IOException
     {
-        os.write(bytes, off, len);
-    }
-
-    final void writeElements(ASN1Encodable[] elements)
-        throws IOException
-    {
-        int count = elements.length;
-        for (int i = 0; i < count; ++i)
-        {
-            ASN1Primitive primitive = elements[i].toASN1Primitive();
-
-            writePrimitive(primitive, true);
-        }
-    }
-
-    final void writeElements(Enumeration elements)
-        throws IOException
-    {
-        while (elements.hasMoreElements())
-        {
-            ASN1Primitive primitive = ((ASN1Encodable)elements.nextElement()).toASN1Primitive();
-
-            writePrimitive(primitive, true);
-        }
-    }
-
-    final void writeEncoded(
-        boolean withTag,
-        int     tag,
-        byte    contents)
-        throws IOException
-    {
-        if (withTag)
-        {
-            write(tag);
-        }
-        writeLength(1);
-        write(contents);
-    }
-
-    final void writeEncoded(
-        boolean withTag,
-        int     tag,
-        byte[]  contents)
-        throws IOException
-    {
-        if (withTag)
-        {
-            write(tag);
-        }
-        writeLength(contents.length);
-        write(contents, 0, contents.length);
-    }
-
-    final void writeEncoded(
-        boolean withTag,
-        int     tag,
-        byte[]  contents,
-        int     contentsOff,
-        int     contentsLen)
-        throws IOException
-    {
-        if (withTag)
-        {
-            write(tag);
-        }
-        writeLength(contentsLen);
-        write(contents, contentsOff, contentsLen);
-    }
-
-    final void writeEncoded(
-        boolean withTag,
-        int     tag,
-        byte    headByte,
-        byte[]  tailBytes)
-        throws IOException
-    {
-        if (withTag)
-        {
-            write(tag);
-        }
-        writeLength(1 + tailBytes.length);
-        write(headByte);
-        write(tailBytes, 0, tailBytes.length);
-    }
-
-    final void writeEncoded(
-        boolean withTag,
-        int     tag,
-        byte    headByte,
-        byte[]  body,
-        int     bodyOff,
-        int     bodyLen,
-        byte    tailByte)
-        throws IOException
-    {
-        if (withTag)
-        {
-            write(tag);
-        }
-        writeLength(2 + bodyLen);
-        write(headByte);
-        write(body, bodyOff, bodyLen);
-        write(tailByte);
-    }
-
-    final void writeEncoded(boolean withTag, int flags, int tagNo, byte[] contents)
-        throws IOException
-    {
-        writeTag(withTag, flags, tagNo);
-        writeLength(contents.length);
-        write(contents, 0, contents.length);
-    }
-
-    final void writeEncodedIndef(boolean withTag, int flags, int tagNo, byte[] contents)
-        throws IOException
-    {
-        writeTag(withTag, flags, tagNo);
-        write(0x80);
-        write(contents, 0, contents.length);
-        write(0x00);
-        write(0x00);
-    }
-
-    final void writeEncodedIndef(boolean withTag, int tag, ASN1Encodable[] elements)
-        throws IOException
-    {
-        if (withTag)
-        {
-            write(tag);
-        }
-        write(0x80);
-        writeElements(elements);
-        write(0x00);
-        write(0x00);
-    }
-
-    final void writeEncodedIndef(boolean withTag, int tag, Enumeration elements)
-        throws IOException
-    {
-        if (withTag)
-        {
-            write(tag);
-        }
-        write(0x80);
-        writeElements(elements);
-        write(0x00);
-        write(0x00);
-    }
-
-    final void writeTag(boolean withTag, int flags, int tagNo)
-        throws IOException
-    {
-        if (!withTag)
-        {
-            return;
-        }
-
-        if (tagNo < 31)
-        {
-            write(flags | tagNo);
-        }
-        else
-        {
-            write(flags | 0x1f);
-            if (tagNo < 128)
-            {
-                write(tagNo);
-            }
-            else
-            {
-                byte[] stack = new byte[5];
-                int pos = stack.length;
-
-                stack[--pos] = (byte)(tagNo & 0x7F);
-
-                do
-                {
-                    tagNo >>= 7;
-                    stack[--pos] = (byte)(tagNo & 0x7F | 0x80);
-                }
-                while (tagNo > 127);
-
-                write(stack, pos, stack.length - pos);
-            }
-        }
-    }
-
-    public void writeObject(ASN1Encodable obj) throws IOException
-    {
-        if (null == obj)
+        if (null == encodable)
         {
             throw new IOException("null object detected");
         }
 
-        writePrimitive(obj.toASN1Primitive(), true);
+        writePrimitive(encodable.toASN1Primitive(), true);
         flushInternal();
     }
 
-    public void writeObject(ASN1Primitive primitive) throws IOException
+    public final void writeObject(ASN1Primitive primitive) throws IOException
     {
         if (null == primitive)
         {
@@ -288,25 +71,7 @@
         flushInternal();
     }
 
-    void writePrimitive(ASN1Primitive primitive, boolean withTag) throws IOException
-    {
-        primitive.encode(this, withTag);
-    }
-
-    public void close()
-        throws IOException
-    {
-        os.close();
-    }
-
-    public void flush()
-        throws IOException
-    {
-        os.flush();
-    }
-
-    void flushInternal()
-        throws IOException
+    void flushInternal() throws IOException
     {
         // Placeholder to support future internal buffering
     }
@@ -316,8 +81,192 @@
         return new DEROutputStream(os);
     }
 
-    ASN1OutputStream getDLSubStream()
+    DLOutputStream getDLSubStream()
     {
         return new DLOutputStream(os);
     }
+
+    final void writeDL(int length) throws IOException
+    {
+        if (length < 128)
+        {
+            write(length);
+        }
+        else
+        {
+            byte[] stack = new byte[5];
+            int pos = stack.length;
+
+            do
+            {
+                stack[--pos] = (byte)length;
+                length >>>= 8;
+            }
+            while (length != 0);
+
+            int count = stack.length - pos;
+            stack[--pos] = (byte)(0x80 | count);
+
+            write(stack, pos, count + 1);
+        }
+    }
+
+    final void write(int b) throws IOException
+    {
+        os.write(b);
+    }
+
+    final void write(byte[] bytes, int off, int len) throws IOException
+    {
+        os.write(bytes, off, len);
+    }
+
+    void writeElements(ASN1Encodable[] elements)
+        throws IOException
+    {
+        for (int i = 0, count = elements.length; i < count; ++i)
+        {
+            elements[i].toASN1Primitive().encode(this, true);
+        }
+    }
+
+    final void writeEncodingDL(boolean withID, int identifier, byte contents) throws IOException
+    {
+        writeIdentifier(withID, identifier);
+        writeDL(1);
+        write(contents);
+    }
+
+    final void writeEncodingDL(boolean withID, int identifier, byte[] contents) throws IOException
+    {
+        writeIdentifier(withID, identifier);
+        writeDL(contents.length);
+        write(contents, 0, contents.length);
+    }
+
+    final void writeEncodingDL(boolean withID, int identifier, byte[] contents, int contentsOff, int contentsLen)
+        throws IOException
+    {
+        writeIdentifier(withID, identifier);
+        writeDL(contentsLen);
+        write(contents, contentsOff, contentsLen);
+    }
+
+    final void writeEncodingDL(boolean withID, int identifier, byte contentsPrefix, byte[] contents, int contentsOff,
+        int contentsLen) throws IOException
+    {
+        writeIdentifier(withID, identifier);
+        writeDL(1 + contentsLen);
+        write(contentsPrefix);
+        write(contents, contentsOff, contentsLen);
+    }
+
+    final void writeEncodingDL(boolean withID, int identifier, byte[] contents, int contentsOff, int contentsLen,
+        byte contentsSuffix) throws IOException
+    {
+        writeIdentifier(withID, identifier);
+        writeDL(contentsLen + 1);
+        write(contents, contentsOff, contentsLen);
+        write(contentsSuffix);
+    }
+
+    final void writeEncodingDL(boolean withID, int flags, int tag, byte[] contents) throws IOException
+    {
+        writeIdentifier(withID, flags, tag);
+        writeDL(contents.length);
+        write(contents, 0, contents.length);
+    }
+
+    final void writeEncodingIL(boolean withID, int identifier, ASN1Encodable[] elements) throws IOException
+    {
+        writeIdentifier(withID, identifier);
+        write(0x80);
+        writeElements(elements);
+        write(0x00);
+        write(0x00);
+    }
+
+    final void writeIdentifier(boolean withID, int identifier) throws IOException
+    {
+        if (withID)
+        {
+            write(identifier);
+        }
+    }
+
+    final void writeIdentifier(boolean withID, int flags, int tag) throws IOException
+    {
+        if (!withID)
+        {
+            // Don't write the identifier
+        }
+        else if (tag < 31)
+        {
+            write(flags | tag);
+        }
+        else
+        {
+            byte[] stack = new byte[6];
+            int pos = stack.length;
+
+            stack[--pos] = (byte)(tag & 0x7F);
+            while (tag > 127)
+            {
+                tag >>>= 7;
+                stack[--pos] = (byte)(tag & 0x7F | 0x80);
+            }
+
+            stack[--pos] = (byte)(flags | 0x1F);
+
+            write(stack, pos, stack.length - pos);
+        }
+    }
+
+    void writePrimitive(ASN1Primitive primitive, boolean withID) throws IOException
+    {
+        primitive.encode(this, withID);
+    }
+
+    void writePrimitives(ASN1Primitive[] primitives) throws IOException
+    {
+        for (int i = 0, count = primitives.length; i < count; ++i)
+        {
+            primitives[i].encode(this, true);
+        }
+    }
+
+    static int getLengthOfDL(int dl)
+    {
+        if (dl < 128)
+        {
+            return 1;
+        }
+
+        int length = 2;
+        while ((dl >>>= 8) != 0)
+        {
+            ++length;
+        }
+        return length;
+    }
+
+    static int getLengthOfEncodingDL(boolean withID, int contentsLength)
+    {
+        return (withID ? 1 : 0) + getLengthOfDL(contentsLength) + contentsLength;
+    }
+
+    static int getLengthOfIdentifier(int tag)
+    {
+        if (tag < 31)
+        {
+            return 1;
+        }
+
+        int length = 2;
+        while ((tag >>>= 7) != 0)
+        {
+            ++length;
+        }
+        return length;
+    }
 }
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1Primitive.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1Primitive.java
index c3b423b..e646dd0 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1Primitive.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1Primitive.java
@@ -17,12 +17,16 @@
 
     public void encodeTo(OutputStream output) throws IOException
     {
-        ASN1OutputStream.create(output).writeObject(this);
+        ASN1OutputStream asn1Out = ASN1OutputStream.create(output); 
+        asn1Out.writePrimitive(this, true);
+        asn1Out.flushInternal();
     }
 
     public void encodeTo(OutputStream output, String encoding) throws IOException
     {
-        ASN1OutputStream.create(output, encoding).writeObject(this);
+        ASN1OutputStream asn1Out = ASN1OutputStream.create(output, encoding); 
+        asn1Out.writePrimitive(this, true);
+        asn1Out.flushInternal();
     }
 
     /**
@@ -105,14 +109,9 @@
      * Return true if this objected is a CONSTRUCTED one, false otherwise.
      * @return true if CONSTRUCTED bit set on object's tag, false otherwise.
      */
-    abstract boolean isConstructed();
+    abstract boolean encodeConstructed();
 
-    /**
-     * Return the length of the encoding this object will produce.
-     * @return the length of the object's encoding.
-     * @throws IOException if the encoding length cannot be calculated.
-     */
-    abstract int encodedLength() throws IOException;
+    abstract int encodedLength(boolean withTag) throws IOException;
 
     abstract void encode(ASN1OutputStream out, boolean withTag) throws IOException;
 
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1PrintableString.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1PrintableString.java
new file mode 100644
index 0000000..a0de296
--- /dev/null
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1PrintableString.java
@@ -0,0 +1,231 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.org.bouncycastle.asn1;
+
+import java.io.IOException;
+
+import com.android.org.bouncycastle.util.Arrays;
+import com.android.org.bouncycastle.util.Strings;
+
+/**
+ * ASN.1 PrintableString object.
+ * <p>
+ * X.680 section 37.4 defines PrintableString character codes as ASCII subset of following characters:
+ * </p>
+ * <ul>
+ * <li>Latin capital letters: 'A' .. 'Z'</li>
+ * <li>Latin small letters: 'a' .. 'z'</li>
+ * <li>Digits: '0'..'9'</li>
+ * <li>Space</li>
+ * <li>Apostrophe: '\''</li>
+ * <li>Left parenthesis: '('</li>
+ * <li>Right parenthesis: ')'</li>
+ * <li>Plus sign: '+'</li>
+ * <li>Comma: ','</li>
+ * <li>Hyphen-minus: '-'</li>
+ * <li>Full stop: '.'</li>
+ * <li>Solidus: '/'</li>
+ * <li>Colon: ':'</li>
+ * <li>Equals sign: '='</li>
+ * <li>Question mark: '?'</li>
+ * </ul>
+ * <p>
+ * Explicit character set escape sequences are not allowed.
+ * </p>
+ * @hide This class is not part of the Android public SDK API
+ */
+public abstract class ASN1PrintableString
+    extends ASN1Primitive
+    implements ASN1String
+{
+    static final ASN1UniversalType TYPE = new ASN1UniversalType(ASN1PrintableString.class, BERTags.PRINTABLE_STRING)
+    {
+        ASN1Primitive fromImplicitPrimitive(DEROctetString octetString)
+        {
+            return createPrimitive(octetString.getOctets());
+        }
+    };
+
+    /**
+     * Return a printable string from the passed in object.
+     *
+     * @param obj an ASN1PrintableString or an object that can be converted into one.
+     * @exception IllegalArgumentException if the object cannot be converted.
+     * @return an ASN1PrintableString instance, or null.
+     */
+    public static ASN1PrintableString getInstance(Object obj)
+    {
+        if (obj == null || obj instanceof ASN1PrintableString)
+        {
+            return (ASN1PrintableString)obj;
+        }
+        if (obj instanceof ASN1Encodable)
+        {
+            ASN1Primitive primitive = ((ASN1Encodable)obj).toASN1Primitive();
+            if (primitive instanceof ASN1PrintableString)
+            {
+                return (ASN1PrintableString)primitive;
+            }
+        }
+        if (obj instanceof byte[])
+        {
+            try
+            {
+                return (ASN1PrintableString)TYPE.fromByteArray((byte[])obj);
+            }
+            catch (Exception e)
+            {
+                throw new IllegalArgumentException("encoding error in getInstance: " + e.toString());
+            }
+        }
+
+        throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
+    }
+
+    /**
+     * Return a Printable String from a tagged object.
+     *
+     * @param taggedObject the tagged object holding the object we want
+     * @param explicit true if the object is meant to be explicitly
+     *              tagged false otherwise.
+     * @exception IllegalArgumentException if the tagged object cannot
+     *               be converted.
+     * @return an ASN1PrintableString instance, or null.
+     */
+    public static ASN1PrintableString getInstance(ASN1TaggedObject taggedObject, boolean explicit)
+    {
+        return (ASN1PrintableString)TYPE.getContextInstance(taggedObject, explicit);
+    }
+
+    final byte[] contents;
+
+    /**
+     * Constructor with optional validation.
+     *
+     * @param string the base string to wrap.
+     * @param validate whether or not to check the string.
+     * @throws IllegalArgumentException if validate is true and the string
+     * contains characters that should not be in a PrintableString.
+     */
+    ASN1PrintableString(String string, boolean validate)
+    {
+        if (validate && !isPrintableString(string))
+        {
+            throw new IllegalArgumentException("string contains illegal characters");
+        }
+
+        this.contents = Strings.toByteArray(string);
+    }
+
+    ASN1PrintableString(byte[] contents, boolean clone)
+    {
+        this.contents = clone ? Arrays.clone(contents) : contents;
+    }
+
+    public final String getString()
+    {
+        return Strings.fromByteArray(contents);
+    }
+
+    public final byte[] getOctets()
+    {
+        return Arrays.clone(contents);
+    }
+
+    final boolean encodeConstructed()
+    {
+        return false;
+    }
+
+    final int encodedLength(boolean withTag)
+    {
+        return ASN1OutputStream.getLengthOfEncodingDL(withTag, contents.length);
+    }
+
+    final void encode(ASN1OutputStream out, boolean withTag) throws IOException
+    {
+        out.writeEncodingDL(withTag, BERTags.PRINTABLE_STRING, contents);
+    }
+
+    final boolean asn1Equals(ASN1Primitive other)
+    {
+        if (!(other instanceof ASN1PrintableString))
+        {
+            return false;
+        }
+
+        ASN1PrintableString that = (ASN1PrintableString)other;
+
+        return Arrays.areEqual(this.contents, that.contents);
+    }
+
+    public final int hashCode()
+    {
+        return Arrays.hashCode(contents);
+    }
+
+    public String toString()
+    {
+        return getString();
+    }
+
+    /**
+     * return true if the passed in String can be represented without
+     * loss as a PrintableString, false otherwise.
+     *
+     * @return true if in printable set, false otherwise.
+     */
+    public static boolean isPrintableString(
+        String  str)
+    {
+        for (int i = str.length() - 1; i >= 0; i--)
+        {
+            char    ch = str.charAt(i);
+
+            if (ch > 0x007f)
+            {
+                return false;
+            }
+
+            if ('a' <= ch && ch <= 'z')
+            {
+                continue;
+            }
+
+            if ('A' <= ch && ch <= 'Z')
+            {
+                continue;
+            }
+
+            if ('0' <= ch && ch <= '9')
+            {
+                continue;
+            }
+
+            switch (ch)
+            {
+            case ' ':
+            case '\'':
+            case '(':
+            case ')':
+            case '+':
+            case '-':
+            case '.':
+            case ':':
+            case '=':
+            case '?':
+            case '/':
+            case ',':
+                continue;
+            }
+
+            return false;
+        }
+
+        return true;
+    }
+
+    static ASN1PrintableString createPrimitive(byte[] contents)
+    {
+        return new DERPrintableString(contents, false);
+    }
+}
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1RelativeOID.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1RelativeOID.java
new file mode 100644
index 0000000..b6174bb
--- /dev/null
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1RelativeOID.java
@@ -0,0 +1,317 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.org.bouncycastle.asn1;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.math.BigInteger;
+
+import com.android.org.bouncycastle.util.Arrays;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+public class ASN1RelativeOID
+    extends ASN1Primitive
+{
+    static final ASN1UniversalType TYPE = new ASN1UniversalType(ASN1RelativeOID.class, BERTags.RELATIVE_OID)
+    {
+        ASN1Primitive fromImplicitPrimitive(DEROctetString octetString)
+        {
+            return createPrimitive(octetString.getOctets(), false);
+        }
+    };
+
+    public static ASN1RelativeOID fromContents(byte[] contents)
+    {
+        return createPrimitive(contents, true);
+    }
+
+    public static ASN1RelativeOID getInstance(Object obj)
+    {
+        if (obj == null || obj instanceof ASN1RelativeOID)
+        {
+            return (ASN1RelativeOID)obj;
+        }
+        if (obj instanceof ASN1Encodable)
+        {
+            ASN1Primitive primitive = ((ASN1Encodable)obj).toASN1Primitive();
+            if (primitive instanceof ASN1RelativeOID)
+            {
+                return (ASN1RelativeOID)primitive;
+            }
+        }
+        else if (obj instanceof byte[])
+        {
+            byte[] enc = (byte[])obj;
+            try
+            {
+                return (ASN1RelativeOID)TYPE.fromByteArray(enc);
+            }
+            catch (IOException e)
+            {
+                throw new IllegalArgumentException("failed to construct relative OID from byte[]: " + e.getMessage());
+            }
+        }
+
+        throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
+    }
+
+    public static ASN1RelativeOID getInstance(ASN1TaggedObject taggedObject, boolean explicit)
+    {
+        return (ASN1RelativeOID)TYPE.getContextInstance(taggedObject, explicit);
+    }
+
+    private static final long LONG_LIMIT = (Long.MAX_VALUE >> 7) - 0x7F;
+
+    private final String identifier;
+    private byte[] contents;
+
+    public ASN1RelativeOID(String identifier)
+    {
+        if (identifier == null)
+        {
+            throw new NullPointerException("'identifier' cannot be null");
+        }
+        if (!isValidIdentifier(identifier, 0))
+        {
+            throw new IllegalArgumentException("string " + identifier + " not a relative OID");
+        }
+
+        this.identifier = identifier;
+    }
+
+    ASN1RelativeOID(ASN1RelativeOID oid, String branchID)
+    {
+        if (!isValidIdentifier(branchID, 0))
+        {
+            throw new IllegalArgumentException("string " + branchID + " not a valid OID branch");
+        }
+
+        this.identifier = oid.getId() + "." + branchID;
+    }
+
+    private ASN1RelativeOID(byte[] contents, boolean clone)
+    {
+        StringBuffer objId = new StringBuffer();
+        long value = 0;
+        BigInteger bigValue = null;
+        boolean first = true;
+
+        for (int i = 0; i != contents.length; i++)
+        {
+            int b = contents[i] & 0xff;
+
+            if (value <= LONG_LIMIT)
+            {
+                value += b & 0x7F;
+                if ((b & 0x80) == 0)
+                {
+                    if (first)
+                    {
+                        first = false;
+                    }
+                    else
+                    {
+                        objId.append('.');
+                    }
+
+                    objId.append(value);
+                    value = 0;
+                }
+                else
+                {
+                    value <<= 7;
+                }
+            }
+            else
+            {
+                if (bigValue == null)
+                {
+                    bigValue = BigInteger.valueOf(value);
+                }
+                bigValue = bigValue.or(BigInteger.valueOf(b & 0x7F));
+                if ((b & 0x80) == 0)
+                {
+                    if (first)
+                    {
+                        first = false;
+                    }
+                    else
+                    {
+                        objId.append('.');
+                    }
+
+                    objId.append(bigValue);
+                    bigValue = null;
+                    value = 0;
+                }
+                else
+                {
+                    bigValue = bigValue.shiftLeft(7);
+                }
+            }
+        }
+
+        this.identifier = objId.toString();
+        this.contents = clone ? Arrays.clone(contents) : contents;
+    }
+
+    public ASN1RelativeOID branch(String branchID)
+    {
+        return new ASN1RelativeOID(this, branchID);
+    }
+
+    public String getId()
+    {
+        return identifier;
+    }
+
+    public int hashCode()
+    {
+        return identifier.hashCode();
+    }
+
+    public String toString()
+    {
+        return getId();
+    }
+
+    boolean asn1Equals(ASN1Primitive other)
+    {
+        if (this == other)
+        {
+            return true;
+        }
+        if (!(other instanceof ASN1RelativeOID))
+        {
+            return false;
+        }
+
+        ASN1RelativeOID that = (ASN1RelativeOID)other;
+
+        return this.identifier.equals(that.identifier);
+    }
+
+    int encodedLength(boolean withTag)
+    {
+        return ASN1OutputStream.getLengthOfEncodingDL(withTag, getContents().length);
+    }
+
+    void encode(ASN1OutputStream out, boolean withTag) throws IOException
+    {
+        out.writeEncodingDL(withTag, BERTags.RELATIVE_OID, getContents());
+    }
+
+    boolean encodeConstructed()
+    {
+        return false;
+    }
+
+    private void doOutput(ByteArrayOutputStream aOut)
+    {
+        OIDTokenizer tok = new OIDTokenizer(identifier);
+        while (tok.hasMoreTokens())
+        {
+            String token = tok.nextToken();
+            if (token.length() <= 18)
+            {
+                writeField(aOut, Long.parseLong(token));
+            }
+            else
+            {
+                writeField(aOut, new BigInteger(token));
+            }
+        }
+    }
+
+    private synchronized byte[] getContents()
+    {
+        if (contents == null)
+        {
+            ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+
+            doOutput(bOut);
+
+            contents = bOut.toByteArray();
+        }
+
+        return contents;
+    }
+
+    static ASN1RelativeOID createPrimitive(byte[] contents, boolean clone)
+    {
+        return new ASN1RelativeOID(contents, clone);
+    }
+
+    static boolean isValidIdentifier(String identifier, int from)
+    {
+        int digitCount = 0;
+
+        int pos = identifier.length();
+        while (--pos >= from)
+        {
+            char ch = identifier.charAt(pos);
+
+            if (ch == '.')
+            {
+                if (0 == digitCount
+                    || (digitCount > 1 && identifier.charAt(pos + 1) == '0'))
+                {
+                    return false;
+                }
+
+                digitCount = 0;
+            }
+            else if ('0' <= ch && ch <= '9')
+            {
+                ++digitCount;
+            }
+            else
+            {
+                return false;
+            }
+        }
+
+        if (0 == digitCount
+            || (digitCount > 1 && identifier.charAt(pos + 1) == '0'))
+        {
+            return false;
+        }
+
+        return true;
+    }
+
+    static void writeField(ByteArrayOutputStream out, long fieldValue)
+    {
+        byte[] result = new byte[9];
+        int pos = 8;
+        result[pos] = (byte)((int)fieldValue & 0x7F);
+        while (fieldValue >= (1L << 7))
+        {
+            fieldValue >>= 7;
+            result[--pos] = (byte)((int)fieldValue | 0x80);
+        }
+        out.write(result, pos, 9 - pos);
+    }
+
+    static void writeField(ByteArrayOutputStream out, BigInteger fieldValue)
+    {
+        int byteCount = (fieldValue.bitLength() + 6) / 7;
+        if (byteCount == 0)
+        {
+            out.write(0);
+        }
+        else
+        {
+            BigInteger tmpValue = fieldValue;
+            byte[] tmp = new byte[byteCount];
+            for (int i = byteCount - 1; i >= 0; i--)
+            {
+                tmp[i] = (byte)(tmpValue.intValue() | 0x80);
+                tmpValue = tmpValue.shiftRight(7);
+            }
+            tmp[byteCount - 1] &= 0x7F;
+            out.write(tmp, 0, tmp.length);
+        }
+    }
+}
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1Sequence.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1Sequence.java
index 6984a50..8a747dd 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1Sequence.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1Sequence.java
@@ -62,8 +62,13 @@
     extends ASN1Primitive
     implements com.android.org.bouncycastle.util.Iterable<ASN1Encodable>
 {
-    // NOTE: Only non-final to support LazyEncodedSequence
-    ASN1Encodable[] elements;
+    static final ASN1UniversalType TYPE = new ASN1UniversalType(ASN1Sequence.class, BERTags.SEQUENCE)
+    {
+        ASN1Primitive fromImplicitConstructed(ASN1Sequence sequence)
+        {
+            return sequence;
+        }
+    };
 
     /**
      * Return an ASN1Sequence from the given object.
@@ -72,37 +77,32 @@
      * @exception IllegalArgumentException if the object cannot be converted.
      * @return an ASN1Sequence instance, or null.
      */
-    public static ASN1Sequence getInstance(
-        Object  obj)
+    public static ASN1Sequence getInstance(Object obj)
     {
         if (obj == null || obj instanceof ASN1Sequence)
         {
             return (ASN1Sequence)obj;
         }
-        else if (obj instanceof ASN1SequenceParser)
+//      else if (obj instanceof ASN1SequenceParser)
+        else if (obj instanceof ASN1Encodable)
         {
-            return ASN1Sequence.getInstance(((ASN1SequenceParser)obj).toASN1Primitive());
+            ASN1Primitive primitive = ((ASN1Encodable)obj).toASN1Primitive();
+            if (primitive instanceof ASN1Sequence)
+            {
+                return (ASN1Sequence)primitive;
+            }
         }
         else if (obj instanceof byte[])
         {
             try
             {
-                return ASN1Sequence.getInstance(fromByteArray((byte[])obj));
+                return (ASN1Sequence)TYPE.fromByteArray((byte[])obj);
             }
             catch (IOException e)
             {
                 throw new IllegalArgumentException("failed to construct sequence from byte[]: " + e.getMessage());
             }
         }
-        else if (obj instanceof ASN1Encodable)
-        {
-            ASN1Primitive primitive = ((ASN1Encodable)obj).toASN1Primitive();
-
-            if (primitive instanceof ASN1Sequence)
-            {
-                return (ASN1Sequence)primitive;
-            }
-        }
 
         throw new IllegalArgumentException("unknown object in getInstance: " + obj.getClass().getName());
     }
@@ -124,51 +124,14 @@
      *          be converted.
      * @return an ASN1Sequence instance.
      */
-    public static ASN1Sequence getInstance(
-        ASN1TaggedObject    taggedObject,
-        boolean             explicit)
+    public static ASN1Sequence getInstance(ASN1TaggedObject taggedObject, boolean explicit)
     {
-        if (explicit)
-        {
-            if (!taggedObject.isExplicit())
-            {
-                throw new IllegalArgumentException("object implicit - explicit expected.");
-            }
-
-            return getInstance(taggedObject.getObject());
-        }
-
-        ASN1Primitive o = taggedObject.getObject();
-
-        /*
-         * constructed object which appears to be explicitly tagged when it should be implicit means
-         * we have to add the surrounding sequence.
-         */
-        if (taggedObject.isExplicit())
-        {
-            if (taggedObject instanceof BERTaggedObject)
-            {
-                return new BERSequence(o);
-            }
-
-            return new DLSequence(o);
-        }
-
-        if (o instanceof ASN1Sequence)
-        {
-            ASN1Sequence s = (ASN1Sequence)o;
-
-            if (taggedObject instanceof BERTaggedObject)
-            {
-                return s;
-            }
-
-            return (ASN1Sequence)s.toDLObject();
-        }
-
-        throw new IllegalArgumentException("unknown object in getInstance: " + taggedObject.getClass().getName());
+        return (ASN1Sequence)TYPE.getContextInstance(taggedObject, explicit);
     }
 
+    // NOTE: Only non-final to support LazyEncodedSequence
+    ASN1Encodable[] elements;
+
     /**
      * Create an empty SEQUENCE
      */
@@ -342,6 +305,7 @@
 
         ASN1Sequence that = (ASN1Sequence)other;
 
+        // NOTE: Call size() here (on both) to 'force' a LazyEncodedSequence
         int count = this.size();
         if (that.size() != count)
         {
@@ -380,13 +344,19 @@
         return new DLSequence(elements, false);
     }
 
-    boolean isConstructed()
+    abstract ASN1BitString toASN1BitString();
+
+    abstract ASN1External toASN1External();
+
+    abstract ASN1OctetString toASN1OctetString();
+
+    abstract ASN1Set toASN1Set();
+
+    boolean encodeConstructed()
     {
         return true;
     }
 
-    abstract void encode(ASN1OutputStream out, boolean withTag) throws IOException;
-
     public String toString() 
     {
         // NOTE: Call size() here to 'force' a LazyEncodedSequence
@@ -415,4 +385,28 @@
     {
         return new Arrays.Iterator<ASN1Encodable>(elements);
     }
+
+    ASN1BitString[] getConstructedBitStrings()
+    {
+        // NOTE: Call size() here to 'force' a LazyEncodedSequence
+        int count = size();
+        ASN1BitString[] bitStrings = new ASN1BitString[count];
+        for (int i = 0; i < count; ++i)
+        {
+            bitStrings[i] = ASN1BitString.getInstance(elements[i]);
+        }
+        return bitStrings;
+    }
+
+    ASN1OctetString[] getConstructedOctetStrings()
+    {
+        // NOTE: Call size() here to 'force' a LazyEncodedSequence
+        int count = size();
+        ASN1OctetString[] octetStrings = new ASN1OctetString[count];
+        for (int i = 0; i < count; ++i)
+        {
+            octetStrings[i] = ASN1OctetString.getInstance(elements[i]);
+        }
+        return octetStrings;
+    }
 }
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1Set.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1Set.java
index 564d151..0c112dc 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1Set.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1Set.java
@@ -100,8 +100,13 @@
     extends ASN1Primitive
     implements com.android.org.bouncycastle.util.Iterable<ASN1Encodable>
 {
-    protected final ASN1Encodable[] elements;
-    protected final boolean isSorted;
+    static final ASN1UniversalType TYPE = new ASN1UniversalType(ASN1Set.class, BERTags.SET)
+    {
+        ASN1Primitive fromImplicitConstructed(ASN1Sequence sequence)
+        {
+            return sequence.toASN1Set();
+        }
+    };
 
     /**
      * return an ASN1Set from the given object.
@@ -110,37 +115,32 @@
      * @exception IllegalArgumentException if the object cannot be converted.
      * @return an ASN1Set instance, or null.
      */
-    public static ASN1Set getInstance(
-        Object  obj)
+    public static ASN1Set getInstance(Object obj)
     {
         if (obj == null || obj instanceof ASN1Set)
         {
             return (ASN1Set)obj;
         }
-        else if (obj instanceof ASN1SetParser)
+//      else if (obj instanceof ASN1SetParser)
+        else if (obj instanceof ASN1Encodable)
         {
-            return ASN1Set.getInstance(((ASN1SetParser)obj).toASN1Primitive());
+            ASN1Primitive primitive = ((ASN1Encodable)obj).toASN1Primitive();
+            if (primitive instanceof ASN1Set)
+            {
+                return (ASN1Set)primitive;
+            }
         }
         else if (obj instanceof byte[])
         {
             try
             {
-                return ASN1Set.getInstance(ASN1Primitive.fromByteArray((byte[])obj));
+                return (ASN1Set)TYPE.fromByteArray((byte[])obj);
             }
             catch (IOException e)
             {
                 throw new IllegalArgumentException("failed to construct set from byte[]: " + e.getMessage());
             }
         }
-        else if (obj instanceof ASN1Encodable)
-        {
-            ASN1Primitive primitive = ((ASN1Encodable)obj).toASN1Primitive();
-
-            if (primitive instanceof ASN1Set)
-            {
-                return (ASN1Set)primitive;
-            }
-        }
 
         throw new IllegalArgumentException("unknown object in getInstance: " + obj.getClass().getName());
     }
@@ -162,73 +162,19 @@
      *          be converted.
      * @return an ASN1Set instance.
      */
-    public static ASN1Set getInstance(
-        ASN1TaggedObject    taggedObject,
-        boolean             explicit)
+    public static ASN1Set getInstance(ASN1TaggedObject taggedObject, boolean explicit)
     {
-        if (explicit)
-        {
-            if (!taggedObject.isExplicit())
-            {
-                throw new IllegalArgumentException("object implicit - explicit expected.");
-            }
-
-            return getInstance(taggedObject.getObject());
-        }
-
-        ASN1Primitive o = taggedObject.getObject();
-
-        /*
-         * constructed object which appears to be explicitly tagged and it's really implicit means
-         * we have to add the surrounding set.
-         */
-        if (taggedObject.isExplicit())
-        {
-            if (taggedObject instanceof BERTaggedObject)
-            {
-                return new BERSet(o);
-            }
-
-            return new DLSet(o);
-        }
-
-        if (o instanceof ASN1Set)
-        {
-            ASN1Set s = (ASN1Set)o;
-
-            if (taggedObject instanceof BERTaggedObject)
-            {
-                return s;
-            }
-
-            return (ASN1Set)s.toDLObject();
-        }
-
-        /*
-         * in this case the parser returns a sequence, convert it into a set.
-         */
-        if (o instanceof ASN1Sequence)
-        {
-            ASN1Sequence s = (ASN1Sequence)o;
-
-            // NOTE: Will force() a LazyEncodedSequence
-            ASN1Encodable[] elements = s.toArrayInternal();
-
-            if (taggedObject instanceof BERTaggedObject)
-            {
-                return new BERSet(false, elements);
-            }
-
-            return new DLSet(false, elements);
-        }
-
-        throw new IllegalArgumentException("unknown object in getInstance: " + taggedObject.getClass().getName());
+        return (ASN1Set)TYPE.getContextInstance(taggedObject, explicit);
     }
 
+    protected final ASN1Encodable[] elements;
+
+    protected ASN1Encodable[] sortedElements;
+
     protected ASN1Set()
     {
         this.elements = ASN1EncodableVector.EMPTY_ELEMENTS;
-        this.isSorted = true;
+        this.sortedElements = elements;
     }
 
     /**
@@ -243,7 +189,7 @@
         }
 
         this.elements = new ASN1Encodable[]{ element };
-        this.isSorted = true;
+        this.sortedElements = elements;
     }
 
     /**
@@ -270,7 +216,7 @@
         }
 
         this.elements = tmp;
-        this.isSorted = doSort || tmp.length < 2;
+        this.sortedElements = (doSort || tmp.length < 2) ? elements : null;
     }
 
     /**
@@ -292,13 +238,19 @@
         }
 
         this.elements = tmp;
-        this.isSorted = doSort || tmp.length < 2;
+        this.sortedElements = (doSort || tmp.length < 2) ? elements : null;
     }
 
     ASN1Set(boolean isSorted, ASN1Encodable[] elements)
     {
         this.elements = elements;
-        this.isSorted = isSorted || elements.length < 2;
+        this.sortedElements = (isSorted || elements.length < 2) ? elements : null;
+    }
+
+    ASN1Set(ASN1Encodable[] elements, ASN1Encodable[] sortedElements)
+    {
+        this.elements = elements;
+        this.sortedElements = sortedElements;
     }
 
     public Enumeration getObjects()
@@ -410,18 +362,13 @@
      */
     ASN1Primitive toDERObject()
     {
-        ASN1Encodable[] tmp;
-        if (isSorted)
+        if (sortedElements == null)
         {
-            tmp = elements;
-        }
-        else
-        {
-            tmp = (ASN1Encodable[])elements.clone();
-            sort(tmp);
+            sortedElements = (ASN1Encodable[])elements.clone();
+            sort(sortedElements);
         }
 
-        return new DERSet(true, tmp);
+        return new DERSet(true, sortedElements);
     }
 
     /**
@@ -430,7 +377,7 @@
      */
     ASN1Primitive toDLObject()
     {
-        return new DLSet(isSorted, elements);
+        return new DLSet(elements, sortedElements);
     }
 
     boolean asn1Equals(ASN1Primitive other)
@@ -465,13 +412,11 @@
         return true;
     }
 
-    boolean isConstructed()
+    boolean encodeConstructed()
     {
         return true;
     }
 
-    abstract void encode(ASN1OutputStream out, boolean withTag) throws IOException;
-
     public String toString() 
     {
         int count = size();
@@ -531,8 +476,8 @@
          * primitive form accordingly. Failing to ignore the CONSTRUCTED bit could therefore lead to
          * ordering inversions.
          */
-        int a0 = a[0] & ~BERTags.CONSTRUCTED;
-        int b0 = b[0] & ~BERTags.CONSTRUCTED;
+        int a0 = a[0] & (~BERTags.CONSTRUCTED & 0xff);
+        int b0 = b[0] & (~BERTags.CONSTRUCTED & 0xff);
         if (a0 != b0)
         {
             return a0 < b0;
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1StreamParser.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1StreamParser.java
index 2ae9ca7..21484a1 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1StreamParser.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1StreamParser.java
@@ -12,214 +12,257 @@
 public class ASN1StreamParser
 {
     private final InputStream _in;
-    private final int         _limit;
+    private final int _limit;
     private final byte[][] tmpBuffers;
 
-    public ASN1StreamParser(
-        InputStream in)
+    public ASN1StreamParser(InputStream in)
     {
         this(in, StreamUtil.findLimit(in));
     }
 
-    public ASN1StreamParser(
-        InputStream in,
-        int         limit)
-    {
-        this._in = in;
-        this._limit = limit;
-
-        this.tmpBuffers = new byte[11][];
-    }
-
-    public ASN1StreamParser(
-        byte[] encoding)
+    public ASN1StreamParser(byte[] encoding)
     {
         this(new ByteArrayInputStream(encoding), encoding.length);
     }
 
-    ASN1Encodable readIndef(int tagValue) throws IOException
+    public ASN1StreamParser(InputStream in, int limit)
     {
-        // Note: INDEF => CONSTRUCTED
-
-        // TODO There are other tags that may be constructed (e.g. BIT_STRING)
-        switch (tagValue)
-        {
-            case BERTags.EXTERNAL:
-                return new DERExternalParser(this);
-            case BERTags.OCTET_STRING:
-                return new BEROctetStringParser(this);
-            case BERTags.SEQUENCE:
-                return new BERSequenceParser(this);
-            case BERTags.SET:
-                return new BERSetParser(this);
-            default:
-                throw new ASN1Exception("unknown BER object encountered: 0x" + Integer.toHexString(tagValue));
-        }
+        this(in, limit, new byte[11][]);
     }
 
-    ASN1Encodable readImplicit(boolean constructed, int tag) throws IOException
+    ASN1StreamParser(InputStream in, int limit, byte[][] tmpBuffers)
     {
-        if (_in instanceof IndefiniteLengthInputStream)
-        {
-            if (!constructed)
-            {
-                throw new IOException("indefinite-length primitive encoding encountered");
-            }
-            
-            return readIndef(tag);
-        }
-
-        if (constructed)
-        {
-            switch (tag)
-            {
-                case BERTags.SET:
-                    return new DLSetParser(this);
-                case BERTags.SEQUENCE:
-                    return new DLSequenceParser(this);
-                case BERTags.OCTET_STRING:
-                    return new BEROctetStringParser(this);
-            }
-        }
-        else
-        {
-            switch (tag)
-            {
-                case BERTags.SET:
-                    throw new ASN1Exception("sequences must use constructed encoding (see X.690 8.9.1/8.10.1)");
-                case BERTags.SEQUENCE:
-                    throw new ASN1Exception("sets must use constructed encoding (see X.690 8.11.1/8.12.1)");
-                case BERTags.OCTET_STRING:
-                    return new DEROctetStringParser((DefiniteLengthInputStream)_in);
-            }
-        }
-
-        throw new ASN1Exception("implicit tagging not implemented");
+        this._in = in;
+        this._limit = limit;
+        this.tmpBuffers = tmpBuffers;
     }
 
-    ASN1Primitive readTaggedObject(boolean constructed, int tag) throws IOException
+    public ASN1Encodable readObject() throws IOException
     {
-        if (!constructed)
-        {
-            // Note: !CONSTRUCTED => IMPLICIT
-            DefiniteLengthInputStream defIn = (DefiniteLengthInputStream)_in;
-            return new DLTaggedObject(false, tag, new DEROctetString(defIn.toByteArray()));
-        }
-
-        ASN1EncodableVector v = readVector();
-
-        if (_in instanceof IndefiniteLengthInputStream)
-        {
-            return v.size() == 1
-                ?   new BERTaggedObject(true, tag, v.get(0))
-                :   new BERTaggedObject(false, tag, BERFactory.createSequence(v));
-        }
-
-        return v.size() == 1
-            ?   new DLTaggedObject(true, tag, v.get(0))
-            :   new DLTaggedObject(false, tag, DLFactory.createSequence(v));
-    }
-
-    public ASN1Encodable readObject()
-        throws IOException
-    {
-        int tag = _in.read();
-        if (tag == -1)
+        int tagHdr = _in.read();
+        if (tagHdr < 0)
         {
             return null;
         }
 
+        return implParseObject(tagHdr);
+    }
+
+    ASN1Encodable implParseObject(int tagHdr) throws IOException
+    {
         //
-        // turn of looking for "00" while we resolve the tag
+        // turn off looking for "00" while we resolve the tag
         //
         set00Check(false);
 
         //
         // calculate tag number
         //
-        int tagNo = ASN1InputStream.readTagNumber(_in, tag);
-
-        boolean isConstructed = (tag & BERTags.CONSTRUCTED) != 0;
+        int tagNo = ASN1InputStream.readTagNumber(_in, tagHdr);
 
         //
         // calculate length
         //
         int length = ASN1InputStream.readLength(_in, _limit,
-            tagNo == BERTags.OCTET_STRING || tagNo == BERTags.SEQUENCE || tagNo == BERTags.SET || tagNo == BERTags.EXTERNAL);
+            tagNo == BERTags.BIT_STRING || tagNo == BERTags.OCTET_STRING || tagNo == BERTags.SEQUENCE
+                || tagNo == BERTags.SET || tagNo == BERTags.EXTERNAL);
 
         if (length < 0) // indefinite-length method
         {
-            if (!isConstructed)
+            if (0 == (tagHdr & BERTags.CONSTRUCTED))
             {
                 throw new IOException("indefinite-length primitive encoding encountered");
             }
 
             IndefiniteLengthInputStream indIn = new IndefiniteLengthInputStream(_in, _limit);
-            ASN1StreamParser sp = new ASN1StreamParser(indIn, _limit);
+            ASN1StreamParser sp = new ASN1StreamParser(indIn, _limit, tmpBuffers);
 
-            if ((tag & BERTags.APPLICATION) != 0)
+            int tagClass = tagHdr & BERTags.PRIVATE;
+            if (0 != tagClass)
             {
-                return new BERApplicationSpecificParser(tagNo, sp);
+                return new BERTaggedObjectParser(tagClass, tagNo, sp);
             }
 
-            if ((tag & BERTags.TAGGED) != 0)
-            {
-                return new BERTaggedObjectParser(true, tagNo, sp);
-            }
-
-            return sp.readIndef(tagNo);
+            return sp.parseImplicitConstructedIL(tagNo);
         }
         else
         {
             DefiniteLengthInputStream defIn = new DefiniteLengthInputStream(_in, length, _limit);
 
-            if ((tag & BERTags.APPLICATION) != 0)
+            if (0 == (tagHdr & BERTags.FLAGS))
             {
-                return new DLApplicationSpecific(isConstructed, tagNo, defIn.toByteArray());
+                return parseImplicitPrimitive(tagNo, defIn);
             }
 
-            if ((tag & BERTags.TAGGED) != 0)
+            ASN1StreamParser sp = new ASN1StreamParser(defIn, defIn.getLimit(), tmpBuffers);
+
+            int tagClass = tagHdr & BERTags.PRIVATE;
+            if (0 != tagClass)
             {
-                return new BERTaggedObjectParser(isConstructed, tagNo, new ASN1StreamParser(defIn));
+                boolean isConstructed = (tagHdr & BERTags.CONSTRUCTED) != 0;
+
+                return new DLTaggedObjectParser(tagClass, tagNo, isConstructed, sp);
             }
 
-            if (isConstructed)
-            {
-                // TODO There are other tags that may be constructed (e.g. BIT_STRING)
-                switch (tagNo)
-                {
-                    case BERTags.OCTET_STRING:
-                        //
-                        // yes, people actually do this...
-                        //
-                        return new BEROctetStringParser(new ASN1StreamParser(defIn));
-                    case BERTags.SEQUENCE:
-                        return new DLSequenceParser(new ASN1StreamParser(defIn));
-                    case BERTags.SET:
-                        return new DLSetParser(new ASN1StreamParser(defIn));
-                    case BERTags.EXTERNAL:
-                        return new DERExternalParser(new ASN1StreamParser(defIn));
-                    default:
-                        throw new IOException("unknown tag " + tagNo + " encountered");
-                }
-            }
+            return sp.parseImplicitConstructedDL(tagNo);
+        }
+    }
 
-            // Some primitive encodings can be handled by parsers too...
-            switch (tagNo)
-            {
-                case BERTags.OCTET_STRING:
-                    return new DEROctetStringParser(defIn);
-            }
+    ASN1Primitive loadTaggedDL(int tagClass, int tagNo, boolean constructed) throws IOException
+    {
+        if (!constructed)
+        {
+            byte[] contentsOctets = ((DefiniteLengthInputStream) _in).toByteArray();
+            return ASN1TaggedObject.createPrimitive(tagClass, tagNo, contentsOctets);
+        }
 
-            try
+        ASN1EncodableVector contentsElements = readVector();
+        return ASN1TaggedObject.createConstructedDL(tagClass, tagNo, contentsElements);
+    }
+
+    ASN1Primitive loadTaggedIL(int tagClass, int tagNo) throws IOException
+    {
+        ASN1EncodableVector contentsElements = readVector();
+        return ASN1TaggedObject.createConstructedIL(tagClass, tagNo, contentsElements);
+    }
+
+    ASN1Encodable parseImplicitConstructedDL(int univTagNo) throws IOException
+    {
+        switch (univTagNo)
+        {
+        case BERTags.BIT_STRING:
+            // TODO[asn1] DLConstructedBitStringParser
+            return new BERBitStringParser(this);
+        case BERTags.EXTERNAL:
+            return new DERExternalParser(this);
+        case BERTags.OCTET_STRING:
+            // TODO[asn1] DLConstructedOctetStringParser
+            return new BEROctetStringParser(this);
+        case BERTags.SET:
+            return new DLSetParser(this);
+        case BERTags.SEQUENCE:
+            return new DLSequenceParser(this);
+        default:
+            // -DM toHexString
+            throw new ASN1Exception("unknown DL object encountered: 0x" + Integer.toHexString(univTagNo));
+        }
+    }
+
+    ASN1Encodable parseImplicitConstructedIL(int univTagNo) throws IOException
+    {
+        switch (univTagNo)
+        {
+        case BERTags.BIT_STRING:
+            return new BERBitStringParser(this);
+        case BERTags.OCTET_STRING:
+            return new BEROctetStringParser(this);
+        case BERTags.EXTERNAL:
+            // TODO[asn1] BERExternalParser
+            return new DERExternalParser(this);
+        case BERTags.SEQUENCE:
+            return new BERSequenceParser(this);
+        case BERTags.SET:
+            return new BERSetParser(this);
+        default:
+            throw new ASN1Exception("unknown BER object encountered: 0x" + Integer.toHexString(univTagNo));
+        }
+    }
+
+    ASN1Encodable parseImplicitPrimitive(int univTagNo) throws IOException
+    {
+        return parseImplicitPrimitive(univTagNo, (DefiniteLengthInputStream)_in);
+    }
+
+    ASN1Encodable parseImplicitPrimitive(int univTagNo, DefiniteLengthInputStream defIn) throws IOException
+    {
+        // Some primitive encodings can be handled by parsers too...
+        switch (univTagNo)
+        {
+        case BERTags.BIT_STRING:
+            return new DLBitStringParser(defIn);
+        case BERTags.EXTERNAL:
+            throw new ASN1Exception("externals must use constructed encoding (see X.690 8.18)");
+        case BERTags.OCTET_STRING:
+            return new DEROctetStringParser(defIn);
+        case BERTags.SET:
+            throw new ASN1Exception("sequences must use constructed encoding (see X.690 8.9.1/8.10.1)");
+        case BERTags.SEQUENCE:
+            throw new ASN1Exception("sets must use constructed encoding (see X.690 8.11.1/8.12.1)");
+        }
+
+        try
+        {
+            return ASN1InputStream.createPrimitiveDERObject(univTagNo, defIn, tmpBuffers);
+        }
+        catch (IllegalArgumentException e)
+        {
+            throw new ASN1Exception("corrupted stream detected", e);
+        }
+    }
+
+    ASN1Encodable parseObject(int univTagNo) throws IOException
+    {
+        if (univTagNo < 0 || univTagNo > 30)
+        {
+            throw new IllegalArgumentException("invalid universal tag number: " + univTagNo);
+        }
+
+        int tagHdr = _in.read();
+        if (tagHdr < 0)
+        {
+            return null;
+        }
+
+        if ((tagHdr & ~BERTags.CONSTRUCTED) != univTagNo)
+        {
+            throw new IOException("unexpected identifier encountered: " + tagHdr);
+        }
+
+        return implParseObject(tagHdr);
+    }
+
+    ASN1TaggedObjectParser parseTaggedObject() throws IOException
+    {
+        int tagHdr = _in.read();
+        if (tagHdr < 0)
+        {
+            return null;
+        }
+
+        int tagClass = tagHdr & BERTags.PRIVATE;
+        if (0 == tagClass)
+        {
+            throw new ASN1Exception("no tagged object found");
+        }
+
+        return (ASN1TaggedObjectParser)implParseObject(tagHdr);
+    }
+
+    // TODO[asn1] Prefer 'loadVector'
+    ASN1EncodableVector readVector() throws IOException
+    {
+        int tagHdr = _in.read();
+        if (tagHdr < 0)
+        {
+            return new ASN1EncodableVector(0);
+        }
+
+        ASN1EncodableVector v = new ASN1EncodableVector();
+        do
+        {
+            ASN1Encodable obj = implParseObject(tagHdr);
+
+            if (obj instanceof InMemoryRepresentable)
             {
-                return ASN1InputStream.createPrimitiveDERObject(tagNo, defIn, tmpBuffers);
+                v.add(((InMemoryRepresentable) obj).getLoadedObject());
             }
-            catch (IllegalArgumentException e)
+            else
             {
-                throw new ASN1Exception("corrupted stream detected", e);
+                v.add(obj.toASN1Primitive());
             }
         }
+        while ((tagHdr = _in.read()) >= 0);
+        return v;
     }
 
     private void set00Check(boolean enabled)
@@ -229,28 +272,4 @@
             ((IndefiniteLengthInputStream)_in).setEofOn00(enabled);
         }
     }
-
-    ASN1EncodableVector readVector() throws IOException
-    {
-        ASN1Encodable obj = readObject();
-        if (null == obj)
-        {
-            return new ASN1EncodableVector(0);
-        }
-
-        ASN1EncodableVector v = new ASN1EncodableVector();
-        do
-        {
-            if (obj instanceof InMemoryRepresentable)
-            {
-                v.add(((InMemoryRepresentable)obj).getLoadedObject());
-            }
-            else
-            {
-                v.add(obj.toASN1Primitive());
-            }
-        }
-        while ((obj = readObject()) != null);
-        return v;
-    }
 }
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1T61String.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1T61String.java
new file mode 100644
index 0000000..1dd062d
--- /dev/null
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1T61String.java
@@ -0,0 +1,147 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.org.bouncycastle.asn1;
+
+import java.io.IOException;
+
+import com.android.org.bouncycastle.util.Arrays;
+import com.android.org.bouncycastle.util.Strings;
+
+/**
+ * ASN.1 T61String (also the teletex string), try not to use this if you don't need to. The standard support the encoding for
+ * this has been withdrawn.
+ * @hide This class is not part of the Android public SDK API
+ */
+public abstract class ASN1T61String
+    extends ASN1Primitive
+    implements ASN1String
+{
+    static final ASN1UniversalType TYPE = new ASN1UniversalType(ASN1T61String.class, BERTags.T61_STRING)
+    {
+        ASN1Primitive fromImplicitPrimitive(DEROctetString octetString)
+        {
+            return createPrimitive(octetString.getOctets());
+        }
+    };
+
+    /**
+     * Return a T61 string from the passed in object.
+     *
+     * @param obj an ASN1T61String or an object that can be converted into one.
+     * @exception IllegalArgumentException if the object cannot be converted.
+     * @return an ASN1T61String instance, or null
+     */
+    public static ASN1T61String getInstance(Object obj)
+    {
+        if (obj == null || obj instanceof ASN1T61String)
+        {
+            return (ASN1T61String)obj;
+        }
+        if (obj instanceof ASN1Encodable)
+        {
+            ASN1Primitive primitive = ((ASN1Encodable)obj).toASN1Primitive();
+            if (primitive instanceof ASN1T61String)
+            {
+                return (ASN1T61String)primitive;
+            }
+        }
+        if (obj instanceof byte[])
+        {
+            try
+            {
+                return (ASN1T61String)TYPE.fromByteArray((byte[])obj);
+            }
+            catch (Exception e)
+            {
+                throw new IllegalArgumentException("encoding error in getInstance: " + e.toString());
+            }
+        }
+
+        throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
+    }
+
+    /**
+     * Return an T61 String from a tagged object.
+     *
+     * @param taggedObject      the tagged object holding the object we want
+     * @param explicit true if the object is meant to be explicitly tagged false
+     *                 otherwise.
+     * @exception IllegalArgumentException if the tagged object cannot be converted.
+     * @return an ASN1T61String instance, or null
+     */
+    public static ASN1T61String getInstance(ASN1TaggedObject taggedObject, boolean explicit)
+    {
+        return (ASN1T61String)TYPE.getContextInstance(taggedObject, explicit);
+    }
+
+    final byte[] contents;
+
+    ASN1T61String(String string)
+    {
+        this.contents = Strings.toByteArray(string);
+    }
+
+    ASN1T61String(byte[] contents, boolean clone)
+    {
+        this.contents = clone ? Arrays.clone(contents) : contents;
+    }
+
+    /**
+     * Decode the encoded string and return it, 8 bit encoding assumed.
+     * @return the decoded String
+     */
+    public final String getString()
+    {
+        return Strings.fromByteArray(contents);
+    }
+
+    public String toString()
+    {
+        return getString();
+    }
+
+    final boolean encodeConstructed()
+    {
+        return false;
+    }
+
+    final int encodedLength(boolean withTag)
+    {
+        return ASN1OutputStream.getLengthOfEncodingDL(withTag, contents.length);
+    }
+
+    final void encode(ASN1OutputStream out, boolean withTag) throws IOException
+    {
+        out.writeEncodingDL(withTag, BERTags.T61_STRING, contents);
+    }
+
+    /**
+     * Return the encoded string as a byte array.
+     * @return the actual bytes making up the encoded body of the T61 string.
+     */
+    public final byte[] getOctets()
+    {
+        return Arrays.clone(contents);
+    }
+
+    final boolean asn1Equals(ASN1Primitive other)
+    {
+        if (!(other instanceof ASN1T61String))
+        {
+            return false;
+        }
+
+        ASN1T61String that = (ASN1T61String)other;
+
+        return Arrays.areEqual(this.contents, that.contents);
+    }
+
+    public final int hashCode()
+    {
+        return Arrays.hashCode(contents);
+    }
+
+    static ASN1T61String createPrimitive(byte[] contents)
+    {
+        return new DERT61String(contents, false);
+    }
+}
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1Tag.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1Tag.java
new file mode 100644
index 0000000..c25f733
--- /dev/null
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1Tag.java
@@ -0,0 +1,29 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.org.bouncycastle.asn1;
+
+final class ASN1Tag
+{
+    static ASN1Tag create(int tagClass, int tagNumber)
+    {
+        return new ASN1Tag(tagClass, tagNumber);
+    }
+
+    private final int tagClass;
+    private final int tagNumber;
+
+    private ASN1Tag(int tagClass, int tagNumber)
+    {
+        this.tagClass = tagClass;
+        this.tagNumber = tagNumber;
+    }
+
+    int getTagClass()
+    {
+        return tagClass;
+    }
+
+    int getTagNumber()
+    {
+        return tagNumber;
+    }
+}
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1TaggedObject.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1TaggedObject.java
index bbf6554..de23ed2 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1TaggedObject.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1TaggedObject.java
@@ -3,6 +3,8 @@
 
 import java.io.IOException;
 
+import com.android.org.bouncycastle.util.Arrays;
+
 /**
  * ASN.1 TaggedObject - in ASN.1 notation this is any object preceded by
  * a [n] where n is some number - these are assumed to follow the construction
@@ -13,34 +15,32 @@
     extends ASN1Primitive
     implements ASN1TaggedObjectParser
 {
-    final int           tagNo;
-    final boolean       explicit;
-    final ASN1Encodable obj;
+    private static final int DECLARED_EXPLICIT = 1;
+    private static final int DECLARED_IMPLICIT = 2;
+    // TODO It will probably be better to track parsing constructed vs primitive instead
+    private static final int PARSED_EXPLICIT = 3;
+    private static final int PARSED_IMPLICIT = 4;
 
-    static public ASN1TaggedObject getInstance(
-        ASN1TaggedObject    obj,
-        boolean             explicit)
-    {
-        if (explicit)
-        {
-            return getInstance(obj.getObject());
-        }
-
-        throw new IllegalArgumentException("implicitly tagged tagged object");
-    }
-
-    static public ASN1TaggedObject getInstance(
-        Object obj) 
+    public static ASN1TaggedObject getInstance(Object obj)
     {
         if (obj == null || obj instanceof ASN1TaggedObject) 
         {
             return (ASN1TaggedObject)obj;
         }
+//      else if (obj instanceof ASN1TaggedObjectParser)
+        else if (obj instanceof ASN1Encodable)
+        {
+            ASN1Primitive primitive = ((ASN1Encodable)obj).toASN1Primitive();
+            if (primitive instanceof ASN1TaggedObject)
+            {
+                return (ASN1TaggedObject)primitive;
+            }
+        }
         else if (obj instanceof byte[])
         {
             try
             {
-                return ASN1TaggedObject.getInstance(fromByteArray((byte[])obj));
+                return checkedCast(fromByteArray((byte[])obj));
             }
             catch (IOException e)
             {
@@ -51,6 +51,58 @@
         throw new IllegalArgumentException("unknown object in getInstance: " + obj.getClass().getName());
     }
 
+    public static ASN1TaggedObject getInstance(Object obj, int tagClass)
+    {
+        if (obj == null)
+        {
+            throw new NullPointerException("'obj' cannot be null");
+        }
+
+        ASN1TaggedObject taggedObject = getInstance(obj);
+        if (tagClass != taggedObject.getTagClass())
+        {
+            throw new IllegalArgumentException("unexpected tag in getInstance: " + ASN1Util.getTagText(taggedObject));
+        }
+
+        return taggedObject;
+    }
+
+    public static ASN1TaggedObject getInstance(Object obj, int tagClass, int tagNo)
+    {
+        if (obj == null)
+        {
+            throw new NullPointerException("'obj' cannot be null");
+        }
+
+        ASN1TaggedObject taggedObject = getInstance(obj);
+        if (!taggedObject.hasTag(tagClass, tagNo))
+        {
+            throw new IllegalArgumentException("unexpected tag in getInstance: " + ASN1Util.getTagText(taggedObject));
+        }
+
+        return taggedObject;
+    }
+
+    public static ASN1TaggedObject getInstance(ASN1TaggedObject taggedObject, boolean declaredExplicit)
+    {
+        if (BERTags.CONTEXT_SPECIFIC != taggedObject.getTagClass())
+        {
+            throw new IllegalStateException("this method only valid for CONTEXT_SPECIFIC tags");
+        }
+
+        if (declaredExplicit)
+        {
+            return taggedObject.getExplicitBaseTagged();
+        }
+
+        throw new IllegalArgumentException("this method not valid for implicitly tagged tagged objects");
+    }
+
+    final int           explicitness;
+    final int           tagClass;
+    final int           tagNo;
+    final ASN1Encodable obj;
+
     /**
      * Create a tagged object with the style given by the value of explicit.
      * <p>
@@ -61,22 +113,34 @@
      * @param tagNo the tag number for this object.
      * @param obj the tagged object.
      */
-    public ASN1TaggedObject(
-        boolean         explicit,
-        int             tagNo,
-        ASN1Encodable   obj)
+    protected ASN1TaggedObject(boolean explicit, int tagNo, ASN1Encodable obj)
+    {
+        this(explicit, BERTags.CONTEXT_SPECIFIC, tagNo, obj);
+    }
+
+    protected ASN1TaggedObject(boolean explicit, int tagClass, int tagNo, ASN1Encodable obj)
+    {
+        this(explicit ? DECLARED_EXPLICIT : DECLARED_IMPLICIT, tagClass, tagNo, obj);
+    }
+
+    ASN1TaggedObject(int explicitness, int tagClass, int tagNo, ASN1Encodable obj)
     {
         if (null == obj)
         {
             throw new NullPointerException("'obj' cannot be null");
         }
+        if (tagClass == BERTags.UNIVERSAL || (tagClass & BERTags.PRIVATE) != tagClass)
+        {
+            throw new IllegalArgumentException("invalid tag class: " + tagClass);
+        }
 
+        this.explicitness = (obj instanceof ASN1Choice) ? DECLARED_EXPLICIT : explicitness;
+        this.tagClass = tagClass;
         this.tagNo = tagNo;
-        this.explicit = explicit || (obj instanceof ASN1Choice);
         this.obj = obj;
     }
 
-    boolean asn1Equals(ASN1Primitive other)
+    final boolean asn1Equals(ASN1Primitive other)
     {
         if (!(other instanceof ASN1TaggedObject))
         {
@@ -85,20 +149,58 @@
 
         ASN1TaggedObject that = (ASN1TaggedObject)other;
 
-        if (this.tagNo != that.tagNo || this.explicit != that.explicit)
+        if (this.tagNo != that.tagNo ||
+            this.tagClass != that.tagClass)
         {
             return false;
         }
 
+        if (this.explicitness != that.explicitness)
+        {
+            /*
+             * TODO This seems incorrect for some cases of implicit tags e.g. if one is a
+             * declared-implicit SET and the other a parsed object.
+             */
+            if (this.isExplicit() != that.isExplicit())
+            {
+                return false;
+            }
+        }
+
         ASN1Primitive p1 = this.obj.toASN1Primitive();
         ASN1Primitive p2 = that.obj.toASN1Primitive();
 
-        return p1 == p2 || p1.asn1Equals(p2);
+        if (p1 == p2)
+        {
+            return true;
+        }
+
+        if (!this.isExplicit())
+        {
+            try
+            {
+                byte[] d1 = this.getEncoded();
+                byte[] d2 = that.getEncoded();
+                
+                return Arrays.areEqual(d1, d2);
+            }
+            catch (IOException e)
+            {
+                return false;
+            }
+        }
+
+        return p1.asn1Equals(p2);
     }
 
     public int hashCode()
     {
-        return tagNo ^ (explicit ? 0x0F : 0xF0) ^ obj.toASN1Primitive().hashCode();
+        return (tagClass * 7919) ^ tagNo ^ (isExplicit() ? 0x0F : 0xF0) ^ obj.toASN1Primitive().hashCode();
+    }
+
+    public int getTagClass()
+    {
+        return tagClass;
     }
 
     /**
@@ -111,18 +213,24 @@
         return tagNo;
     }
 
-    /**
-     * return whether or not the object may be explicitly tagged. 
-     * <p>
-     * Note: if the object has been read from an input stream, the only
-     * time you can be sure if isExplicit is returning the true state of
-     * affairs is if it returns false. An implicitly tagged object may appear
-     * to be explicitly tagged, so you need to understand the context under
-     * which the reading was done as well, see getObject below.
-     */
-    public boolean isExplicit()
+    public boolean hasContextTag()
     {
-        return explicit;
+        return this.tagClass == BERTags.CONTEXT_SPECIFIC;
+    }
+
+    public boolean hasContextTag(int tagNo)
+    {
+        return this.tagClass == BERTags.CONTEXT_SPECIFIC && this.tagNo == tagNo;
+    }
+
+    public boolean hasTag(int tagClass, int tagNo)
+    {
+        return this.tagClass == tagClass && this.tagNo == tagNo;
+    }
+
+    public boolean hasTagClass(int tagClass)
+    {
+        return this.tagClass == tagClass;
     }
 
     /**
@@ -138,52 +246,246 @@
     }
 
     /**
-     * Return the object held in this tagged object as a parser assuming it has
-     * the type of the passed in tag. If the object doesn't have a parser
-     * associated with it, the base object is returned.
+     * return whether or not the object may be explicitly tagged. 
+     * <p>
+     * Note: if the object has been read from an input stream, the only
+     * time you can be sure if isExplicit is returning the true state of
+     * affairs is if it returns false. An implicitly tagged object may appear
+     * to be explicitly tagged, so you need to understand the context under
+     * which the reading was done as well, see getObject below.
      */
-    public ASN1Encodable getObjectParser(
-        int     tag,
-        boolean isExplicit)
-        throws IOException
+    public boolean isExplicit()
     {
-        switch (tag)
+        // TODO New methods like 'isKnownExplicit' etc. to distinguish uncertain cases?
+        switch (explicitness)
         {
-        case BERTags.SET:
-            return ASN1Set.getInstance(this, isExplicit).parser();
-        case BERTags.SEQUENCE:
-            return ASN1Sequence.getInstance(this, isExplicit).parser();
+        case DECLARED_EXPLICIT:
+        case PARSED_EXPLICIT:
+            return true;
+        default:
+            return false;
+        }
+    }
+
+    boolean isParsed()
+    {
+        switch (explicitness)
+        {
+        case PARSED_EXPLICIT:
+        case PARSED_IMPLICIT:
+            return true;
+        default:
+            return false;
+        }
+    }
+
+    /**
+     * Needed for open types, until we have better type-guided parsing support. Use sparingly for other
+     * purposes, and prefer {@link #getExplicitBaseTagged()}, {@link #getImplicitBaseTagged(int, int)} or
+     * {@link #getBaseUniversal(boolean, int)} where possible. Before using, check for matching tag
+     * {@link #getTagClass() class} and {@link #getTagNo() number}.
+     */
+    public ASN1Object getBaseObject()
+    {
+        return obj instanceof ASN1Object ? (ASN1Object)obj : obj.toASN1Primitive();
+    }
+
+    /**
+     * Needed for open types, until we have better type-guided parsing support. Use
+     * sparingly for other purposes, and prefer {@link #getExplicitBaseTagged()} or
+     * {@link #getBaseUniversal(boolean, int)} where possible. Before using, check
+     * for matching tag {@link #getTagClass() class} and {@link #getTagNo() number}.
+     */
+    public ASN1Object getExplicitBaseObject()
+    {
+        if (!isExplicit())
+        {
+            throw new IllegalStateException("object implicit - explicit expected.");
+        }
+
+        return obj instanceof ASN1Object ? (ASN1Object)obj : obj.toASN1Primitive();
+    }
+
+    public ASN1TaggedObject getExplicitBaseTagged()
+    {
+        if (!isExplicit())
+        {
+            throw new IllegalStateException("object implicit - explicit expected.");
+        }
+
+        return checkedCast(obj.toASN1Primitive());
+    }
+
+    public ASN1TaggedObject getImplicitBaseTagged(int baseTagClass, int baseTagNo)
+    {
+        if (baseTagClass == BERTags.UNIVERSAL || (baseTagClass & BERTags.PRIVATE) != baseTagClass)
+        {
+            throw new IllegalArgumentException("invalid base tag class: " + baseTagClass);
+        }
+
+        switch (explicitness)
+        {
+        case DECLARED_EXPLICIT:
+            throw new IllegalStateException("object explicit - implicit expected.");
+
+        case DECLARED_IMPLICIT:
+        {
+            ASN1TaggedObject declared = checkedCast(obj.toASN1Primitive());
+            return ASN1Util.checkTag(declared, baseTagClass, baseTagNo);
+        }
+
+        // Parsed; return a virtual tag (i.e. that couldn't have been present in the encoding)
+        default:
+            return replaceTag(baseTagClass, baseTagNo);
+        }
+    }
+
+    /**
+     * Note: tagged objects are generally context dependent. Before trying to
+     * extract a tagged object this way, make sure you have checked that both the
+     * {@link #getTagClass() tag class} and {@link #getTagNo() tag number} match
+     * what you are looking for.
+     * 
+     * @param declaredExplicit Whether the tagged type for this object was declared
+     *                         EXPLICIT.
+     * @param tagNo            The universal {@link BERTags tag number} of the
+     *                         expected base object.
+     */
+    public ASN1Primitive getBaseUniversal(boolean declaredExplicit, int tagNo)
+    {
+        ASN1UniversalType universalType = ASN1UniversalTypes.get(tagNo);
+        if (null == universalType)
+        {
+            throw new IllegalArgumentException("unsupported UNIVERSAL tag number: " + tagNo);
+        }
+
+        return getBaseUniversal(declaredExplicit, universalType);
+    }
+
+    ASN1Primitive getBaseUniversal(boolean declaredExplicit, ASN1UniversalType universalType)
+    {
+        if (declaredExplicit)
+        {
+            if (!isExplicit())
+            {
+                throw new IllegalStateException("object explicit - implicit expected.");
+            }
+
+            return universalType.checkedCast(obj.toASN1Primitive());
+        }
+
+        if (DECLARED_EXPLICIT == explicitness)
+        {
+            throw new IllegalStateException("object explicit - implicit expected.");
+        }
+
+        ASN1Primitive primitive = obj.toASN1Primitive();
+        switch (explicitness)
+        {
+        case PARSED_EXPLICIT:
+            return universalType.fromImplicitConstructed(rebuildConstructed(primitive));
+        case PARSED_IMPLICIT:
+        {
+            if (primitive instanceof ASN1Sequence)
+            {
+                return universalType.fromImplicitConstructed((ASN1Sequence)primitive);
+            }
+            return universalType.fromImplicitPrimitive((DEROctetString)primitive);
+        }
+        default:
+            return universalType.checkedCast(primitive);
+        }
+    }
+
+    public ASN1Encodable parseBaseUniversal(boolean declaredExplicit, int baseTagNo) throws IOException
+    {
+        ASN1Primitive primitive = getBaseUniversal(declaredExplicit, baseTagNo);
+
+        switch (baseTagNo)
+        {
+        case BERTags.BIT_STRING:
+            return ((ASN1BitString)primitive).parser();
         case BERTags.OCTET_STRING:
-            return ASN1OctetString.getInstance(this, isExplicit).parser();
+            return ((ASN1OctetString)primitive).parser();
+        case BERTags.SEQUENCE:
+            return ((ASN1Sequence)primitive).parser();
+        case BERTags.SET:
+            return ((ASN1Set)primitive).parser();
         }
 
-        if (isExplicit)
-        {
-            return getObject();
-        }
-
-        throw new ASN1Exception("implicit tagging not implemented for tag: " + tag);
+        return primitive;
     }
 
-    public ASN1Primitive getLoadedObject()
+    public ASN1Encodable parseExplicitBaseObject() throws IOException
     {
-        return this.toASN1Primitive();
+        return getExplicitBaseObject();
     }
 
+    public ASN1TaggedObjectParser parseExplicitBaseTagged() throws IOException
+    {
+        return getExplicitBaseTagged();
+    }
+
+    public ASN1TaggedObjectParser parseImplicitBaseTagged(int baseTagClass, int baseTagNo) throws IOException
+    {
+        return getImplicitBaseTagged(baseTagClass, baseTagNo);
+    }
+
+    public final ASN1Primitive getLoadedObject()
+    {
+        return this;
+    }
+
+    abstract ASN1Sequence rebuildConstructed(ASN1Primitive primitive);
+
+    abstract ASN1TaggedObject replaceTag(int tagClass, int tagNo);
+
     ASN1Primitive toDERObject()
     {
-        return new DERTaggedObject(explicit, tagNo, obj);
+        return new DERTaggedObject(explicitness, tagClass, tagNo, obj);
     }
 
     ASN1Primitive toDLObject()
     {
-        return new DLTaggedObject(explicit, tagNo, obj);
+        return new DLTaggedObject(explicitness, tagClass, tagNo, obj);
     }
 
-    abstract void encode(ASN1OutputStream out, boolean withTag) throws IOException;
-
     public String toString()
     {
-        return "[" + tagNo + "]" + obj;
+        return ASN1Util.getTagText(tagClass, tagNo) + obj;
+    }
+
+    static ASN1Primitive createConstructedDL(int tagClass, int tagNo, ASN1EncodableVector contentsElements)
+    {
+        boolean maybeExplicit = (contentsElements.size() == 1);
+
+        return maybeExplicit
+            ?   new DLTaggedObject(PARSED_EXPLICIT, tagClass, tagNo, contentsElements.get(0))
+            :   new DLTaggedObject(PARSED_IMPLICIT, tagClass, tagNo, DLFactory.createSequence(contentsElements));
+    }
+
+    static ASN1Primitive createConstructedIL(int tagClass, int tagNo, ASN1EncodableVector contentsElements)
+    {
+        boolean maybeExplicit = (contentsElements.size() == 1);
+
+        return maybeExplicit
+            ?   new BERTaggedObject(PARSED_EXPLICIT, tagClass, tagNo, contentsElements.get(0))
+            :   new BERTaggedObject(PARSED_IMPLICIT, tagClass, tagNo, BERFactory.createSequence(contentsElements));
+    }
+
+    static ASN1Primitive createPrimitive(int tagClass, int tagNo, byte[] contentsOctets)
+    {
+        // Note: !CONSTRUCTED => IMPLICIT
+        return new DLTaggedObject(PARSED_IMPLICIT, tagClass, tagNo, new DEROctetString(contentsOctets));
+    }
+
+    private static ASN1TaggedObject checkedCast(ASN1Primitive primitive)
+    {
+        if (primitive instanceof ASN1TaggedObject)
+        {
+            return (ASN1TaggedObject)primitive;
+        }
+
+        throw new IllegalStateException("unexpected object: " + primitive.getClass().getName());
     }
 }
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1TaggedObjectParser.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1TaggedObjectParser.java
index 77eeca2..1fe2595 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1TaggedObjectParser.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1TaggedObjectParser.java
@@ -11,19 +11,38 @@
     extends ASN1Encodable, InMemoryRepresentable
 {
     /**
-     * Return the tag number associated with the underlying tagged object.
-     * @return the object's tag number.
+     * Return the tag class associated with this object.
+     *
+     * @return the tag class.
+     */
+    int getTagClass();
+
+    /**
+     * Return the tag number associated with this object.
+     *
+     * @return the tag number.
      */
     int getTagNo();
 
+    boolean hasContextTag();
+
+    boolean hasContextTag(int tagNo);
+
+    boolean hasTag(int tagClass, int tagNo);
+
+    boolean hasTagClass(int tagClass);
+
+    ASN1Encodable parseBaseUniversal(boolean declaredExplicit, int baseTagNo) throws IOException;
+
     /**
-     * Return a parser for the actual object tagged.
-     *
-     * @param tag the primitive tag value for the object tagged originally.
-     * @param isExplicit true if the tagging was done explicitly.
-     * @return a parser for the tagged object.
-     * @throws IOException if a parser cannot be constructed.
+     * Needed for open types, until we have better type-guided parsing support. Use sparingly for other
+     * purposes, and prefer {@link #parseExplicitBaseTagged()} or {@link #parseBaseUniversal(boolean, int)}
+     * where possible. Before using, check for matching tag {@link #getTagClass() class} and
+     * {@link #getTagNo() number}.
      */
-    ASN1Encodable getObjectParser(int tag, boolean isExplicit)
-        throws IOException;
+    ASN1Encodable parseExplicitBaseObject() throws IOException;
+
+    ASN1TaggedObjectParser parseExplicitBaseTagged() throws IOException;
+
+    ASN1TaggedObjectParser parseImplicitBaseTagged(int baseTagClass, int baseTagNo) throws IOException;
 }
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1Type.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1Type.java
new file mode 100644
index 0000000..9d45117
--- /dev/null
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1Type.java
@@ -0,0 +1,27 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.org.bouncycastle.asn1;
+
+abstract class ASN1Type
+{
+    final Class javaClass;
+
+    ASN1Type(Class javaClass)
+    {
+        this.javaClass = javaClass;
+    }
+
+    final Class getJavaClass()
+    {
+        return javaClass;
+    }
+
+    public final boolean equals(Object that)
+    {
+        return this == that;
+    }
+
+    public final int hashCode()
+    {
+        return super.hashCode();
+    }
+}
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1UTCTime.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1UTCTime.java
index e49ce01..d9b834d 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1UTCTime.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1UTCTime.java
@@ -38,7 +38,13 @@
 public class ASN1UTCTime
     extends ASN1Primitive
 {
-    private byte[]      time;
+    static final ASN1UniversalType TYPE = new ASN1UniversalType(ASN1UTCTime.class, BERTags.UTC_TIME)
+    {
+        ASN1Primitive fromImplicitPrimitive(DEROctetString octetString)
+        {
+            return createPrimitive(octetString.getOctets());
+        }
+    };
 
     /**
      * Return an UTC Time from the passed in object.
@@ -54,12 +60,19 @@
         {
             return (ASN1UTCTime)obj;
         }
-
+        if (obj instanceof ASN1Encodable)
+        {
+            ASN1Primitive primitive = ((ASN1Encodable)obj).toASN1Primitive();
+            if (primitive instanceof ASN1UTCTime)
+            {
+                return (ASN1UTCTime)primitive;
+            }
+        }
         if (obj instanceof byte[])
         {
             try
             {
-                return (ASN1UTCTime)fromByteArray((byte[])obj);
+                return (ASN1UTCTime)TYPE.fromByteArray((byte[])obj);
             }
             catch (Exception e)
             {
@@ -73,29 +86,19 @@
     /**
      * Return an UTC Time from a tagged object.
      *
-     * @param obj the tagged object holding the object we want
-     * @param explicit true if the object is meant to be explicitly
-     *              tagged false otherwise.
-     * @exception IllegalArgumentException if the tagged object cannot
-     *               be converted.
+     * @param taggedObject the tagged object holding the object we want
+     * @param explicit     true if the object is meant to be explicitly tagged false
+     *                     otherwise.
+     * @exception IllegalArgumentException if the tagged object cannot be converted.
      * @return an ASN1UTCTime instance, or null.
      */
-    public static ASN1UTCTime getInstance(
-        ASN1TaggedObject obj,
-        boolean          explicit)
+    public static ASN1UTCTime getInstance(ASN1TaggedObject taggedObject, boolean explicit)
     {
-        ASN1Object o = obj.getObject();
-
-        if (explicit || o instanceof ASN1UTCTime)
-        {
-            return getInstance(o);
-        }
-        else
-        {
-            return new ASN1UTCTime(ASN1OctetString.getInstance(o).getOctets());
-        }
+        return (ASN1UTCTime)TYPE.getContextInstance(taggedObject, explicit);
     }
 
+    final byte[] contents;
+
     /**
      * The correct format for this is YYMMDDHHMMSSZ (it used to be that seconds were
      * never encoded. When you're creating one of these objects from scratch, that's
@@ -109,7 +112,7 @@
     public ASN1UTCTime(
         String time)
     {
-        this.time = Strings.toByteArray(time);
+        this.contents = Strings.toByteArray(time);
         try
         {
             this.getDate();
@@ -133,7 +136,7 @@
 
         dateF.setTimeZone(new SimpleTimeZone(0,"Z"));
 
-        this.time = Strings.toByteArray(dateF.format(time));
+        this.contents = Strings.toByteArray(dateF.format(time));
     }
 
     /**
@@ -155,17 +158,16 @@
 
         dateF.setTimeZone(new SimpleTimeZone(0,"Z"));
 
-        this.time = Strings.toByteArray(dateF.format(time));
+        this.contents = Strings.toByteArray(dateF.format(time));
     }
 
-    ASN1UTCTime(
-        byte[] time)
+    ASN1UTCTime(byte[] contents)
     {
-        if (time.length < 2)
+        if (contents.length < 2)
         {
             throw new IllegalArgumentException("UTCTime string too short");
         }
-        this.time = time;
+        this.contents = contents;
         if (!(isDigit(0) && isDigit(1)))
         {
             throw new IllegalArgumentException("illegal characters in UTCTime string");
@@ -186,7 +188,7 @@
         // SimpleDateFormat dateF = new SimpleDateFormat("yyMMddHHmmssz");
         SimpleDateFormat dateF = new SimpleDateFormat("yyMMddHHmmssz", Locale.US);
 
-        return DateUtil.epochAdjust(dateF.parse(getTime()));
+        return dateF.parse(getTime());
     }
 
     /**
@@ -204,8 +206,8 @@
         SimpleDateFormat dateF = new SimpleDateFormat("yyyyMMddHHmmssz", Locale.US);
 
         dateF.setTimeZone(new SimpleTimeZone(0,"Z"));
-        
-        return DateUtil.epochAdjust(dateF.parse(getAdjustedTime()));
+
+        return dateF.parse(getAdjustedTime());
     }
 
     /**
@@ -226,7 +228,7 @@
      */
     public String getTime()
     {
-        String stime = Strings.fromByteArray(time);
+        String stime = Strings.fromByteArray(contents);
 
         //
         // standardise the format.
@@ -287,24 +289,22 @@
 
     private boolean isDigit(int pos)
     {
-        return time.length > pos && time[pos] >= '0' && time[pos] <= '9';
+        return contents.length > pos && contents[pos] >= '0' && contents[pos] <= '9';
     }
 
-    boolean isConstructed()
+    final boolean encodeConstructed()
     {
         return false;
     }
 
-    int encodedLength()
+    int encodedLength(boolean withTag)
     {
-        int length = time.length;
-
-        return 1 + StreamUtil.calculateBodyLength(length) + length;
+        return ASN1OutputStream.getLengthOfEncodingDL(withTag, contents.length);
     }
 
     void encode(ASN1OutputStream out, boolean withTag) throws IOException
     {
-        out.writeEncoded(withTag, BERTags.UTC_TIME, time);
+        out.writeEncodingDL(withTag, BERTags.UTC_TIME, contents);
     }
 
     boolean asn1Equals(
@@ -315,16 +315,21 @@
             return false;
         }
 
-        return Arrays.areEqual(time, ((ASN1UTCTime)o).time);
+        return Arrays.areEqual(contents, ((ASN1UTCTime)o).contents);
     }
 
     public int hashCode()
     {
-        return Arrays.hashCode(time);
+        return Arrays.hashCode(contents);
     }
 
     public String toString()
     {
-      return Strings.fromByteArray(time);
+      return Strings.fromByteArray(contents);
+    }
+
+    static ASN1UTCTime createPrimitive(byte[] contents)
+    {
+        return new ASN1UTCTime(contents);
     }
 }
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1UTF8String.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1UTF8String.java
new file mode 100644
index 0000000..e764f78
--- /dev/null
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1UTF8String.java
@@ -0,0 +1,133 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.org.bouncycastle.asn1;
+
+import java.io.IOException;
+
+import com.android.org.bouncycastle.util.Arrays;
+import com.android.org.bouncycastle.util.Strings;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+public abstract class ASN1UTF8String
+    extends ASN1Primitive
+    implements ASN1String
+{
+    static final ASN1UniversalType TYPE = new ASN1UniversalType(ASN1UTF8String.class, BERTags.UTF8_STRING)
+    {
+        ASN1Primitive fromImplicitPrimitive(DEROctetString octetString)
+        {
+            return createPrimitive(octetString.getOctets());
+        }
+    };
+
+    /**
+     * Return a UTF8 string from the passed in object.
+     *
+     * @param obj an ASN1UTF8String or an object that can be converted into one.
+     * @exception IllegalArgumentException if the object cannot be converted.
+     * @return an ASN1UTF8String instance, or null
+     */
+    public static ASN1UTF8String getInstance(Object obj)
+    {
+        if (obj == null || obj instanceof ASN1UTF8String)
+        {
+            return (ASN1UTF8String)obj;
+        }
+        if (obj instanceof ASN1Encodable)
+        {
+            ASN1Primitive primitive = ((ASN1Encodable)obj).toASN1Primitive();
+            if (primitive instanceof ASN1UTF8String)
+            {
+                return (ASN1UTF8String)primitive;
+            }
+        }
+        if (obj instanceof byte[])
+        {
+            try
+            {
+                return (ASN1UTF8String)TYPE.fromByteArray((byte[])obj);
+            }
+            catch (Exception e)
+            {
+                throw new IllegalArgumentException("encoding error in getInstance: " + e.toString());
+            }
+        }
+
+        throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
+    }
+
+    /**
+     * Return an UTF8 String from a tagged object.
+     * 
+     * @param taggedObject the tagged object holding the object we want
+     * @param explicit     true if the object is meant to be explicitly tagged false
+     *                     otherwise.
+     * @exception IllegalArgumentException if the tagged object cannot be converted.
+     * @return a DERUTF8String instance, or null
+     */
+    public static ASN1UTF8String getInstance(ASN1TaggedObject taggedObject, boolean explicit)
+    {
+        return (ASN1UTF8String)TYPE.getContextInstance(taggedObject, explicit);
+    }
+
+    final byte[] contents;
+
+    ASN1UTF8String(String string)
+    {
+        this(Strings.toUTF8ByteArray(string), false);
+    }
+
+    ASN1UTF8String(byte[] contents, boolean clone)
+    {
+        this.contents = clone ? Arrays.clone(contents) : contents;
+    }
+
+    public final String getString()
+    {
+        return Strings.fromUTF8ByteArray(contents);
+    }
+
+    // TODO Not sure this is useful unless all ASN.1 types have a meaningful one
+    public String toString()
+    {
+        return getString();
+    }
+
+    public final int hashCode()
+    {
+        return Arrays.hashCode(contents);
+    }
+
+    final boolean asn1Equals(ASN1Primitive other)
+    {
+        if (!(other instanceof ASN1UTF8String))
+        {
+            return false;
+        }
+
+        ASN1UTF8String that = (ASN1UTF8String)other;
+
+        return Arrays.areEqual(this.contents, that.contents);
+    }
+
+    final boolean encodeConstructed()
+    {
+        return false;
+    }
+
+    final int encodedLength(boolean withTag)
+    {
+        return ASN1OutputStream.getLengthOfEncodingDL(withTag, contents.length);
+    }
+
+    final void encode(ASN1OutputStream out, boolean withTag) throws IOException
+    {
+        out.writeEncodingDL(withTag, BERTags.UTF8_STRING, contents);
+    }
+
+    static ASN1UTF8String createPrimitive(byte[] contents)
+    {
+        return new DERUTF8String(contents, false);
+    }
+}
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1UniversalString.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1UniversalString.java
new file mode 100644
index 0000000..2b65286
--- /dev/null
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1UniversalString.java
@@ -0,0 +1,180 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.org.bouncycastle.asn1;
+
+import java.io.IOException;
+
+import com.android.org.bouncycastle.util.Arrays;
+
+/**
+ * ASN.1 UniversalString object - encodes UNICODE (ISO 10646) characters using 32-bit format. In Java we
+ * have no way of representing this directly so we rely on byte arrays to carry these.
+ * @hide This class is not part of the Android public SDK API
+ */
+public abstract class ASN1UniversalString
+    extends ASN1Primitive
+    implements ASN1String
+{
+    static final ASN1UniversalType TYPE = new ASN1UniversalType(ASN1UniversalString.class, BERTags.UNIVERSAL_STRING)
+    {
+        ASN1Primitive fromImplicitPrimitive(DEROctetString octetString)
+        {
+            return createPrimitive(octetString.getOctets());
+        }
+    };
+
+    private static final char[]  table = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
+
+    /**
+     * Return a Universal String from the passed in object.
+     *
+     * @param obj an ASN1UniversalString or an object that can be converted into
+     *            one.
+     * @exception IllegalArgumentException if the object cannot be converted.
+     * @return an ASN1UniversalString instance, or null
+     */
+    public static ASN1UniversalString getInstance(Object obj)
+    {
+        if (obj == null || obj instanceof ASN1UniversalString)
+        {
+            return (ASN1UniversalString)obj;
+        }
+        if (obj instanceof ASN1Encodable)
+        {
+            ASN1Primitive primitive = ((ASN1Encodable)obj).toASN1Primitive();
+            if (primitive instanceof ASN1UniversalString)
+            {
+                return (ASN1UniversalString)primitive;
+            }
+        }
+        if (obj instanceof byte[])
+        {
+            try
+            {
+                return (ASN1UniversalString)TYPE.fromByteArray((byte[])obj);
+            }
+            catch (Exception e)
+            {
+                throw new IllegalArgumentException("encoding error getInstance: " + e.toString());
+            }
+        }
+
+        throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
+    }
+
+    /**
+     * Return a Universal String from a tagged object.
+     *
+     * @param taggedObject      the tagged object holding the object we want
+     * @param explicit true if the object is meant to be explicitly tagged false
+     *                 otherwise.
+     * @exception IllegalArgumentException if the tagged object cannot be converted.
+     * @return a ASN1UniversalString instance, or null
+     */
+    public static ASN1UniversalString getInstance(ASN1TaggedObject taggedObject, boolean explicit)
+    {
+        return (ASN1UniversalString)TYPE.getContextInstance(taggedObject, explicit);
+    }
+
+    final byte[] contents;
+
+    ASN1UniversalString(byte[] contents, boolean clone)
+    {
+        this.contents = clone ? Arrays.clone(contents) : contents;
+    }
+
+    public final String getString()
+    {
+        int dl = contents.length;
+        StringBuffer buf = new StringBuffer(3 + 2 * (ASN1OutputStream.getLengthOfDL(dl) + dl));
+        buf.append("#1C");
+        encodeHexDL(buf, dl);
+
+        for (int i = 0; i < dl; ++i)
+        {
+            encodeHexByte(buf, contents[i]);
+        }
+
+        return buf.toString();
+    }
+
+    public String toString()
+    {
+        return getString();
+    }
+
+    public final byte[] getOctets()
+    {
+        return Arrays.clone(contents);
+    }
+
+    final boolean encodeConstructed()
+    {
+        return false;
+    }
+
+    final int encodedLength(boolean withTag)
+    {
+        return ASN1OutputStream.getLengthOfEncodingDL(withTag, contents.length);
+    }
+
+    final void encode(ASN1OutputStream out, boolean withTag) throws IOException
+    {
+        out.writeEncodingDL(withTag, BERTags.UNIVERSAL_STRING, contents);
+    }
+
+    final boolean asn1Equals(ASN1Primitive other)
+    {
+        if (!(other instanceof ASN1UniversalString))
+        {
+            return false;
+        }
+
+        ASN1UniversalString that = (ASN1UniversalString)other;
+
+        return Arrays.areEqual(this.contents, that.contents);
+    }
+
+    public final int hashCode()
+    {
+        return Arrays.hashCode(contents);
+    }
+
+    static ASN1UniversalString createPrimitive(byte[] contents)
+    {
+        return new DERUniversalString(contents, false);
+    }
+
+    private static void encodeHexByte(StringBuffer buf, int i)
+    {
+        buf.append(table[(i >>> 4) & 0xF]);
+        buf.append(table[i & 0xF]);
+    }
+
+    private static void encodeHexDL(StringBuffer buf, int dl)
+    {
+        if (dl < 128)
+        {
+            encodeHexByte(buf, dl);
+            return;
+        }
+
+        byte[] stack = new byte[5];
+        int pos = 5;
+
+        do
+        {
+            stack[--pos] = (byte)dl;
+            dl >>>= 8;
+        }
+        while (dl != 0);
+
+        int count = stack.length - pos;
+        stack[--pos] = (byte)(0x80 | count);
+
+        do
+        {
+            encodeHexByte(buf, stack[pos++]);
+        }
+        while (pos < stack.length);
+    }
+}
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1UniversalType.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1UniversalType.java
new file mode 100644
index 0000000..6d0cfed
--- /dev/null
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1UniversalType.java
@@ -0,0 +1,57 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.org.bouncycastle.asn1;
+
+import java.io.IOException;
+
+abstract class ASN1UniversalType
+    extends ASN1Type
+{
+    final ASN1Tag tag;
+
+    ASN1UniversalType(Class javaClass, int tagNumber)
+    {
+        super(javaClass);
+
+        this.tag = ASN1Tag.create(BERTags.UNIVERSAL, tagNumber);
+    }
+
+    final ASN1Primitive checkedCast(ASN1Primitive primitive)
+    {
+        if (javaClass.isInstance(primitive))
+        {
+            return primitive;
+        }
+
+        throw new IllegalStateException("unexpected object: " + primitive.getClass().getName());
+    }
+
+    ASN1Primitive fromImplicitPrimitive(DEROctetString octetString)
+    {
+        throw new IllegalStateException("unexpected implicit primitive encoding");
+    }
+
+    ASN1Primitive fromImplicitConstructed(ASN1Sequence sequence)
+    {
+        throw new IllegalStateException("unexpected implicit constructed encoding");
+    }
+
+    final ASN1Primitive fromByteArray(byte[] bytes) throws IOException
+    {
+        return checkedCast(ASN1Primitive.fromByteArray(bytes));
+    }
+
+    final ASN1Primitive getContextInstance(ASN1TaggedObject taggedObject, boolean declaredExplicit)
+    {
+        if (BERTags.CONTEXT_SPECIFIC != taggedObject.getTagClass())
+        {
+            throw new IllegalStateException("this method only valid for CONTEXT_SPECIFIC tags");
+        }
+
+        return checkedCast(taggedObject.getBaseUniversal(declaredExplicit, this));
+    }
+
+    final ASN1Tag getTag()
+    {
+        return tag;
+    }
+}
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1UniversalTypes.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1UniversalTypes.java
new file mode 100644
index 0000000..4f5deef
--- /dev/null
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1UniversalTypes.java
@@ -0,0 +1,72 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.org.bouncycastle.asn1;
+
+final class ASN1UniversalTypes
+{
+    private ASN1UniversalTypes()
+    {
+    }
+
+    static ASN1UniversalType get(int tagNumber)
+    {
+        switch (tagNumber)
+        {
+        case BERTags.BOOLEAN:
+            return ASN1Boolean.TYPE;
+        case BERTags.INTEGER:
+            return ASN1Integer.TYPE;
+        case BERTags.BIT_STRING:
+            return ASN1BitString.TYPE;
+        case BERTags.OCTET_STRING:
+            return ASN1OctetString.TYPE;
+        case BERTags.NULL:
+            return ASN1Null.TYPE;
+        case BERTags.OBJECT_IDENTIFIER:
+            return ASN1ObjectIdentifier.TYPE;
+        case BERTags.OBJECT_DESCRIPTOR:         // [UNIVERSAL 7] IMPLICIT GraphicString
+            return ASN1ObjectDescriptor.TYPE;
+        case BERTags.EXTERNAL:
+            return ASN1External.TYPE;
+        case BERTags.ENUMERATED:
+            return ASN1Enumerated.TYPE;
+        case BERTags.UTF8_STRING:               // [UNIVERSAL 12] IMPLICIT OCTET STRING (encode as if)
+            return ASN1UTF8String.TYPE;
+        case BERTags.RELATIVE_OID:
+            return ASN1RelativeOID.TYPE;
+        case BERTags.SEQUENCE:
+            return ASN1Sequence.TYPE;
+        case BERTags.SET:
+            return ASN1Set.TYPE;
+        case BERTags.NUMERIC_STRING:            // [UNIVERSAL 18] IMPLICIT OCTET STRING (encode as if)
+            return ASN1NumericString.TYPE;
+        case BERTags.PRINTABLE_STRING:          // [UNIVERSAL 19] IMPLICIT OCTET STRING (encode as if)
+            return ASN1PrintableString.TYPE;
+        case BERTags.T61_STRING:                // [UNIVERSAL 20] IMPLICIT OCTET STRING (encode as if)
+            return ASN1T61String.TYPE;
+        case BERTags.VIDEOTEX_STRING:           // [UNIVERSAL 21] IMPLICIT OCTET STRING (encode as if)
+            return ASN1VideotexString.TYPE;
+        case BERTags.IA5_STRING:                // [UNIVERSAL 22] IMPLICIT OCTET STRING (encode as if)
+            return ASN1IA5String.TYPE;
+        case BERTags.UTC_TIME:                  // [UNIVERSAL 23] IMPLICIT VisibleString (restricted values)
+            return ASN1UTCTime.TYPE;
+        case BERTags.GENERALIZED_TIME:          // [UNIVERSAL 24] IMPLICIT VisibleString (restricted values)
+            return ASN1GeneralizedTime.TYPE;
+        case BERTags.GRAPHIC_STRING:            // [UNIVERSAL 25] IMPLICIT OCTET STRING (encode as if)
+            return ASN1GraphicString.TYPE;
+        case BERTags.VISIBLE_STRING:            // [UNIVERSAL 26] IMPLICIT OCTET STRING (encode as if)
+            return ASN1VisibleString.TYPE;
+        case BERTags.GENERAL_STRING:            // [UNIVERSAL 27] IMPLICIT OCTET STRING (encode as if)
+            return ASN1GeneralString.TYPE;
+        case BERTags.UNIVERSAL_STRING:          // [UNIVERSAL 28] IMPLICIT OCTET STRING (encode as if)
+            return ASN1UniversalString.TYPE;
+        case BERTags.BMP_STRING:                // [UNIVERSAL 30] IMPLICIT OCTET STRING (encode as if)
+            return ASN1BMPString.TYPE;
+
+        case BERTags.REAL:
+        case BERTags.EMBEDDED_PDV:
+        case BERTags.UNRESTRICTED_STRING:
+        default:
+            return null;
+        }
+    }
+}
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1Util.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1Util.java
new file mode 100644
index 0000000..d79587c
--- /dev/null
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1Util.java
@@ -0,0 +1,331 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.org.bouncycastle.asn1;
+
+import java.io.IOException;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+public abstract class ASN1Util
+{
+    static ASN1TaggedObject checkTag(ASN1TaggedObject taggedObject, int tagClass, int tagNo)
+    {
+        if (!taggedObject.hasTag(tagClass, tagNo))
+        {
+            String expected = getTagText(tagClass, tagNo);
+            String found = getTagText(taggedObject);
+            throw new IllegalStateException("Expected " + expected + " tag but found " + found);
+        }
+        return taggedObject;
+    }
+
+    static ASN1TaggedObjectParser checkTag(ASN1TaggedObjectParser taggedObjectParser, int tagClass, int tagNo)
+    {
+        if (!taggedObjectParser.hasTag(tagClass, tagNo))
+        {
+            String expected = getTagText(tagClass, tagNo);
+            String found = getTagText(taggedObjectParser);
+            throw new IllegalStateException("Expected " + expected + " tag but found " + found);
+        }
+        return taggedObjectParser;
+    }
+
+
+    /*
+     * Tag text methods
+     */
+
+    static String getTagText(ASN1Tag tag)
+    {
+        return getTagText(tag.getTagClass(), tag.getTagNumber());
+    }
+
+    public static String getTagText(ASN1TaggedObject taggedObject)
+    {
+        return getTagText(taggedObject.getTagClass(), taggedObject.getTagNo());
+    }
+
+    public static String getTagText(ASN1TaggedObjectParser taggedObjectParser)
+    {
+        return getTagText(taggedObjectParser.getTagClass(), taggedObjectParser.getTagNo());
+    }
+
+    public static String getTagText(int tagClass, int tagNo)
+    {
+        switch (tagClass)
+        {
+        case BERTags.APPLICATION:
+            return "[APPLICATION " + tagNo + "]";
+        case BERTags.CONTEXT_SPECIFIC:
+            return "[CONTEXT " + tagNo + "]";
+        case BERTags.PRIVATE:
+            return "[PRIVATE " + tagNo + "]";
+        default:
+            return "[UNIVERSAL " + tagNo + "]";
+        }
+    }
+
+
+    /*
+     * Wrappers for ASN1TaggedObject#getExplicitBaseObject
+     */
+
+    public static ASN1Object getExplicitBaseObject(ASN1TaggedObject taggedObject, int tagClass, int tagNo)
+    {
+        return checkTag(taggedObject, tagClass, tagNo).getExplicitBaseObject();
+    }
+
+    public static ASN1Object getExplicitContextBaseObject(ASN1TaggedObject taggedObject, int tagNo)
+    {
+        return getExplicitBaseObject(taggedObject, BERTags.CONTEXT_SPECIFIC, tagNo);
+    }
+
+    public static ASN1Object tryGetExplicitBaseObject(ASN1TaggedObject taggedObject, int tagClass, int tagNo)
+    {
+        if (!taggedObject.hasTag(tagClass, tagNo))
+        {
+            return null;
+        }
+
+        return taggedObject.getExplicitBaseObject();
+    }
+
+    public static ASN1Object tryGetExplicitContextBaseObject(ASN1TaggedObject taggedObject, int tagNo)
+    {
+        return tryGetExplicitBaseObject(taggedObject, BERTags.CONTEXT_SPECIFIC, tagNo);
+    }
+
+
+    /*
+     * Wrappers for ASN1TaggedObject#getExplicitBaseTagged
+     */
+
+    public static ASN1TaggedObject getExplicitBaseTagged(ASN1TaggedObject taggedObject, int tagClass, int tagNo)
+    {
+        return checkTag(taggedObject, tagClass, tagNo).getExplicitBaseTagged();
+    }
+
+    public static ASN1TaggedObject getExplicitContextBaseTagged(ASN1TaggedObject taggedObject, int tagNo)
+    {
+        return getExplicitBaseTagged(taggedObject, BERTags.CONTEXT_SPECIFIC, tagNo);
+    }
+
+    public static ASN1TaggedObject tryGetExplicitBaseTagged(ASN1TaggedObject taggedObject, int tagClass, int tagNo)
+    {
+        if (!taggedObject.hasTag(tagClass, tagNo))
+        {
+            return null;
+        }
+
+        return taggedObject.getExplicitBaseTagged();
+    }
+
+    public static ASN1TaggedObject tryGetExplicitContextBaseTagged(ASN1TaggedObject taggedObject, int tagNo)
+    {
+        return tryGetExplicitBaseTagged(taggedObject, BERTags.CONTEXT_SPECIFIC, tagNo);
+    }
+
+
+    /*
+     * Wrappers for ASN1TaggedObject#getImplicitBaseTagged
+     */
+
+    public static ASN1TaggedObject getImplicitBaseTagged(ASN1TaggedObject taggedObject, int tagClass, int tagNo,
+        int baseTagClass, int baseTagNo)
+    {
+        return checkTag(taggedObject, tagClass, tagNo).getImplicitBaseTagged(baseTagClass, baseTagNo);
+    }
+
+    public static ASN1TaggedObject getImplicitContextBaseTagged(ASN1TaggedObject taggedObject, int tagNo,
+        int baseTagClass, int baseTagNo)
+    {
+        return getImplicitBaseTagged(taggedObject, BERTags.CONTEXT_SPECIFIC, tagNo, baseTagClass, baseTagNo);
+    }
+
+    public static ASN1TaggedObject tryGetImplicitBaseTagged(ASN1TaggedObject taggedObject, int tagClass, int tagNo,
+        int baseTagClass, int baseTagNo)
+    {
+        if (!taggedObject.hasTag(tagClass, tagNo))
+        {
+            return null;
+        }
+
+        return taggedObject.getImplicitBaseTagged(baseTagClass, baseTagNo);
+    }
+
+    public static ASN1TaggedObject tryGetImplicitContextBaseTagged(ASN1TaggedObject taggedObject, int tagNo,
+        int baseTagClass, int baseTagNo)
+    {
+        return tryGetImplicitBaseTagged(taggedObject, BERTags.CONTEXT_SPECIFIC, tagNo, baseTagClass, baseTagNo);
+    }
+
+
+    /*
+     * Wrappers for ASN1TaggedObject#getBaseUniversal
+     */
+
+    public static ASN1Primitive getBaseUniversal(ASN1TaggedObject taggedObject, int tagClass, int tagNo,
+        boolean declaredExplicit, int baseTagNo)
+    {
+        return checkTag(taggedObject, tagClass, tagNo).getBaseUniversal(declaredExplicit, baseTagNo);  
+    }
+
+    public static ASN1Primitive getContextBaseUniversal(ASN1TaggedObject taggedObject, int tagNo,
+        boolean declaredExplicit, int baseTagNo)
+    {
+        return getBaseUniversal(taggedObject, BERTags.CONTEXT_SPECIFIC, tagNo, declaredExplicit, baseTagNo);
+    }
+
+    public static ASN1Primitive tryGetBaseUniversal(ASN1TaggedObject taggedObject, int tagClass, int tagNo,
+        boolean declaredExplicit, int baseTagNo)
+    {
+        if (!taggedObject.hasTag(tagClass, tagNo))
+        {
+            return null;
+        }
+
+        return taggedObject.getBaseUniversal(declaredExplicit, baseTagNo);  
+    }
+
+    public static ASN1Primitive tryGetContextBaseUniversal(ASN1TaggedObject taggedObject, int tagNo,
+        boolean declaredExplicit, int baseTagNo)
+    {
+        return tryGetBaseUniversal(taggedObject, BERTags.CONTEXT_SPECIFIC, tagNo, declaredExplicit, baseTagNo);
+    }
+
+
+    /*
+     * Wrappers for ASN1TaggedObjectParser#parseExplicitBaseTagged
+     */
+
+    public static ASN1TaggedObjectParser parseExplicitBaseTagged(ASN1TaggedObjectParser taggedObjectParser,
+        int tagClass, int tagNo) throws IOException
+    {
+        return checkTag(taggedObjectParser, tagClass, tagNo).parseExplicitBaseTagged();
+    }
+
+    public static ASN1TaggedObjectParser parseExplicitContextBaseTagged(ASN1TaggedObjectParser taggedObjectParser,
+        int tagNo) throws IOException
+    {
+        return parseExplicitBaseTagged(taggedObjectParser, BERTags.CONTEXT_SPECIFIC, tagNo);
+    }
+
+    public static ASN1TaggedObjectParser tryParseExplicitBaseTagged(ASN1TaggedObjectParser taggedObjectParser,
+        int tagClass, int tagNo) throws IOException
+    {
+        if (!taggedObjectParser.hasTag(tagClass, tagNo))
+        {
+            return null;
+        }
+
+        return taggedObjectParser.parseExplicitBaseTagged();
+    }
+
+    public static ASN1TaggedObjectParser tryParseExplicitContextBaseTagged(ASN1TaggedObjectParser taggedObjectParser,
+        int tagNo) throws IOException
+    {
+        return tryParseExplicitBaseTagged(taggedObjectParser, BERTags.CONTEXT_SPECIFIC, tagNo);
+    }
+
+
+    /*
+     * Wrappers for ASN1TaggedObjectParser#parseImplicitBaseTagged
+     */
+
+    public static ASN1TaggedObjectParser parseImplicitBaseTagged(ASN1TaggedObjectParser taggedObjectParser,
+        int tagClass, int tagNo, int baseTagClass, int baseTagNo) throws IOException
+    {
+        return checkTag(taggedObjectParser, tagClass, tagNo).parseImplicitBaseTagged(baseTagClass, baseTagNo);
+    }
+
+    public static ASN1TaggedObjectParser parseImplicitContextBaseTagged(ASN1TaggedObjectParser taggedObjectParser,
+        int tagNo, int baseTagClass, int baseTagNo) throws IOException
+    {
+        return parseImplicitBaseTagged(taggedObjectParser, BERTags.CONTEXT_SPECIFIC, tagNo, baseTagClass, baseTagNo);
+    }
+
+    public static ASN1TaggedObjectParser tryParseImplicitBaseTagged(ASN1TaggedObjectParser taggedObjectParser,
+        int tagClass, int tagNo, int baseTagClass, int baseTagNo) throws IOException
+    {
+        if (!taggedObjectParser.hasTag(tagClass, tagNo))
+        {
+            return null;
+        }
+
+        return taggedObjectParser.parseImplicitBaseTagged(baseTagClass, baseTagNo);
+    }
+
+    public static ASN1TaggedObjectParser tryParseImplicitContextBaseTagged(ASN1TaggedObjectParser taggedObjectParser,
+        int tagNo, int baseTagClass, int baseTagNo) throws IOException
+    {
+        return tryParseImplicitBaseTagged(taggedObjectParser, BERTags.CONTEXT_SPECIFIC, tagNo, baseTagClass, baseTagNo);
+    }
+
+
+    /*
+     * Wrappers for ASN1TaggedObjectParser#parseBaseUniversal
+     */
+
+    public static ASN1Encodable parseBaseUniversal(ASN1TaggedObjectParser taggedObjectParser, int tagClass,
+        int tagNo, boolean declaredExplicit, int baseTagNo) throws IOException
+    {
+        return checkTag(taggedObjectParser, tagClass, tagNo).parseBaseUniversal(declaredExplicit, baseTagNo);
+    }
+
+    public static ASN1Encodable parseContextBaseUniversal(ASN1TaggedObjectParser taggedObjectParser, int tagNo,
+        boolean declaredExplicit, int baseTagNo) throws IOException
+    {
+        return parseBaseUniversal(taggedObjectParser, BERTags.CONTEXT_SPECIFIC, tagNo, declaredExplicit, baseTagNo);
+    }
+
+    public static ASN1Encodable tryParseBaseUniversal(ASN1TaggedObjectParser taggedObjectParser, int tagClass,
+        int tagNo, boolean declaredExplicit, int baseTagNo) throws IOException
+    {
+        if (!taggedObjectParser.hasTag(tagClass, tagNo))
+        {
+            return null;
+        }
+
+        return taggedObjectParser.parseBaseUniversal(declaredExplicit, baseTagNo);
+    }
+
+    public static ASN1Encodable tryParseContextBaseUniversal(ASN1TaggedObjectParser taggedObjectParser, int tagNo,
+        boolean declaredExplicit, int baseTagNo) throws IOException
+    {
+        return tryParseBaseUniversal(taggedObjectParser, BERTags.CONTEXT_SPECIFIC, tagNo, declaredExplicit, baseTagNo);
+    }
+
+
+    /*
+     * Wrappers for ASN1TaggedObjectParser#parseExplicitBaseObject
+     */
+
+    public static ASN1Encodable parseExplicitBaseObject(ASN1TaggedObjectParser taggedObjectParser, int tagClass,
+        int tagNo) throws IOException
+    {
+        return checkTag(taggedObjectParser, tagClass, tagNo).parseExplicitBaseObject();
+    }
+
+    public static ASN1Encodable parseExplicitContextBaseObject(ASN1TaggedObjectParser taggedObjectParser, int tagNo)
+        throws IOException
+    {
+        return parseExplicitBaseObject(taggedObjectParser, BERTags.CONTEXT_SPECIFIC, tagNo);
+    }
+
+    public static ASN1Encodable tryParseExplicitBaseObject(ASN1TaggedObjectParser taggedObjectParser, int tagClass,
+        int tagNo) throws IOException
+    {
+        if (!taggedObjectParser.hasTag(tagClass, tagNo))
+        {
+            return null;
+        }
+
+        return taggedObjectParser.parseExplicitBaseObject();
+    }
+
+    public static ASN1Encodable tryParseExplicitContextBaseObject(ASN1TaggedObjectParser taggedObjectParser, int tagNo)
+        throws IOException
+    {
+        return tryParseExplicitBaseObject(taggedObjectParser, BERTags.CONTEXT_SPECIFIC, tagNo);
+    }
+}
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1VideotexString.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1VideotexString.java
new file mode 100644
index 0000000..1c0d3df
--- /dev/null
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1VideotexString.java
@@ -0,0 +1,131 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.org.bouncycastle.asn1;
+
+import java.io.IOException;
+
+import com.android.org.bouncycastle.util.Arrays;
+import com.android.org.bouncycastle.util.Strings;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+public abstract class ASN1VideotexString
+    extends ASN1Primitive
+    implements ASN1String
+{
+    static final ASN1UniversalType TYPE = new ASN1UniversalType(ASN1VideotexString.class, BERTags.VIDEOTEX_STRING)
+    {
+        ASN1Primitive fromImplicitPrimitive(DEROctetString octetString)
+        {
+            return createPrimitive(octetString.getOctets());
+        }
+    };
+
+    /**
+     * return a Videotex String from the passed in object
+     *
+     * @param obj an ASN1VideotexString or an object that can be converted into one.
+     * @exception IllegalArgumentException if the object cannot be converted.
+     * @return an ASN1VideotexString instance, or null.
+     */
+    public static ASN1VideotexString getInstance(Object obj)
+    {
+        if (obj == null || obj instanceof ASN1VideotexString)
+        {
+            return (ASN1VideotexString)obj;
+        }
+        if (obj instanceof ASN1Encodable)
+        {
+            ASN1Primitive primitive = ((ASN1Encodable)obj).toASN1Primitive();
+            if (primitive instanceof ASN1VideotexString)
+            {
+                return (ASN1VideotexString)primitive;
+            }
+        }
+        if (obj instanceof byte[])
+        {
+            try
+            {
+                return (ASN1VideotexString)TYPE.fromByteArray((byte[])obj);
+            }
+            catch (Exception e)
+            {
+                throw new IllegalArgumentException("encoding error in getInstance: " + e.toString());
+            }
+        }
+
+        throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
+    }
+
+    /**
+     * return a Videotex String from a tagged object.
+     *
+     * @param taggedObject the tagged object holding the object we want
+     * @param explicit     true if the object is meant to be explicitly tagged false
+     *                     otherwise.
+     * @exception IllegalArgumentException if the tagged object cannot be converted.
+     * @return an ASN1VideotexString instance, or null.
+     */
+    public static ASN1VideotexString getInstance(ASN1TaggedObject taggedObject, boolean explicit)
+    {
+        return (ASN1VideotexString)TYPE.getContextInstance(taggedObject, explicit);
+    }
+
+    final byte[] contents;
+
+    /**
+     * basic constructor - with bytes.
+     * @param string the byte encoding of the characters making up the string.
+     */
+    ASN1VideotexString(byte[] contents, boolean clone)
+    {
+        this.contents = clone ? Arrays.clone(contents) : contents;
+    }
+
+    public final byte[] getOctets()
+    {
+        return Arrays.clone(contents);
+    }
+
+    final boolean encodeConstructed()
+    {
+        return false;
+    }
+
+    final int encodedLength(boolean withTag)
+    {
+        return ASN1OutputStream.getLengthOfEncodingDL(withTag, contents.length);
+    }
+
+    final void encode(ASN1OutputStream out, boolean withTag) throws IOException
+    {
+        out.writeEncodingDL(withTag, BERTags.VIDEOTEX_STRING, contents);
+    }
+
+    final boolean asn1Equals(ASN1Primitive other)
+    {
+        if (!(other instanceof ASN1VideotexString))
+        {
+            return false;
+        }
+
+        ASN1VideotexString that = (ASN1VideotexString)other;
+
+        return Arrays.areEqual(this.contents, that.contents);
+    }
+
+    public final int hashCode()
+    {
+        return Arrays.hashCode(contents);
+    }
+
+    public final String getString()
+    {
+        return Strings.fromByteArray(contents);
+    }
+
+    static ASN1VideotexString createPrimitive(byte[] contents)
+    {
+        return new DERVideotexString(contents, false);
+    }
+}
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1VisibleString.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1VisibleString.java
new file mode 100644
index 0000000..7b49fc9
--- /dev/null
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ASN1VisibleString.java
@@ -0,0 +1,143 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.org.bouncycastle.asn1;
+
+import java.io.IOException;
+
+import com.android.org.bouncycastle.util.Arrays;
+import com.android.org.bouncycastle.util.Strings;
+
+/**
+ * ASN.1 VisibleString object encoding ISO 646 (ASCII) character code points 32 to 126.
+ * <p>
+ * Explicit character set escape sequences are not allowed.
+ * </p>
+ * @hide This class is not part of the Android public SDK API
+ */
+public abstract class ASN1VisibleString
+    extends ASN1Primitive
+    implements ASN1String
+{
+    static final ASN1UniversalType TYPE = new ASN1UniversalType(ASN1VisibleString.class, BERTags.VISIBLE_STRING)
+    {
+        ASN1Primitive fromImplicitPrimitive(DEROctetString octetString)
+        {
+            return createPrimitive(octetString.getOctets());
+        }
+    };
+
+    /**
+     * Return a Visible String from the passed in object.
+     *
+     * @param obj an ASN1VisibleString or an object that can be converted into one.
+     * @exception IllegalArgumentException if the object cannot be converted.
+     * @return an ASN1VisibleString instance, or null
+     */
+    public static ASN1VisibleString getInstance(
+        Object  obj)
+    {
+        if (obj == null || obj instanceof ASN1VisibleString)
+        {
+            return (ASN1VisibleString)obj;
+        }
+        if (obj instanceof ASN1Encodable)
+        {
+            ASN1Primitive primitive = ((ASN1Encodable)obj).toASN1Primitive();
+            if (primitive instanceof ASN1VisibleString)
+            {
+                return (ASN1VisibleString)primitive;
+            }
+        }
+        if (obj instanceof byte[])
+        {
+            try
+            {
+                return (ASN1VisibleString)TYPE.fromByteArray((byte[])obj);
+            }
+            catch (Exception e)
+            {
+                throw new IllegalArgumentException("encoding error in getInstance: " + e.toString());
+            }
+        }
+
+        throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
+    }
+
+    /**
+     * Return a Visible String from a tagged object.
+     *
+     * @param taggedObject the tagged object holding the object we want
+     * @param explicit true if the object is meant to be explicitly
+     *              tagged false otherwise.
+     * @exception IllegalArgumentException if the tagged object cannot
+     *               be converted.
+     * @return an ASN1VisibleString instance, or null
+     */
+    public static ASN1VisibleString getInstance(ASN1TaggedObject taggedObject, boolean explicit)
+    {
+        return (ASN1VisibleString)TYPE.getContextInstance(taggedObject, explicit);
+    }
+
+    final byte[] contents;
+
+    ASN1VisibleString(String string)
+    {
+        this.contents = Strings.toByteArray(string);
+    }
+
+    ASN1VisibleString(byte[] contents, boolean clone)
+    {
+        this.contents = clone ? Arrays.clone(contents) : contents;
+    }
+
+    public final String getString()
+    {
+        return Strings.fromByteArray(contents);
+    }
+
+    public String toString()
+    {
+        return getString();
+    }
+
+    public final byte[] getOctets()
+    {
+        return Arrays.clone(contents);
+    }
+
+    final boolean encodeConstructed()
+    {
+        return false;
+    }
+
+    final int encodedLength(boolean withTag)
+    {
+        return ASN1OutputStream.getLengthOfEncodingDL(withTag, contents.length);
+    }
+
+    final void encode(ASN1OutputStream out, boolean withTag) throws IOException
+    {
+        out.writeEncodingDL(withTag, BERTags.VISIBLE_STRING, contents);
+    }
+
+    final boolean asn1Equals(ASN1Primitive other)
+    {
+        if (!(other instanceof ASN1VisibleString))
+        {
+            return false;
+        }
+
+        ASN1VisibleString that = (ASN1VisibleString)other;
+
+        return Arrays.areEqual(this.contents, that.contents);
+    }
+
+    public final int hashCode()
+    {
+        return Arrays.hashCode(contents);
+    }
+
+    static ASN1VisibleString createPrimitive(byte[] contents)
+    {
+        return new DERVisibleString(contents, false);
+    }
+}
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/BERApplicationSpecific.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/BERApplicationSpecific.java
deleted file mode 100644
index ae0d363..0000000
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/BERApplicationSpecific.java
+++ /dev/null
@@ -1,112 +0,0 @@
-/* GENERATED SOURCE. DO NOT MODIFY. */
-package com.android.org.bouncycastle.asn1;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-
-/**
- * An indefinite-length encoding version of an ASN.1 ApplicationSpecific object.
- * @hide This class is not part of the Android public SDK API
- */
-public class BERApplicationSpecific
-    extends ASN1ApplicationSpecific
-{
-    BERApplicationSpecific(
-        boolean isConstructed,
-        int tag,
-        byte[] octets)
-    {
-        super(isConstructed, tag, octets);
-    }
-
-    /**
-     * Create an application specific object with a tagging of explicit/constructed.
-     *
-     * @param tag the tag number for this object.
-     * @param object the object to be contained.
-     */
-    public BERApplicationSpecific(
-        int tag,
-        ASN1Encodable object)
-        throws IOException
-    {
-        this(true, tag, object);
-    }
-
-    /**
-     * Create an application specific object with the tagging style given by the value of constructed.
-     *
-     * @param constructed true if the object is constructed.
-     * @param tag the tag number for this object.
-     * @param object the object to be contained.
-     */
-    public BERApplicationSpecific(
-        boolean constructed,
-        int tag,
-        ASN1Encodable object)
-        throws IOException
-    {
-        super(constructed || object.toASN1Primitive().isConstructed(), tag, getEncoding(constructed, object));
-    }
-
-    private static byte[] getEncoding(boolean explicit, ASN1Encodable object)
-        throws IOException
-    {
-        byte[] data = object.toASN1Primitive().getEncoded(ASN1Encoding.BER);
-
-        if (explicit)
-        {
-            return data;
-        }
-        else
-        {
-            int lenBytes = getLengthOfHeader(data);
-            byte[] tmp = new byte[data.length - lenBytes];
-            System.arraycopy(data, lenBytes, tmp, 0, tmp.length);
-            return tmp;
-        }
-    }
-
-    /**
-     * Create an application specific object which is marked as constructed
-     *
-     * @param tagNo the tag number for this object.
-     * @param vec the objects making up the application specific object.
-     */
-    public BERApplicationSpecific(int tagNo, ASN1EncodableVector vec)
-    {
-        super(true, tagNo, getEncodedVector(vec));
-    }
-
-    private static byte[] getEncodedVector(ASN1EncodableVector vec)
-    {
-        ByteArrayOutputStream bOut = new ByteArrayOutputStream();
-
-        for (int i = 0; i != vec.size(); i++)
-        {
-            try
-            {
-                bOut.write(((ASN1Object)vec.get(i)).getEncoded(ASN1Encoding.BER));
-            }
-            catch (IOException e)
-            {
-                throw new ASN1ParsingException("malformed object: " + e, e);
-            }
-        }
-        return bOut.toByteArray();
-    }
-
-    /* (non-Javadoc)
-     * @see org.bouncycastle.asn1.ASN1Primitive#encode(org.bouncycastle.asn1.DEROutputStream)
-     */
-    void encode(ASN1OutputStream out, boolean withTag) throws IOException
-    {
-        int flags = BERTags.APPLICATION;
-        if (isConstructed)
-        {
-            flags |= BERTags.CONSTRUCTED;
-        }
-
-        out.writeEncodedIndef(withTag, flags, tag, octets);
-    }
-}
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/BERApplicationSpecificParser.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/BERApplicationSpecificParser.java
deleted file mode 100644
index 2474f18..0000000
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/BERApplicationSpecificParser.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/* GENERATED SOURCE. DO NOT MODIFY. */
-package com.android.org.bouncycastle.asn1;
-
-import java.io.IOException;
-
-/**
- * A parser for indefinite-length ASN.1 ApplicationSpecific objects.
- * @hide This class is not part of the Android public SDK API
- */
-public class BERApplicationSpecificParser
-    implements ASN1ApplicationSpecificParser
-{
-    private final int tag;
-    private final ASN1StreamParser parser;
-
-    BERApplicationSpecificParser(int tag, ASN1StreamParser parser)
-    {
-        this.tag = tag;
-        this.parser = parser;
-    }
-
-    /**
-     * Return the object contained in this application specific object,
-     * @return the contained object.
-     * @throws IOException if the underlying stream cannot be read, or does not contain an ASN.1 encoding.
-     */
-    public ASN1Encodable readObject()
-        throws IOException
-    {
-        return parser.readObject();
-    }
-
-    /**
-     * Return an in-memory, encodable, representation of the application specific object.
-     *
-     * @return a BERApplicationSpecific.
-     * @throws IOException if there is an issue loading the data.
-     */
-    public ASN1Primitive getLoadedObject()
-        throws IOException
-    {
-         return new BERApplicationSpecific(tag, parser.readVector());
-    }
-
-    /**
-     * Return a BERApplicationSpecific representing this parser and its contents.
-     *
-     * @return a BERApplicationSpecific
-     */
-    public ASN1Primitive toASN1Primitive()
-    {
-        try
-        {
-            return getLoadedObject();
-        }
-        catch (IOException e)
-        {
-            throw new ASN1ParsingException(e.getMessage(), e);
-        }
-    }
-}
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/BERBitString.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/BERBitString.java
new file mode 100644
index 0000000..e1e7a15
--- /dev/null
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/BERBitString.java
@@ -0,0 +1,192 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.org.bouncycastle.asn1;
+
+import java.io.IOException;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+public class BERBitString
+    extends ASN1BitString
+{
+    private static final int DEFAULT_SEGMENT_LIMIT = 1000;
+
+    private final int segmentLimit;
+    private final ASN1BitString[] elements;
+
+    /**
+     * Convert a vector of bit strings into a single bit string
+     */
+    static byte[] flattenBitStrings(ASN1BitString[] bitStrings)
+    {
+        int count = bitStrings.length;
+        switch (count)
+        {
+        case 0:
+            // No bits
+            return new byte[]{ 0 };
+        case 1:
+            return bitStrings[0].contents;
+        default:
+        {
+            int last = count - 1, totalLength = 0;
+            for (int i = 0; i < last; ++i)
+            {
+                byte[] elementContents = bitStrings[i].contents;
+                if (elementContents[0] != 0)
+                {
+                    throw new IllegalArgumentException("only the last nested bitstring can have padding");
+                }
+
+                totalLength += elementContents.length - 1;
+            }
+
+            // Last one can have padding
+            byte[] lastElementContents = bitStrings[last].contents;
+            byte padBits = lastElementContents[0];
+            totalLength += lastElementContents.length;
+
+            byte[] contents = new byte[totalLength];
+            contents[0] = padBits;
+
+            int pos = 1;
+            for (int i = 0; i < count; ++i)
+            {
+                byte[] elementContents = bitStrings[i].contents;
+                int length = elementContents.length - 1;
+                System.arraycopy(elementContents, 1, contents, pos, length);
+                pos += length;
+            }
+
+//            assert pos == totalLength;
+            return contents;
+        }
+        }
+    }
+    
+    public BERBitString(byte[] data)
+    {
+        this(data, 0);
+    }
+
+    public BERBitString(byte data, int padBits)
+    {
+        super(data, padBits);
+        this.elements = null;
+        this.segmentLimit = DEFAULT_SEGMENT_LIMIT;
+    }
+
+    public BERBitString(byte[] data, int padBits)
+    {
+        this(data, padBits, DEFAULT_SEGMENT_LIMIT);
+    }
+
+    public BERBitString(byte[] data, int padBits, int segmentLimit)
+    {
+        super(data, padBits);
+        this.elements = null;
+        this.segmentLimit = segmentLimit;
+    }
+
+    public BERBitString(ASN1Encodable obj) throws IOException
+    {
+        this(obj.toASN1Primitive().getEncoded(ASN1Encoding.DER), 0);
+    }
+
+    public BERBitString(ASN1BitString[] elements)
+    {
+        this(elements, DEFAULT_SEGMENT_LIMIT);
+    }
+
+    public BERBitString(ASN1BitString[] elements, int segmentLimit)
+    {
+        super(flattenBitStrings(elements), false);
+        this.elements = elements;
+        this.segmentLimit = segmentLimit;
+    }    
+
+    BERBitString(byte[] contents, boolean check)
+    {
+        super(contents, check);
+        this.elements = null;
+        this.segmentLimit = DEFAULT_SEGMENT_LIMIT;
+    }
+
+    boolean encodeConstructed()
+    {
+        return null != elements || contents.length > segmentLimit;
+    }
+
+    int encodedLength(boolean withTag)
+        throws IOException
+    {
+        if (!encodeConstructed())
+        {
+            return DLBitString.encodedLength(withTag, contents.length);
+        }
+
+        int totalLength = withTag ? 4 : 3;
+
+        if (null != elements)
+        {
+            for (int i = 0; i < elements.length; ++i)
+            {
+                totalLength += elements[i].encodedLength(true);
+            }
+        }
+        else if (contents.length < 2)
+        {
+            // No bits
+        }
+        else
+        {
+            int extraSegments = (contents.length - 2) / (segmentLimit - 1);
+            totalLength += extraSegments * DLBitString.encodedLength(true, segmentLimit);
+
+            int lastSegmentLength = contents.length - (extraSegments * (segmentLimit - 1));
+            totalLength += DLBitString.encodedLength(true, lastSegmentLength);
+        }
+
+        return totalLength;
+    }
+
+    void encode(ASN1OutputStream out, boolean withTag) throws IOException
+    {
+        if (!encodeConstructed())
+        {
+            DLBitString.encode(out, withTag, contents, 0, contents.length);
+            return;
+        }
+
+        out.writeIdentifier(withTag, BERTags.CONSTRUCTED | BERTags.BIT_STRING);
+        out.write(0x80);
+
+        if (null != elements)
+        {
+            out.writePrimitives(elements);
+        }
+        else if (contents.length < 2)
+        {
+            // No bits
+        }
+        else
+        {
+            byte pad = contents[0];
+            int length = contents.length;
+            int remaining = length - 1;
+            int segmentLength = segmentLimit - 1;
+
+            while (remaining > segmentLength)
+            {
+                DLBitString.encode(out, true, (byte)0, contents, length - remaining, segmentLength);
+                remaining -= segmentLength;
+            }
+
+            DLBitString.encode(out, true, pad, contents, length - remaining, remaining);
+        }
+
+        out.write(0x00);
+        out.write(0x00);
+    }
+}
+
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/BERBitStringParser.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/BERBitStringParser.java
new file mode 100644
index 0000000..ed4f54d
--- /dev/null
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/BERBitStringParser.java
@@ -0,0 +1,68 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.org.bouncycastle.asn1;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import com.android.org.bouncycastle.util.io.Streams;
+
+/**
+ * A parser for indefinite-length BIT STRINGs.
+ * 
+ * @deprecated Check for 'ASN1BitStringParser' instead 
+ * @hide This class is not part of the Android public SDK API
+ */
+public class BERBitStringParser
+    implements ASN1BitStringParser
+{
+    private ASN1StreamParser _parser;
+
+    private ConstructedBitStream _bitStream;
+
+    BERBitStringParser(
+        ASN1StreamParser parser)
+    {
+        _parser = parser;
+    }
+
+    public InputStream getOctetStream() throws IOException
+    {
+        return _bitStream = new ConstructedBitStream(_parser, true);
+    }
+
+    public InputStream getBitStream() throws IOException
+    {
+        return _bitStream = new ConstructedBitStream(_parser, false);
+    }
+
+    public int getPadBits()
+    {
+        return _bitStream.getPadBits();
+    }
+
+    public ASN1Primitive getLoadedObject()
+        throws IOException
+    {
+        return parse(_parser);
+    }
+
+    public ASN1Primitive toASN1Primitive()
+    {
+        try
+        {
+            return getLoadedObject();
+        }
+        catch (IOException e)
+        {
+            throw new ASN1ParsingException("IOException converting stream to byte array: " + e.getMessage(), e);
+        }
+    }
+
+    static BERBitString parse(ASN1StreamParser sp) throws IOException
+    {
+        ConstructedBitStream bitStream = new ConstructedBitStream(sp, false);
+        byte[] data = Streams.readAll(bitStream);
+        int padBits = bitStream.getPadBits();
+        return new BERBitString(data, padBits);
+    }
+}
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/BERConstructedOctetString.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/BERConstructedOctetString.java
deleted file mode 100644
index 23ffd48..0000000
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/BERConstructedOctetString.java
+++ /dev/null
@@ -1,146 +0,0 @@
-/* GENERATED SOURCE. DO NOT MODIFY. */
-package com.android.org.bouncycastle.asn1;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.util.Enumeration;
-import java.util.Vector;
-
-/**
- * @deprecated use BEROctetString
- * @hide This class is not part of the Android public SDK API
- */
-public class BERConstructedOctetString
-    extends BEROctetString
-{
-    private static final int MAX_LENGTH = 1000;
-
-    /**
-     * convert a vector of octet strings into a single byte string
-     */
-    static private byte[] toBytes(
-        Vector  octs)
-    {
-        ByteArrayOutputStream   bOut = new ByteArrayOutputStream();
-
-        for (int i = 0; i != octs.size(); i++)
-        {
-            try
-            {
-                DEROctetString  o = (DEROctetString)octs.elementAt(i);
-
-                bOut.write(o.getOctets());
-            }
-            catch (ClassCastException e)
-            {
-                throw new IllegalArgumentException(octs.elementAt(i).getClass().getName() + " found in input should only contain DEROctetString");
-            }
-            catch (IOException e)
-            {
-                throw new IllegalArgumentException("exception converting octets " + e.toString());
-            }
-        }
-
-        return bOut.toByteArray();
-    }
-
-    private Vector  octs;
-
-    /**
-     * @param string the octets making up the octet string.
-     */
-    public BERConstructedOctetString(
-        byte[]  string)
-    {
-        super(string);
-    }
-
-    public BERConstructedOctetString(
-        Vector  octs)
-    {
-        super(toBytes(octs));
-
-        this.octs = octs;
-    }
-
-    public BERConstructedOctetString(
-        ASN1Primitive  obj)
-    {
-        super(toByteArray(obj));
-    }
-
-    private static byte[] toByteArray(ASN1Primitive obj)
-    {
-        try
-        {
-            return obj.getEncoded();
-        }
-        catch (IOException e)
-        {
-            throw new IllegalArgumentException("Unable to encode object");
-        }
-    }
-
-    public BERConstructedOctetString(
-        ASN1Encodable  obj)
-    {
-        this(obj.toASN1Primitive());
-    }
-
-    public byte[] getOctets()
-    {
-        return string;
-    }
-
-    /**
-     * return the DER octets that make up this string.
-     */
-    public Enumeration getObjects()
-    {
-        if (octs == null)
-        {
-            return generateOcts().elements();
-        }
-
-        return octs.elements();
-    }
-
-    private Vector generateOcts() 
-    { 
-        Vector vec = new Vector(); 
-        for (int i = 0; i < string.length; i += MAX_LENGTH) 
-        { 
-            int end; 
-
-            if (i + MAX_LENGTH > string.length) 
-            { 
-                end = string.length; 
-            } 
-            else 
-            { 
-                end = i + MAX_LENGTH; 
-            } 
-
-            byte[] nStr = new byte[end - i]; 
-
-            System.arraycopy(string, i, nStr, 0, nStr.length); 
-
-            vec.addElement(new DEROctetString(nStr)); 
-         } 
-        
-         return vec; 
-    }
-
-    public static BEROctetString fromSequence(ASN1Sequence seq)
-    {
-        Vector      v = new Vector();
-        Enumeration e = seq.getObjects();
-
-        while (e.hasMoreElements())
-        {
-            v.addElement(e.nextElement());
-        }
-
-        return new BERConstructedOctetString(v);
-    }
-}
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/BERGenerator.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/BERGenerator.java
index af27f8a..d5e0b61 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/BERGenerator.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/BERGenerator.java
@@ -8,7 +8,7 @@
  * Base class for generators for indefinite-length structures.
  * @hide This class is not part of the Android public SDK API
  */
-public class BERGenerator
+public abstract class BERGenerator
     extends ASN1Generator
 {
     private boolean _tagged = false;
@@ -45,7 +45,7 @@
     {
         if (_tagged)
         {
-            int tagNum = _tagNo | BERTags.TAGGED;
+            int tagNum = _tagNo | BERTags.CONTEXT_SPECIFIC;
 
             if (_isExplicit)
             {
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/BEROctetString.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/BEROctetString.java
index 04c1fad..63d88cd 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/BEROctetString.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/BEROctetString.java
@@ -1,10 +1,7 @@
 /* GENERATED SOURCE. DO NOT MODIFY. */
 package com.android.org.bouncycastle.asn1;
 
-import java.io.ByteArrayOutputStream;
 import java.io.IOException;
-import java.util.Enumeration;
-import java.util.NoSuchElementException;
 
 /**
  * ASN.1 OctetStrings, with indefinite length rules, and <i>constructed form</i> support.
@@ -24,172 +21,148 @@
 public class BEROctetString
     extends ASN1OctetString
 {
-    private static final int DEFAULT_CHUNK_SIZE = 1000;
+    private static final int DEFAULT_SEGMENT_LIMIT = 1000;
 
-    private final int chunkSize;
-    private final ASN1OctetString[] octs;
+    private final int segmentLimit;
+    private final ASN1OctetString[] elements;
 
     /**
      * Convert a vector of octet strings into a single byte string
      */
-    static private byte[] toBytes(
-        ASN1OctetString[]  octs)
+    static byte[] flattenOctetStrings(ASN1OctetString[] octetStrings)
     {
-        ByteArrayOutputStream bOut = new ByteArrayOutputStream();
-
-        for (int i = 0; i != octs.length; i++)
+        int count = octetStrings.length;
+        switch (count)
         {
-            try
+        case 0:
+            return EMPTY_OCTETS;
+        case 1:
+            return octetStrings[0].string;
+        default:
+        {
+            int totalOctets = 0;
+            for (int i = 0; i < count; ++i)
             {
-                bOut.write(octs[i].getOctets());
+                totalOctets += octetStrings[i].string.length;
             }
-            catch (IOException e)
+
+            byte[] string = new byte[totalOctets];
+            for (int i = 0, pos = 0; i < count; ++i)
             {
-                throw new IllegalArgumentException("exception converting octets " + e.toString());
+                byte[] octets = octetStrings[i].string;
+                System.arraycopy(octets, 0, string, pos, octets.length);
+                pos += octets.length;
             }
+
+//            assert pos == totalOctets;
+            return string;
         }
-
-        return bOut.toByteArray();
+        }
     }
 
     /**
      * Create an OCTET-STRING object from a byte[]
      * @param string the octets making up the octet string.
      */
-    public BEROctetString(
-        byte[] string)
+    public BEROctetString(byte[] string)
     {
-        this(string, DEFAULT_CHUNK_SIZE);
+        this(string, DEFAULT_SEGMENT_LIMIT);
     }
 
     /**
      * Multiple {@link ASN1OctetString} data blocks are input,
      * the result is <i>constructed form</i>.
      *
-     * @param octs an array of OCTET STRING to construct the BER OCTET STRING from.
+     * @param elements an array of OCTET STRING to construct the BER OCTET STRING from.
      */
-    public BEROctetString(
-        ASN1OctetString[] octs)
+    public BEROctetString(ASN1OctetString[] elements)
     {
-        this(octs, DEFAULT_CHUNK_SIZE);
+        this(elements, DEFAULT_SEGMENT_LIMIT);
     }
 
     /**
      * Create an OCTET-STRING object from a byte[]
      * @param string the octets making up the octet string.
-     * @param chunkSize the number of octets stored in each DER encoded component OCTET STRING.
+     * @param segmentLimit the number of octets stored in each DER encoded component OCTET STRING.
      */
-    public BEROctetString(
-        byte[] string,
-        int    chunkSize)
+    public BEROctetString(byte[] string, int segmentLimit)
     {
-        this(string, null, chunkSize);
+        this(string, null, segmentLimit);
     }
 
     /**
      * Multiple {@link ASN1OctetString} data blocks are input,
      * the result is <i>constructed form</i>.
      *
-     * @param octs an array of OCTET STRING to construct the BER OCTET STRING from.
-     * @param chunkSize the number of octets stored in each DER encoded component OCTET STRING.
+     * @param elements an array of OCTET STRING to construct the BER OCTET STRING from.
+     * @param segmentLimit the number of octets stored in each DER encoded component OCTET STRING.
      */
-    public BEROctetString(
-        ASN1OctetString[] octs,
-        int chunkSize)
+    public BEROctetString(ASN1OctetString[] elements, int segmentLimit)
     {
-        this(toBytes(octs), octs, chunkSize);
+        this(flattenOctetStrings(elements), elements, segmentLimit);
     }
 
-    private BEROctetString(byte[] string, ASN1OctetString[] octs, int chunkSize)
+    private BEROctetString(byte[] string, ASN1OctetString[] elements, int segmentLimit)
     {
         super(string);
-        this.octs = octs;
-        this.chunkSize = chunkSize;
+        this.elements = elements;
+        this.segmentLimit = segmentLimit;
     }
 
-    /**
-     * Return the OCTET STRINGs that make up this string.
-     *
-     * @return an Enumeration of the component OCTET STRINGs.
-     */
-    public Enumeration getObjects()
-    {
-        if (octs == null)
-        {
-            return new Enumeration()
-            {
-                int pos = 0;
-
-                public boolean hasMoreElements()
-                {
-                    return pos < string.length;
-                }
-
-                public Object nextElement()
-                {
-                    if (pos < string.length)
-                    {
-                        int length = Math.min(string.length - pos, chunkSize);
-                        byte[] chunk = new byte[length];
-                        System.arraycopy(string, pos, chunk, 0, length);
-                        pos += length;
-                        return new DEROctetString(chunk);
-                    }
-                    throw new NoSuchElementException();
-                }
-            };
-        }
-
-        return new Enumeration()
-        {
-            int counter = 0;
-
-            public boolean hasMoreElements()
-            {
-                return counter < octs.length;
-            }
-
-            public Object nextElement()
-            {
-                if (counter < octs.length)
-                {
-                    return octs[counter++];
-                }
-                throw new NoSuchElementException();
-            }
-        };
-    }
-
-    boolean isConstructed()
+    boolean encodeConstructed()
     {
         return true;
     }
 
-    int encodedLength()
+    int encodedLength(boolean withTag)
         throws IOException
     {
-        int length = 0;
-        for (Enumeration e = getObjects(); e.hasMoreElements();)
+        int totalLength = withTag ? 4 : 3;
+
+        if (null != elements)
         {
-            length += ((ASN1Encodable)e.nextElement()).toASN1Primitive().encodedLength();
+            for (int i = 0; i < elements.length; ++i)
+            {
+                totalLength += elements[i].encodedLength(true);
+            }
+        }
+        else
+        {
+            int fullSegments = string.length / segmentLimit;
+            totalLength += fullSegments * DEROctetString.encodedLength(true, segmentLimit);
+
+            int lastSegmentLength = string.length - (fullSegments * segmentLimit);
+            if (lastSegmentLength > 0)
+            {
+                totalLength += DEROctetString.encodedLength(true, lastSegmentLength);
+            }
         }
 
-        return 2 + length + 2;
+        return totalLength;
     }
 
     void encode(ASN1OutputStream out, boolean withTag) throws IOException
     {
-        out.writeEncodedIndef(withTag, BERTags.CONSTRUCTED | BERTags.OCTET_STRING,  getObjects());
-    }
+        out.writeIdentifier(withTag, BERTags.CONSTRUCTED | BERTags.OCTET_STRING);
+        out.write(0x80);
 
-    static BEROctetString fromSequence(ASN1Sequence seq)
-    {
-        int count = seq.size();
-        ASN1OctetString[] v = new ASN1OctetString[count];
-        for (int i = 0; i < count; ++i)
+        if (null != elements)
         {
-            v[i] = ASN1OctetString.getInstance(seq.getObjectAt(i));
+            out.writePrimitives(elements);
         }
-        return new BEROctetString(v);
+        else
+        {
+            int pos = 0;
+            while (pos < string.length)
+            {
+                int segmentLength = Math.min(string.length - pos, segmentLimit);
+                DEROctetString.encode(out, true, string, pos, segmentLength);
+                pos += segmentLength;
+            }
+        }
+
+        out.write(0x00);
+        out.write(0x00);
     }
 }
+
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/BEROctetStringGenerator.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/BEROctetStringGenerator.java
index 6d21f9c..f7a9852 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/BEROctetStringGenerator.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/BEROctetStringGenerator.java
@@ -98,23 +98,32 @@
 
         public void write(byte[] b, int off, int len) throws IOException
         {
-            while (len > 0)
+            int bufLen = _buf.length;
+            int available = bufLen - _off;
+            if (len < available)
             {
-                int numToCopy = Math.min(len, _buf.length - _off);
-                System.arraycopy(b, off, _buf, _off, numToCopy);
-
-                _off += numToCopy;
-                if (_off < _buf.length)
-                {
-                    break;
-                }
-
-                DEROctetString.encode(_derOut, true, _buf, 0, _buf.length);
-                _off = 0;
-
-                off += numToCopy;
-                len -= numToCopy;
+                System.arraycopy(b, off, _buf, _off, len);
+                _off += len;
+                return;
             }
+
+            int count = 0;
+            if (_off > 0)
+            {
+                System.arraycopy(b, off, _buf, _off, available);
+                count += available;
+                DEROctetString.encode(_derOut, true, _buf, 0, bufLen);
+            }
+
+            int remaining;
+            while ((remaining = (len - count)) >= bufLen)
+            {
+                DEROctetString.encode(_derOut, true, b, off + count, bufLen);
+                count += bufLen;
+            }
+
+            System.arraycopy(b, off + count, _buf, 0, remaining);
+            this._off = remaining;
         }
 
         public void close()
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/BEROctetStringParser.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/BEROctetStringParser.java
index 674e488..4f2372e 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/BEROctetStringParser.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/BEROctetStringParser.java
@@ -8,6 +8,8 @@
 
 /**
  * A parser for indefinite-length OCTET STRINGs.
+ * 
+ * @deprecated Check for 'ASN1OctetStringParser' instead 
  * @hide This class is not part of the Android public SDK API
  */
 public class BEROctetStringParser
@@ -40,7 +42,7 @@
     public ASN1Primitive getLoadedObject()
         throws IOException
     {
-        return new BEROctetString(Streams.readAll(getOctetStream()));
+        return parse(_parser);
     }
 
     /**
@@ -59,4 +61,9 @@
             throw new ASN1ParsingException("IOException converting stream to byte array: " + e.getMessage(), e);
         }
     }
+
+    static BEROctetString parse(ASN1StreamParser sp) throws IOException
+    {
+        return new BEROctetString(Streams.readAll(new ConstructedOctetStream(sp)));
+    }
 }
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/BERSequence.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/BERSequence.java
index c4531d2..f0cf781 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/BERSequence.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/BERSequence.java
@@ -46,22 +46,42 @@
         super(elements);
     }
 
-    int encodedLength() throws IOException
+    int encodedLength(boolean withTag) throws IOException
     {
-        int count = elements.length;
-        int totalLength = 0;
+        int totalLength = withTag ? 4 : 3;
 
-        for (int i = 0; i < count; ++i)
+        for (int i = 0, count = elements.length; i < count; ++i)
         {
             ASN1Primitive p = elements[i].toASN1Primitive();
-            totalLength += p.encodedLength();
+            totalLength += p.encodedLength(true);
         }
 
-        return 2 + totalLength + 2;
+        return totalLength;
     }
 
     void encode(ASN1OutputStream out, boolean withTag) throws IOException
     {
-        out.writeEncodedIndef(withTag, BERTags.SEQUENCE | BERTags.CONSTRUCTED, elements);
+        out.writeEncodingIL(withTag, BERTags.CONSTRUCTED | BERTags.SEQUENCE, elements);
+    }
+
+    ASN1BitString toASN1BitString()
+    {
+        return new BERBitString(getConstructedBitStrings());
+    }
+
+    ASN1External toASN1External()
+    {
+        // TODO There is currently no BERExternal class
+        return ((ASN1Sequence)toDLObject()).toASN1External();
+    }
+
+    ASN1OctetString toASN1OctetString()
+    {
+        return new BEROctetString(getConstructedOctetStrings());
+    }
+
+    ASN1Set toASN1Set()
+    {
+        return new BERSet(false, toArrayInternal());
     }
 }
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/BERSequenceParser.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/BERSequenceParser.java
index 4963546..48475b5 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/BERSequenceParser.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/BERSequenceParser.java
@@ -5,6 +5,8 @@
 
 /**
  * Parser for indefinite-length SEQUENCEs.
+ * 
+ * @deprecated Check for 'ASN1SequenceParser' instead 
  * @hide This class is not part of the Android public SDK API
  */
 public class BERSequenceParser
@@ -38,7 +40,7 @@
     public ASN1Primitive getLoadedObject()
         throws IOException
     {
-        return new BERSequence(_parser.readVector());
+        return parse(_parser);
     }
 
     /**
@@ -57,4 +59,9 @@
             throw new IllegalStateException(e.getMessage());
         }
     }
+
+    static BERSequence parse(ASN1StreamParser sp) throws IOException
+    {
+        return new BERSequence(sp.readVector());
+    }
 }
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/BERSet.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/BERSet.java
index 041bdd2..3f299ed 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/BERSet.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/BERSet.java
@@ -61,22 +61,21 @@
         super(isSorted, elements);
     }
 
-    int encodedLength() throws IOException
+    int encodedLength(boolean withTag) throws IOException
     {
-        int count = elements.length;
-        int totalLength = 0;
+        int totalLength = withTag ? 4 : 3;
 
-        for (int i = 0; i < count; ++i)
+        for (int i = 0, count = elements.length; i < count; ++i)
         {
             ASN1Primitive p = elements[i].toASN1Primitive();
-            totalLength += p.encodedLength();
+            totalLength += p.encodedLength(true);
         }
 
-        return 2 + totalLength + 2;
+        return totalLength;
     }
 
     void encode(ASN1OutputStream out, boolean withTag) throws IOException
     {
-        out.writeEncodedIndef(withTag, BERTags.SET | BERTags.CONSTRUCTED, elements);
+        out.writeEncodingIL(withTag, BERTags.CONSTRUCTED | BERTags.SET, elements);
     }
 }
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/BERSetParser.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/BERSetParser.java
index 8362aa1..fa833cb 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/BERSetParser.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/BERSetParser.java
@@ -5,6 +5,8 @@
 
 /**
  * Parser for indefinite-length SETs.
+ * 
+ * @deprecated Check for 'ASN1SetParser' instead 
  * @hide This class is not part of the Android public SDK API
  */
 public class BERSetParser
@@ -38,7 +40,7 @@
     public ASN1Primitive getLoadedObject()
         throws IOException
     {
-        return new BERSet(_parser.readVector());
+        return parse(_parser);
     }
 
     /**
@@ -57,4 +59,9 @@
             throw new ASN1ParsingException(e.getMessage(), e);
         }
     }
-}
\ No newline at end of file
+
+    static BERSet parse(ASN1StreamParser sp) throws IOException
+    {
+        return new BERSet(sp.readVector());
+    }
+}
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/BERTaggedObject.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/BERTaggedObject.java
index 0bdfc35..7cd4e83 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/BERTaggedObject.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/BERTaggedObject.java
@@ -2,7 +2,6 @@
 package com.android.org.bouncycastle.asn1;
 
 import java.io.IOException;
-import java.util.Enumeration;
 
 /**
  * BER TaggedObject - in ASN.1 notation this is any object preceded by
@@ -17,124 +16,96 @@
      * @param tagNo the tag number for this object.
      * @param obj the tagged object.
      */
-    public BERTaggedObject(
-        int             tagNo,
-        ASN1Encodable    obj)
+    public BERTaggedObject(int tagNo, ASN1Encodable obj)
     {
         super(true, tagNo, obj);
     }
 
+    public BERTaggedObject(int tagClass, int tagNo, ASN1Encodable obj)
+    {
+        super(true, tagClass, tagNo, obj);
+    }
+
     /**
      * @param explicit true if an explicitly tagged object.
      * @param tagNo the tag number for this object.
      * @param obj the tagged object.
      */
-    public BERTaggedObject(
-        boolean         explicit,
-        int             tagNo,
-        ASN1Encodable    obj)
+    public BERTaggedObject(boolean explicit, int tagNo, ASN1Encodable obj)
     {
         super(explicit, tagNo, obj);
     }
 
-    /**
-     * create an implicitly tagged object that contains a zero
-     * length sequence.
-     */
-    public BERTaggedObject(
-        int             tagNo)
+    public BERTaggedObject(boolean explicit, int tagClass, int tagNo, ASN1Encodable obj)
     {
-        super(false, tagNo, new BERSequence());
+        super(explicit, tagClass, tagNo, obj);
     }
 
-    boolean isConstructed()
+    BERTaggedObject(int explicitness, int tagClass, int tagNo, ASN1Encodable obj)
     {
-        return explicit || obj.toASN1Primitive().isConstructed();
+        super(explicitness, tagClass, tagNo, obj);
     }
 
-    int encodedLength()
-        throws IOException
+    boolean encodeConstructed()
+    {
+        return isExplicit() || obj.toASN1Primitive().encodeConstructed();
+    }
+
+    int encodedLength(boolean withTag) throws IOException
     {
         ASN1Primitive primitive = obj.toASN1Primitive();
-        int length = primitive.encodedLength();
+        boolean explicit = isExplicit();
+
+        int length = primitive.encodedLength(explicit);
 
         if (explicit)
         {
-            return StreamUtil.calculateTagLength(tagNo) + StreamUtil.calculateBodyLength(length) + length;
+            length += 3;
         }
-        else
-        {
-            // header length already in calculation
-            length = length - 1;
 
-            return StreamUtil.calculateTagLength(tagNo) + length;
-        }
+        length += withTag ? ASN1OutputStream.getLengthOfIdentifier(tagNo) : 0;
+
+        return length;
     }
 
     void encode(ASN1OutputStream out, boolean withTag) throws IOException
     {
-        out.writeTag(withTag, BERTags.CONSTRUCTED | BERTags.TAGGED, tagNo);
-        out.write(0x80);
+//        assert out.getClass().isAssignableFrom(ASN1OutputStream.class);
 
-        if (!explicit)
+        ASN1Primitive primitive = obj.toASN1Primitive();
+        boolean explicit = isExplicit();
+
+        if (withTag)
         {
-            Enumeration e;
-            if (obj instanceof ASN1OctetString)
+            int flags = tagClass;
+            if (explicit || primitive.encodeConstructed())
             {
-                if (obj instanceof BEROctetString)
-                {
-                    e = ((BEROctetString)obj).getObjects();
-                }
-                else
-                {
-                    ASN1OctetString octs = (ASN1OctetString)obj;
-                    BEROctetString berO = new BEROctetString(octs.getOctets());
-                    e = berO.getObjects();
-                }
-            }
-            else if (obj instanceof ASN1Sequence)
-            {
-                e = ((ASN1Sequence)obj).getObjects();
-            }
-            else if (obj instanceof ASN1Set)
-            {
-                e = ((ASN1Set)obj).getObjects();
-            }
-            else
-            {
-                throw new ASN1Exception("not implemented: " + obj.getClass().getName());
+                flags |= BERTags.CONSTRUCTED;
             }
 
-            out.writeElements(e);
+            out.writeIdentifier(true, flags, tagNo);
+        }
+
+        if (explicit)
+        {
+            out.write(0x80);
+            primitive.encode(out, true);
+            out.write(0x00);
+            out.write(0x00);
         }
         else
         {
-            out.writePrimitive(obj.toASN1Primitive(), true);
+            primitive.encode(out, false);
         }
+    }
 
-        out.write(0x00);
-        out.write(0x00);
+    ASN1Sequence rebuildConstructed(ASN1Primitive primitive)
+    {
+        return new BERSequence(primitive);
+    }
 
-//        ASN1Primitive primitive = obj.toASN1Primitive();
-//
-//        int flags = BERTags.TAGGED;
-//        if (explicit || primitive.isConstructed())
-//        {
-//            flags |= BERTags.CONSTRUCTED;
-//        }
-//
-//        out.writeTag(withTag, flags, tagNo);
-//
-//        if (explicit)
-//        {
-//            out.write(0x80);
-//            out.writePrimitive(obj.toASN1Primitive(), true);
-//            out.write(0x00);
-//            out.write(0x00);
-//        }
-//        else
-//        {
-//            out.writePrimitive(obj.toASN1Primitive(), false);
-//        }
+    ASN1TaggedObject replaceTag(int tagClass, int tagNo)
+    {
+        return new BERTaggedObject(explicitness, tagClass, tagNo, obj);
     }
 }
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/BERTaggedObjectParser.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/BERTaggedObjectParser.java
index efc2691..7cd2a14 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/BERTaggedObjectParser.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/BERTaggedObjectParser.java
@@ -5,68 +5,49 @@
 
 /**
  * Parser for indefinite-length tagged objects.
- * @hide This class is not part of the Android public SDK API
  */
-public class BERTaggedObjectParser
+class BERTaggedObjectParser
     implements ASN1TaggedObjectParser
 {
-    private boolean _constructed;
-    private int _tagNumber;
-    private ASN1StreamParser _parser;
+    final int _tagClass;
+    final int _tagNo;
+    final ASN1StreamParser _parser;
 
-    BERTaggedObjectParser(
-        boolean             constructed,
-        int                 tagNumber,
-        ASN1StreamParser    parser)
+    BERTaggedObjectParser(int tagClass, int tagNo, ASN1StreamParser parser)
     {
-        _constructed = constructed;
-        _tagNumber = tagNumber;
+        _tagClass = tagClass;
+        _tagNo = tagNo;
         _parser = parser;
     }
 
-    /**
-     * Return true if this tagged object is marked as constructed.
-     *
-     * @return true if constructed, false otherwise.
-     */
-    public boolean isConstructed()
+    public int getTagClass()
     {
-        return _constructed;
+        return _tagClass;
     }
 
-    /**
-     * Return the tag number associated with this object.
-     *
-     * @return the tag number.
-     */
     public int getTagNo()
     {
-        return _tagNumber;
+        return _tagNo;
     }
 
-    /**
-     * Return an object parser for the contents of this tagged object.
-     *
-     * @param tag the actual tag number of the object (needed if implicit).
-     * @param isExplicit true if the contained object was explicitly tagged, false if implicit.
-     * @return an ASN.1 encodable object parser.
-     * @throws IOException if there is an issue building the object parser from the stream.
-     */
-    public ASN1Encodable getObjectParser(
-        int     tag,
-        boolean isExplicit)
-        throws IOException
+    public boolean hasContextTag()
     {
-        if (isExplicit)
-        {
-            if (!_constructed)
-            {
-                throw new IOException("Explicit tags must be constructed (see X.690 8.14.2)");
-            }
-            return _parser.readObject();
-        }
+        return this._tagClass == BERTags.CONTEXT_SPECIFIC;
+    }
 
-        return _parser.readImplicit(_constructed, tag);
+    public boolean hasContextTag(int tagNo)
+    {
+        return this._tagClass == BERTags.CONTEXT_SPECIFIC && this._tagNo == tagNo;
+    }
+
+    public boolean hasTag(int tagClass, int tagNo)
+    {
+        return this._tagClass == tagClass && this._tagNo == tagNo;
+    }
+
+    public boolean hasTagClass(int tagClass)
+    {
+        return this._tagClass == tagClass;
     }
 
     /**
@@ -78,7 +59,32 @@
     public ASN1Primitive getLoadedObject()
         throws IOException
     {
-        return _parser.readTaggedObject(_constructed, _tagNumber);
+        return _parser.loadTaggedIL(_tagClass, _tagNo);
+    }
+
+    public ASN1Encodable parseBaseUniversal(boolean declaredExplicit, int baseTagNo) throws IOException
+    {
+        if (declaredExplicit)
+        {
+            return _parser.parseObject(baseTagNo);
+        }
+
+        return _parser.parseImplicitConstructedIL(baseTagNo);
+    }
+
+    public ASN1Encodable parseExplicitBaseObject() throws IOException
+    {
+        return _parser.readObject();
+    }
+
+    public ASN1TaggedObjectParser parseExplicitBaseTagged() throws IOException
+    {
+        return _parser.parseTaggedObject();
+    }
+
+    public ASN1TaggedObjectParser parseImplicitBaseTagged(int baseTagClass, int baseTagNo) throws IOException
+    {
+        return new BERTaggedObjectParser(baseTagClass, baseTagNo, _parser);
     }
 
     /**
@@ -90,7 +96,7 @@
     {
         try
         {
-            return this.getLoadedObject();
+            return getLoadedObject();
         }
         catch (IOException e)
         {
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/BERTags.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/BERTags.java
index a8a9644..8531e69 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/BERTags.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/BERTags.java
@@ -6,20 +6,26 @@
  */
 public interface BERTags
 {
+    // 0x00: Reserved for use by the encoding rules
     public static final int BOOLEAN             = 0x01;
     public static final int INTEGER             = 0x02;
     public static final int BIT_STRING          = 0x03;
     public static final int OCTET_STRING        = 0x04;
     public static final int NULL                = 0x05;
     public static final int OBJECT_IDENTIFIER   = 0x06;
+    public static final int OBJECT_DESCRIPTOR   = 0x07;
     public static final int EXTERNAL            = 0x08;
+    public static final int REAL                = 0x09;
     public static final int ENUMERATED          = 0x0a; // decimal 10
+    public static final int EMBEDDED_PDV        = 0x0b; // decimal 11
+    public static final int UTF8_STRING         = 0x0c; // decimal 12
+    public static final int RELATIVE_OID        = 0x0d; // decimal 13
+    public static final int TIME                = 0x0e;
+    // 0x0f: Reserved for future editions of this Recommendation | International Standard
     public static final int SEQUENCE            = 0x10; // decimal 16
     public static final int SEQUENCE_OF         = 0x10; // for completeness - used to model a SEQUENCE of the same type.
     public static final int SET                 = 0x11; // decimal 17
     public static final int SET_OF              = 0x11; // for completeness - used to model a SET of the same type.
-
-
     public static final int NUMERIC_STRING      = 0x12; // decimal 18
     public static final int PRINTABLE_STRING    = 0x13; // decimal 19
     public static final int T61_STRING          = 0x14; // decimal 20
@@ -31,10 +37,23 @@
     public static final int VISIBLE_STRING      = 0x1a; // decimal 26
     public static final int GENERAL_STRING      = 0x1b; // decimal 27
     public static final int UNIVERSAL_STRING    = 0x1c; // decimal 28
+    public static final int UNRESTRICTED_STRING = 0x1d; // decimal 29
     public static final int BMP_STRING          = 0x1e; // decimal 30
-    public static final int UTF8_STRING         = 0x0c; // decimal 12
-    
+    public static final int DATE                = 0x1f;
+    public static final int TIME_OF_DAY         = 0x20;
+    public static final int DATE_TIME           = 0x21;
+    public static final int DURATION            = 0x22;
+    public static final int OBJECT_IDENTIFIER_IRI = 0x23;
+    public static final int RELATIVE_OID_IRI    = 0x24;
+    // 0x25..: Reserved for addenda to this Recommendation | International Standard
+
     public static final int CONSTRUCTED         = 0x20; // decimal 32
+
+    public static final int UNIVERSAL           = 0x00; // decimal 32
     public static final int APPLICATION         = 0x40; // decimal 64
-    public static final int TAGGED              = 0x80; // decimal 128
+    public static final int TAGGED              = 0x80; // decimal 128 - maybe should deprecate this.
+    public static final int CONTEXT_SPECIFIC    = 0x80; // decimal 128
+    public static final int PRIVATE             = 0xC0; // decimal 192
+
+    public static final int FLAGS               = 0xE0;
 }
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ConstructedBitStream.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ConstructedBitStream.java
new file mode 100644
index 0000000..ea06af1
--- /dev/null
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ConstructedBitStream.java
@@ -0,0 +1,146 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.org.bouncycastle.asn1;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+class ConstructedBitStream
+    extends InputStream
+{
+    private final ASN1StreamParser _parser;
+    private final boolean _octetAligned;
+
+    private boolean                _first = true;
+    private int                    _padBits = 0;
+
+    private ASN1BitStringParser    _currentParser;
+    private InputStream            _currentStream;
+
+    ConstructedBitStream(ASN1StreamParser parser, boolean octetAligned)
+    {
+        _parser = parser;
+        _octetAligned = octetAligned;
+    }
+
+    int getPadBits()
+    {
+        return _padBits;
+    }
+
+    public int read(byte[] b, int off, int len) throws IOException
+    {
+        if (_currentStream == null)
+        {
+            if (!_first)
+            {
+                return -1;
+            }
+
+            _currentParser = getNextParser();
+            if (_currentParser == null)
+            {
+                return -1;
+            }
+
+            _first = false;
+            _currentStream = _currentParser.getBitStream();
+            
+        }
+
+        int totalRead = 0;
+
+        for (;;)
+        {
+            int numRead = _currentStream.read(b, off + totalRead, len - totalRead);
+
+            if (numRead >= 0)
+            {
+                totalRead += numRead;
+
+                if (totalRead == len)
+                {
+                    return totalRead;
+                }
+            }
+            else
+            {
+                _padBits = _currentParser.getPadBits();
+                _currentParser = getNextParser();
+                if (_currentParser == null)
+                {
+                    _currentStream = null;
+                    return totalRead < 1 ? -1 : totalRead;
+                }
+
+                _currentStream = _currentParser.getBitStream();
+            }
+        }
+    }
+
+    public int read()
+        throws IOException
+    {
+        if (_currentStream == null)
+        {
+            if (!_first)
+            {
+                return -1;
+            }
+
+            _currentParser = getNextParser();
+            if (_currentParser == null)
+            {
+                return -1;
+            }
+
+            _first = false;
+            _currentStream = _currentParser.getBitStream();
+        }
+
+        for (;;)
+        {
+            int b = _currentStream.read();
+
+            if (b >= 0)
+            {
+                return b;
+            }
+
+            _padBits = _currentParser.getPadBits();
+            _currentParser = getNextParser();
+            if (_currentParser == null)
+            {
+                _currentStream = null;
+                return -1;
+            }
+
+            _currentStream = _currentParser.getBitStream();
+        }
+    }
+
+    private ASN1BitStringParser getNextParser() throws IOException
+    {
+        ASN1Encodable asn1Obj = _parser.readObject();
+        if (asn1Obj == null)
+        {
+            if (_octetAligned && _padBits != 0)
+            {
+                throw new IOException("expected octet-aligned bitstring, but found padBits: " + _padBits);
+            }
+
+            return null;
+        }
+
+        if (asn1Obj instanceof ASN1BitStringParser)
+        {
+            if (_padBits != 0)
+            {
+                throw new IOException("only the last nested bitstring can have padding");
+            }
+
+            return (ASN1BitStringParser)asn1Obj;
+        }
+
+        throw new IOException("unknown object encountered: " + asn1Obj.getClass());
+    }
+}
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DERApplicationSpecific.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DERApplicationSpecific.java
deleted file mode 100644
index b77373d..0000000
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DERApplicationSpecific.java
+++ /dev/null
@@ -1,126 +0,0 @@
-/* GENERATED SOURCE. DO NOT MODIFY. */
-package com.android.org.bouncycastle.asn1;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-
-/**
- * A DER encoding version of an application specific object.
- * @hide This class is not part of the Android public SDK API
- */
-public class DERApplicationSpecific 
-    extends ASN1ApplicationSpecific
-{
-    DERApplicationSpecific(
-        boolean isConstructed,
-        int     tag,
-        byte[]  octets)
-    {
-        super(isConstructed, tag, octets);
-    }
-
-    /**
-     * Create an application specific object from the passed in data. This will assume
-     * the data does not represent a constructed object.
-     *
-     * @param tag the tag number for this object.
-     * @param octets the encoding of the object's body.
-     */
-    public DERApplicationSpecific(
-        int    tag,
-        byte[] octets)
-    {
-        this(false, tag, octets);
-    }
-
-    /**
-     * Create an application specific object with a tagging of explicit/constructed.
-     *
-     * @param tag the tag number for this object.
-     * @param object the object to be contained.
-     */
-    public DERApplicationSpecific(
-        int           tag,
-        ASN1Encodable object)
-        throws IOException 
-    {
-        this(true, tag, object);
-    }
-
-    /**
-     * Create an application specific object with the tagging style given by the value of constructed.
-     *
-     * @param constructed true if the object is constructed.
-     * @param tag the tag number for this object.
-     * @param object the object to be contained.
-     */
-    public DERApplicationSpecific(
-        boolean      constructed,
-        int          tag,
-        ASN1Encodable object)
-        throws IOException
-    {
-        super(constructed || object.toASN1Primitive().isConstructed(), tag, getEncoding(constructed, object));
-    }
-
-    private static byte[] getEncoding(boolean explicit, ASN1Encodable object)
-        throws IOException
-    {
-        byte[] data = object.toASN1Primitive().getEncoded(ASN1Encoding.DER);
-
-        if (explicit)
-        {
-            return data;
-        }
-        else
-        {
-            int lenBytes = getLengthOfHeader(data);
-            byte[] tmp = new byte[data.length - lenBytes];
-            System.arraycopy(data, lenBytes, tmp, 0, tmp.length);
-            return tmp;
-        }
-    }
-
-    /**
-     * Create an application specific object which is marked as constructed
-     *
-     * @param tagNo the tag number for this object.
-     * @param vec the objects making up the application specific object.
-     */
-    public DERApplicationSpecific(int tagNo, ASN1EncodableVector vec)
-    {
-        super(true, tagNo, getEncodedVector(vec));
-    }
-
-    private static byte[] getEncodedVector(ASN1EncodableVector vec)
-    {
-        ByteArrayOutputStream bOut = new ByteArrayOutputStream();
-
-        for (int i = 0; i != vec.size(); i++)
-        {
-            try
-            {
-                bOut.write(((ASN1Object)vec.get(i)).getEncoded(ASN1Encoding.DER));
-            }
-            catch (IOException e)
-            {
-                throw new ASN1ParsingException("malformed object: " + e, e);
-            }
-        }
-        return bOut.toByteArray();
-    }
-
-    /* (non-Javadoc)
-     * @see org.bouncycastle.asn1.ASN1Primitive#encode(org.bouncycastle.asn1.DEROutputStream)
-     */
-    void encode(ASN1OutputStream out, boolean withTag) throws IOException
-    {
-        int flags = BERTags.APPLICATION;
-        if (isConstructed)
-        {
-            flags |= BERTags.CONSTRUCTED;
-        }
-
-        out.writeEncoded(withTag, flags, tag, octets);
-    }
-}
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DERBMPString.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DERBMPString.java
index 3803c2b..b5cc119 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DERBMPString.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DERBMPString.java
@@ -1,10 +1,6 @@
 /* GENERATED SOURCE. DO NOT MODIFY. */
 package com.android.org.bouncycastle.asn1;
 
-import java.io.IOException;
-
-import com.android.org.bouncycastle.util.Arrays;
-
 /**
  * DER BMPString object encodes BMP (<i>Basic Multilingual Plane</i>) subset
  * (aka UCS-2) of UNICODE (ISO 10646) characters in codepoints 0 to 65535.
@@ -15,203 +11,28 @@
  * @hide This class is not part of the Android public SDK API
  */
 public class DERBMPString
-    extends ASN1Primitive
-    implements ASN1String
+    extends ASN1BMPString
 {
-    private final char[]  string;
-
-    /**
-     * Return a BMP String from the given object.
-     *
-     * @param obj the object we want converted.
-     * @exception IllegalArgumentException if the object cannot be converted.
-     * @return a DERBMPString instance, or null.
-     */
-    public static DERBMPString getInstance(
-        Object  obj)
-    {
-        if (obj == null || obj instanceof DERBMPString)
-        {
-            return (DERBMPString)obj;
-        }
-
-        if (obj instanceof byte[])
-        {
-            try
-            {
-                return (DERBMPString)fromByteArray((byte[])obj);
-            }
-            catch (Exception e)
-            {
-                throw new IllegalArgumentException("encoding error in getInstance: " + e.toString());
-            }
-        }
-
-        throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
-    }
-
-    /**
-     * Return a BMP String from a tagged object.
-     *
-     * @param obj the tagged object holding the object we want
-     * @param explicit true if the object is meant to be explicitly
-     *              tagged false otherwise.
-     * @exception IllegalArgumentException if the tagged object cannot
-     *              be converted.
-     * @return a DERBMPString instance.
-     */
-    public static DERBMPString getInstance(
-        ASN1TaggedObject obj,
-        boolean          explicit)
-    {
-        ASN1Primitive o = obj.getObject();
-
-        if (explicit || o instanceof DERBMPString)
-        {
-            return getInstance(o);
-        }
-        else
-        {
-            return new DERBMPString(ASN1OctetString.getInstance(o).getOctets());
-        }
-    }
-
-    /**
-     * Basic constructor - byte encoded string.
-     * @param string the encoded BMP STRING to wrap.
-     */
-    DERBMPString(
-        byte[]   string)
-    {
-        if (string == null)
-        {
-            throw new NullPointerException("'string' cannot be null");
-        }
-
-        int byteLen = string.length;
-        if (0 != (byteLen & 1))
-        {
-            throw new IllegalArgumentException("malformed BMPString encoding encountered");
-        }
-
-        int charLen = byteLen / 2;
-        char[] cs = new char[charLen];
-
-        for (int i = 0; i != charLen; i++)
-        {
-            cs[i] = (char)((string[2 * i] << 8) | (string[2 * i + 1] & 0xff));
-        }
-
-        this.string = cs;
-    }
-
-    DERBMPString(char[] string)
-    {
-        if (string == null)
-        {
-            throw new NullPointerException("'string' cannot be null");
-        }
-
-        this.string = string;
-    }
-
     /**
      * Basic constructor
      * @param string a String to wrap as a BMP STRING.
      */
-    public DERBMPString(
-        String   string)
+    public DERBMPString(String string)
     {
-        if (string == null)
-        {
-            throw new NullPointerException("'string' cannot be null");
-        }
-
-        this.string = string.toCharArray();
+        super(string);
     }
 
-    public String getString()
+    /**
+     * Basic constructor - byte encoded string.
+     * @param contents the encoded BMP STRING to wrap.
+     */
+    DERBMPString(byte[] contents)
     {
-        return new String(string);
+        super(contents);
     }
 
-    public String toString()
+    DERBMPString(char[] string)
     {
-        return getString();
-    }
-
-    public int hashCode()
-    {
-        return Arrays.hashCode(string);
-    }
-
-    protected boolean asn1Equals(
-        ASN1Primitive o)
-    {
-        if (!(o instanceof DERBMPString))
-        {
-            return false;
-        }
-
-        DERBMPString  s = (DERBMPString)o;
-
-        return Arrays.areEqual(string, s.string);
-    }
-
-    boolean isConstructed()
-    {
-        return false;
-    }
-
-    int encodedLength()
-    {
-        return 1 + StreamUtil.calculateBodyLength(string.length * 2) + (string.length * 2);
-    }
-
-    void encode(
-        ASN1OutputStream out, boolean withTag)
-        throws IOException
-    {
-        int count = string.length;
-        if (withTag)
-        {
-            out.write(BERTags.BMP_STRING);
-        }
-        out.writeLength(count * 2);
-
-        byte[] buf = new byte[8];
-
-        int i = 0, limit = count & -4;
-        while (i < limit)
-        {
-            char c0 = string[i], c1 = string[i + 1], c2 = string[i + 2], c3 = string[i + 3];
-            i += 4;
-
-            buf[0] = (byte)(c0 >> 8);
-            buf[1] = (byte)c0;
-            buf[2] = (byte)(c1 >> 8);
-            buf[3] = (byte)c1;
-            buf[4] = (byte)(c2 >> 8);
-            buf[5] = (byte)c2;
-            buf[6] = (byte)(c3 >> 8);
-            buf[7] = (byte)c3;
-
-            out.write(buf, 0, 8);
-        }
-        if (i < count)
-        {
-            int bufPos = 0;
-            do
-            {
-                char c0 = string[i];
-                i += 1;
-
-                buf[bufPos++] = (byte)(c0 >> 8);
-                buf[bufPos++] = (byte)c0;
-            }
-            while (i < count);
-
-            out.write(buf, 0, bufPos);
-        }
+        super(string);
     }
 }
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DERBitString.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DERBitString.java
index fbfb67c..185e62c 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DERBitString.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DERBitString.java
@@ -10,124 +10,70 @@
 public class DERBitString
     extends ASN1BitString
 {
-    /**
-     * return a Bit String from the passed in object
-     *
-     * @param obj a DERBitString or an object that can be converted into one.
-     * @exception IllegalArgumentException if the object cannot be converted.
-     * @return a DERBitString instance, or null.
-     */
-    public static DERBitString getInstance(
-        Object  obj)
+    public static DERBitString convert(ASN1BitString bitString)
     {
-        if (obj == null || obj instanceof DERBitString)
-        {
-            return (DERBitString)obj;
-        }
-        if (obj instanceof DLBitString)
-        {
-            return new DERBitString(((DLBitString)obj).data, ((DLBitString)obj).padBits);
-        }
-        if (obj instanceof byte[])
-        {
-            try
-            {
-                return (DERBitString)fromByteArray((byte[])obj);
-            }
-            catch (Exception e)
-            {
-                throw new IllegalArgumentException("encoding error in getInstance: " + e.toString());
-            }
-        }
-
-        throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
-    }
-
-    /**
-     * return a Bit String from a tagged object.
-     *
-     * @param obj the tagged object holding the object we want
-     * @param explicit true if the object is meant to be explicitly
-     *              tagged false otherwise.
-     * @exception IllegalArgumentException if the tagged object cannot
-     *               be converted.
-     * @return a DERBitString instance, or null.
-     */
-    public static DERBitString getInstance(
-        ASN1TaggedObject obj,
-        boolean          explicit)
-    {
-        ASN1Primitive o = obj.getObject();
-
-        if (explicit || o instanceof DERBitString)
-        {
-            return getInstance(o);
-        }
-        else
-        {
-            return fromOctetString(ASN1OctetString.getInstance(o).getOctets());
-        }
-    }
-
-    protected DERBitString(byte data, int padBits)
-    {
-        super(data, padBits);
-    }
-
-    /**
-     * @param data the octets making up the bit string.
-     * @param padBits the number of extra bits at the end of the string.
-     */
-    public DERBitString(
-        byte[]  data,
-        int     padBits)
-    {
-        super(data, padBits);
+        return (DERBitString)bitString.toDERObject();
     }
 
     @android.compat.annotation.UnsupportedAppUsage
-    public DERBitString(
-        byte[]  data)
+    public DERBitString(byte[] data)
     {
         this(data, 0);
     }
 
-    public DERBitString(
-        int value)
+    public DERBitString(byte data, int padBits)
     {
+        super(data, padBits);
+    }
+
+    public DERBitString(byte[] data, int padBits)
+    {
+        super(data, padBits);
+    }
+
+    public DERBitString(int value)
+    {
+        // TODO[asn1] Unify in single allocation of 'contents'
         super(getBytes(value), getPadBits(value));
     }
 
-    public DERBitString(
-        ASN1Encodable obj)
-        throws IOException
+    public DERBitString(ASN1Encodable obj) throws IOException
     {
+        // TODO[asn1] Unify in single allocation of 'contents'
         super(obj.toASN1Primitive().getEncoded(ASN1Encoding.DER), 0);
     }
 
-    boolean isConstructed()
+    DERBitString(byte[] contents, boolean check)
+    {
+        super(contents, check);
+    }
+
+    boolean encodeConstructed()
     {
         return false;
     }
 
-    int encodedLength()
+    int encodedLength(boolean withTag)
     {
-        return 1 + StreamUtil.calculateBodyLength(data.length + 1) + data.length + 1;
+        return ASN1OutputStream.getLengthOfEncodingDL(withTag, contents.length);
     }
 
     void encode(ASN1OutputStream out, boolean withTag) throws IOException
     {
-        int len = data.length;
-        if (0 == len
-            || 0 == padBits
-            || (data[len - 1] == (byte)(data[len - 1] & (0xFF << padBits))))
+        int padBits = contents[0] & 0xFF;
+        int length = contents.length;
+        int last = length - 1;
+
+        byte lastOctet = contents[last];
+        byte lastOctetDER = (byte)(contents[last] & (0xFF << padBits));
+
+        if (lastOctet == lastOctetDER)
         {
-            out.writeEncoded(withTag, BERTags.BIT_STRING, (byte)padBits, data);
+            out.writeEncodingDL(withTag, BERTags.BIT_STRING, contents);
         }
         else
         {
-            byte der = (byte)(data[len - 1] & (0xFF << padBits));
-            out.writeEncoded(withTag, BERTags.BIT_STRING, (byte)padBits, data, 0, len - 1, der);
+            out.writeEncodingDL(withTag, BERTags.BIT_STRING, contents, 0, last, lastOctetDER);
         }
     }
 
@@ -141,21 +87,8 @@
         return this;
     }
 
-    static DERBitString fromOctetString(byte[] bytes)
+    static DERBitString fromOctetString(ASN1OctetString octetString)
     {
-        if (bytes.length < 1)
-        {
-            throw new IllegalArgumentException("truncated BIT STRING detected");
-        }
-
-        int padBits = bytes[0];
-        byte[] data = new byte[bytes.length - 1];
-
-        if (data.length != 0)
-        {
-            System.arraycopy(bytes, 1, data, 0, bytes.length - 1);
-        }
-
-        return new DERBitString(data, padBits);
+        return new DERBitString(octetString.getOctets(), true);
     }
 }
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DEREncodableVector.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DEREncodableVector.java
deleted file mode 100644
index c216ef2..0000000
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DEREncodableVector.java
+++ /dev/null
@@ -1,21 +0,0 @@
-/* GENERATED SOURCE. DO NOT MODIFY. */
-package com.android.org.bouncycastle.asn1;
-
-/**
- * a general class for building up a vector of DER encodable objects -
- * this will eventually be superseded by ASN1EncodableVector so you should
- * use that class in preference.
- * @hide This class is not part of the Android public SDK API
- */
-public class DEREncodableVector
-    extends ASN1EncodableVector
-{
-    /**
-     * @deprecated use ASN1EncodableVector instead.
-     */
-    @android.compat.annotation.UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
-    public DEREncodableVector()
-    {
-
-    }
-}
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DEREnumerated.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DEREnumerated.java
deleted file mode 100644
index 5d3ce32..0000000
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DEREnumerated.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/* GENERATED SOURCE. DO NOT MODIFY. */
-package com.android.org.bouncycastle.asn1;
-
-import java.math.BigInteger;
-
-/**
- * @deprecated Use ASN1Enumerated instead of this.
- * @hide This class is not part of the Android public SDK API
- */
-public class DEREnumerated
-    extends ASN1Enumerated
-{
-    /**
-     * @param bytes the value of this enumerated as an encoded BigInteger (signed).
-     * @deprecated use ASN1Enumerated
-     */
-    DEREnumerated(byte[] bytes)
-    {
-        super(bytes);
-    }
-
-    /**
-     * @param value the value of this enumerated.
-     * @deprecated use ASN1Enumerated
-     */
-    public DEREnumerated(BigInteger value)
-    {
-        super(value);
-    }
-
-    /**
-     * @param value the value of this enumerated.
-     * @deprecated use ASN1Enumerated
-     */
-    public DEREnumerated(int value)
-    {
-        super(value);
-    }
-}
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DERExternal.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DERExternal.java
index 018b892..f0e60ce 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DERExternal.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DERExternal.java
@@ -1,9 +1,6 @@
 /* GENERATED SOURCE. DO NOT MODIFY. */
 package com.android.org.bouncycastle.asn1;
 
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-
 /**
  * Class representing the DER-type External
  * @hide This class is not part of the Android public SDK API
@@ -21,11 +18,30 @@
      * <li> Anything but {@link DERTaggedObject} + data {@link DERTaggedObject} (data value form)</li>
      * </ul>
      *
-     * @throws IllegalArgumentException if input size is wrong, or
+     * @throws IllegalArgumentException if input size is wrong, or input is not an acceptable format
+     * 
+     * @deprecated Use {@link DERExternal#DERExternal(DERSequence)} instead.
      */
     public DERExternal(ASN1EncodableVector vector)
     {
-        super(vector);
+        this(DERFactory.createSequence(vector));
+    }
+
+    /**
+     * Construct a DER EXTERNAL object, the input sequence must have exactly two elements on it.
+     * <p>
+     * Acceptable input formats are:
+     * <ul>
+     * <li> {@link ASN1ObjectIdentifier} + data {@link DERTaggedObject} (direct reference form)</li>
+     * <li> {@link ASN1Integer} + data {@link DERTaggedObject} (indirect reference form)</li>
+     * <li> Anything but {@link DERTaggedObject} + data {@link DERTaggedObject} (data value form)</li>
+     * </ul>
+     *
+     * @throws IllegalArgumentException if input size is wrong, or input is not an acceptable format
+     */
+    public DERExternal(DERSequence sequence)
+    {
+        super(sequence);
     }
 
     /**
@@ -36,9 +52,10 @@
      * @param dataValueDescriptor The data value descriptor or <code>null</code> if not set.
      * @param externalData The external data in its encoded form.
      */
-    public DERExternal(ASN1ObjectIdentifier directReference, ASN1Integer indirectReference, ASN1Primitive dataValueDescriptor, DERTaggedObject externalData)
+    public DERExternal(ASN1ObjectIdentifier directReference, ASN1Integer indirectReference,
+        ASN1Primitive dataValueDescriptor, DERTaggedObject externalData)
     {
-        this(directReference, indirectReference, dataValueDescriptor, externalData.getTagNo(), externalData.toASN1Primitive());
+        super(directReference, indirectReference, dataValueDescriptor, externalData);
     }
 
     /**
@@ -50,11 +67,33 @@
      * @param encoding The encoding to be used for the external data
      * @param externalData The external data
      */
-    public DERExternal(ASN1ObjectIdentifier directReference, ASN1Integer indirectReference, ASN1Primitive dataValueDescriptor, int encoding, ASN1Primitive externalData)
+    public DERExternal(ASN1ObjectIdentifier directReference, ASN1Integer indirectReference,
+        ASN1Primitive dataValueDescriptor, int encoding, ASN1Primitive externalData)
     {
         super(directReference, indirectReference, dataValueDescriptor, encoding, externalData);
     }
 
+    ASN1Sequence buildSequence()
+    {
+        ASN1EncodableVector v = new ASN1EncodableVector(4);
+        if (directReference != null)
+        {
+            v.add(directReference);
+        }
+        if (indirectReference != null)
+        {
+            v.add(indirectReference);
+        }
+        if (dataValueDescriptor != null)
+        {
+            v.add(dataValueDescriptor.toDERObject());
+        }
+
+        v.add(new DERTaggedObject(0 == encoding, encoding, externalContent));
+
+        return new DERSequence(v);
+    }
+
     ASN1Primitive toDERObject()
     {
         return this;
@@ -64,34 +103,4 @@
     {
         return this;
     }
-
-    int encodedLength()
-        throws IOException
-    {
-        return this.getEncoded().length;
-    }
-
-    /* (non-Javadoc)
-     * @see org.bouncycastle.asn1.ASN1Primitive#encode(org.bouncycastle.asn1.DEROutputStream)
-     */
-    void encode(ASN1OutputStream out, boolean withTag) throws IOException
-    {
-        ByteArrayOutputStream baos = new ByteArrayOutputStream();
-        if (directReference != null)
-        {
-            baos.write(directReference.getEncoded(ASN1Encoding.DER));
-        }
-        if (indirectReference != null)
-        {
-            baos.write(indirectReference.getEncoded(ASN1Encoding.DER));
-        }
-        if (dataValueDescriptor != null)
-        {
-            baos.write(dataValueDescriptor.getEncoded(ASN1Encoding.DER));
-        }
-        DERTaggedObject obj = new DERTaggedObject(true, encoding, externalContent);
-        baos.write(obj.getEncoded(ASN1Encoding.DER));
-
-        out.writeEncoded(withTag, BERTags.CONSTRUCTED, BERTags.EXTERNAL, baos.toByteArray());
-    }
 }
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DERExternalParser.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DERExternalParser.java
index ede2658..9358566 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DERExternalParser.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DERExternalParser.java
@@ -8,7 +8,7 @@
  * @hide This class is not part of the Android public SDK API
  */
 public class DERExternalParser
-    implements ASN1Encodable, InMemoryRepresentable
+    implements ASN1ExternalParser
 {
     private ASN1StreamParser _parser;
 
@@ -37,14 +37,7 @@
     public ASN1Primitive getLoadedObject()
         throws IOException
     {
-        try
-        {
-            return new DLExternal(_parser.readVector());
-        }
-        catch (IllegalArgumentException e)
-        {
-            throw new ASN1Exception(e.getMessage(), e);
-        }
+        return parse(_parser);
     }
 
     /**
@@ -67,4 +60,16 @@
             throw new ASN1ParsingException("unable to get DER object", ioe);
         }
     }
+
+    static DLExternal parse(ASN1StreamParser sp) throws IOException
+    {
+        try
+        {
+            return new DLExternal(new DLSequence(sp.readVector()));
+        }
+        catch (IllegalArgumentException e)
+        {
+            throw new ASN1Exception(e.getMessage(), e);
+        }
+    }
 }
\ No newline at end of file
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DERFactory.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DERFactory.java
index 0c4f774..3195ae3 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DERFactory.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DERFactory.java
@@ -3,10 +3,10 @@
 
 class DERFactory
 {
-    static final ASN1Sequence EMPTY_SEQUENCE = new DERSequence();
-    static final ASN1Set EMPTY_SET = new DERSet();
+    static final DERSequence EMPTY_SEQUENCE = new DERSequence();
+    static final DERSet EMPTY_SET = new DERSet();
 
-    static ASN1Sequence createSequence(ASN1EncodableVector v)
+    static DERSequence createSequence(ASN1EncodableVector v)
     {
         if (v.size() < 1)
         {
@@ -16,7 +16,7 @@
         return new DERSequence(v);
     }
 
-    static ASN1Set createSet(ASN1EncodableVector v)
+    static DERSet createSet(ASN1EncodableVector v)
     {
         if (v.size() < 1)
         {
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DERGeneralString.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DERGeneralString.java
index 6781839..c1e3338 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DERGeneralString.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DERGeneralString.java
@@ -1,11 +1,6 @@
 /* GENERATED SOURCE. DO NOT MODIFY. */
 package com.android.org.bouncycastle.asn1;
 
-import java.io.IOException;
-
-import com.android.org.bouncycastle.util.Arrays;
-import com.android.org.bouncycastle.util.Strings;
-
 /**
  * ASN.1 GENERAL-STRING data type.
  * <p>
@@ -15,136 +10,20 @@
  * @hide This class is not part of the Android public SDK API
  */
 public class DERGeneralString 
-    extends ASN1Primitive
-    implements ASN1String
+    extends ASN1GeneralString
 {
-    private final byte[] string;
-
-    /**
-     * Return a GeneralString from the given object.
-     *
-     * @param obj the object we want converted.
-     * @exception IllegalArgumentException if the object cannot be converted.
-     * @return a DERBMPString instance, or null.
-     */
-    public static DERGeneralString getInstance(
-        Object obj) 
-    {
-        if (obj == null || obj instanceof DERGeneralString) 
-        {
-            return (DERGeneralString) obj;
-        }
-
-        if (obj instanceof byte[])
-        {
-            try
-            {
-                return (DERGeneralString)fromByteArray((byte[])obj);
-            }
-            catch (Exception e)
-            {
-                throw new IllegalArgumentException("encoding error in getInstance: " + e.toString());
-            }
-        }
-
-        throw new IllegalArgumentException("illegal object in getInstance: "
-                + obj.getClass().getName());
-    }
-
-    /**
-     * Return a GeneralString from a tagged object.
-     *
-     * @param obj the tagged object holding the object we want
-     * @param explicit true if the object is meant to be explicitly
-     *              tagged false otherwise.
-     * @exception IllegalArgumentException if the tagged object cannot
-     *              be converted.
-     * @return a DERGeneralString instance.
-     */
-    public static DERGeneralString getInstance(
-        ASN1TaggedObject obj, 
-        boolean explicit) 
-    {
-        ASN1Primitive o = obj.getObject();
-
-        if (explicit || o instanceof DERGeneralString)
-        {
-            return getInstance(o);
-        }
-        else
-        {
-            return new DERGeneralString(ASN1OctetString.getInstance(o).getOctets());
-        }
-    }
-
-    DERGeneralString(byte[] string)
-    {
-        this.string = string;
-    }
-
     /**
      * Construct a GeneralString from the passed in String.
      *
      * @param string the string to be contained in this object.
      */
-    public DERGeneralString(String string) 
+    public DERGeneralString(String string)
     {
-        this.string = Strings.toByteArray(string);
+        super(string);
     }
 
-    /**
-     * Return a Java String representation of our contained String.
-     *
-     * @return a Java String representing our contents.
-     */
-    public String getString() 
+    DERGeneralString(byte[] contents, boolean clone)
     {
-        return Strings.fromByteArray(string);
-    }
-
-    public String toString()
-    {
-        return getString();
-    }
-
-    /**
-     * Return a byte array representation of our contained String.
-     *
-     * @return a byte array representing our contents.
-     */
-    public byte[] getOctets() 
-    {
-        return Arrays.clone(string);
-    }
-
-    boolean isConstructed()
-    {
-        return false;
-    }
-
-    int encodedLength()
-    {
-        return 1 + StreamUtil.calculateBodyLength(string.length) + string.length;
-    }
-
-    void encode(ASN1OutputStream out, boolean withTag) throws IOException
-    {
-        out.writeEncoded(withTag, BERTags.GENERAL_STRING, string);
-    }
-
-    public int hashCode() 
-    {
-        return Arrays.hashCode(string);
-    }
-    
-    boolean asn1Equals(ASN1Primitive o)
-    {
-        if (!(o instanceof DERGeneralString)) 
-        {
-            return false;
-        }
-        DERGeneralString s = (DERGeneralString)o;
-
-        return Arrays.areEqual(string, s.string);
+        super(contents, clone);
     }
 }
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DERGeneralizedTime.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DERGeneralizedTime.java
index b8f3969..fe4a3ad 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DERGeneralizedTime.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DERGeneralizedTime.java
@@ -44,39 +44,39 @@
 
     private byte[] getDERTime()
     {
-        if (time[time.length - 1] == 'Z')
+        if (contents[contents.length - 1] == 'Z')
         {
             if (!hasMinutes())
             {
-                byte[] derTime = new byte[time.length + 4];
+                byte[] derTime = new byte[contents.length + 4];
 
-                System.arraycopy(time, 0, derTime, 0, time.length - 1);
-                System.arraycopy(Strings.toByteArray("0000Z"), 0, derTime, time.length - 1, 5);
+                System.arraycopy(contents, 0, derTime, 0, contents.length - 1);
+                System.arraycopy(Strings.toByteArray("0000Z"), 0, derTime, contents.length - 1, 5);
 
                 return derTime;
             }
             else if (!hasSeconds())
             {
-                byte[] derTime = new byte[time.length + 2];
+                byte[] derTime = new byte[contents.length + 2];
 
-                System.arraycopy(time, 0, derTime, 0, time.length - 1);
-                System.arraycopy(Strings.toByteArray("00Z"), 0, derTime, time.length - 1, 3);
+                System.arraycopy(contents, 0, derTime, 0, contents.length - 1);
+                System.arraycopy(Strings.toByteArray("00Z"), 0, derTime, contents.length - 1, 3);
 
                 return derTime;
             }
             else if (hasFractionalSeconds())
             {
-                int ind = time.length - 2;
-                while (ind > 0 && time[ind] == '0')
+                int ind = contents.length - 2;
+                while (ind > 0 && contents[ind] == '0')
                 {
                     ind--;
                 }
 
-                if (time[ind] == '.')
+                if (contents[ind] == '.')
                 {
                     byte[] derTime = new byte[ind + 1];
 
-                    System.arraycopy(time, 0, derTime, 0, ind);
+                    System.arraycopy(contents, 0, derTime, 0, ind);
                     derTime[ind] = (byte)'Z';
 
                     return derTime;
@@ -85,7 +85,7 @@
                 {
                     byte[] derTime = new byte[ind + 2];
 
-                    System.arraycopy(time, 0, derTime, 0, ind + 1);
+                    System.arraycopy(contents, 0, derTime, 0, ind + 1);
                     derTime[ind + 1] = (byte)'Z';
 
                     return derTime;
@@ -93,25 +93,23 @@
             }
             else
             {
-                return time;
+                return contents;
             }
         }
         else
         {
-            return time; // TODO: is there a better way?
+            return contents; // TODO: is there a better way?
         }
     }
 
-    int encodedLength()
+    int encodedLength(boolean withTag)
     {
-        int length = getDERTime().length;
-
-        return 1 + StreamUtil.calculateBodyLength(length) + length;
+        return ASN1OutputStream.getLengthOfEncodingDL(withTag, getDERTime().length);
     }
 
     void encode(ASN1OutputStream out, boolean withTag) throws IOException
     {
-        out.writeEncoded(withTag, BERTags.GENERALIZED_TIME, getDERTime());
+        out.writeEncodingDL(withTag, BERTags.GENERALIZED_TIME, getDERTime());
     }
 
     ASN1Primitive toDERObject()
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DERGraphicString.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DERGraphicString.java
index 1000928..b130aaa 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DERGraphicString.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DERGraphicString.java
@@ -1,126 +1,19 @@
 /* GENERATED SOURCE. DO NOT MODIFY. */
 package com.android.org.bouncycastle.asn1;
 
-import java.io.IOException;
-
-import com.android.org.bouncycastle.util.Arrays;
-import com.android.org.bouncycastle.util.Strings;
-
 /**
  * @hide This class is not part of the Android public SDK API
  */
 public class DERGraphicString
-    extends ASN1Primitive
-    implements ASN1String
+    extends ASN1GraphicString
 {
-    private final byte[] string;
-    
-    /**
-     * return a Graphic String from the passed in object
-     *
-     * @param obj a DERGraphicString or an object that can be converted into one.
-     * @exception IllegalArgumentException if the object cannot be converted.
-     * @return a DERGraphicString instance, or null.
-     */
-    public static DERGraphicString getInstance(
-        Object  obj)
+    public DERGraphicString(byte[] octets)
     {
-        if (obj == null || obj instanceof DERGraphicString)
-        {
-            return (DERGraphicString)obj;
-        }
-
-        if (obj instanceof byte[])
-        {
-            try
-            {
-                return (DERGraphicString)fromByteArray((byte[])obj);
-            }
-            catch (Exception e)
-            {
-                throw new IllegalArgumentException("encoding error in getInstance: " + e.toString());
-            }
-        }
-
-        throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
+        this(octets, true);
     }
 
-    /**
-     * return a Graphic String from a tagged object.
-     *
-     * @param obj the tagged object holding the object we want
-     * @param explicit true if the object is meant to be explicitly
-     *              tagged false otherwise.
-     * @exception IllegalArgumentException if the tagged object cannot
-     *               be converted.
-     * @return a DERGraphicString instance, or null.
-     */
-    public static DERGraphicString getInstance(
-        ASN1TaggedObject obj,
-        boolean          explicit)
+    DERGraphicString(byte[] contents, boolean clone)
     {
-        ASN1Primitive o = obj.getObject();
-
-        if (explicit || o instanceof DERGraphicString)
-        {
-            return getInstance(o);
-        }
-        else
-        {
-            return new DERGraphicString(ASN1OctetString.getInstance(o).getOctets());
-        }
-    }
-
-    /**
-     * basic constructor - with bytes.
-     * @param string the byte encoding of the characters making up the string.
-     */
-    public DERGraphicString(
-        byte[]   string)
-    {
-        this.string = Arrays.clone(string);
-    }
-    
-    public byte[] getOctets()
-    {
-        return Arrays.clone(string);
-    }
-
-    boolean isConstructed()
-    {
-        return false;
-    }
-
-    int encodedLength()
-    {
-        return 1 + StreamUtil.calculateBodyLength(string.length) + string.length;
-    }
-
-    void encode(ASN1OutputStream out, boolean withTag) throws IOException
-    {
-        out.writeEncoded(withTag, BERTags.GRAPHIC_STRING, string);
-    }
-
-    public int hashCode()
-    {
-        return Arrays.hashCode(string);
-    }
-
-    boolean asn1Equals(
-        ASN1Primitive o)
-    {
-        if (!(o instanceof DERGraphicString))
-        {
-            return false;
-        }
-
-        DERGraphicString  s = (DERGraphicString)o;
-
-        return Arrays.areEqual(string, s.string);
-    }
-
-    public String getString()
-    {
-        return Strings.fromByteArray(string);
+        super(contents, clone);
     }
 }
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DERIA5String.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DERIA5String.java
index b4364e3..cdf85f4 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DERIA5String.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DERIA5String.java
@@ -1,11 +1,6 @@
 /* GENERATED SOURCE. DO NOT MODIFY. */
 package com.android.org.bouncycastle.asn1;
 
-import java.io.IOException;
-
-import com.android.org.bouncycastle.util.Arrays;
-import com.android.org.bouncycastle.util.Strings;
-
 /**
  * DER IA5String object - this is a ISO 646 (ASCII) string encoding code points 0 to 127.
  * <p>
@@ -14,83 +9,13 @@
  * @hide This class is not part of the Android public SDK API
  */
 public class DERIA5String
-    extends ASN1Primitive
-    implements ASN1String
+    extends ASN1IA5String
 {
-    private final byte[]  string;
-
-    /**
-     * Return an IA5 string from the passed in object
-     *
-     * @param obj a DERIA5String or an object that can be converted into one.
-     * @exception IllegalArgumentException if the object cannot be converted.
-     * @return a DERIA5String instance, or null.
-     */
-    public static DERIA5String getInstance(
-        Object  obj)
-    {
-        if (obj == null || obj instanceof DERIA5String)
-        {
-            return (DERIA5String)obj;
-        }
-
-        if (obj instanceof byte[])
-        {
-            try
-            {
-                return (DERIA5String)fromByteArray((byte[])obj);
-            }
-            catch (Exception e)
-            {
-                throw new IllegalArgumentException("encoding error in getInstance: " + e.toString());
-            }
-        }
-
-        throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
-    }
-
-    /**
-     * Return an IA5 String from a tagged object.
-     *
-     * @param obj the tagged object holding the object we want
-     * @param explicit true if the object is meant to be explicitly
-     *              tagged false otherwise.
-     * @exception IllegalArgumentException if the tagged object cannot
-     *               be converted.
-     * @return a DERIA5String instance, or null.
-     */
-    public static DERIA5String getInstance(
-        ASN1TaggedObject obj,
-        boolean          explicit)
-    {
-        ASN1Primitive o = obj.getObject();
-
-        if (explicit || o instanceof DERIA5String)
-        {
-            return getInstance(o);
-        }
-        else
-        {
-            return new DERIA5String(ASN1OctetString.getInstance(o).getOctets());
-        }
-    }
-
-    /**
-     * Basic constructor - with bytes.
-     * @param string the byte encoding of the characters making up the string.
-     */
-    DERIA5String(
-        byte[]   string)
-    {
-        this.string = string;
-    }
-
     /**
      * Basic constructor - without validation.
      * @param string the base string to use..
      */
-    public DERIA5String(
-        String   string)
+    public DERIA5String(String string)
     {
         this(string, false);
     }
@@ -103,90 +28,13 @@
      * @throws IllegalArgumentException if validate is true and the string
      * contains characters that should not be in an IA5String.
      */
-    public DERIA5String(
-        String   string,
-        boolean  validate)
+    public DERIA5String(String string, boolean validate)
     {
-        if (string == null)
-        {
-            throw new NullPointerException("'string' cannot be null");
-        }
-        if (validate && !isIA5String(string))
-        {
-            throw new IllegalArgumentException("'string' contains illegal characters");
-        }
-
-        this.string = Strings.toByteArray(string);
+        super(string, validate);
     }
 
-    public String getString()
+    DERIA5String(byte[] contents, boolean clone)
     {
-        return Strings.fromByteArray(string);
-    }
-
-    public String toString()
-    {
-        return getString();
-    }
-
-    public byte[] getOctets()
-    {
-        return Arrays.clone(string);
-    }
-
-    boolean isConstructed()
-    {
-        return false;
-    }
-
-    int encodedLength()
-    {
-        return 1 + StreamUtil.calculateBodyLength(string.length) + string.length;
-    }
-
-    void encode(ASN1OutputStream out, boolean withTag) throws IOException
-    {
-        out.writeEncoded(withTag, BERTags.IA5_STRING, string);
-    }
-
-    public int hashCode()
-    {
-        return Arrays.hashCode(string);
-    }
-
-    boolean asn1Equals(
-        ASN1Primitive o)
-    {
-        if (!(o instanceof DERIA5String))
-        {
-            return false;
-        }
-
-        DERIA5String  s = (DERIA5String)o;
-
-        return Arrays.areEqual(string, s.string);
-    }
-
-    /**
-     * return true if the passed in String can be represented without
-     * loss as an IA5String, false otherwise.
-     *
-     * @param str the string to check.
-     * @return true if character set in IA5String set, false otherwise.
-     */
-    public static boolean isIA5String(
-        String  str)
-    {
-        for (int i = str.length() - 1; i >= 0; i--)
-        {
-            char    ch = str.charAt(i);
-
-            if (ch > 0x007f)
-            {
-                return false;
-            }
-        }
-
-        return true;
+        super(contents, clone);
     }
 }
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DERInteger.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DERInteger.java
deleted file mode 100644
index 75e8aea..0000000
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DERInteger.java
+++ /dev/null
@@ -1,34 +0,0 @@
-/* GENERATED SOURCE. DO NOT MODIFY. */
-package com.android.org.bouncycastle.asn1;
-
-import java.math.BigInteger;
-
-/**
- * @deprecated  Use ASN1Integer instead of this,
- * @hide This class is not part of the Android public SDK API
- */
-public class DERInteger
-    extends ASN1Integer
-{
-    /**
-     * Constructor from a byte array containing a signed representation of the number.
-     *
-     * @param bytes a byte array containing the signed number.A copy is made of the byte array.
-     */
-    public DERInteger(byte[] bytes)
-    {
-        super(bytes, true);
-    }
-
-    @android.compat.annotation.UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
-    public DERInteger(BigInteger value)
-    {
-        super(value);
-    }
-
-    @android.compat.annotation.UnsupportedAppUsage
-    public DERInteger(long value)
-    {
-        super(value);
-    }
-}
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DERNull.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DERNull.java
index b4de8ae..83720a8 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DERNull.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DERNull.java
@@ -21,18 +21,18 @@
     {
     }
 
-    boolean isConstructed()
+    boolean encodeConstructed()
     {
         return false;
     }
 
-    int encodedLength()
+    int encodedLength(boolean withTag)
     {
-        return 2;
+        return ASN1OutputStream.getLengthOfEncodingDL(withTag, 0);
     }
 
     void encode(ASN1OutputStream out, boolean withTag) throws IOException
     {
-        out.writeEncoded(withTag, BERTags.NULL, zeroBytes);
+        out.writeEncodingDL(withTag, BERTags.NULL, zeroBytes);
     }
 }
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DERNumericString.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DERNumericString.java
index 1ff789c..f888c58 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DERNumericString.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DERNumericString.java
@@ -1,11 +1,6 @@
 /* GENERATED SOURCE. DO NOT MODIFY. */
 package com.android.org.bouncycastle.asn1;
 
-import java.io.IOException;
-
-import com.android.org.bouncycastle.util.Arrays;
-import com.android.org.bouncycastle.util.Strings;
-
 /**
  * DER NumericString object - this is an ascii string of characters {0,1,2,3,4,5,6,7,8,9, }.
  * ASN.1 NUMERIC-STRING object.
@@ -18,81 +13,12 @@
  * @hide This class is not part of the Android public SDK API
  */
 public class DERNumericString
-    extends ASN1Primitive
-    implements ASN1String
+    extends ASN1NumericString
 {
-    private final byte[]  string;
-
-    /**
-     * Return a Numeric string from the passed in object
-     *
-     * @param obj a DERNumericString or an object that can be converted into one.
-     * @exception IllegalArgumentException if the object cannot be converted.
-     * @return a DERNumericString instance, or null
-     */
-    public static DERNumericString getInstance(
-        Object  obj)
-    {
-        if (obj == null || obj instanceof DERNumericString)
-        {
-            return (DERNumericString)obj;
-        }
-
-        if (obj instanceof byte[])
-        {
-            try
-            {
-                return (DERNumericString)fromByteArray((byte[])obj);
-            }
-            catch (Exception e)
-            {
-                throw new IllegalArgumentException("encoding error in getInstance: " + e.toString());
-            }
-        }
-
-        throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
-    }
-
-    /**
-     * Return an Numeric String from a tagged object.
-     *
-     * @param obj the tagged object holding the object we want
-     * @param explicit true if the object is meant to be explicitly
-     *              tagged false otherwise.
-     * @exception IllegalArgumentException if the tagged object cannot
-     *               be converted.
-     * @return a DERNumericString instance, or null.
-     */
-    public static DERNumericString getInstance(
-        ASN1TaggedObject obj,
-        boolean          explicit)
-    {
-        ASN1Primitive o = obj.getObject();
-
-        if (explicit || o instanceof DERNumericString)
-        {
-            return getInstance(o);
-        }
-        else
-        {
-            return new DERNumericString(ASN1OctetString.getInstance(o).getOctets());
-        }
-    }
-
-    /**
-     * Basic constructor - with bytes.
-     */
-    DERNumericString(
-        byte[]   string)
-    {
-        this.string = string;
-    }
-
     /**
      * Basic constructor -  without validation..
      */
-    public DERNumericString(
-        String   string)
+    public DERNumericString(String string)
     {
         this(string, false);
     }
@@ -105,92 +31,13 @@
      * @throws IllegalArgumentException if validate is true and the string
      * contains characters that should not be in a NumericString.
      */
-    public DERNumericString(
-        String   string,
-        boolean  validate)
+    public DERNumericString(String string, boolean validate)
     {
-        if (validate && !isNumericString(string))
-        {
-            throw new IllegalArgumentException("string contains illegal characters");
-        }
-
-        this.string = Strings.toByteArray(string);
+        super(string, validate);
     }
 
-    public String getString()
+    DERNumericString(byte[] contents, boolean clone)
     {
-        return Strings.fromByteArray(string);
-    }
-
-    public String toString()
-    {
-        return getString();
-    }
-
-    public byte[] getOctets()
-    {
-        return Arrays.clone(string);
-    }
-
-    boolean isConstructed()
-    {
-        return false;
-    }
-
-    int encodedLength()
-    {
-        return 1 + StreamUtil.calculateBodyLength(string.length) + string.length;
-    }
-
-    void encode(ASN1OutputStream out, boolean withTag) throws IOException
-    {
-        out.writeEncoded(withTag, BERTags.NUMERIC_STRING, string);
-    }
-
-    public int hashCode()
-    {
-        return Arrays.hashCode(string);
-    }
-
-    boolean asn1Equals(
-        ASN1Primitive o)
-    {
-        if (!(o instanceof DERNumericString))
-        {
-            return false;
-        }
-
-        DERNumericString  s = (DERNumericString)o;
-
-        return Arrays.areEqual(string, s.string);
-    }
-
-    /**
-     * Return true if the string can be represented as a NumericString ('0'..'9', ' ')
-     *
-     * @param str string to validate.
-     * @return true if numeric, fale otherwise.
-     */
-    public static boolean isNumericString(
-        String  str)
-    {
-        for (int i = str.length() - 1; i >= 0; i--)
-        {
-            char    ch = str.charAt(i);
-
-            if (ch > 0x007f)
-            {
-                return false;
-            }
-
-            if (('0' <= ch && ch <= '9') || ch == ' ')
-            {
-                continue;
-            }
-
-            return false;
-        }
-
-        return true;
+        super(contents, clone);
     }
 }
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DERObjectIdentifier.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DERObjectIdentifier.java
deleted file mode 100644
index 05c8380..0000000
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DERObjectIdentifier.java
+++ /dev/null
@@ -1,27 +0,0 @@
-/* GENERATED SOURCE. DO NOT MODIFY. */
-package com.android.org.bouncycastle.asn1;
-
-/**
- *
- * @deprecated Use ASN1ObjectIdentifier instead of this,
- * @hide This class is not part of the Android public SDK API
- */
-public class DERObjectIdentifier
-    extends ASN1ObjectIdentifier
-{
-    @android.compat.annotation.UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
-    public DERObjectIdentifier(String identifier)
-    {
-        super(identifier);
-    }
-
-    DERObjectIdentifier(byte[] bytes)
-    {
-        super(bytes);
-    }
-
-    DERObjectIdentifier(ASN1ObjectIdentifier oid, String branch)
-    {
-        super(oid, branch);
-    }
-}
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DEROctetString.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DEROctetString.java
index 52cbfa4..b69d63e 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DEROctetString.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DEROctetString.java
@@ -34,19 +34,19 @@
         super(obj.toASN1Primitive().getEncoded(ASN1Encoding.DER));
     }
 
-    boolean isConstructed()
+    boolean encodeConstructed()
     {
         return false;
     }
 
-    int encodedLength()
+    int encodedLength(boolean withTag)
     {
-        return 1 + StreamUtil.calculateBodyLength(string.length) + string.length;
+        return ASN1OutputStream.getLengthOfEncodingDL(withTag, string.length);
     }
 
     void encode(ASN1OutputStream out, boolean withTag) throws IOException
     {
-        out.writeEncoded(withTag, BERTags.OCTET_STRING, string);
+        out.writeEncodingDL(withTag, BERTags.OCTET_STRING, string);
     }
 
     ASN1Primitive toDERObject()
@@ -59,8 +59,13 @@
         return this;
     }
 
-    static void encode(ASN1OutputStream derOut, boolean withTag, byte[] buf, int off, int len) throws IOException
+    static void encode(ASN1OutputStream out, boolean withTag, byte[] buf, int off, int len) throws IOException
     {
-        derOut.writeEncoded(withTag, BERTags.OCTET_STRING, buf, off, len);
+        out.writeEncodingDL(withTag, BERTags.OCTET_STRING, buf, off, len);
+    }
+
+    static int encodedLength(boolean withTag, int contentsLength)
+    {
+        return ASN1OutputStream.getLengthOfEncodingDL(withTag, contentsLength);
     }
 }
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DEROctetStringParser.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DEROctetStringParser.java
index 8ea3ef4..8c4636a 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DEROctetStringParser.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DEROctetStringParser.java
@@ -6,6 +6,8 @@
 
 /**
  * Parser for DER encoded OCTET STRINGS
+ * 
+ * @deprecated Check for 'ASN1OctetStringParser' instead 
  * @hide This class is not part of the Android public SDK API
  */
 public class DEROctetStringParser
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DEROutputStream.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DEROutputStream.java
index 2cae73b..cfb2d70 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DEROutputStream.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DEROutputStream.java
@@ -19,18 +19,32 @@
         super(os);
     }
 
-    void writePrimitive(ASN1Primitive primitive, boolean withTag) throws IOException
-    {
-        primitive.toDERObject().encode(this, withTag);
-    }
-
     DEROutputStream getDERSubStream()
     {
         return this;
     }
 
-    ASN1OutputStream getDLSubStream()
+    void writeElements(ASN1Encodable[] elements)
+        throws IOException
     {
-        return this;
+        for (int i = 0, count = elements.length; i < count; ++i)
+        {
+            elements[i].toASN1Primitive().toDERObject().encode(this, true);
+        }
+    }
+
+    void writePrimitive(ASN1Primitive primitive, boolean withTag) throws IOException
+    {
+        primitive.toDERObject().encode(this, withTag);
+    }
+
+    void writePrimitives(ASN1Primitive[] primitives)
+        throws IOException
+    {
+        int count = primitives.length;
+        for (int i = 0; i < count; ++i)
+        {
+            primitives[i].toDERObject().encode(this, true);
+        }
     }
 }
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DERPrintableString.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DERPrintableString.java
index f32ecff..6e3b558 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DERPrintableString.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DERPrintableString.java
@@ -1,11 +1,6 @@
 /* GENERATED SOURCE. DO NOT MODIFY. */
 package com.android.org.bouncycastle.asn1;
 
-import java.io.IOException;
-
-import com.android.org.bouncycastle.util.Arrays;
-import com.android.org.bouncycastle.util.Strings;
-
 /**
  * DER PrintableString object.
  * <p>
@@ -34,76 +29,8 @@
  * @hide This class is not part of the Android public SDK API
  */
 public class DERPrintableString
-    extends ASN1Primitive
-    implements ASN1String
+    extends ASN1PrintableString
 {
-    private final byte[]  string;
-
-    /**
-     * Return a printable string from the passed in object.
-     *
-     * @param obj a DERPrintableString or an object that can be converted into one.
-     * @exception IllegalArgumentException if the object cannot be converted.
-     * @return a DERPrintableString instance, or null.
-     */
-    public static DERPrintableString getInstance(
-        Object  obj)
-    {
-        if (obj == null || obj instanceof DERPrintableString)
-        {
-            return (DERPrintableString)obj;
-        }
-
-        if (obj instanceof byte[])
-        {
-            try
-            {
-                return (DERPrintableString)fromByteArray((byte[])obj);
-            }
-            catch (Exception e)
-            {
-                throw new IllegalArgumentException("encoding error in getInstance: " + e.toString());
-            }
-        }
-
-        throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
-    }
-
-    /**
-     * Return a Printable String from a tagged object.
-     *
-     * @param obj the tagged object holding the object we want
-     * @param explicit true if the object is meant to be explicitly
-     *              tagged false otherwise.
-     * @exception IllegalArgumentException if the tagged object cannot
-     *               be converted.
-     * @return a DERPrintableString instance, or null.
-     */
-    public static DERPrintableString getInstance(
-        ASN1TaggedObject obj,
-        boolean          explicit)
-    {
-        ASN1Primitive o = obj.getObject();
-
-        if (explicit || o instanceof DERPrintableString)
-        {
-            return getInstance(o);
-        }
-        else
-        {
-            return new DERPrintableString(ASN1OctetString.getInstance(o).getOctets());
-        }
-    }
-
-    /**
-     * Basic constructor - byte encoded string.
-     */
-    DERPrintableString(
-        byte[]   string)
-    {
-        this.string = string;
-    }
-
     /**
      * Basic constructor - this does not validate the string
      */
@@ -125,115 +52,11 @@
         String   string,
         boolean  validate)
     {
-        if (validate && !isPrintableString(string))
-        {
-            throw new IllegalArgumentException("string contains illegal characters");
-        }
-
-        this.string = Strings.toByteArray(string);
+        super(string, validate);
     }
 
-    public String getString()
+    DERPrintableString(byte[] contents, boolean clone)
     {
-        return Strings.fromByteArray(string);
-    }
-
-    public byte[] getOctets()
-    {
-        return Arrays.clone(string);
-    }
-
-    boolean isConstructed()
-    {
-        return false;
-    }
-
-    int encodedLength()
-    {
-        return 1 + StreamUtil.calculateBodyLength(string.length) + string.length;
-    }
-
-    void encode(ASN1OutputStream out, boolean withTag) throws IOException
-    {
-        out.writeEncoded(withTag, BERTags.PRINTABLE_STRING, string);
-    }
-
-    public int hashCode()
-    {
-        return Arrays.hashCode(string);
-    }
-
-    boolean asn1Equals(
-        ASN1Primitive o)
-    {
-        if (!(o instanceof DERPrintableString))
-        {
-            return false;
-        }
-
-        DERPrintableString  s = (DERPrintableString)o;
-
-        return Arrays.areEqual(string, s.string);
-    }
-
-    public String toString()
-    {
-        return getString();
-    }
-
-    /**
-     * return true if the passed in String can be represented without
-     * loss as a PrintableString, false otherwise.
-     *
-     * @return true if in printable set, false otherwise.
-     */
-    public static boolean isPrintableString(
-        String  str)
-    {
-        for (int i = str.length() - 1; i >= 0; i--)
-        {
-            char    ch = str.charAt(i);
-
-            if (ch > 0x007f)
-            {
-                return false;
-            }
-
-            if ('a' <= ch && ch <= 'z')
-            {
-                continue;
-            }
-
-            if ('A' <= ch && ch <= 'Z')
-            {
-                continue;
-            }
-
-            if ('0' <= ch && ch <= '9')
-            {
-                continue;
-            }
-
-            switch (ch)
-            {
-            case ' ':
-            case '\'':
-            case '(':
-            case ')':
-            case '+':
-            case '-':
-            case '.':
-            case ':':
-            case '=':
-            case '?':
-            case '/':
-            case ',':
-                continue;
-            }
-
-            return false;
-        }
-
-        return true;
+        super(contents, clone);
     }
 }
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DERSequence.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DERSequence.java
index 9e8b6e5..85dafab 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DERSequence.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DERSequence.java
@@ -18,7 +18,7 @@
         return (DERSequence)seq.toDERObject();
     }
 
-    private int bodyLength = -1;
+    private int contentsLength = -1;
 
     /**
      * Create an empty sequence
@@ -61,9 +61,9 @@
         super(elements, clone);
     }
 
-    private int getBodyLength() throws IOException
+    private int getContentsLength() throws IOException
     {
-        if (bodyLength < 0)
+        if (contentsLength < 0)
         {
             int count = elements.length;
             int totalLength = 0;
@@ -71,20 +71,18 @@
             for (int i = 0; i < count; ++i)
             {
                 ASN1Primitive derObject = elements[i].toASN1Primitive().toDERObject();
-                totalLength += derObject.encodedLength();
+                totalLength += derObject.encodedLength(true);
             }
 
-            this.bodyLength = totalLength;
+            this.contentsLength = totalLength;
         }
 
-        return bodyLength;
+        return contentsLength;
     }
 
-    int encodedLength() throws IOException
+    int encodedLength(boolean withTag) throws IOException
     {
-        int length = getBodyLength();
-
-        return 1 + StreamUtil.calculateBodyLength(length) + length;
+        return ASN1OutputStream.getLengthOfEncodingDL(withTag, getContentsLength());
     }
 
     /*
@@ -97,17 +95,14 @@
      */
     void encode(ASN1OutputStream out, boolean withTag) throws IOException
     {
-        if (withTag)
-        {
-            out.write(BERTags.SEQUENCE | BERTags.CONSTRUCTED);
-        }
+        out.writeIdentifier(withTag, BERTags.CONSTRUCTED | BERTags.SEQUENCE);
 
         DEROutputStream derOut = out.getDERSubStream();
 
         int count = elements.length;
-        if (bodyLength >= 0 || count > 16)
+        if (contentsLength >= 0 || count > 16)
         {
-            out.writeLength(getBodyLength());
+            out.writeDL(getContentsLength());
 
             for (int i = 0; i < count; ++i)
             {
@@ -124,11 +119,11 @@
             {
                 ASN1Primitive derObject = elements[i].toASN1Primitive().toDERObject();
                 derObjects[i] = derObject;
-                totalLength += derObject.encodedLength();
+                totalLength += derObject.encodedLength(true);
             }
 
-            this.bodyLength = totalLength;
-            out.writeLength(totalLength);
+            this.contentsLength = totalLength;
+            out.writeDL(totalLength);
 
             for (int i = 0; i < count; ++i)
             {
@@ -137,6 +132,27 @@
         }
     }
 
+    ASN1BitString toASN1BitString()
+    {
+        return new DERBitString(BERBitString.flattenBitStrings(getConstructedBitStrings()), false);
+    }
+
+    ASN1External toASN1External()
+    {
+        return new DERExternal(this);
+    }
+
+    ASN1OctetString toASN1OctetString()
+    {
+        return new DEROctetString(BEROctetString.flattenOctetStrings(getConstructedOctetStrings()));
+    }
+
+    ASN1Set toASN1Set()
+    {
+        // NOTE: DLSet is intentional, we don't want sorting
+        return new DLSet(false, toArrayInternal());
+    }
+
     ASN1Primitive toDERObject()
     {
         return this;
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DERSequenceParser.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DERSequenceParser.java
deleted file mode 100644
index 918db1f..0000000
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DERSequenceParser.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/* GENERATED SOURCE. DO NOT MODIFY. */
-package com.android.org.bouncycastle.asn1;
-
-import java.io.IOException;
-
-/**
- * @deprecated Use DLSequenceParser instead
- * @hide This class is not part of the Android public SDK API
- */
-public class DERSequenceParser
-    implements ASN1SequenceParser
-{
-    private ASN1StreamParser _parser;
-
-    DERSequenceParser(ASN1StreamParser parser)
-    {
-        this._parser = parser;
-    }
-
-    /**
-     * Return the next object in the SEQUENCE.
-     *
-     * @return next object in SEQUENCE.
-     * @throws IOException if there is an issue loading the object.
-     */
-    public ASN1Encodable readObject()
-        throws IOException
-    {
-        return _parser.readObject();
-    }
-
-    /**
-     * Return an in memory, encodable, representation of the SEQUENCE.
-     *
-     * @return a DERSequence.
-     * @throws IOException if there is an issue loading the data.
-     */
-    public ASN1Primitive getLoadedObject()
-        throws IOException
-    {
-         return new DLSequence(_parser.readVector());
-    }
-
-    /**
-     * Return a DERSequence representing this parser and its contents.
-     *
-     * @return a DERSequence.
-     */
-    public ASN1Primitive toASN1Primitive()
-    {
-        try
-        {
-            return getLoadedObject();
-        }
-        catch (IOException e)
-        {
-            throw new IllegalStateException(e.getMessage());
-        }
-    }
-}
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DERSet.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DERSet.java
index bacdb68..79e5c58 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DERSet.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DERSet.java
@@ -22,7 +22,7 @@
         return (DERSet)set.toDERObject();
     }
 
-    private int bodyLength = -1;
+    private int contentsLength = -1;
 
     /**
      * create an empty set
@@ -64,9 +64,9 @@
         super(checkSorted(isSorted), elements);
     }
 
-    private int getBodyLength() throws IOException
+    private int getContentsLength() throws IOException
     {
-        if (bodyLength < 0)
+        if (contentsLength < 0)
         {
             int count = elements.length;
             int totalLength = 0;
@@ -74,20 +74,18 @@
             for (int i = 0; i < count; ++i)
             {
                 ASN1Primitive derObject = elements[i].toASN1Primitive().toDERObject();
-                totalLength += derObject.encodedLength();
+                totalLength += derObject.encodedLength(true);
             }
 
-            this.bodyLength = totalLength;
+            this.contentsLength = totalLength;
         }
 
-        return bodyLength;
+        return contentsLength;
     }
 
-    int encodedLength() throws IOException
+    int encodedLength(boolean withTag) throws IOException
     {
-        int length = getBodyLength();
-
-        return 1 + StreamUtil.calculateBodyLength(length) + length;
+        return ASN1OutputStream.getLengthOfEncodingDL(withTag, getContentsLength());
     }
 
     /*
@@ -100,17 +98,14 @@
      */
     void encode(ASN1OutputStream out, boolean withTag) throws IOException
     {
-        if (withTag)
-        {
-            out.write(BERTags.SET | BERTags.CONSTRUCTED);
-        }
+        out.writeIdentifier(withTag, BERTags.CONSTRUCTED | BERTags.SET);
 
         DEROutputStream derOut = out.getDERSubStream();
 
         int count = elements.length;
-        if (bodyLength >= 0 || count > 16)
+        if (contentsLength >= 0 || count > 16)
         {
-            out.writeLength(getBodyLength());
+            out.writeDL(getContentsLength());
 
             for (int i = 0; i < count; ++i)
             {
@@ -127,11 +122,11 @@
             {
                 ASN1Primitive derObject = elements[i].toASN1Primitive().toDERObject();
                 derObjects[i] = derObject;
-                totalLength += derObject.encodedLength();
+                totalLength += derObject.encodedLength(true);
             }
 
-            this.bodyLength = totalLength;
-            out.writeLength(totalLength);
+            this.contentsLength = totalLength;
+            out.writeDL(totalLength);
 
             for (int i = 0; i < count; ++i)
             {
@@ -142,7 +137,7 @@
 
     ASN1Primitive toDERObject()
     {
-        return isSorted ? this : super.toDERObject();
+        return (sortedElements != null) ? this : super.toDERObject();
     }
 
     ASN1Primitive toDLObject()
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DERSetParser.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DERSetParser.java
deleted file mode 100644
index 31b1577..0000000
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DERSetParser.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/* GENERATED SOURCE. DO NOT MODIFY. */
-package com.android.org.bouncycastle.asn1;
-
-import java.io.IOException;
-
-/**
- * @deprecated Use DLSetParser instead
- * @hide This class is not part of the Android public SDK API
- */
-public class DERSetParser
-    implements ASN1SetParser
-{
-    private ASN1StreamParser _parser;
-
-    DERSetParser(ASN1StreamParser parser)
-    {
-        this._parser = parser;
-    }
-
-    /**
-     * Return the next object in the SET.
-     *
-     * @return next object in SET.
-     * @throws IOException if there is an issue loading the object.
-     */
-    public ASN1Encodable readObject()
-        throws IOException
-    {
-        return _parser.readObject();
-    }
-
-    /**
-     * Return an in memory, encodable, representation of the SET.
-     *
-     * @return a DERSet.
-     * @throws IOException if there is an issue loading the data.
-     */
-    public ASN1Primitive getLoadedObject()
-        throws IOException
-    {
-        return new DLSet(_parser.readVector());
-    }
-
-    /**
-     * Return a DERSet representing this parser and its contents.
-     *
-     * @return a DERSet
-     */
-    public ASN1Primitive toASN1Primitive()
-    {
-        try
-        {
-            return getLoadedObject();
-        }
-        catch (IOException e)
-        {
-            throw new ASN1ParsingException(e.getMessage(), e);
-        }
-    }
-}
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DERT61String.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DERT61String.java
index a0a7b3c..2f73b48 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DERT61String.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DERT61String.java
@@ -1,76 +1,22 @@
 /* GENERATED SOURCE. DO NOT MODIFY. */
 package com.android.org.bouncycastle.asn1;
 
-import java.io.IOException;
-
-import com.android.org.bouncycastle.util.Arrays;
-import com.android.org.bouncycastle.util.Strings;
-
 /**
  * DER T61String (also the teletex string), try not to use this if you don't need to. The standard support the encoding for
  * this has been withdrawn.
  * @hide This class is not part of the Android public SDK API
  */
 public class DERT61String
-    extends ASN1Primitive
-    implements ASN1String
+    extends ASN1T61String
 {
-    private byte[] string;
-
     /**
-     * Return a T61 string from the passed in object.
+     * Basic constructor - with string 8 bit assumed.
      *
-     * @param obj a DERT61String or an object that can be converted into one.
-     * @exception IllegalArgumentException if the object cannot be converted.
-     * @return a DERT61String instance, or null
+     * @param string the string to be wrapped.
      */
-    public static DERT61String getInstance(
-        Object  obj)
+    public DERT61String(String string)
     {
-        if (obj == null || obj instanceof DERT61String)
-        {
-            return (DERT61String)obj;
-        }
-
-        if (obj instanceof byte[])
-        {
-            try
-            {
-                return (DERT61String)fromByteArray((byte[])obj);
-            }
-            catch (Exception e)
-            {
-                throw new IllegalArgumentException("encoding error in getInstance: " + e.toString());
-            }
-        }
-
-        throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
-    }
-
-    /**
-     * Return an T61 String from a tagged object.
-     *
-     * @param obj the tagged object holding the object we want
-     * @param explicit true if the object is meant to be explicitly
-     *              tagged false otherwise.
-     * @exception IllegalArgumentException if the tagged object cannot
-     *               be converted.
-     * @return a DERT61String instance, or null
-     */
-    public static DERT61String getInstance(
-        ASN1TaggedObject obj,
-        boolean          explicit)
-    {
-        ASN1Primitive o = obj.getObject();
-
-        if (explicit || o instanceof DERT61String)
-        {
-            return getInstance(o);
-        }
-        else
-        {
-            return new DERT61String(ASN1OctetString.getInstance(o).getOctets());
-        }
+        super(string);
     }
 
     /**
@@ -78,74 +24,13 @@
      *
      * @param string the byte encoding of the string to be wrapped.
      */
-    public DERT61String(
-        byte[]   string)
+    public DERT61String(byte[] string)
     {
-        this.string = Arrays.clone(string);
+        this(string, true);
     }
 
-    /**
-     * Basic constructor - with string 8 bit assumed.
-     *
-     * @param string the string to be wrapped.
-     */
-    public DERT61String(
-        String   string)
+    DERT61String(byte[] contents, boolean clone)
     {
-        this.string = Strings.toByteArray(string);
-    }
-
-    /**
-     * Decode the encoded string and return it, 8 bit encoding assumed.
-     * @return the decoded String
-     */
-    public String getString()
-    {
-        return Strings.fromByteArray(string);
-    }
-
-    public String toString()
-    {
-        return getString();
-    }
-
-    boolean isConstructed()
-    {
-        return false;
-    }
-
-    int encodedLength()
-    {
-        return 1 + StreamUtil.calculateBodyLength(string.length) + string.length;
-    }
-
-    void encode(ASN1OutputStream out, boolean withTag) throws IOException
-    {
-        out.writeEncoded(withTag, BERTags.T61_STRING, string);
-    }
-
-    /**
-     * Return the encoded string as a byte array.
-     * @return the actual bytes making up the encoded body of the T61 string.
-     */
-    public byte[] getOctets()
-    {
-        return Arrays.clone(string);
-    }
-
-    boolean asn1Equals(
-        ASN1Primitive o)
-    {
-        if (!(o instanceof DERT61String))
-        {
-            return false;
-        }
-
-        return Arrays.areEqual(string, ((DERT61String)o).string);
-    }
-    
-    public int hashCode()
-    {
-        return Arrays.hashCode(string);
+        super(contents, clone);
     }
 }
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DERTaggedObject.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DERTaggedObject.java
index 2aaf86a..5570a48 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DERTaggedObject.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DERTaggedObject.java
@@ -12,68 +12,94 @@
 public class DERTaggedObject
     extends ASN1TaggedObject
 {
-    /**
-     * @param explicit true if an explicitly tagged object.
-     * @param tagNo the tag number for this object.
-     * @param obj the tagged object.
-     */
-    public DERTaggedObject(
-        boolean       explicit,
-        int           tagNo,
-        ASN1Encodable obj)
-    {
-        super(explicit, tagNo, obj);
-    }
-
     public DERTaggedObject(int tagNo, ASN1Encodable encodable)
     {
         super(true, tagNo, encodable);
     }
 
-    boolean isConstructed()
+    public DERTaggedObject(int tagClass, int tagNo, ASN1Encodable obj)
     {
-        return explicit || obj.toASN1Primitive().toDERObject().isConstructed();
+        super(true, tagClass, tagNo, obj);
     }
 
-    int encodedLength()
-        throws IOException
+    /**
+     * @param explicit true if an explicitly tagged object.
+     * @param tagNo the tag number for this object.
+     * @param obj the tagged object.
+     */
+    public DERTaggedObject(boolean explicit, int tagNo, ASN1Encodable obj)
+    {
+        super(explicit, tagNo, obj);
+    }
+
+    public DERTaggedObject(boolean explicit, int tagClass, int tagNo, ASN1Encodable obj)
+    {
+        super(explicit, tagClass, tagNo, obj);
+    }
+
+    DERTaggedObject(int explicitness, int tagClass, int tagNo, ASN1Encodable obj)
+    {
+        super(explicitness, tagClass, tagNo, obj);
+    }
+
+    boolean encodeConstructed()
+    {
+        return isExplicit() || obj.toASN1Primitive().toDERObject().encodeConstructed();
+    }
+
+    int encodedLength(boolean withTag) throws IOException
     {
         ASN1Primitive primitive = obj.toASN1Primitive().toDERObject();
-        int length = primitive.encodedLength();
+        boolean explicit = isExplicit();
+
+        int length = primitive.encodedLength(explicit);
 
         if (explicit)
         {
-            return StreamUtil.calculateTagLength(tagNo) + StreamUtil.calculateBodyLength(length) + length;
+            length += ASN1OutputStream.getLengthOfDL(length);
         }
-        else
-        {
-            // header length already in calculation
-            length = length - 1;
 
-            return StreamUtil.calculateTagLength(tagNo) + length;
-        }
+        length += withTag ? ASN1OutputStream.getLengthOfIdentifier(tagNo) : 0;
+
+        return length;
     }
 
     void encode(ASN1OutputStream out, boolean withTag) throws IOException
     {
+//      assert out.getClass().isAssignableFrom(DEROutputStream.class);
+
         ASN1Primitive primitive = obj.toASN1Primitive().toDERObject();
+        boolean explicit = isExplicit();
 
-        int flags = BERTags.TAGGED;
-        if (explicit || primitive.isConstructed())
+        if (withTag)
         {
-            flags |= BERTags.CONSTRUCTED;
-        }
+            int flags = tagClass;
+            if (explicit || primitive.encodeConstructed())
+            {
+                flags |= BERTags.CONSTRUCTED;
+            }
 
-        out.writeTag(withTag, flags, tagNo);
+            out.writeIdentifier(true, flags, tagNo);
+        }
 
         if (explicit)
         {
-            out.writeLength(primitive.encodedLength());
+            out.writeDL(primitive.encodedLength(true));
         }
 
         primitive.encode(out.getDERSubStream(), explicit);
     }
 
+    ASN1Sequence rebuildConstructed(ASN1Primitive primitive)
+    {
+        return new DERSequence(primitive);
+    }
+
+    ASN1TaggedObject replaceTag(int tagClass, int tagNo)
+    {
+        return new DERTaggedObject(explicitness, tagClass, tagNo, obj);
+    }
+
     ASN1Primitive toDERObject()
     {
         return this;
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DERTags.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DERTags.java
deleted file mode 100644
index eddc5f1..0000000
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DERTags.java
+++ /dev/null
@@ -1,11 +0,0 @@
-/* GENERATED SOURCE. DO NOT MODIFY. */
-package com.android.org.bouncycastle.asn1;
-
-/**
- * @deprecated use BERTags
- * @hide This class is not part of the Android public SDK API
- */
-public interface DERTags
-    extends BERTags
-{
-}
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DERUTF8String.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DERUTF8String.java
index 8136d45..b2444c2 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DERUTF8String.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DERUTF8String.java
@@ -1,88 +1,13 @@
 /* GENERATED SOURCE. DO NOT MODIFY. */
 package com.android.org.bouncycastle.asn1;
 
-import java.io.IOException;
-
-import com.android.org.bouncycastle.util.Arrays;
-import com.android.org.bouncycastle.util.Strings;
-
 /**
  * DER UTF8String object.
  * @hide This class is not part of the Android public SDK API
  */
 public class DERUTF8String
-    extends ASN1Primitive
-    implements ASN1String
+    extends ASN1UTF8String
 {
-    private final byte[]  string;
-
-    /**
-     * Return an UTF8 string from the passed in object.
-     *
-     * @param obj a DERUTF8String or an object that can be converted into one.
-     * @exception IllegalArgumentException
-     *                if the object cannot be converted.
-     * @return a DERUTF8String instance, or null
-     */
-    public static DERUTF8String getInstance(Object obj)
-    {
-        if (obj == null || obj instanceof DERUTF8String)
-        {
-            return (DERUTF8String)obj;
-        }
-
-        if (obj instanceof byte[])
-        {
-            try
-            {
-                return (DERUTF8String)fromByteArray((byte[])obj);
-            }
-            catch (Exception e)
-            {
-                throw new IllegalArgumentException("encoding error in getInstance: " + e.toString());
-            }
-        }
-
-        throw new IllegalArgumentException("illegal object in getInstance: "
-                + obj.getClass().getName());
-    }
-
-    /**
-     * Return an UTF8 String from a tagged object.
-     * 
-     * @param obj
-     *            the tagged object holding the object we want
-     * @param explicit
-     *            true if the object is meant to be explicitly tagged false
-     *            otherwise.
-     * @exception IllegalArgumentException
-     *                if the tagged object cannot be converted.
-     * @return a DERUTF8String instance, or null
-     */
-    public static DERUTF8String getInstance(
-        ASN1TaggedObject obj,
-        boolean explicit)
-    {
-        ASN1Primitive o = obj.getObject();
-
-        if (explicit || o instanceof DERUTF8String)
-        {
-            return getInstance(o);
-        }
-        else
-        {
-            return new DERUTF8String(ASN1OctetString.getInstance(o).getOctets());
-        }
-    }
-
-    /*
-     * Basic constructor - byte encoded string.
-     */
-    DERUTF8String(byte[] string)
-    {
-        this.string = string;
-    }
-
     /**
      * Basic constructor
      *
@@ -90,49 +15,11 @@
      */
     public DERUTF8String(String string)
     {
-        this.string = Strings.toUTF8ByteArray(string);
+        super(string);
     }
 
-    public String getString()
+    DERUTF8String(byte[] contents, boolean clone)
     {
-        return Strings.fromUTF8ByteArray(string);
-    }
-
-    public String toString()
-    {
-        return getString();
-    }
-
-    public int hashCode()
-    {
-        return Arrays.hashCode(string);
-    }
-
-    boolean asn1Equals(ASN1Primitive o)
-    {
-        if (!(o instanceof DERUTF8String))
-        {
-            return false;
-        }
-
-        DERUTF8String s = (DERUTF8String)o;
-
-        return Arrays.areEqual(string, s.string);
-    }
-
-    boolean isConstructed()
-    {
-        return false;
-    }
-
-    int encodedLength()
-        throws IOException
-    {
-        return 1 + StreamUtil.calculateBodyLength(string.length) + string.length;
-    }
-
-    void encode(ASN1OutputStream out, boolean withTag) throws IOException
-    {
-        out.writeEncoded(withTag, BERTags.UTF8_STRING, string);
+        super(contents, clone);
     }
 }
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DERUniversalString.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DERUniversalString.java
index 0aaef1e..56c5c9b 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DERUniversalString.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DERUniversalString.java
@@ -1,150 +1,26 @@
 /* GENERATED SOURCE. DO NOT MODIFY. */
 package com.android.org.bouncycastle.asn1;
 
-import java.io.IOException;
-
-import com.android.org.bouncycastle.util.Arrays;
-
 /**
  * DER UniversalString object - encodes UNICODE (ISO 10646) characters using 32-bit format. In Java we
  * have no way of representing this directly so we rely on byte arrays to carry these.
  * @hide This class is not part of the Android public SDK API
  */
 public class DERUniversalString
-    extends ASN1Primitive
-    implements ASN1String
+    extends ASN1UniversalString
 {
-    private static final char[]  table = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
-    private final byte[] string;
-    
-    /**
-     * Return a Universal String from the passed in object.
-     *
-     * @param obj a DERUniversalString or an object that can be converted into one.
-     * @exception IllegalArgumentException if the object cannot be converted.
-     * @return a DERUniversalString instance, or null
-     */
-    public static DERUniversalString getInstance(
-        Object  obj)
-    {
-        if (obj == null || obj instanceof DERUniversalString)
-        {
-            return (DERUniversalString)obj;
-        }
-
-        if (obj instanceof byte[])
-        {
-            try
-            {
-                return (DERUniversalString)fromByteArray((byte[])obj);
-            }
-            catch (Exception e)
-            {
-                throw new IllegalArgumentException("encoding error getInstance: " + e.toString());
-            }
-        }
-
-        throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
-    }
-
-    /**
-     * Return a Universal String from a tagged object.
-     *
-     * @param obj the tagged object holding the object we want
-     * @param explicit true if the object is meant to be explicitly
-     *              tagged false otherwise.
-     * @exception IllegalArgumentException if the tagged object cannot
-     *               be converted.
-     * @return a DERUniversalString instance, or null
-     */
-    public static DERUniversalString getInstance(
-        ASN1TaggedObject obj,
-        boolean          explicit)
-    {
-        ASN1Primitive o = obj.getObject();
-
-        if (explicit || o instanceof DERUniversalString)
-        {
-            return getInstance(o);
-        }
-        else
-        {
-            return new DERUniversalString(ASN1OctetString.getInstance(o).getOctets());
-        }
-    }
-
     /**
      * Basic constructor - byte encoded string.
      *
      * @param string the byte encoding of the string to be carried in the UniversalString object,
      */
-    public DERUniversalString(
-        byte[]   string)
+    public DERUniversalString(byte[] string)
     {
-        this.string = Arrays.clone(string);
+        this(string, true);
     }
 
-    public String getString()
+    DERUniversalString(byte[] contents, boolean clone)
     {
-        StringBuffer buf = new StringBuffer("#");
-
-        byte[] string;
-        try
-        {
-            string = getEncoded();
-        }
-        catch (IOException e)
-        {
-           throw new ASN1ParsingException("internal error encoding UniversalString");
-        }
-
-        for (int i = 0; i != string.length; i++)
-        {
-            buf.append(table[(string[i] >>> 4) & 0xf]);
-            buf.append(table[string[i] & 0xf]);
-        }
-        
-        return buf.toString();
-    }
-
-    public String toString()
-    {
-        return getString();
-    }
-
-    public byte[] getOctets()
-    {
-        return Arrays.clone(string);
-    }
-
-    boolean isConstructed()
-    {
-        return false;
-    }
-
-    int encodedLength()
-    {
-        return 1 + StreamUtil.calculateBodyLength(string.length) + string.length;
-    }
-
-    void encode(ASN1OutputStream out, boolean withTag) throws IOException
-    {
-        out.writeEncoded(withTag, BERTags.UNIVERSAL_STRING, string);
-    }
-
-    boolean asn1Equals(
-        ASN1Primitive o)
-    {
-        if (!(o instanceof DERUniversalString))
-        {
-            return false;
-        }
-
-        return Arrays.areEqual(string, ((DERUniversalString)o).string);
-    }
-    
-    public int hashCode()
-    {
-        return Arrays.hashCode(string);
+        super(contents, clone);
     }
 }
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DERVideotexString.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DERVideotexString.java
index 505072f..e1340d3 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DERVideotexString.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DERVideotexString.java
@@ -1,126 +1,19 @@
 /* GENERATED SOURCE. DO NOT MODIFY. */
 package com.android.org.bouncycastle.asn1;
 
-import java.io.IOException;
-
-import com.android.org.bouncycastle.util.Arrays;
-import com.android.org.bouncycastle.util.Strings;
-
 /**
  * @hide This class is not part of the Android public SDK API
  */
 public class DERVideotexString
-    extends ASN1Primitive
-    implements ASN1String
+    extends ASN1VideotexString
 {
-    private final byte[] string;
-    
-    /**
-     * return a Videotex String from the passed in object
-     *
-     * @param obj a DERVideotexString or an object that can be converted into one.
-     * @exception IllegalArgumentException if the object cannot be converted.
-     * @return a DERVideotexString instance, or null.
-     */
-    public static DERVideotexString getInstance(
-        Object  obj)
+    public DERVideotexString(byte[] octets)
     {
-        if (obj == null || obj instanceof DERVideotexString)
-        {
-            return (DERVideotexString)obj;
-        }
-
-        if (obj instanceof byte[])
-        {
-            try
-            {
-                return (DERVideotexString)fromByteArray((byte[])obj);
-            }
-            catch (Exception e)
-            {
-                throw new IllegalArgumentException("encoding error in getInstance: " + e.toString());
-            }
-        }
-
-        throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
+        this(octets, true);
     }
 
-    /**
-     * return a Videotex String from a tagged object.
-     *
-     * @param obj the tagged object holding the object we want
-     * @param explicit true if the object is meant to be explicitly
-     *              tagged false otherwise.
-     * @exception IllegalArgumentException if the tagged object cannot
-     *               be converted.
-     * @return a DERVideotexString instance, or null.
-     */
-    public static DERVideotexString getInstance(
-        ASN1TaggedObject obj,
-        boolean          explicit)
+    DERVideotexString(byte[] contents, boolean clone)
     {
-        ASN1Primitive o = obj.getObject();
-
-        if (explicit || o instanceof DERVideotexString)
-        {
-            return getInstance(o);
-        }
-        else
-        {
-            return new DERVideotexString(ASN1OctetString.getInstance(o).getOctets());
-        }
-    }
-
-    /**
-     * basic constructor - with bytes.
-     * @param string the byte encoding of the characters making up the string.
-     */
-    public DERVideotexString(
-        byte[]   string)
-    {
-        this.string = Arrays.clone(string);
-    }
-    
-    public byte[] getOctets()
-    {
-        return Arrays.clone(string);
-    }
-
-    boolean isConstructed()
-    {
-        return false;
-    }
-
-    int encodedLength()
-    {
-        return 1 + StreamUtil.calculateBodyLength(string.length) + string.length;
-    }
-
-    void encode(ASN1OutputStream out, boolean withTag) throws IOException
-    {
-        out.writeEncoded(withTag, BERTags.VIDEOTEX_STRING, string);
-    }
-
-    public int hashCode()
-    {
-        return Arrays.hashCode(string);
-    }
-
-    boolean asn1Equals(
-        ASN1Primitive o)
-    {
-        if (!(o instanceof DERVideotexString))
-        {
-            return false;
-        }
-
-        DERVideotexString  s = (DERVideotexString)o;
-
-        return Arrays.areEqual(string, s.string);
-    }
-
-    public String getString()
-    {
-        return Strings.fromByteArray(string);
+        super(contents, clone);
     }
 }
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DERVisibleString.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DERVisibleString.java
index dbfb118..6478807 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DERVisibleString.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DERVisibleString.java
@@ -1,11 +1,6 @@
 /* GENERATED SOURCE. DO NOT MODIFY. */
 package com.android.org.bouncycastle.asn1;
 
-import java.io.IOException;
-
-import com.android.org.bouncycastle.util.Arrays;
-import com.android.org.bouncycastle.util.Strings;
-
 /**
  * DER VisibleString object encoding ISO 646 (ASCII) character code points 32 to 126.
  * <p>
@@ -14,130 +9,20 @@
  * @hide This class is not part of the Android public SDK API
  */
 public class DERVisibleString
-    extends ASN1Primitive
-    implements ASN1String
+    extends ASN1VisibleString
 {
-    private final byte[]  string;
-
-    /**
-     * Return a Visible String from the passed in object.
-     *
-     * @param obj a DERVisibleString or an object that can be converted into one.
-     * @exception IllegalArgumentException if the object cannot be converted.
-     * @return a DERVisibleString instance, or null
-     */
-    public static DERVisibleString getInstance(
-        Object  obj)
-    {
-        if (obj == null || obj instanceof DERVisibleString)
-        {
-            return (DERVisibleString)obj;
-        }
-
-        if (obj instanceof byte[])
-        {
-            try
-            {
-                return (DERVisibleString)fromByteArray((byte[])obj);
-            }
-            catch (Exception e)
-            {
-                throw new IllegalArgumentException("encoding error in getInstance: " + e.toString());
-            }
-        }
-
-        throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
-    }
-
-    /**
-     * Return a Visible String from a tagged object.
-     *
-     * @param obj the tagged object holding the object we want
-     * @param explicit true if the object is meant to be explicitly
-     *              tagged false otherwise.
-     * @exception IllegalArgumentException if the tagged object cannot
-     *               be converted.
-     * @return a DERVisibleString instance, or null
-     */
-    public static DERVisibleString getInstance(
-        ASN1TaggedObject obj,
-        boolean          explicit)
-    {
-        ASN1Primitive o = obj.getObject();
-
-        if (explicit || o instanceof DERVisibleString)
-        {
-            return getInstance(o);
-        }
-        else
-        {
-            return new DERVisibleString(ASN1OctetString.getInstance(o).getOctets());
-        }
-    }
-
-    /*
-     * Basic constructor - byte encoded string.
-     */
-    DERVisibleString(
-        byte[]   string)
-    {
-        this.string = string;
-    }
-
     /**
      * Basic constructor
      *
      * @param string the string to be carried in the VisibleString object,
      */
-    public DERVisibleString(
-        String   string)
+    public DERVisibleString(String string)
     {
-        this.string = Strings.toByteArray(string);
+        super(string);
     }
 
-    public String getString()
+    DERVisibleString(byte[] contents, boolean clone)
     {
-        return Strings.fromByteArray(string);
-    }
-
-    public String toString()
-    {
-        return getString();
-    }
-
-    public byte[] getOctets()
-    {
-        return Arrays.clone(string);
-    }
-
-    boolean isConstructed()
-    {
-        return false;
-    }
-
-    int encodedLength()
-    {
-        return 1 + StreamUtil.calculateBodyLength(string.length) + string.length;
-    }
-
-    void encode(ASN1OutputStream out, boolean withTag) throws IOException
-    {
-        out.writeEncoded(withTag, BERTags.VISIBLE_STRING, this.string);
-    }
-
-    boolean asn1Equals(
-        ASN1Primitive o)
-    {
-        if (!(o instanceof DERVisibleString))
-        {
-            return false;
-        }
-
-        return Arrays.areEqual(string, ((DERVisibleString)o).string);
-    }
-    
-    public int hashCode()
-    {
-        return Arrays.hashCode(string);
+        super(contents, clone);
     }
 }
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DLApplicationSpecific.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DLApplicationSpecific.java
deleted file mode 100644
index 53bf2d7..0000000
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DLApplicationSpecific.java
+++ /dev/null
@@ -1,126 +0,0 @@
-/* GENERATED SOURCE. DO NOT MODIFY. */
-package com.android.org.bouncycastle.asn1;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-
-/**
- * A DER encoding version of an application specific object.
- * @hide This class is not part of the Android public SDK API
- */
-public class DLApplicationSpecific
-    extends ASN1ApplicationSpecific
-{
-    DLApplicationSpecific(
-        boolean isConstructed,
-        int     tag,
-        byte[]  octets)
-    {
-        super(isConstructed, tag, octets);
-    }
-
-    /**
-     * Create an application specific object from the passed in data. This will assume
-     * the data does not represent a constructed object.
-     *
-     * @param tag the tag number for this object.
-     * @param octets the encoding of the object's body.
-     */
-    public DLApplicationSpecific(
-        int    tag,
-        byte[] octets)
-    {
-        this(false, tag, octets);
-    }
-
-    /**
-     * Create an application specific object with a tagging of explicit/constructed.
-     *
-     * @param tag the tag number for this object.
-     * @param object the object to be contained.
-     */
-    public DLApplicationSpecific(
-        int           tag,
-        ASN1Encodable object)
-        throws IOException
-    {
-        this(true, tag, object);
-    }
-
-    /**
-     * Create an application specific object with the tagging style given by the value of constructed.
-     *
-     * @param constructed true if the object is constructed.
-     * @param tag the tag number for this object.
-     * @param object the object to be contained.
-     */
-    public DLApplicationSpecific(
-        boolean      constructed,
-        int          tag,
-        ASN1Encodable object)
-        throws IOException
-    {
-        super(constructed || object.toASN1Primitive().isConstructed(), tag, getEncoding(constructed, object));
-    }
-
-    private static byte[] getEncoding(boolean explicit, ASN1Encodable object)
-        throws IOException
-    {
-        byte[] data = object.toASN1Primitive().getEncoded(ASN1Encoding.DL);
-
-        if (explicit)
-        {
-            return data;
-        }
-        else
-        {
-            int lenBytes = getLengthOfHeader(data);
-            byte[] tmp = new byte[data.length - lenBytes];
-            System.arraycopy(data, lenBytes, tmp, 0, tmp.length);
-            return tmp;
-        }
-    }
-
-    /**
-     * Create an application specific object which is marked as constructed
-     *
-     * @param tagNo the tag number for this object.
-     * @param vec the objects making up the application specific object.
-     */
-    public DLApplicationSpecific(int tagNo, ASN1EncodableVector vec)
-    {
-        super(true, tagNo, getEncodedVector(vec));
-    }
-
-    private static byte[] getEncodedVector(ASN1EncodableVector vec)
-    {
-        ByteArrayOutputStream bOut = new ByteArrayOutputStream();
-
-        for (int i = 0; i != vec.size(); i++)
-        {
-            try
-            {
-                bOut.write(((ASN1Object)vec.get(i)).getEncoded(ASN1Encoding.DL));
-            }
-            catch (IOException e)
-            {
-                throw new ASN1ParsingException("malformed object: " + e, e);
-            }
-        }
-        return bOut.toByteArray();
-    }
-
-    /* (non-Javadoc)
-     * @see org.bouncycastle.asn1.ASN1Primitive#encode(org.bouncycastle.asn1.DEROutputStream)
-     */
-    void encode(ASN1OutputStream out, boolean withTag) throws IOException
-    {
-        int flags = BERTags.APPLICATION;
-        if (isConstructed)
-        {
-            flags |= BERTags.CONSTRUCTED;
-        }
-
-        out.writeEncoded(withTag, flags, tag, octets);
-    }
-}
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DLBitString.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DLBitString.java
index 7ee84e4..b076d4a 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DLBitString.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DLBitString.java
@@ -10,113 +10,51 @@
 public class DLBitString
     extends ASN1BitString
 {
-    /**
-     * return a Bit String that can be definite-length encoded from the passed in object.
-     *
-     * @param obj a DL or DER BitString or an object that can be converted into one.
-     * @exception IllegalArgumentException if the object cannot be converted.
-     * @return an ASN1BitString instance, or null.
-     */
-    public static ASN1BitString getInstance(
-        Object  obj)
-    {
-        if (obj == null || obj instanceof DLBitString)
-        {
-            return (DLBitString)obj;
-        }
-        if (obj instanceof DERBitString)
-        {
-            return (DERBitString)obj;
-        }
-        if (obj instanceof byte[])
-        {
-            try
-            {
-                return (ASN1BitString)fromByteArray((byte[])obj);
-            }
-            catch (Exception e)
-            {
-                throw new IllegalArgumentException("encoding error in getInstance: " + e.toString());
-            }
-        }
-
-        throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
-    }
-
-    /**
-     * return a Bit String from a tagged object.
-     *
-     * @param obj the tagged object holding the object we want
-     * @param explicit true if the object is meant to be explicitly
-     *              tagged false otherwise.
-     * @exception IllegalArgumentException if the tagged object cannot
-     *               be converted.
-     * @return an ASN1BitString instance, or null.
-     */
-    public static ASN1BitString getInstance(
-        ASN1TaggedObject obj,
-        boolean          explicit)
-    {
-        ASN1Primitive o = obj.getObject();
-
-        if (explicit || o instanceof DLBitString)
-        {
-            return getInstance(o);
-        }
-        else
-        {
-            return fromOctetString(ASN1OctetString.getInstance(o).getOctets());
-        }
-    }
-
-    protected DLBitString(byte data, int padBits)
-    {
-        super(data, padBits);
-    }
-
-    /**
-     * @param data the octets making up the bit string.
-     * @param padBits the number of extra bits at the end of the string.
-     */
-    public DLBitString(
-        byte[]  data,
-        int     padBits)
-    {
-        super(data, padBits);
-    }
-
-    public DLBitString(
-        byte[]  data)
+    public DLBitString(byte[] data)
     {
         this(data, 0);
     }
 
-    public DLBitString(
-        int value)
+    public DLBitString(byte data, int padBits)
     {
+        super(data, padBits);
+    }
+
+    public DLBitString(byte[] data, int padBits)
+    {
+        super(data, padBits);
+    }
+
+    public DLBitString(int value)
+    {
+        // TODO[asn1] Unify in single allocation of 'contents'
         super(getBytes(value), getPadBits(value));
     }
 
-    public DLBitString(
-        ASN1Encodable obj)
-        throws IOException
+    public DLBitString(ASN1Encodable obj) throws IOException
     {
+        // TODO[asn1] Unify in single allocation of 'contents'
         super(obj.toASN1Primitive().getEncoded(ASN1Encoding.DER), 0);
     }
 
-    boolean isConstructed()
+    DLBitString(byte[] contents, boolean check)
+    {
+        super(contents, check);
+    }
+
+    boolean encodeConstructed()
     {
         return false;
     }
 
-    int encodedLength()
+    int encodedLength(boolean withTag)
     {
-        return 1 + StreamUtil.calculateBodyLength(data.length + 1) + data.length + 1;
+        return ASN1OutputStream.getLengthOfEncodingDL(withTag, contents.length);
     }
 
     void encode(ASN1OutputStream out, boolean withTag) throws IOException
     {
-        out.writeEncoded(withTag, BERTags.BIT_STRING, (byte)padBits, data);
+        out.writeEncodingDL(withTag, BERTags.BIT_STRING, contents);
     }
 
     ASN1Primitive toDLObject()
@@ -124,21 +62,19 @@
         return this;
     }
 
-    static DLBitString fromOctetString(byte[] bytes)
+    static int encodedLength(boolean withTag, int contentsLength)
     {
-        if (bytes.length < 1)
-        {
-            throw new IllegalArgumentException("truncated BIT STRING detected");
-        }
+        return ASN1OutputStream.getLengthOfEncodingDL(withTag, contentsLength);
+    }
 
-        int padBits = bytes[0];
-        byte[] data = new byte[bytes.length - 1];
+    static void encode(ASN1OutputStream out, boolean withTag, byte[] buf, int off, int len) throws IOException
+    {
+        out.writeEncodingDL(withTag, BERTags.BIT_STRING, buf, off, len);
+    }
 
-        if (data.length != 0)
-        {
-            System.arraycopy(bytes, 1, data, 0, bytes.length - 1);
-        }
-
-        return new DLBitString(data, padBits);
+    static void encode(ASN1OutputStream out, boolean withTag, byte pad, byte[] buf, int off, int len)
+        throws IOException
+    {
+        out.writeEncodingDL(withTag, BERTags.BIT_STRING, pad, buf, off, len);
     }
 }
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DLBitStringParser.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DLBitStringParser.java
new file mode 100644
index 0000000..ec98b36
--- /dev/null
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DLBitStringParser.java
@@ -0,0 +1,85 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.org.bouncycastle.asn1;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Parser for a DL encoded BIT STRING.
+ * 
+ * @deprecated Check for 'ASN1BitStringParser' instead 
+ * @hide This class is not part of the Android public SDK API
+ */
+public class DLBitStringParser
+    implements ASN1BitStringParser
+{
+    private final DefiniteLengthInputStream stream;
+    private int padBits = 0;
+
+    DLBitStringParser(
+        DefiniteLengthInputStream stream)
+    {
+        this.stream = stream;
+    }
+
+    public InputStream getBitStream() throws IOException
+    {
+        return getBitStream(false);
+    }
+
+    public InputStream getOctetStream() throws IOException
+    {
+        return getBitStream(true);
+    }
+
+    public int getPadBits()
+    {
+        return padBits;
+    }
+
+    public ASN1Primitive getLoadedObject()
+        throws IOException
+    {
+        return ASN1BitString.createPrimitive(stream.toByteArray());
+    }
+
+    public ASN1Primitive toASN1Primitive()
+    {
+        try
+        {
+            return getLoadedObject();
+        }
+        catch (IOException e)
+        {
+            throw new ASN1ParsingException("IOException converting stream to byte array: " + e.getMessage(), e);
+        }
+    }
+
+    private InputStream getBitStream(boolean octetAligned) throws IOException
+    {
+        int length = stream.getRemaining();
+        if (length < 1)
+        {
+            throw new IllegalStateException("content octets cannot be empty");
+        }
+
+        padBits = stream.read();
+        if (padBits > 0)
+        {
+            if (length < 2)
+            {
+                throw new IllegalStateException("zero length data with non-zero pad bits");
+            }
+            if (padBits > 7)
+            {
+                throw new IllegalStateException("pad bits cannot be greater than 7 or less than 0");
+            }
+            if (octetAligned)
+            {
+                throw new IOException("expected octet-aligned bitstring, but found padBits: " + padBits);
+            }
+        }
+
+        return stream;
+    }
+}
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DLExternal.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DLExternal.java
index 0945a86..47d4aba 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DLExternal.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DLExternal.java
@@ -1,9 +1,6 @@
 /* GENERATED SOURCE. DO NOT MODIFY. */
 package com.android.org.bouncycastle.asn1;
 
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-
 /**
  * Class representing the Definite-Length-type External
  * @hide This class is not part of the Android public SDK API
@@ -21,11 +18,30 @@
      * <li> Anything but {@link DERTaggedObject} + data {@link DERTaggedObject} (data value form)</li>
      * </ul>
      *
-     * @throws IllegalArgumentException if input size is wrong, or
+     * @throws IllegalArgumentException if input size is wrong, or input is not an acceptable format
+     * 
+     * @deprecated Use {@link DLExternal#DLExternal(DLSequence)} instead.
      */
     public DLExternal(ASN1EncodableVector vector)
     {
-        super(vector);
+        this(DLFactory.createSequence(vector));
+    }
+
+    /**
+     * Construct a Definite-Length EXTERNAL object, the input sequence must have exactly two elements on it.
+     * <p>
+     * Acceptable input formats are:
+     * <ul>
+     * <li> {@link ASN1ObjectIdentifier} + data {@link DERTaggedObject} (direct reference form)</li>
+     * <li> {@link ASN1Integer} + data {@link DERTaggedObject} (indirect reference form)</li>
+     * <li> Anything but {@link DERTaggedObject} + data {@link DERTaggedObject} (data value form)</li>
+     * </ul>
+     *
+     * @throws IllegalArgumentException if input size is wrong, or input is not an acceptable format
+     */
+    public DLExternal(DLSequence sequence)
+    {
+        super(sequence);
     }
 
     /**
@@ -36,9 +52,10 @@
      * @param dataValueDescriptor The data value descriptor or <code>null</code> if not set.
      * @param externalData The external data in its encoded form.
      */
-    public DLExternal(ASN1ObjectIdentifier directReference, ASN1Integer indirectReference, ASN1Primitive dataValueDescriptor, DERTaggedObject externalData)
+    public DLExternal(ASN1ObjectIdentifier directReference, ASN1Integer indirectReference,
+        ASN1Primitive dataValueDescriptor, DERTaggedObject externalData)
     {
-        this(directReference, indirectReference, dataValueDescriptor, externalData.getTagNo(), externalData.toASN1Primitive());
+        super(directReference, indirectReference, dataValueDescriptor, externalData);
     }
 
     /**
@@ -50,43 +67,35 @@
      * @param encoding The encoding to be used for the external data
      * @param externalData The external data
      */
-    public DLExternal(ASN1ObjectIdentifier directReference, ASN1Integer indirectReference, ASN1Primitive dataValueDescriptor, int encoding, ASN1Primitive externalData)
+    public DLExternal(ASN1ObjectIdentifier directReference, ASN1Integer indirectReference,
+        ASN1Primitive dataValueDescriptor, int encoding, ASN1Primitive externalData)
     {
         super(directReference, indirectReference, dataValueDescriptor, encoding, externalData);
     }
 
+    ASN1Sequence buildSequence()
+    {
+        ASN1EncodableVector v = new ASN1EncodableVector(4);
+        if (directReference != null)
+        {
+            v.add(directReference);
+        }
+        if (indirectReference != null)
+        {
+            v.add(indirectReference);
+        }
+        if (dataValueDescriptor != null)
+        {
+            v.add(dataValueDescriptor.toDLObject());
+        }
+
+        v.add(new DLTaggedObject(0 == encoding, encoding, externalContent));
+
+        return new DLSequence(v);
+    }
+
     ASN1Primitive toDLObject()
     {
         return this;
     }
-
-    int encodedLength()
-        throws IOException
-    {
-        return this.getEncoded().length;
-    }
-
-    /* (non-Javadoc)
-     * @see org.bouncycastle.asn1.ASN1Primitive#encode(org.bouncycastle.asn1.DEROutputStream)
-     */
-    void encode(ASN1OutputStream out, boolean withTag) throws IOException
-    {
-        ByteArrayOutputStream baos = new ByteArrayOutputStream();
-        if (directReference != null)
-        {
-            baos.write(directReference.getEncoded(ASN1Encoding.DL));
-        }
-        if (indirectReference != null)
-        {
-            baos.write(indirectReference.getEncoded(ASN1Encoding.DL));
-        }
-        if (dataValueDescriptor != null)
-        {
-            baos.write(dataValueDescriptor.getEncoded(ASN1Encoding.DL));
-        }
-        ASN1TaggedObject obj = new DLTaggedObject(true, encoding, externalContent);
-        baos.write(obj.getEncoded(ASN1Encoding.DL));
-        
-        out.writeEncoded(withTag, BERTags.CONSTRUCTED, BERTags.EXTERNAL, baos.toByteArray());
-    }
 }
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DLFactory.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DLFactory.java
index ad993d1..738fcb2 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DLFactory.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DLFactory.java
@@ -3,10 +3,10 @@
 
 class DLFactory
 {
-    static final ASN1Sequence EMPTY_SEQUENCE = new DLSequence();
-    static final ASN1Set EMPTY_SET = new DLSet();
+    static final DLSequence EMPTY_SEQUENCE = new DLSequence();
+    static final DLSet EMPTY_SET = new DLSet();
 
-    static ASN1Sequence createSequence(ASN1EncodableVector v)
+    static DLSequence createSequence(ASN1EncodableVector v)
     {
         if (v.size() < 1)
         {
@@ -16,7 +16,7 @@
         return new DLSequence(v);
     }
 
-    static ASN1Set createSet(ASN1EncodableVector v)
+    static DLSet createSet(ASN1EncodableVector v)
     {
         if (v.size() < 1)
         {
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DLOutputStream.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DLOutputStream.java
index 1ab6e15..993882a 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DLOutputStream.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DLOutputStream.java
@@ -15,13 +15,32 @@
         super(os);
     }
 
+    DLOutputStream getDLSubStream()
+    {
+        return this;
+    }
+
+    void writeElements(ASN1Encodable[] elements)
+        throws IOException
+    {
+        for (int i = 0, count = elements.length; i < count; ++i)
+        {
+            elements[i].toASN1Primitive().toDLObject().encode(this, true);
+        }
+    }
+
     void writePrimitive(ASN1Primitive primitive, boolean withTag) throws IOException
     {
         primitive.toDLObject().encode(this, withTag);
     }
 
-    ASN1OutputStream getDLSubStream()
+    void writePrimitives(ASN1Primitive[] primitives)
+        throws IOException
     {
-        return this;
+        int count = primitives.length;
+        for (int i = 0; i < count; ++i)
+        {
+            primitives[i].toDLObject().encode(this, true);
+        }
     }
 }
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DLSequence.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DLSequence.java
index 1255e33..278bd15 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DLSequence.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DLSequence.java
@@ -10,7 +10,7 @@
 public class DLSequence
     extends ASN1Sequence
 {
-    private int bodyLength = -1;
+    private int contentsLength = -1;
 
     /**
      * Create an empty sequence
@@ -51,9 +51,9 @@
         super(elements, clone);
     }
 
-    private int getBodyLength() throws IOException
+    private int getContentsLength() throws IOException
     {
-        if (bodyLength < 0)
+        if (contentsLength < 0)
         {
             int count = elements.length;
             int totalLength = 0;
@@ -61,20 +61,18 @@
             for (int i = 0; i < count; ++i)
             {
                 ASN1Primitive dlObject = elements[i].toASN1Primitive().toDLObject();
-                totalLength += dlObject.encodedLength();
+                totalLength += dlObject.encodedLength(true);
             }
 
-            this.bodyLength = totalLength;
+            this.contentsLength = totalLength;
         }
 
-        return bodyLength;
+        return contentsLength;
     }
 
-    int encodedLength() throws IOException
+    int encodedLength(boolean withTag) throws IOException
     {
-        int length = getBodyLength();
-
-        return 1 + StreamUtil.calculateBodyLength(length) + length;
+        return ASN1OutputStream.getLengthOfEncodingDL(withTag, getContentsLength());
     }
 
     /**
@@ -87,17 +85,14 @@
      */
     void encode(ASN1OutputStream out, boolean withTag) throws IOException
     {
-        if (withTag)
-        {
-            out.write(BERTags.SEQUENCE | BERTags.CONSTRUCTED);
-        }
+        out.writeIdentifier(withTag, BERTags.CONSTRUCTED | BERTags.SEQUENCE);
 
         ASN1OutputStream dlOut = out.getDLSubStream();
 
         int count = elements.length;
-        if (bodyLength >= 0 || count > 16)
+        if (contentsLength >= 0 || count > 16)
         {
-            out.writeLength(getBodyLength());
+            out.writeDL(getContentsLength());
 
             for (int i = 0; i < count; ++i)
             {
@@ -113,11 +108,11 @@
             {
                 ASN1Primitive dlObject = elements[i].toASN1Primitive().toDLObject();
                 dlObjects[i] = dlObject;
-                totalLength += dlObject.encodedLength();
+                totalLength += dlObject.encodedLength(true);
             }
 
-            this.bodyLength = totalLength;
-            out.writeLength(totalLength);
+            this.contentsLength = totalLength;
+            out.writeDL(totalLength);
 
             for (int i = 0; i < count; ++i)
             {
@@ -126,8 +121,29 @@
         }
     }
 
+    ASN1BitString toASN1BitString()
+    {
+        return new DLBitString(BERBitString.flattenBitStrings(getConstructedBitStrings()), false);
+    }
+
+    ASN1External toASN1External()
+    {
+        return new DLExternal(this);
+    }
+
+    ASN1OctetString toASN1OctetString()
+    {
+        // NOTE: There is no DLOctetString
+        return new DEROctetString(BEROctetString.flattenOctetStrings(getConstructedOctetStrings()));
+    }
+
+    ASN1Set toASN1Set()
+    {
+        return new DLSet(false, toArrayInternal());
+    }
+
     ASN1Primitive toDLObject()
     {
         return this;
     }
-}
\ No newline at end of file
+}
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DLSequenceParser.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DLSequenceParser.java
index e5c17f6..8933837 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DLSequenceParser.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DLSequenceParser.java
@@ -6,7 +6,7 @@
 /**
  * Parser class for DL SEQUENCEs.
  *
- * TODO The class is only publicly visible to support 'instanceof' checks; provide an alternative
+ * @deprecated Check for 'ASN1SequenceParser' instead
  * @hide This class is not part of the Android public SDK API
  */
 public class DLSequenceParser
@@ -40,7 +40,7 @@
     public ASN1Primitive getLoadedObject()
         throws IOException
     {
-         return new DLSequence(_parser.readVector());
+         return DLFactory.createSequence(_parser.readVector());
     }
 
     /**
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DLSet.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DLSet.java
index 85b71a8..85932d4 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DLSet.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DLSet.java
@@ -55,7 +55,7 @@
 public class DLSet
     extends ASN1Set
 {
-    private int bodyLength = -1;
+    private int contentsLength = -1;
 
     /**
      * create an empty set
@@ -93,9 +93,14 @@
         super(isSorted, elements);
     }
 
-    private int getBodyLength() throws IOException
+    DLSet(ASN1Encodable[] elements, ASN1Encodable[] sortedElements)
     {
-        if (bodyLength < 0)
+        super(elements, sortedElements);
+    }
+
+    private int getContentsLength() throws IOException
+    {
+        if (contentsLength < 0)
         {
             int count = elements.length;
             int totalLength = 0;
@@ -103,20 +108,18 @@
             for (int i = 0; i < count; ++i)
             {
                 ASN1Primitive dlObject = elements[i].toASN1Primitive().toDLObject();
-                totalLength += dlObject.encodedLength();
+                totalLength += dlObject.encodedLength(true);
             }
 
-            this.bodyLength = totalLength;
+            this.contentsLength = totalLength;
         }
 
-        return bodyLength;
+        return contentsLength;
     }
 
-    int encodedLength() throws IOException
+    int encodedLength(boolean withTag) throws IOException
     {
-        int length = getBodyLength();
-
-        return 1 + StreamUtil.calculateBodyLength(length) + length;
+        return ASN1OutputStream.getLengthOfEncodingDL(withTag, getContentsLength());
     }
 
     /**
@@ -129,17 +132,14 @@
      */
     void encode(ASN1OutputStream out, boolean withTag) throws IOException
     {
-        if (withTag)
-        {
-            out.write(BERTags.SET | BERTags.CONSTRUCTED);
-        }
+        out.writeIdentifier(withTag, BERTags.CONSTRUCTED | BERTags.SET);
 
         ASN1OutputStream dlOut = out.getDLSubStream();
 
         int count = elements.length;
-        if (bodyLength >= 0 || count > 16)
+        if (contentsLength >= 0 || count > 16)
         {
-            out.writeLength(getBodyLength());
+            out.writeDL(getContentsLength());
 
             for (int i = 0; i < count; ++i)
             {
@@ -155,11 +155,11 @@
             {
                 ASN1Primitive dlObject = elements[i].toASN1Primitive().toDLObject();
                 dlObjects[i] = dlObject;
-                totalLength += dlObject.encodedLength();
+                totalLength += dlObject.encodedLength(true);
             }
 
-            this.bodyLength = totalLength;
-            out.writeLength(totalLength);
+            this.contentsLength = totalLength;
+            out.writeDL(totalLength);
 
             for (int i = 0; i < count; ++i)
             {
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DLSetParser.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DLSetParser.java
index 6d2ef11..9d6cd8e 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DLSetParser.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DLSetParser.java
@@ -6,7 +6,7 @@
 /**
  * Parser class for DL SETs.
  *
- * TODO The class is only publicly visible to support 'instanceof' checks; provide an alternative
+ * @deprecated Check for 'ASN1SetParser' instead
  * @hide This class is not part of the Android public SDK API
  */
 public class DLSetParser
@@ -40,7 +40,7 @@
     public ASN1Primitive getLoadedObject()
         throws IOException
     {
-        return new DLSet(_parser.readVector());
+        return DLFactory.createSet(_parser.readVector());
     }
 
     /**
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DLTaggedObject.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DLTaggedObject.java
index e9f7479..acbcb2c 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DLTaggedObject.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DLTaggedObject.java
@@ -12,60 +12,92 @@
 public class DLTaggedObject
     extends ASN1TaggedObject
 {
+    public DLTaggedObject(int tagNo, ASN1Encodable encodable)
+    {
+        super(true, tagNo, encodable);
+    }
+
+    public DLTaggedObject(int tagClass, int tagNo, ASN1Encodable encodable)
+    {
+        super(true, tagClass, tagNo, encodable);
+    }
+
     /**
      * @param explicit true if an explicitly tagged object.
      * @param tagNo the tag number for this object.
      * @param obj the tagged object.
      */
-    public DLTaggedObject(
-        boolean explicit,
-        int tagNo,
-        ASN1Encodable obj)
+    public DLTaggedObject(boolean explicit, int tagNo, ASN1Encodable obj)
     {
         super(explicit, tagNo, obj);
     }
 
-    boolean isConstructed()
+    public DLTaggedObject(boolean explicit, int tagClass, int tagNo, ASN1Encodable obj)
     {
-        return explicit || obj.toASN1Primitive().toDLObject().isConstructed();
+        super(explicit, tagClass, tagNo, obj);
     }
 
-    int encodedLength()
-        throws IOException
+    DLTaggedObject(int explicitness, int tagClass, int tagNo, ASN1Encodable obj)
     {
-        int length = obj.toASN1Primitive().toDLObject().encodedLength();
+        super(explicitness, tagClass, tagNo, obj);
+    }
+
+    boolean encodeConstructed()
+    {
+        return isExplicit() || obj.toASN1Primitive().toDLObject().encodeConstructed();
+    }
+
+    int encodedLength(boolean withTag) throws IOException
+    {
+        ASN1Primitive primitive = obj.toASN1Primitive().toDLObject();
+        boolean explicit = isExplicit();
+
+        int length = primitive.encodedLength(explicit);
 
         if (explicit)
         {
-            return  StreamUtil.calculateTagLength(tagNo) + StreamUtil.calculateBodyLength(length) + length;
+            length += ASN1OutputStream.getLengthOfDL(length);
         }
-        else
-        {
-            // header length already in calculation
-            length = length - 1;
 
-            return StreamUtil.calculateTagLength(tagNo) + length;
-        }
+        length += withTag ? ASN1OutputStream.getLengthOfIdentifier(tagNo) : 0;
+
+        return length;
     }
 
     void encode(ASN1OutputStream out, boolean withTag) throws IOException
     {
+//        assert out.getClass().isAssignableFrom(DLOutputStream.class);
+
         ASN1Primitive primitive = obj.toASN1Primitive().toDLObject();
+        boolean explicit = isExplicit();
 
-        int flags = BERTags.TAGGED;
-        if (explicit || primitive.isConstructed())
+        if (withTag)
         {
-            flags |= BERTags.CONSTRUCTED;
-        }
+            int flags = tagClass;
+            if (explicit || primitive.encodeConstructed())
+            {
+                flags |= BERTags.CONSTRUCTED;
+            }
 
-        out.writeTag(withTag, flags, tagNo);
+            out.writeIdentifier(true, flags, tagNo);
+        }
 
         if (explicit)
         {
-            out.writeLength(primitive.encodedLength());
+            out.writeDL(primitive.encodedLength(true));
         }
 
-        out.getDLSubStream().writePrimitive(primitive, explicit);
+        primitive.encode(out.getDLSubStream(), explicit);
+    }
+
+    ASN1Sequence rebuildConstructed(ASN1Primitive primitive)
+    {
+        return new DLSequence(primitive);
+    }
+
+    ASN1TaggedObject replaceTag(int tagClass, int tagNo)
+    {
+        return new DLTaggedObject(explicitness, tagClass, tagNo, obj);
     }
 
     ASN1Primitive toDLObject()
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DLTaggedObjectParser.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DLTaggedObjectParser.java
new file mode 100644
index 0000000..e1b68e0
--- /dev/null
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DLTaggedObjectParser.java
@@ -0,0 +1,74 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.org.bouncycastle.asn1;
+
+import java.io.IOException;
+
+/**
+ * Parser for definite-length tagged objects.
+ */
+class DLTaggedObjectParser
+    extends BERTaggedObjectParser
+{
+    private final boolean _constructed;
+
+    DLTaggedObjectParser(int tagClass, int tagNo, boolean constructed, ASN1StreamParser parser)
+    {
+        super(tagClass, tagNo, parser);
+
+        _constructed = constructed;
+    }
+
+    /**
+     * Return an in-memory, encodable, representation of the tagged object.
+     *
+     * @return an ASN1TaggedObject.
+     * @throws IOException if there is an issue loading the data.
+     */
+    public ASN1Primitive getLoadedObject()
+        throws IOException
+    {
+        return _parser.loadTaggedDL(_tagClass, _tagNo, _constructed);
+    }
+
+    public ASN1Encodable parseBaseUniversal(boolean declaredExplicit, int baseTagNo) throws IOException
+    {
+        if (declaredExplicit)
+        {
+            if (!_constructed)
+            {
+                throw new IOException("Explicit tags must be constructed (see X.690 8.14.2)");
+            }
+
+            return _parser.parseObject(baseTagNo);
+        }
+
+        return _constructed
+            ?  _parser.parseImplicitConstructedDL(baseTagNo)
+            :  _parser.parseImplicitPrimitive(baseTagNo);
+    }
+
+    public ASN1Encodable parseExplicitBaseObject() throws IOException
+    {
+        if (!_constructed)
+        {
+            throw new IOException("Explicit tags must be constructed (see X.690 8.14.2)");
+        }
+
+        return _parser.readObject();
+    }
+
+    public ASN1TaggedObjectParser parseExplicitBaseTagged() throws IOException
+    {
+        if (!_constructed)
+        {
+            throw new IOException("Explicit tags must be constructed (see X.690 8.14.2)");
+        }
+
+        return _parser.parseTaggedObject();
+    }
+
+    public ASN1TaggedObjectParser parseImplicitBaseTagged(int baseTagClass, int baseTagNo) throws IOException
+    {
+        return new DLTaggedObjectParser(baseTagClass, baseTagNo, _constructed, _parser);
+    }
+}
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DefiniteLengthInputStream.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DefiniteLengthInputStream.java
index 5ab6e11..3d95d72 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DefiniteLengthInputStream.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DefiniteLengthInputStream.java
@@ -26,18 +26,18 @@
     {
         super(in, limit);
 
-        if (length < 0)
+        if (length <= 0)
         {
-            throw new IllegalArgumentException("negative lengths not allowed");
+            if (length < 0)
+            {
+                throw new IllegalArgumentException("negative lengths not allowed");
+            }
+
+            setParentEofDetect(true);
         }
 
         this._originalLength = length;
         this._remaining = length;
-
-        if (length == 0)
-        {
-            setParentEofDetect(true);
-        }
     }
 
     int getRemaining()
@@ -112,7 +112,7 @@
             throw new IOException("corrupted stream - out of bounds length found: " + _remaining + " >= " + limit);
         }
 
-        if ((_remaining -= Streams.readFully(_in, buf)) != 0)
+        if ((_remaining -= Streams.readFully(_in, buf, 0, buf.length)) != 0)
         {
             throw new EOFException("DEF length " + _originalLength + " object truncated by " + _remaining);
         }
@@ -135,7 +135,7 @@
         }
 
         byte[] bytes = new byte[_remaining];
-        if ((_remaining -= Streams.readFully(_in, bytes)) != 0)
+        if ((_remaining -= Streams.readFully(_in, bytes, 0, bytes.length)) != 0)
         {
             throw new EOFException("DEF length " + _originalLength + " object truncated by " + _remaining);
         }
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/LazyConstructionEnumeration.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/LazyConstructionEnumeration.java
index ebda1a7..a196822 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/LazyConstructionEnumeration.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/LazyConstructionEnumeration.java
@@ -41,7 +41,7 @@
         }
         catch (IOException e)
         {
-            throw new ASN1ParsingException("malformed DER construction: " + e, e);
+            throw new ASN1ParsingException("malformed ASN.1: " + e, e);
         }
     }
 }
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/LazyEncodedSequence.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/LazyEncodedSequence.java
index 8ec6478..86ec78d 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/LazyEncodedSequence.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/LazyEncodedSequence.java
@@ -18,18 +18,24 @@
         // NOTE: Initially, the actual 'elements' will be empty
         super();
 
+        if (null == encoded)
+        {
+            throw new NullPointerException("'encoded' cannot be null");
+        }
+
         this.encoded = encoded;
     }
 
-    public synchronized ASN1Encodable getObjectAt(int index)
+    public ASN1Encodable getObjectAt(int index)
     {
         force();
 
         return super.getObjectAt(index);
     }
 
-    public synchronized Enumeration getObjects()
+    public Enumeration getObjects()
     {
+        byte[] encoded = getContents();
         if (null != encoded)
         {
             return new LazyConstructionEnumeration(encoded);
@@ -38,28 +44,28 @@
         return super.getObjects();
     }
 
-    public synchronized int hashCode()
+    public int hashCode()
     {
         force();
 
         return super.hashCode();
     }
 
-    public synchronized Iterator<ASN1Encodable> iterator()
+    public Iterator<ASN1Encodable> iterator()
     {
         force();
 
         return super.iterator();
     }
 
-    public synchronized int size()
+    public int size()
     {
         force();
 
         return super.size();
     }
 
-    public synchronized ASN1Encodable[] toArray()
+    public ASN1Encodable[] toArray()
     {
         force();
 
@@ -73,27 +79,48 @@
         return super.toArrayInternal();
     }
 
-    synchronized int encodedLength()
+    int encodedLength(boolean withTag)
         throws IOException
     {
+        byte[] encoded = getContents();
         if (null != encoded)
         {
-            return 1 + StreamUtil.calculateBodyLength(encoded.length) + encoded.length;
+            return ASN1OutputStream.getLengthOfEncodingDL(withTag, encoded.length);
         }
 
-        return super.toDLObject().encodedLength();
+        return super.toDLObject().encodedLength(withTag);
     }
 
-    synchronized void encode(ASN1OutputStream out, boolean withTag) throws IOException
+    void encode(ASN1OutputStream out, boolean withTag) throws IOException
     {
+        byte[] encoded = getContents();
         if (null != encoded)
         {
-            out.writeEncoded(withTag, BERTags.SEQUENCE | BERTags.CONSTRUCTED, encoded);
+            out.writeEncodingDL(withTag, BERTags.CONSTRUCTED | BERTags.SEQUENCE, encoded);
+            return;
         }
-        else
-        {
-            super.toDLObject().encode(out, withTag);
-        }
+
+        super.toDLObject().encode(out, withTag);
+    }
+
+    ASN1BitString toASN1BitString()
+    {
+        return ((ASN1Sequence)toDLObject()).toASN1BitString();
+    }
+
+    ASN1External toASN1External()
+    {
+        return ((ASN1Sequence)toDLObject()).toASN1External();
+    }
+
+    ASN1OctetString toASN1OctetString()
+    {
+        return ((ASN1Sequence)toDLObject()).toASN1OctetString();
+    }
+
+    ASN1Set toASN1Set()
+    {
+        return ((ASN1Sequence)toDLObject()).toASN1Set();
     }
 
     synchronized ASN1Primitive toDERObject()
@@ -110,20 +137,28 @@
         return super.toDLObject();
     }
 
-    private void force()
+    private synchronized void force()
     {
         if (null != encoded)
         {
-            ASN1EncodableVector v = new ASN1EncodableVector();
-
-            Enumeration en = new LazyConstructionEnumeration(encoded);
-            while (en.hasMoreElements())
+            ASN1InputStream aIn = new ASN1InputStream(encoded, true);
+            try
             {
-                v.add((ASN1Primitive)en.nextElement());
-            }
+                ASN1EncodableVector v = aIn.readVector();
+                aIn.close();
 
-            this.elements = v.takeElements();
-            this.encoded = null;
+                this.elements = v.takeElements();
+                this.encoded = null;
+            }
+            catch (IOException e)
+            {
+                throw new ASN1ParsingException("malformed ASN.1: " + e, e);
+            }
         }
     }
+
+    private synchronized byte[] getContents()
+    {
+        return encoded;
+    }
 }
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DateUtil.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/LocaleUtil.java
similarity index 77%
rename from repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DateUtil.java
rename to repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/LocaleUtil.java
index 0c322a1..9d04f66 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/DateUtil.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/LocaleUtil.java
@@ -8,13 +8,18 @@
 import java.util.Locale;
 import java.util.Map;
 
-class DateUtil
-{
-    private static Long ZERO = longValueOf(0);
+import com.android.org.bouncycastle.util.Longs;
 
+/**
+ * ASN.1 uses an EN locale for its internal formatting. This class finds the nearest equivalent in the
+ * current JVM to ensure date formats are always respected.
+ * @hide This class is not part of the Android public SDK API
+ */
+public class LocaleUtil
+{
     private static final Map localeCache = new HashMap();
 
-    static Locale EN_Locale = forEN();
+    public static Locale EN_Locale = forEN();
 
     private static Locale forEN()
     {
@@ -53,19 +58,12 @@
                 SimpleDateFormat dateF = new SimpleDateFormat("yyyyMMddHHmmssz");
                 long v = dateF.parse("19700101000000GMT+00:00").getTime();
 
-                if (v == 0)
-                {
-                    adj = ZERO;
-                }
-                else
-                {
-                    adj = longValueOf(v);
-                }
+                adj = longValueOf(v);
 
                 localeCache.put(locale, adj);
             }
 
-            if (adj != ZERO)
+            if (adj.longValue() != 0L)
             {
                 return new Date(date.getTime() - adj.longValue());
             }
@@ -76,6 +74,6 @@
 
     private static Long longValueOf(long v)
     {
-        return Long.valueOf(v);
+        return Longs.valueOf(v);
     }
 }
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/bc/BCObjectIdentifiers.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/bc/BCObjectIdentifiers.java
index 74005e9..ff17380 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/bc/BCObjectIdentifiers.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/bc/BCObjectIdentifiers.java
@@ -4,75 +4,103 @@
 import com.android.org.bouncycastle.asn1.ASN1ObjectIdentifier;
 
 /**
- *  Object Identifiers belonging to iso.org.dod.internet.private.enterprise.legion-of-the-bouncy-castle (1.3.6.1.4.1.22554)
+ * Object Identifiers belonging to iso.org.dod.internet.private.enterprise.legion-of-the-bouncy-castle (1.3.6.1.4.1.22554)
  * @hide This class is not part of the Android public SDK API
  */
 public interface BCObjectIdentifiers
 {
     /**
-     *  iso.org.dod.internet.private.enterprise.legion-of-the-bouncy-castle
-     *<p>
-     *  1.3.6.1.4.1.22554
+     * iso.org.dod.internet.private.enterprise.legion-of-the-bouncy-castle
+     * <p>
+     * 1.3.6.1.4.1.22554
      */
-    public static final ASN1ObjectIdentifier bc = new ASN1ObjectIdentifier("1.3.6.1.4.1.22554");
+    ASN1ObjectIdentifier bc = new ASN1ObjectIdentifier("1.3.6.1.4.1.22554");
 
     /**
      * pbe(1) algorithms
      * <p>
      * 1.3.6.1.4.1.22554.1
      */
-    public static final ASN1ObjectIdentifier bc_pbe        = bc.branch("1");
+    ASN1ObjectIdentifier bc_pbe = bc.branch("1");
 
     /**
      * SHA-1(1)
      * <p>
      * 1.3.6.1.4.1.22554.1.1
      */
-    public static final ASN1ObjectIdentifier bc_pbe_sha1   = bc_pbe.branch("1");
+    ASN1ObjectIdentifier bc_pbe_sha1 = bc_pbe.branch("1");
 
-    /** SHA-2.SHA-256; 1.3.6.1.4.1.22554.1.2.1 */
-    public static final ASN1ObjectIdentifier bc_pbe_sha256 = bc_pbe.branch("2.1");
-    /** SHA-2.SHA-384; 1.3.6.1.4.1.22554.1.2.2 */
-    public static final ASN1ObjectIdentifier bc_pbe_sha384 = bc_pbe.branch("2.2");
-    /** SHA-2.SHA-512; 1.3.6.1.4.1.22554.1.2.3 */
-    public static final ASN1ObjectIdentifier bc_pbe_sha512 = bc_pbe.branch("2.3");
-    /** SHA-2.SHA-224; 1.3.6.1.4.1.22554.1.2.4 */
-    public static final ASN1ObjectIdentifier bc_pbe_sha224 = bc_pbe.branch("2.4");
+    /**
+     * SHA-2.SHA-256; 1.3.6.1.4.1.22554.1.2.1
+     */
+    ASN1ObjectIdentifier bc_pbe_sha256 = bc_pbe.branch("2.1");
+    /**
+     * SHA-2.SHA-384; 1.3.6.1.4.1.22554.1.2.2
+     */
+    ASN1ObjectIdentifier bc_pbe_sha384 = bc_pbe.branch("2.2");
+    /**
+     * SHA-2.SHA-512; 1.3.6.1.4.1.22554.1.2.3
+     */
+    ASN1ObjectIdentifier bc_pbe_sha512 = bc_pbe.branch("2.3");
+    /**
+     * SHA-2.SHA-224; 1.3.6.1.4.1.22554.1.2.4
+     */
+    ASN1ObjectIdentifier bc_pbe_sha224 = bc_pbe.branch("2.4");
 
     /**
      * PKCS-5(1)|PKCS-12(2)
      */
-    /** SHA-1.PKCS5;  1.3.6.1.4.1.22554.1.1.1 */
-    public static final ASN1ObjectIdentifier bc_pbe_sha1_pkcs5    = bc_pbe_sha1.branch("1");
-    /** SHA-1.PKCS12; 1.3.6.1.4.1.22554.1.1.2 */
-    public static final ASN1ObjectIdentifier bc_pbe_sha1_pkcs12   = bc_pbe_sha1.branch("2");
+    /**
+     * SHA-1.PKCS5;  1.3.6.1.4.1.22554.1.1.1
+     */
+    ASN1ObjectIdentifier bc_pbe_sha1_pkcs5 = bc_pbe_sha1.branch("1");
+    /**
+     * SHA-1.PKCS12; 1.3.6.1.4.1.22554.1.1.2
+     */
+    ASN1ObjectIdentifier bc_pbe_sha1_pkcs12 = bc_pbe_sha1.branch("2");
 
-    /** SHA-256.PKCS12; 1.3.6.1.4.1.22554.1.2.1.1 */
-    public static final ASN1ObjectIdentifier bc_pbe_sha256_pkcs5  = bc_pbe_sha256.branch("1");
-    /** SHA-256.PKCS12; 1.3.6.1.4.1.22554.1.2.1.2 */
-    public static final ASN1ObjectIdentifier bc_pbe_sha256_pkcs12 = bc_pbe_sha256.branch("2");
+    /**
+     * SHA-256.PKCS5; 1.3.6.1.4.1.22554.1.2.1.1
+     */
+    ASN1ObjectIdentifier bc_pbe_sha256_pkcs5 = bc_pbe_sha256.branch("1");
+    /**
+     * SHA-256.PKCS12; 1.3.6.1.4.1.22554.1.2.1.2
+     */
+    ASN1ObjectIdentifier bc_pbe_sha256_pkcs12 = bc_pbe_sha256.branch("2");
 
     /**
      * AES(1) . (CBC-128(2)|CBC-192(22)|CBC-256(42))
      */
-    /** 1.3.6.1.4.1.22554.1.1.2.1.2 */
-    public static final ASN1ObjectIdentifier bc_pbe_sha1_pkcs12_aes128_cbc   = bc_pbe_sha1_pkcs12.branch("1.2");
-    /** 1.3.6.1.4.1.22554.1.1.2.1.22 */
-    public static final ASN1ObjectIdentifier bc_pbe_sha1_pkcs12_aes192_cbc   = bc_pbe_sha1_pkcs12.branch("1.22");
-    /** 1.3.6.1.4.1.22554.1.1.2.1.42 */
-    public static final ASN1ObjectIdentifier bc_pbe_sha1_pkcs12_aes256_cbc   = bc_pbe_sha1_pkcs12.branch("1.42");
+    /**
+     * 1.3.6.1.4.1.22554.1.1.2.1.2
+     */
+    ASN1ObjectIdentifier bc_pbe_sha1_pkcs12_aes128_cbc = bc_pbe_sha1_pkcs12.branch("1.2");
+    /**
+     * 1.3.6.1.4.1.22554.1.1.2.1.22
+     */
+    ASN1ObjectIdentifier bc_pbe_sha1_pkcs12_aes192_cbc = bc_pbe_sha1_pkcs12.branch("1.22");
+    /**
+     * 1.3.6.1.4.1.22554.1.1.2.1.42
+     */
+    ASN1ObjectIdentifier bc_pbe_sha1_pkcs12_aes256_cbc = bc_pbe_sha1_pkcs12.branch("1.42");
 
-    /** 1.3.6.1.4.1.22554.1.1.2.2.2 */
-    public static final ASN1ObjectIdentifier bc_pbe_sha256_pkcs12_aes128_cbc = bc_pbe_sha256_pkcs12.branch("1.2");
-    /** 1.3.6.1.4.1.22554.1.1.2.2.22 */
-    public static final ASN1ObjectIdentifier bc_pbe_sha256_pkcs12_aes192_cbc = bc_pbe_sha256_pkcs12.branch("1.22");
-    /** 1.3.6.1.4.1.22554.1.1.2.2.42 */
-    public static final ASN1ObjectIdentifier bc_pbe_sha256_pkcs12_aes256_cbc = bc_pbe_sha256_pkcs12.branch("1.42");
+    /**
+     * 1.3.6.1.4.1.22554.1.1.2.2.2
+     */
+    ASN1ObjectIdentifier bc_pbe_sha256_pkcs12_aes128_cbc = bc_pbe_sha256_pkcs12.branch("1.2");
+    /**
+     * 1.3.6.1.4.1.22554.1.1.2.2.22
+     */
+    ASN1ObjectIdentifier bc_pbe_sha256_pkcs12_aes192_cbc = bc_pbe_sha256_pkcs12.branch("1.22");
+    /**
+     * 1.3.6.1.4.1.22554.1.1.2.2.42
+     */
+    ASN1ObjectIdentifier bc_pbe_sha256_pkcs12_aes256_cbc = bc_pbe_sha256_pkcs12.branch("1.42");
 
     /**
      * signature(2) algorithms
      */
-    public static final ASN1ObjectIdentifier bc_sig        = bc.branch("2");
+    ASN1ObjectIdentifier bc_sig = bc.branch("2");
 
     // BEGIN Android-removed: Unsupported algorithms
     /*
@@ -83,101 +111,329 @@
     public static final ASN1ObjectIdentifier sphincs256_with_BLAKE512        = sphincs256.branch("1");
     public static final ASN1ObjectIdentifier sphincs256_with_SHA512          = sphincs256.branch("2");
     public static final ASN1ObjectIdentifier sphincs256_with_SHA3_512        = sphincs256.branch("3");
+    ASN1ObjectIdentifier sphincs256 = bc_sig.branch("1");
+    ASN1ObjectIdentifier sphincs256_with_BLAKE512 = sphincs256.branch("1");
+    ASN1ObjectIdentifier sphincs256_with_SHA512 = sphincs256.branch("2");
+    ASN1ObjectIdentifier sphincs256_with_SHA3_512 = sphincs256.branch("3");
+     */
 
     /**
      * XMSS
      */
-    public static final ASN1ObjectIdentifier xmss = bc_sig.branch("2");
-    public static final ASN1ObjectIdentifier xmss_SHA256ph = xmss.branch("1");
-    public static final ASN1ObjectIdentifier xmss_SHA512ph = xmss.branch("2");
-    public static final ASN1ObjectIdentifier xmss_SHAKE128ph = xmss.branch("3");
-    public static final ASN1ObjectIdentifier xmss_SHAKE256ph = xmss.branch("4");
-    public static final ASN1ObjectIdentifier xmss_SHA256 = xmss.branch("5");
-    public static final ASN1ObjectIdentifier xmss_SHA512 = xmss.branch("6");
-    public static final ASN1ObjectIdentifier xmss_SHAKE128 = xmss.branch("7");
-    public static final ASN1ObjectIdentifier xmss_SHAKE256 = xmss.branch("8");
+    ASN1ObjectIdentifier xmss = bc_sig.branch("2");
+    ASN1ObjectIdentifier xmss_SHA256ph = xmss.branch("1");
+    ASN1ObjectIdentifier xmss_SHA512ph = xmss.branch("2");
+    ASN1ObjectIdentifier xmss_SHAKE128_512ph = xmss.branch("3");
+    ASN1ObjectIdentifier xmss_SHAKE256_1024ph = xmss.branch("4");
+    ASN1ObjectIdentifier xmss_SHA256 = xmss.branch("5");
+    ASN1ObjectIdentifier xmss_SHA512 = xmss.branch("6");
+    ASN1ObjectIdentifier xmss_SHAKE128 = xmss.branch("7");
+    ASN1ObjectIdentifier xmss_SHAKE256 = xmss.branch("8");
+    ASN1ObjectIdentifier xmss_SHAKE128ph = xmss.branch("9");
+    ASN1ObjectIdentifier xmss_SHAKE256ph = xmss.branch("10");
 
     /**
      * XMSS^MT
      */
-    public static final ASN1ObjectIdentifier xmss_mt = bc_sig.branch("3");
-    public static final ASN1ObjectIdentifier xmss_mt_SHA256ph = xmss_mt.branch("1");
-    public static final ASN1ObjectIdentifier xmss_mt_SHA512ph = xmss_mt.branch("2");
-    public static final ASN1ObjectIdentifier xmss_mt_SHAKE128ph = xmss_mt.branch("3");
-    public static final ASN1ObjectIdentifier xmss_mt_SHAKE256ph = xmss_mt.branch("4");
-    public static final ASN1ObjectIdentifier xmss_mt_SHA256 = xmss_mt.branch("5");
-    public static final ASN1ObjectIdentifier xmss_mt_SHA512 = xmss_mt.branch("6");
-    public static final ASN1ObjectIdentifier xmss_mt_SHAKE128 = xmss_mt.branch("7");
-    public static final ASN1ObjectIdentifier xmss_mt_SHAKE256 = xmss_mt.branch("8");
-
-    // old OIDs.
-    /**
-     * @deprecated use xmss_SHA256ph
-     */
-    public static final ASN1ObjectIdentifier xmss_with_SHA256          = xmss_SHA256ph;
-    /**
-     * @deprecated use xmss_SHA512ph
-     */
-    public static final ASN1ObjectIdentifier xmss_with_SHA512 = xmss_SHA512ph;
-    /**
-     * @deprecated use xmss_SHAKE128ph
-     */
-    public static final ASN1ObjectIdentifier xmss_with_SHAKE128 = xmss_SHAKE128ph;
-    /**
-     * @deprecated use xmss_SHAKE256ph
-     */
-    public static final ASN1ObjectIdentifier xmss_with_SHAKE256        = xmss_SHAKE256ph;
-
-    /**
-     * @deprecated use xmss_mt_SHA256ph
-     */
-    public static final ASN1ObjectIdentifier xmss_mt_with_SHA256          = xmss_mt_SHA256ph;
-    /**
-     * @deprecated use xmss_mt_SHA512ph
-     */
-    public static final ASN1ObjectIdentifier xmss_mt_with_SHA512          = xmss_mt_SHA512ph;
-    /**
-     * @deprecated use xmss_mt_SHAKE128ph
-     */
-    public static final ASN1ObjectIdentifier xmss_mt_with_SHAKE128        = xmss_mt_SHAKE128;
-    /**
-     * @deprecated use xmss_mt_SHAKE256ph
-     */
-    public static final ASN1ObjectIdentifier xmss_mt_with_SHAKE256        = xmss_mt_SHAKE256;
+    ASN1ObjectIdentifier xmss_mt = bc_sig.branch("3");
+    ASN1ObjectIdentifier xmss_mt_SHA256ph = xmss_mt.branch("1");
+    ASN1ObjectIdentifier xmss_mt_SHA512ph = xmss_mt.branch("2");
+    ASN1ObjectIdentifier xmss_mt_SHAKE128_512ph = xmss_mt.branch("3");
+    ASN1ObjectIdentifier xmss_mt_SHAKE256_1024ph = xmss_mt.branch("4");
+    ASN1ObjectIdentifier xmss_mt_SHA256 = xmss_mt.branch("5");
+    ASN1ObjectIdentifier xmss_mt_SHA512 = xmss_mt.branch("6");
+    ASN1ObjectIdentifier xmss_mt_SHAKE128 = xmss_mt.branch("7");
+    ASN1ObjectIdentifier xmss_mt_SHAKE256 = xmss_mt.branch("8");
+    ASN1ObjectIdentifier xmss_mt_SHAKE128ph = xmss_mt.branch("9");
+    ASN1ObjectIdentifier xmss_mt_SHAKE256ph = xmss_mt.branch("10");
 
     /**
      * qTESLA
      */
-    public static final ASN1ObjectIdentifier qTESLA = bc_sig.branch("4");
+    ASN1ObjectIdentifier qTESLA = bc_sig.branch("4");
 
-    public static final ASN1ObjectIdentifier qTESLA_Rnd1_I = qTESLA.branch("1");
-    public static final ASN1ObjectIdentifier qTESLA_Rnd1_III_size = qTESLA.branch("2");
-    public static final ASN1ObjectIdentifier qTESLA_Rnd1_III_speed = qTESLA.branch("3");
-    public static final ASN1ObjectIdentifier qTESLA_Rnd1_p_I = qTESLA.branch("4");
-    public static final ASN1ObjectIdentifier qTESLA_Rnd1_p_III = qTESLA.branch("5");
+    ASN1ObjectIdentifier qTESLA_Rnd1_I = qTESLA.branch("1");
+    ASN1ObjectIdentifier qTESLA_Rnd1_III_size = qTESLA.branch("2");
+    ASN1ObjectIdentifier qTESLA_Rnd1_III_speed = qTESLA.branch("3");
+    ASN1ObjectIdentifier qTESLA_Rnd1_p_I = qTESLA.branch("4");
+    ASN1ObjectIdentifier qTESLA_Rnd1_p_III = qTESLA.branch("5");
 
 
-    public static final ASN1ObjectIdentifier qTESLA_p_I = qTESLA.branch("11");
-    public static final ASN1ObjectIdentifier qTESLA_p_III = qTESLA.branch("12");
+    ASN1ObjectIdentifier qTESLA_p_I = qTESLA.branch("11");
+    ASN1ObjectIdentifier qTESLA_p_III = qTESLA.branch("12");
+
+    /**
+     * SPHINCS+
+     */
+    ASN1ObjectIdentifier sphincsPlus = bc_sig.branch("5");
+    ASN1ObjectIdentifier sphincsPlus_sha2_128s_r3 = sphincsPlus.branch("1");
+    ASN1ObjectIdentifier sphincsPlus_sha2_128f_r3 = sphincsPlus.branch("2");
+    ASN1ObjectIdentifier sphincsPlus_shake_128s_r3 = sphincsPlus.branch("3");
+    ASN1ObjectIdentifier sphincsPlus_shake_128f_r3 = sphincsPlus.branch("4");
+    ASN1ObjectIdentifier sphincsPlus_haraka_128s_r3 = sphincsPlus.branch("5");
+    ASN1ObjectIdentifier sphincsPlus_haraka_128f_r3 = sphincsPlus.branch("6");
+
+    ASN1ObjectIdentifier sphincsPlus_sha2_192s_r3 = sphincsPlus.branch("7");
+    ASN1ObjectIdentifier sphincsPlus_sha2_192f_r3 = sphincsPlus.branch("8");
+    ASN1ObjectIdentifier sphincsPlus_shake_192s_r3 = sphincsPlus.branch("9");
+    ASN1ObjectIdentifier sphincsPlus_shake_192f_r3 = sphincsPlus.branch("10");
+    ASN1ObjectIdentifier sphincsPlus_haraka_192s_r3 = sphincsPlus.branch("11");
+    ASN1ObjectIdentifier sphincsPlus_haraka_192f_r3 = sphincsPlus.branch("12");
+
+    ASN1ObjectIdentifier sphincsPlus_sha2_256s_r3 = sphincsPlus.branch("13");
+    ASN1ObjectIdentifier sphincsPlus_sha2_256f_r3 = sphincsPlus.branch("14");
+    ASN1ObjectIdentifier sphincsPlus_shake_256s_r3 = sphincsPlus.branch("15");
+    ASN1ObjectIdentifier sphincsPlus_shake_256f_r3 = sphincsPlus.branch("16");
+    ASN1ObjectIdentifier sphincsPlus_haraka_256s_r3 = sphincsPlus.branch("17");
+    ASN1ObjectIdentifier sphincsPlus_haraka_256f_r3 = sphincsPlus.branch("18");
+
+    ASN1ObjectIdentifier sphincsPlus_sha2_128s_r3_simple = sphincsPlus.branch("19");
+    ASN1ObjectIdentifier sphincsPlus_sha2_128f_r3_simple = sphincsPlus.branch("20");
+    ASN1ObjectIdentifier sphincsPlus_shake_128s_r3_simple = sphincsPlus.branch("21");
+    ASN1ObjectIdentifier sphincsPlus_shake_128f_r3_simple = sphincsPlus.branch("22");
+    ASN1ObjectIdentifier sphincsPlus_haraka_128s_r3_simple = sphincsPlus.branch("23");
+    ASN1ObjectIdentifier sphincsPlus_haraka_128f_r3_simple = sphincsPlus.branch("24");
+
+    ASN1ObjectIdentifier sphincsPlus_sha2_192s_r3_simple = sphincsPlus.branch("25");
+    ASN1ObjectIdentifier sphincsPlus_sha2_192f_r3_simple = sphincsPlus.branch("26");
+    ASN1ObjectIdentifier sphincsPlus_shake_192s_r3_simple = sphincsPlus.branch("27");
+    ASN1ObjectIdentifier sphincsPlus_shake_192f_r3_simple = sphincsPlus.branch("28");
+    ASN1ObjectIdentifier sphincsPlus_haraka_192s_r3_simple = sphincsPlus.branch("29");
+    ASN1ObjectIdentifier sphincsPlus_haraka_192f_r3_simple = sphincsPlus.branch("30");
+
+    ASN1ObjectIdentifier sphincsPlus_sha2_256s_r3_simple = sphincsPlus.branch("31");
+    ASN1ObjectIdentifier sphincsPlus_sha2_256f_r3_simple = sphincsPlus.branch("32");
+    ASN1ObjectIdentifier sphincsPlus_shake_256s_r3_simple = sphincsPlus.branch("33");
+    ASN1ObjectIdentifier sphincsPlus_shake_256f_r3_simple = sphincsPlus.branch("34");
+    ASN1ObjectIdentifier sphincsPlus_haraka_256s_r3_simple = sphincsPlus.branch("35");
+    ASN1ObjectIdentifier sphincsPlus_haraka_256f_r3_simple = sphincsPlus.branch("36");
+
+
+    ASN1ObjectIdentifier sphincsPlus_interop = new ASN1ObjectIdentifier("1.3.9999.6");
+
+    ASN1ObjectIdentifier sphincsPlus_sha2_128f = new ASN1ObjectIdentifier("1.3.9999.6.4.13");
+    ASN1ObjectIdentifier sphincsPlus_sha2_128s = new ASN1ObjectIdentifier("1.3.9999.6.4.16");
+    ASN1ObjectIdentifier sphincsPlus_sha2_192f = new ASN1ObjectIdentifier("1.3.9999.6.5.10");
+    ASN1ObjectIdentifier sphincsPlus_sha2_192s = new ASN1ObjectIdentifier("1.3.9999.6.5.12");
+    ASN1ObjectIdentifier sphincsPlus_sha2_256f = new ASN1ObjectIdentifier("1.3.9999.6.6.10");
+    ASN1ObjectIdentifier sphincsPlus_sha2_256s = new ASN1ObjectIdentifier("1.3.9999.6.6.12");
+
+    ASN1ObjectIdentifier sphincsPlus_shake_128f = new ASN1ObjectIdentifier("1.3.9999.6.7.13");
+    ASN1ObjectIdentifier sphincsPlus_shake_128s = new ASN1ObjectIdentifier("1.3.9999.6.7.16");
+    ASN1ObjectIdentifier sphincsPlus_shake_192f = new ASN1ObjectIdentifier("1.3.9999.6.8.10");
+    ASN1ObjectIdentifier sphincsPlus_shake_192s = new ASN1ObjectIdentifier("1.3.9999.6.8.12");
+    ASN1ObjectIdentifier sphincsPlus_shake_256f = new ASN1ObjectIdentifier("1.3.9999.6.9.10");
+    ASN1ObjectIdentifier sphincsPlus_shake_256s = new ASN1ObjectIdentifier("1.3.9999.6.9.12");
+
+    /**
+     * Picnic
+     */
+    ASN1ObjectIdentifier picnic = bc_sig.branch("6");
+
+    ASN1ObjectIdentifier picnic_key = picnic.branch("1");
+
+    ASN1ObjectIdentifier picnicl1fs = picnic_key.branch("1");
+    ASN1ObjectIdentifier picnicl1ur = picnic_key.branch("2");
+    ASN1ObjectIdentifier picnicl3fs = picnic_key.branch("3");
+    ASN1ObjectIdentifier picnicl3ur = picnic_key.branch("4");
+    ASN1ObjectIdentifier picnicl5fs = picnic_key.branch("5");
+    ASN1ObjectIdentifier picnicl5ur = picnic_key.branch("6");
+    ASN1ObjectIdentifier picnic3l1 = picnic_key.branch("7");
+    ASN1ObjectIdentifier picnic3l3 = picnic_key.branch("8");
+    ASN1ObjectIdentifier picnic3l5 = picnic_key.branch("9");
+    ASN1ObjectIdentifier picnicl1full = picnic_key.branch("10");
+    ASN1ObjectIdentifier picnicl3full = picnic_key.branch("11");
+    ASN1ObjectIdentifier picnicl5full = picnic_key.branch("12");
+
+    ASN1ObjectIdentifier picnic_signature = picnic.branch("2");
+    ASN1ObjectIdentifier picnic_with_sha512 = picnic_signature.branch("1");
+    ASN1ObjectIdentifier picnic_with_shake256 = picnic_signature.branch("2");
+    ASN1ObjectIdentifier picnic_with_sha3_512 = picnic_signature.branch("3");
+
+
+    /*
+     * Falcon
+     */
+    ASN1ObjectIdentifier falcon = bc_sig.branch("7");
+
+    ASN1ObjectIdentifier falcon_512 = new ASN1ObjectIdentifier("1.3.9999.3.6");  // falcon.branch("1");
+    ASN1ObjectIdentifier falcon_1024 =  new ASN1ObjectIdentifier("1.3.9999.3.9"); // falcon.branch("2");
+
+    /*
+     * Dilithium
+     */
+    ASN1ObjectIdentifier dilithium = bc_sig.branch("8");
+
+    // OpenSSL OIDs
+    ASN1ObjectIdentifier dilithium2 = new ASN1ObjectIdentifier("1.3.6.1.4.1.2.267.12.4.4"); // dilithium.branch("1");
+    ASN1ObjectIdentifier dilithium3 = new ASN1ObjectIdentifier("1.3.6.1.4.1.2.267.12.6.5"); // dilithium.branch("2");
+    ASN1ObjectIdentifier dilithium5 = new ASN1ObjectIdentifier("1.3.6.1.4.1.2.267.12.8.7"); // dilithium.branch("3");
+    ASN1ObjectIdentifier dilithium2_aes = new ASN1ObjectIdentifier("1.3.6.1.4.1.2.267.11.4.4"); // dilithium.branch("4");
+    ASN1ObjectIdentifier dilithium3_aes = new ASN1ObjectIdentifier("1.3.6.1.4.1.2.267.11.6.5"); // dilithium.branch("5");
+    ASN1ObjectIdentifier dilithium5_aes = new ASN1ObjectIdentifier("1.3.6.1.4.1.2.267.11.8.7"); // dilithium.branch("6");
+
+    /*
+     * Rainbow
+     */
+    ASN1ObjectIdentifier rainbow = bc_sig.branch("9");
+
+    ASN1ObjectIdentifier rainbow_III_classic = rainbow.branch("1");
+    ASN1ObjectIdentifier rainbow_III_circumzenithal = rainbow.branch("2");
+    ASN1ObjectIdentifier rainbow_III_compressed = rainbow.branch("3");
+    ASN1ObjectIdentifier rainbow_V_classic = rainbow.branch("4");
+    ASN1ObjectIdentifier rainbow_V_circumzenithal = rainbow.branch("5");
+    ASN1ObjectIdentifier rainbow_V_compressed = rainbow.branch("6");
 
     /**
      * key_exchange(3) algorithms
-     *
-    public static final ASN1ObjectIdentifier bc_exch = bc.branch("3");
+     */
+    ASN1ObjectIdentifier bc_exch = bc.branch("3");
 
     /**
      * NewHope
-     *
-    public static final ASN1ObjectIdentifier newHope = bc_exch.branch("1");
-    */
-    // END Android-removed: Unsupported algorithms
+     */
+    ASN1ObjectIdentifier newHope = bc_exch.branch("1");
 
     /**
-     * X.509 extension(4) values
+     * X.509 extension/certificate types
      * <p>
      * 1.3.6.1.4.1.22554.4
      */
-    public static final ASN1ObjectIdentifier bc_ext        = bc.branch("4");
+    ASN1ObjectIdentifier bc_ext = bc.branch("4");
 
-    public static final ASN1ObjectIdentifier linkedCertificate = bc_ext.branch("1");
+    ASN1ObjectIdentifier linkedCertificate = bc_ext.branch("1");
+    ASN1ObjectIdentifier external_value = bc_ext.branch("2");
+
+    /**
+     * KEM(5) algorithms
+     */
+    ASN1ObjectIdentifier bc_kem = bc.branch("5");
+
+    /**
+     * Classic McEliece
+     */
+    ASN1ObjectIdentifier pqc_kem_mceliece = bc_kem.branch("1");
+
+    ASN1ObjectIdentifier mceliece348864_r3 = pqc_kem_mceliece.branch("1");
+    ASN1ObjectIdentifier mceliece348864f_r3 = pqc_kem_mceliece.branch("2");
+    ASN1ObjectIdentifier mceliece460896_r3 = pqc_kem_mceliece.branch("3");
+    ASN1ObjectIdentifier mceliece460896f_r3 = pqc_kem_mceliece.branch("4");
+    ASN1ObjectIdentifier mceliece6688128_r3 = pqc_kem_mceliece.branch("5");
+    ASN1ObjectIdentifier mceliece6688128f_r3 = pqc_kem_mceliece.branch("6");
+    ASN1ObjectIdentifier mceliece6960119_r3 = pqc_kem_mceliece.branch("7");
+    ASN1ObjectIdentifier mceliece6960119f_r3 = pqc_kem_mceliece.branch("8");
+    ASN1ObjectIdentifier mceliece8192128_r3 = pqc_kem_mceliece.branch("9");
+    ASN1ObjectIdentifier mceliece8192128f_r3 = pqc_kem_mceliece.branch("10");
+
+
+    /**
+     * Frodo
+     */
+    ASN1ObjectIdentifier pqc_kem_frodo = bc_kem.branch("2");
+
+    ASN1ObjectIdentifier frodokem640aes = pqc_kem_frodo.branch("1");
+    ASN1ObjectIdentifier frodokem640shake = pqc_kem_frodo.branch("2");
+    ASN1ObjectIdentifier frodokem976aes = pqc_kem_frodo.branch("3");
+    ASN1ObjectIdentifier frodokem976shake = pqc_kem_frodo.branch("4");
+    ASN1ObjectIdentifier frodokem1344aes = pqc_kem_frodo.branch("5");
+    ASN1ObjectIdentifier frodokem1344shake = pqc_kem_frodo.branch("6");
+
+    /**
+     * SABER
+     */
+    ASN1ObjectIdentifier pqc_kem_saber = bc_kem.branch("3");
+
+    ASN1ObjectIdentifier lightsaberkem128r3 = pqc_kem_saber.branch("1");
+    ASN1ObjectIdentifier saberkem128r3 = pqc_kem_saber.branch("2");
+    ASN1ObjectIdentifier firesaberkem128r3 = pqc_kem_saber.branch("3");
+    ASN1ObjectIdentifier lightsaberkem192r3 = pqc_kem_saber.branch("4");
+    ASN1ObjectIdentifier saberkem192r3 = pqc_kem_saber.branch("5");
+    ASN1ObjectIdentifier firesaberkem192r3 = pqc_kem_saber.branch("6");
+    ASN1ObjectIdentifier lightsaberkem256r3 = pqc_kem_saber.branch("7");
+    ASN1ObjectIdentifier saberkem256r3 = pqc_kem_saber.branch("8");
+    ASN1ObjectIdentifier firesaberkem256r3 = pqc_kem_saber.branch("9");
+    ASN1ObjectIdentifier ulightsaberkemr3 = pqc_kem_saber.branch("10");
+    ASN1ObjectIdentifier usaberkemr3 = pqc_kem_saber.branch("11");
+    ASN1ObjectIdentifier ufiresaberkemr3 = pqc_kem_saber.branch("12");
+    ASN1ObjectIdentifier lightsaberkem90sr3 = pqc_kem_saber.branch("13");
+    ASN1ObjectIdentifier saberkem90sr3 = pqc_kem_saber.branch("14");
+    ASN1ObjectIdentifier firesaberkem90sr3 = pqc_kem_saber.branch("15");
+    ASN1ObjectIdentifier ulightsaberkem90sr3 = pqc_kem_saber.branch("16");
+    ASN1ObjectIdentifier usaberkem90sr3 = pqc_kem_saber.branch("17");
+    ASN1ObjectIdentifier ufiresaberkem90sr3 = pqc_kem_saber.branch("18");
+
+    /**
+     * SIKE
+     */
+    ASN1ObjectIdentifier pqc_kem_sike = bc_kem.branch("4");
+
+    ASN1ObjectIdentifier sikep434 = pqc_kem_sike.branch("1");
+    ASN1ObjectIdentifier sikep503 = pqc_kem_sike.branch("2");
+    ASN1ObjectIdentifier sikep610 = pqc_kem_sike.branch("3");
+    ASN1ObjectIdentifier sikep751 = pqc_kem_sike.branch("4");
+    ASN1ObjectIdentifier sikep434_compressed = pqc_kem_sike.branch("5");
+    ASN1ObjectIdentifier sikep503_compressed = pqc_kem_sike.branch("6");
+    ASN1ObjectIdentifier sikep610_compressed = pqc_kem_sike.branch("7");
+    ASN1ObjectIdentifier sikep751_compressed = pqc_kem_sike.branch("8");
+
+    /**
+     * NTRU
+     */
+    ASN1ObjectIdentifier pqc_kem_ntru = bc_kem.branch("5");
+
+    ASN1ObjectIdentifier ntruhps2048509 = pqc_kem_ntru.branch("1");
+    ASN1ObjectIdentifier ntruhps2048677 = pqc_kem_ntru.branch("2");
+    ASN1ObjectIdentifier ntruhps4096821 = pqc_kem_ntru.branch("3");
+    ASN1ObjectIdentifier ntruhrss701 = pqc_kem_ntru.branch("4");
+
+    /**
+     * Kyber
+     */
+    ASN1ObjectIdentifier pqc_kem_kyber = bc_kem.branch("6");
+
+    ASN1ObjectIdentifier kyber512 = pqc_kem_kyber.branch("1");
+    ASN1ObjectIdentifier kyber768 = pqc_kem_kyber.branch("2");
+    ASN1ObjectIdentifier kyber1024 = pqc_kem_kyber.branch("3");
+    ASN1ObjectIdentifier kyber512_aes = pqc_kem_kyber.branch("4");
+    ASN1ObjectIdentifier kyber768_aes = pqc_kem_kyber.branch("5");
+    ASN1ObjectIdentifier kyber1024_aes = pqc_kem_kyber.branch("6");
+
+    /**
+     * NTRUPrime
+     */
+    ASN1ObjectIdentifier pqc_kem_ntruprime = bc_kem.branch("7");
+
+    ASN1ObjectIdentifier pqc_kem_ntrulprime = pqc_kem_ntruprime.branch("1");
+    ASN1ObjectIdentifier ntrulpr653 = pqc_kem_ntrulprime.branch("1");
+    ASN1ObjectIdentifier ntrulpr761 = pqc_kem_ntrulprime.branch("2");
+    ASN1ObjectIdentifier ntrulpr857 = pqc_kem_ntrulprime.branch("3");
+    ASN1ObjectIdentifier ntrulpr953 = pqc_kem_ntrulprime.branch("4");
+    ASN1ObjectIdentifier ntrulpr1013 = pqc_kem_ntrulprime.branch("5");
+    ASN1ObjectIdentifier ntrulpr1277 = pqc_kem_ntrulprime.branch("6");
+    
+    ASN1ObjectIdentifier pqc_kem_sntruprime = pqc_kem_ntruprime.branch("2");
+    ASN1ObjectIdentifier sntrup653 = pqc_kem_sntruprime.branch("1");
+    ASN1ObjectIdentifier sntrup761 = pqc_kem_sntruprime.branch("2");
+    ASN1ObjectIdentifier sntrup857 = pqc_kem_sntruprime.branch("3");
+    ASN1ObjectIdentifier sntrup953 = pqc_kem_sntruprime.branch("4");
+    ASN1ObjectIdentifier sntrup1013 = pqc_kem_sntruprime.branch("5");
+    ASN1ObjectIdentifier sntrup1277 = pqc_kem_sntruprime.branch("6");
+    
+    /**
+     * BIKE
+     **/
+    ASN1ObjectIdentifier pqc_kem_bike = bc_kem.branch("8");
+
+    ASN1ObjectIdentifier bike128 = pqc_kem_bike.branch("1");
+    ASN1ObjectIdentifier bike192 = pqc_kem_bike.branch("2");
+    ASN1ObjectIdentifier bike256 = pqc_kem_bike.branch("3");
+
+    /**
+     * HQC
+     **/
+    ASN1ObjectIdentifier pqc_kem_hqc = bc_kem.branch("9");
+
+    ASN1ObjectIdentifier hqc128 = pqc_kem_hqc.branch("1");
+    ASN1ObjectIdentifier hqc192 = pqc_kem_hqc.branch("2");
+    ASN1ObjectIdentifier hqc256 = pqc_kem_hqc.branch("3");
 }
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/bc/ExternalValue.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/bc/ExternalValue.java
new file mode 100644
index 0000000..dd6ea4d
--- /dev/null
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/bc/ExternalValue.java
@@ -0,0 +1,119 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.org.bouncycastle.asn1.bc;
+
+import com.android.org.bouncycastle.asn1.ASN1BitString;
+import com.android.org.bouncycastle.asn1.ASN1EncodableVector;
+import com.android.org.bouncycastle.asn1.ASN1Object;
+import com.android.org.bouncycastle.asn1.ASN1OctetString;
+import com.android.org.bouncycastle.asn1.ASN1Primitive;
+import com.android.org.bouncycastle.asn1.ASN1Sequence;
+import com.android.org.bouncycastle.asn1.DERBitString;
+import com.android.org.bouncycastle.asn1.DEROctetString;
+import com.android.org.bouncycastle.asn1.DERSequence;
+import com.android.org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import com.android.org.bouncycastle.asn1.x509.GeneralName;
+import com.android.org.bouncycastle.asn1.x509.GeneralNames;
+import com.android.org.bouncycastle.util.Arrays;
+
+/**
+ * Based on External Keys And Signatures For Use In Internet PKI
+ * draft-ounsworth-pq-external-pubkeys-00
+ * <pre>
+ *  ExternalValue ::= SEQUENCE {
+ *      location GeneralNames,    # MUST refer to a DER encoded SubjectPublicKeyInfo/Signature  (may be Base64)
+ *      hashAlg AlgorithmIdentifier,
+ *      hashVal OCTET STRING }
+ * </pre>
+ * @hide This class is not part of the Android public SDK API
+ */
+public class ExternalValue
+    extends ASN1Object
+{
+    private final GeneralNames location;
+    private final AlgorithmIdentifier hashAlg;
+    private final byte[] hashValue;
+
+    public ExternalValue(GeneralName location, AlgorithmIdentifier hashAlg, byte[] hashVal)
+    {
+        this.location = new GeneralNames(location);
+        this.hashAlg = hashAlg;
+        this.hashValue = Arrays.clone(hashVal);
+    }
+
+    private ExternalValue(ASN1Sequence seq)
+    {
+        if (seq.size() == 3)
+        {
+            location = GeneralNames.getInstance(seq.getObjectAt(0));
+            hashAlg = AlgorithmIdentifier.getInstance(seq.getObjectAt(1));
+            if (seq.getObjectAt(2) instanceof ASN1BitString)    // legacy implementation on 2021 draft
+            {
+                hashValue = ASN1BitString.getInstance(seq.getObjectAt(2)).getOctets();
+            }
+            else
+            {
+                hashValue = ASN1OctetString.getInstance(seq.getObjectAt(2)).getOctets();
+            }
+        }
+        else
+        {
+            throw new IllegalArgumentException("unknown sequence");
+        }
+    }
+
+    public static ExternalValue getInstance(Object o)
+    {
+        if (o instanceof ExternalValue)
+        {
+            return (ExternalValue)o;
+        }
+        else if (o != null)
+        {
+            return new ExternalValue(ASN1Sequence.getInstance(o));
+        }
+
+        return null;
+    }
+
+    public GeneralName getLocation()
+    {
+        return location.getNames()[0];
+    }
+
+    public GeneralName[] getLocations()
+    {
+        return location.getNames();
+    }
+
+    public AlgorithmIdentifier getHashAlg()
+    {
+        return hashAlg;
+    }
+
+    public byte[] getHashValue()
+    {
+        return Arrays.clone(hashValue);
+    }
+
+    /**
+     * Get the hash value as a BIT STRING.
+     *
+     * @return the hash value as a BIT STRING
+     * @deprecated use getHash(), the internal encoding is now an OCTET STRING
+     */
+    public ASN1BitString getHashVal()
+    {
+        return new DERBitString(hashValue);
+    }
+
+    public ASN1Primitive toASN1Primitive()
+    {
+        ASN1EncodableVector v = new ASN1EncodableVector();
+
+        v.add(location);
+        v.add(hashAlg);
+        v.add(new DEROctetString(hashValue));
+
+        return new DERSequence(v);
+    }
+}
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/bc/LinkedCertificate.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/bc/LinkedCertificate.java
new file mode 100644
index 0000000..a3e4469
--- /dev/null
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/bc/LinkedCertificate.java
@@ -0,0 +1,128 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.org.bouncycastle.asn1.bc;
+
+import com.android.org.bouncycastle.asn1.ASN1EncodableVector;
+import com.android.org.bouncycastle.asn1.ASN1Object;
+import com.android.org.bouncycastle.asn1.ASN1Primitive;
+import com.android.org.bouncycastle.asn1.ASN1Sequence;
+import com.android.org.bouncycastle.asn1.ASN1TaggedObject;
+import com.android.org.bouncycastle.asn1.DERSequence;
+import com.android.org.bouncycastle.asn1.DERTaggedObject;
+import com.android.org.bouncycastle.asn1.x500.X500Name;
+import com.android.org.bouncycastle.asn1.x509.DigestInfo;
+import com.android.org.bouncycastle.asn1.x509.GeneralName;
+import com.android.org.bouncycastle.asn1.x509.GeneralNames;
+
+/**
+ * Extension to tie an alternate certificate to the containing certificate.
+ * <pre>
+ *     LinkedCertificate := SEQUENCE {
+ *         digest        DigestInfo,                   -- digest of PQC certificate
+ *         certLocation  GeneralName,                  -- location of PQC certificate
+ *         certIssuer    [0] Name OPTIONAL,            -- issuer of PQC cert (if different from current certificate)
+ *         cACerts       [1] GeneralNames OPTIONAL,    -- CA certificates for PQC cert (one of more locations)
+ * }
+ * </pre>
+ * @hide This class is not part of the Android public SDK API
+ */
+public class LinkedCertificate
+    extends ASN1Object
+{
+    private final DigestInfo digest;
+    private final GeneralName certLocation;
+
+    private X500Name certIssuer;
+    private GeneralNames cACerts;
+
+    public LinkedCertificate(DigestInfo digest, GeneralName certLocation)
+    {
+        this(digest, certLocation, null, null);
+    }
+
+    public LinkedCertificate(DigestInfo digest, GeneralName certLocation, X500Name certIssuer, GeneralNames cACerts)
+    {
+        this.digest = digest;
+        this.certLocation = certLocation;
+        this.certIssuer = certIssuer;
+        this.cACerts = cACerts;
+    }
+
+    private LinkedCertificate(ASN1Sequence seq)
+    {
+        this.digest = DigestInfo.getInstance(seq.getObjectAt(0));
+        this.certLocation = GeneralName.getInstance(seq.getObjectAt(1));
+
+        if (seq.size() > 2)
+        {
+            for (int i = 2; i != seq.size(); i++)
+            {
+                ASN1TaggedObject tagged =  ASN1TaggedObject.getInstance(seq.getObjectAt(i));
+
+                switch (tagged.getTagNo())
+                {
+                case 0:
+                    certIssuer = X500Name.getInstance(tagged, false);
+                    break;
+                case 1:
+                    cACerts = GeneralNames.getInstance(tagged, false);
+                    break;
+                default:
+                    throw new IllegalArgumentException("unknown tag in tagged field");
+                }
+            }
+        }
+    }
+
+    public static LinkedCertificate getInstance(Object o)
+    {
+        if (o instanceof LinkedCertificate)
+        {
+            return (LinkedCertificate)o;
+        }
+        else if (o != null)
+        {
+            return new LinkedCertificate(ASN1Sequence.getInstance(o));
+        }
+
+        return null;
+    }
+
+    public DigestInfo getDigest()
+    {
+        return digest;
+    }
+
+    public GeneralName getCertLocation()
+    {
+        return certLocation;
+    }
+
+    public X500Name getCertIssuer()
+    {
+        return certIssuer;
+    }
+
+    public GeneralNames getCACerts()
+    {
+        return cACerts;
+    }
+
+    public ASN1Primitive toASN1Primitive()
+    {
+        ASN1EncodableVector v = new ASN1EncodableVector(4);
+
+        v.add(digest);
+        v.add(certLocation);
+
+        if (certIssuer != null)
+        {
+            v.add(new DERTaggedObject(false, 0, certIssuer));
+        }
+        if (cACerts != null)
+        {
+            v.add(new DERTaggedObject(false, 1, cACerts));
+        }
+
+        return new DERSequence(v);
+    }
+}
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/cms/ContentInfo.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/cms/ContentInfo.java
index cbde2d3..fe02988 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/cms/ContentInfo.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/cms/ContentInfo.java
@@ -130,3 +130,4 @@
         return new BERSequence(v);
     }
 }
+
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/eac/EACObjectIdentifiers.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/eac/EACObjectIdentifiers.java
deleted file mode 100644
index a437f4e..0000000
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/eac/EACObjectIdentifiers.java
+++ /dev/null
@@ -1,112 +0,0 @@
-/* GENERATED SOURCE. DO NOT MODIFY. */
-package com.android.org.bouncycastle.asn1.eac;
-
-import com.android.org.bouncycastle.asn1.ASN1ObjectIdentifier;
-
-/**
- * German Federal Office for Information Security
- * (Bundesamt f&uuml;r Sicherheit in der Informationstechnik)
- * <a href="https://www.bsi.bund.de/">https://www.bsi.bund.de/</a>
- * <p>
- * <a href="https://www.bsi.bund.de/EN/Publications/TechnicalGuidelines/TR03110/BSITR03110.html">BSI TR-03110</a>
- * Technical Guideline Advanced Security Mechanisms for Machine Readable Travel Documents
- * <p>
- * <a href="https://www.bsi.bund.de/SharedDocs/Downloads/EN/BSI/Publications/TechGuidelines/TR03110/TR-03110_v2.1_P3pdf.pdf">
- * Technical Guideline TR-03110-3</a>
- * Advanced Security Mechanisms for Machine Readable Travel Documents;
- * Part 3: Common Specifications.
- * @hide This class is not part of the Android public SDK API
- */
-public interface EACObjectIdentifiers
-{
-    /**
-     * <pre>
-     * bsi-de OBJECT IDENTIFIER ::= {
-     *     itu-t(0) identified-organization(4) etsi(0)
-     *     reserved(127) etsi-identified-organization(0) 7
-     * }
-     * </pre>
-     * OID: 0.4.0.127.0.7
-     */
-    static final ASN1ObjectIdentifier    bsi_de      = new ASN1ObjectIdentifier("0.4.0.127.0.7");
-
-    /**
-     * <pre>
-     * id-PK OBJECT IDENTIFIER ::= {
-     *     bsi-de protocols(2) smartcard(2) 1
-     * }
-     * </pre>
-     * OID: 0.4.0.127.0.7.2.2.1
-     */
-    static final ASN1ObjectIdentifier    id_PK      = bsi_de.branch("2.2.1");
-
-    /** OID: 0.4.0.127.0.7.2.2.1.1 */
-    static final ASN1ObjectIdentifier    id_PK_DH   = id_PK.branch("1");
-    /** OID: 0.4.0.127.0.7.2.2.1.2 */
-    static final ASN1ObjectIdentifier    id_PK_ECDH = id_PK.branch("2");
-
-    /**
-     * <pre>
-     * id-CA OBJECT IDENTIFIER ::= {
-     *     bsi-de protocols(2) smartcard(2) 3
-     * }
-     * </pre>
-     * OID: 0.4.0.127.0.7.2.2.3
-     */
-    static final ASN1ObjectIdentifier    id_CA                   = bsi_de.branch("2.2.3");
-    /** OID: 0.4.0.127.0.7.2.2.3.1 */
-    static final ASN1ObjectIdentifier    id_CA_DH                = id_CA.branch("1");
-    /** OID: 0.4.0.127.0.7.2.2.3.1.1 */
-    static final ASN1ObjectIdentifier    id_CA_DH_3DES_CBC_CBC   = id_CA_DH.branch("1");
-    /** OID: 0.4.0.127.0.7.2.2.3.2 */
-    static final ASN1ObjectIdentifier    id_CA_ECDH              = id_CA.branch("2");
-    /** OID: 0.4.0.127.0.7.2.2.3.2.1 */
-    static final ASN1ObjectIdentifier    id_CA_ECDH_3DES_CBC_CBC = id_CA_ECDH.branch("1");
-
-    /**
-     * <pre>
-     * id-TA OBJECT IDENTIFIER ::= {
-     *     bsi-de protocols(2) smartcard(2) 2
-     * }
-     * </pre>
-     * OID: 0.4.0.127.0.7.2.2.2
-     */
-    static final ASN1ObjectIdentifier    id_TA = bsi_de.branch("2.2.2");
-
-    /** OID: 0.4.0.127.0.7.2.2.2.1 */
-    static final ASN1ObjectIdentifier    id_TA_RSA              = id_TA.branch("1");
-    /** OID: 0.4.0.127.0.7.2.2.2.1.1 */
-    static final ASN1ObjectIdentifier    id_TA_RSA_v1_5_SHA_1   = id_TA_RSA.branch("1");
-    /** OID: 0.4.0.127.0.7.2.2.2.1.2 */
-    static final ASN1ObjectIdentifier    id_TA_RSA_v1_5_SHA_256 = id_TA_RSA.branch("2");
-    /** OID: 0.4.0.127.0.7.2.2.2.1.3 */
-    static final ASN1ObjectIdentifier    id_TA_RSA_PSS_SHA_1    = id_TA_RSA.branch("3");
-    /** OID: 0.4.0.127.0.7.2.2.2.1.4 */
-    static final ASN1ObjectIdentifier    id_TA_RSA_PSS_SHA_256  = id_TA_RSA.branch("4");
-    /** OID: 0.4.0.127.0.7.2.2.2.1.5 */
-    static final ASN1ObjectIdentifier    id_TA_RSA_v1_5_SHA_512 = id_TA_RSA.branch("5");
-    /** OID: 0.4.0.127.0.7.2.2.2.1.6 */
-    static final ASN1ObjectIdentifier    id_TA_RSA_PSS_SHA_512  = id_TA_RSA.branch("6");
-    /** OID: 0.4.0.127.0.7.2.2.2.2 */
-    static final ASN1ObjectIdentifier    id_TA_ECDSA            = id_TA.branch("2");
-    /** OID: 0.4.0.127.0.7.2.2.2.2.1 */
-    static final ASN1ObjectIdentifier    id_TA_ECDSA_SHA_1      = id_TA_ECDSA.branch("1");
-    /** OID: 0.4.0.127.0.7.2.2.2.2.2 */
-    static final ASN1ObjectIdentifier    id_TA_ECDSA_SHA_224    = id_TA_ECDSA.branch("2");
-    /** OID: 0.4.0.127.0.7.2.2.2.2.3 */
-    static final ASN1ObjectIdentifier    id_TA_ECDSA_SHA_256    = id_TA_ECDSA.branch("3");
-    /** OID: 0.4.0.127.0.7.2.2.2.2.4 */
-    static final ASN1ObjectIdentifier    id_TA_ECDSA_SHA_384    = id_TA_ECDSA.branch("4");
-    /** OID: 0.4.0.127.0.7.2.2.2.2.5 */
-    static final ASN1ObjectIdentifier    id_TA_ECDSA_SHA_512    = id_TA_ECDSA.branch("5");
-
-    /**
-     * <pre>
-     * id-EAC-ePassport OBJECT IDENTIFIER ::= {
-     *     bsi-de applications(3) mrtd(1) roles(2) 1
-     * }
-     * </pre>
-     * OID: 0.4.0.127.0.7.3.1.2.1
-     */
-    static final ASN1ObjectIdentifier id_EAC_ePassport = bsi_de.branch("3.1.2.1");
-}
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/gm/GMObjectIdentifiers.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/gm/GMObjectIdentifiers.java
index 3d1a30c..0c10eb4 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/gm/GMObjectIdentifiers.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/gm/GMObjectIdentifiers.java
@@ -8,6 +8,8 @@
  */
 public interface GMObjectIdentifiers
 {
+    //1.2.156.10197: Chinese Cryptography Standardization Technology Committee (CCSTC)
+    //1.2.156.11235: China Broadband Wireless IP Standard Group
     ASN1ObjectIdentifier sm_scheme = new ASN1ObjectIdentifier("1.2.156.10197.1");
 
     ASN1ObjectIdentifier sm6_ecb = sm_scheme.branch("101.1");
@@ -50,7 +52,20 @@
     ASN1ObjectIdentifier sm2exchange = sm_scheme.branch("301.2");
     ASN1ObjectIdentifier sm2encrypt = sm_scheme.branch("301.3");
 
-    ASN1ObjectIdentifier wapip192v1 = sm_scheme.branch("301.101");
+    /**
+     * <Information security technology — Cryptographic application identifier criterion specification>
+     * <url>http&#058;//c.gb688.cn/bzgk/gb/showGb&#63;type=online&hcno=252CF0F72A7BE339A56DEA7D774E8994</url>,
+     * Page 21 only cover from 301.1 to 301.3
+     * */
+    ASN1ObjectIdentifier wapip192v1 =  sm_scheme.branch("301.101");
+    /**
+     * <WAPI certificate management—Part 5: Example of certificate format (draft)>
+     * <url>http&#058;//www.chinabwips.org.cn/zqyjgs1.htm</url> and
+     * <url>http&#058;//www.chinabwips.org.cn/doc/101.pdf</url>,
+     * Page 9 and page 10 states the OID of ECDSA-192 algorithm based on SHA-256 is 1.2.156.11235.1.1.1
+     * */
+    ASN1ObjectIdentifier wapi192v1 =  new ASN1ObjectIdentifier("1.2.156.11235.1.1.1");
+    ASN1ObjectIdentifier wapi192v1_parameters =  new ASN1ObjectIdentifier("1.2.156.11235.1.1.2.1");
 
     ASN1ObjectIdentifier sm2encrypt_recommendedParameters = sm2encrypt.branch("1");
     ASN1ObjectIdentifier sm2encrypt_specifiedParameters = sm2encrypt.branch("2");
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/isara/IsaraObjectIdentifiers.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/isara/IsaraObjectIdentifiers.java
new file mode 100644
index 0000000..3bef6c0
--- /dev/null
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/isara/IsaraObjectIdentifiers.java
@@ -0,0 +1,26 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.org.bouncycastle.asn1.isara;
+
+import com.android.org.bouncycastle.asn1.ASN1ObjectIdentifier;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+public interface IsaraObjectIdentifiers
+{
+    /*
+    id-alg-xmss  OBJECT IDENTIFIER ::= { itu-t(0)
+             identified-organization(4) etsi(0) reserved(127)
+             etsi-identified-organization(0) isara(15) algorithms(1)
+             asymmetric(1) xmss(13) 0 }
+     */
+    static ASN1ObjectIdentifier id_alg_xmss = new ASN1ObjectIdentifier("0.4.0.127.0.15.1.1.13.0");
+
+    /*
+      id-alg-xmssmt  OBJECT IDENTIFIER ::= { itu-t(0)
+         identified-organization(4) etsi(0) reserved(127)
+         etsi-identified-organization(0) isara(15) algorithms(1)
+         asymmetric(1) xmssmt(14) 0 }
+     */
+    static ASN1ObjectIdentifier id_alg_xmssmt = new ASN1ObjectIdentifier("0.4.0.127.0.15.1.1.14.0");
+}
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/misc/MiscObjectIdentifiers.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/misc/MiscObjectIdentifiers.java
index bbf5cd7..8366925 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/misc/MiscObjectIdentifiers.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/misc/MiscObjectIdentifiers.java
@@ -128,7 +128,7 @@
     ASN1ObjectIdentifier cryptlib_algorithm_blowfish_OFB = cryptlib_algorithm.branch("1.4");
 
     //
-    // Blake2b
+    // Blake2b/Blake2s
     //
     ASN1ObjectIdentifier blake2 = new ASN1ObjectIdentifier("1.3.6.1.4.1.1722.12.2");
 
@@ -142,6 +142,10 @@
     ASN1ObjectIdentifier id_blake2s224 = blake2.branch("2.7");
     ASN1ObjectIdentifier id_blake2s256 = blake2.branch("2.8");
 
+    ASN1ObjectIdentifier blake3 = blake2.branch("3");
+
+    ASN1ObjectIdentifier blake3_256 = blake3.branch("8");
+
     //
     // Scrypt
     ASN1ObjectIdentifier id_scrypt = new ASN1ObjectIdentifier("1.3.6.1.4.1.11591.4.11");
@@ -152,4 +156,15 @@
     //        iso(1)  identified-organization(3) dod(6) internet(1) private(4)
     //        enterprise(1) OpenCA(18227) Algorithms(2) id-alg-composite(1) }
     ASN1ObjectIdentifier id_alg_composite = new ASN1ObjectIdentifier("1.3.6.1.4.1.18227.2.1");
+
+    // -- To be replaced by IANA
+    //
+    //id-composite-key OBJECT IDENTIFIER ::= {
+    //
+    //    joint-iso-itu-t(2) country(16) us(840) organization(1) entrust(114027)
+    //
+    //    Algorithm(80) Composite(4) CompositeKey(1)
+    ASN1ObjectIdentifier id_composite_key = new ASN1ObjectIdentifier("2.16.840.1.114027.80.4.1");
+
+    ASN1ObjectIdentifier id_oracle_pkcs12_trusted_key_usage = new ASN1ObjectIdentifier("2.16.840.1.113894.746875.1.1");
 }
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/misc/NetscapeCertType.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/misc/NetscapeCertType.java
index f8c20bf..53de37c 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/misc/NetscapeCertType.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/misc/NetscapeCertType.java
@@ -1,6 +1,7 @@
 /* GENERATED SOURCE. DO NOT MODIFY. */
 package com.android.org.bouncycastle.asn1.misc;
 
+import com.android.org.bouncycastle.asn1.ASN1BitString;
 import com.android.org.bouncycastle.asn1.DERBitString;
 
 /**
@@ -44,13 +45,18 @@
     }
 
     public NetscapeCertType(
-        DERBitString usage)
+        ASN1BitString usage)
     {
         super(usage.getBytes(), usage.getPadBits());
     }
 
+    public boolean hasUsages(int usages)
+    {
+        return (intValue() & usages) == usages;
+    }
+
     public String toString()
     {
-        return "NetscapeCertType: 0x" + Integer.toHexString(data[0] & 0xff);
+        return "NetscapeCertType: 0x" + Integer.toHexString(intValue());
     }
 }
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/misc/NetscapeRevocationURL.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/misc/NetscapeRevocationURL.java
index b3a4bfe..b0e4f3d 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/misc/NetscapeRevocationURL.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/misc/NetscapeRevocationURL.java
@@ -1,6 +1,7 @@
 /* GENERATED SOURCE. DO NOT MODIFY. */
 package com.android.org.bouncycastle.asn1.misc;
 
+import com.android.org.bouncycastle.asn1.ASN1IA5String;
 import com.android.org.bouncycastle.asn1.DERIA5String;
 
 /**
@@ -10,7 +11,7 @@
     extends DERIA5String
 {
     public NetscapeRevocationURL(
-        DERIA5String str)
+        ASN1IA5String str)
     {
         super(str.getString());
     }
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/misc/VerisignCzagExtension.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/misc/VerisignCzagExtension.java
index c67c7ae..d0e5f7b 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/misc/VerisignCzagExtension.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/misc/VerisignCzagExtension.java
@@ -1,6 +1,7 @@
 /* GENERATED SOURCE. DO NOT MODIFY. */
 package com.android.org.bouncycastle.asn1.misc;
 
+import com.android.org.bouncycastle.asn1.ASN1IA5String;
 import com.android.org.bouncycastle.asn1.DERIA5String;
 
 /**
@@ -10,7 +11,7 @@
     extends DERIA5String
 {
     public VerisignCzagExtension(
-        DERIA5String str)
+        ASN1IA5String str)
     {
         super(str.getString());
     }
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/nist/KMACwithSHAKE128_params.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/nist/KMACwithSHAKE128_params.java
new file mode 100644
index 0000000..9b2926a
--- /dev/null
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/nist/KMACwithSHAKE128_params.java
@@ -0,0 +1,116 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.org.bouncycastle.asn1.nist;
+
+import com.android.org.bouncycastle.asn1.ASN1EncodableVector;
+import com.android.org.bouncycastle.asn1.ASN1Integer;
+import com.android.org.bouncycastle.asn1.ASN1Object;
+import com.android.org.bouncycastle.asn1.ASN1OctetString;
+import com.android.org.bouncycastle.asn1.ASN1Primitive;
+import com.android.org.bouncycastle.asn1.ASN1Sequence;
+import com.android.org.bouncycastle.asn1.DEROctetString;
+import com.android.org.bouncycastle.asn1.DERSequence;
+import com.android.org.bouncycastle.util.Arrays;
+
+/**
+ * <pre>
+ *   KMACwithSHAKE128-params ::= SEQUENCE {
+ *      kMACOutputLength     INTEGER DEFAULT 256, -- Output length in bits
+ *      customizationString  OCTET STRING DEFAULT ''H
+ *    }
+ * </pre>
+ * @hide This class is not part of the Android public SDK API
+ */
+public class KMACwithSHAKE128_params
+    extends ASN1Object
+{
+    private static final byte[] EMPTY_STRING = new byte[0];
+    private static final int DEF_LENGTH = 256;
+
+    private final int outputLength;
+    private final byte[] customizationString;
+
+    public KMACwithSHAKE128_params(int outputLength)
+    {
+        this.outputLength = outputLength;
+        this.customizationString = EMPTY_STRING;
+    }
+
+    public KMACwithSHAKE128_params(int outputLength, byte[] customizationString)
+    {
+        this.outputLength = outputLength;
+        this.customizationString = Arrays.clone(customizationString);
+    }
+
+    public static KMACwithSHAKE128_params getInstance(Object o)
+    {
+        if (o instanceof KMACwithSHAKE128_params)
+        {
+            return (KMACwithSHAKE128_params)o;
+        }
+        else if (o != null)
+        {
+            return new KMACwithSHAKE128_params(ASN1Sequence.getInstance(o));
+        }
+
+        return null;
+    }
+
+    private KMACwithSHAKE128_params(ASN1Sequence seq)
+    {
+        if (seq.size() > 2)
+        {
+            throw new IllegalArgumentException("sequence size greater than 2");
+        }
+
+        if (seq.size() == 2)
+        {
+            this.outputLength = ASN1Integer.getInstance(seq.getObjectAt(0)).intValueExact();
+            this.customizationString = Arrays.clone(ASN1OctetString.getInstance(seq.getObjectAt(1)).getOctets());
+        }
+        else if (seq.size() == 1)
+        {
+            if (seq.getObjectAt(0) instanceof ASN1Integer)
+            {
+                this.outputLength = ASN1Integer.getInstance(seq.getObjectAt(0)).intValueExact();
+                this.customizationString = EMPTY_STRING;
+            }
+            else
+            {
+                this.outputLength = DEF_LENGTH;
+                this.customizationString = Arrays.clone(ASN1OctetString.getInstance(seq.getObjectAt(0)).getOctets());
+            }
+        }
+        else
+        {
+            this.outputLength = DEF_LENGTH;
+            this.customizationString = EMPTY_STRING;
+        }
+    }
+
+    public int getOutputLength()
+    {
+        return outputLength;
+    }
+
+    public byte[] getCustomizationString()
+    {
+        return Arrays.clone(customizationString);
+    }
+
+    public ASN1Primitive toASN1Primitive()
+    {
+        ASN1EncodableVector v = new ASN1EncodableVector();
+
+        if (outputLength != DEF_LENGTH)
+        {
+            v.add(new ASN1Integer(outputLength));
+        }
+
+        if (customizationString.length != 0)
+        {
+            v.add(new DEROctetString(getCustomizationString()));
+        }
+
+        return new DERSequence(v);
+    }
+}
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/nist/KMACwithSHAKE256_params.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/nist/KMACwithSHAKE256_params.java
new file mode 100644
index 0000000..2ad37f7
--- /dev/null
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/nist/KMACwithSHAKE256_params.java
@@ -0,0 +1,116 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.org.bouncycastle.asn1.nist;
+
+import com.android.org.bouncycastle.asn1.ASN1EncodableVector;
+import com.android.org.bouncycastle.asn1.ASN1Integer;
+import com.android.org.bouncycastle.asn1.ASN1Object;
+import com.android.org.bouncycastle.asn1.ASN1OctetString;
+import com.android.org.bouncycastle.asn1.ASN1Primitive;
+import com.android.org.bouncycastle.asn1.ASN1Sequence;
+import com.android.org.bouncycastle.asn1.DEROctetString;
+import com.android.org.bouncycastle.asn1.DERSequence;
+import com.android.org.bouncycastle.util.Arrays;
+
+/**
+ * <pre>
+ *   KMACwithSHAKE256-params ::= SEQUENCE {
+ *      kMACOutputLength     INTEGER DEFAULT 512, -- Output length in bits
+ *      customizationString  OCTET STRING DEFAULT ''H
+ *    }
+ * </pre>
+ * @hide This class is not part of the Android public SDK API
+ */
+public class KMACwithSHAKE256_params
+    extends ASN1Object
+{
+    private static final byte[] EMPTY_STRING = new byte[0];
+    private static final int DEF_LENGTH = 512;
+
+    private final int outputLength;
+    private final byte[] customizationString;
+
+    public KMACwithSHAKE256_params(int outputLength)
+    {
+        this.outputLength = outputLength;
+        this.customizationString = EMPTY_STRING;
+    }
+
+    public KMACwithSHAKE256_params(int outputLength, byte[] customizationString)
+    {
+        this.outputLength = outputLength;
+        this.customizationString = Arrays.clone(customizationString);
+    }
+
+    public static KMACwithSHAKE256_params getInstance(Object o)
+    {
+        if (o instanceof KMACwithSHAKE256_params)
+        {
+            return (KMACwithSHAKE256_params)o;
+        }
+        else if (o != null)
+        {
+            return new KMACwithSHAKE256_params(ASN1Sequence.getInstance(o));
+        }
+
+        return null;
+    }
+
+    private KMACwithSHAKE256_params(ASN1Sequence seq)
+    {
+        if (seq.size() > 2)
+        {
+            throw new IllegalArgumentException("sequence size greater than 2");
+        }
+
+        if (seq.size() == 2)
+        {
+            this.outputLength = ASN1Integer.getInstance(seq.getObjectAt(0)).intValueExact();
+            this.customizationString = Arrays.clone(ASN1OctetString.getInstance(seq.getObjectAt(1)).getOctets());
+        }
+        else if (seq.size() == 1)
+        {
+            if (seq.getObjectAt(0) instanceof ASN1Integer)
+            {
+                this.outputLength = ASN1Integer.getInstance(seq.getObjectAt(0)).intValueExact();
+                this.customizationString = EMPTY_STRING;
+            }
+            else
+            {
+                this.outputLength = DEF_LENGTH;
+                this.customizationString = Arrays.clone(ASN1OctetString.getInstance(seq.getObjectAt(0)).getOctets());
+            }
+        }
+        else
+        {
+            this.outputLength = DEF_LENGTH;
+            this.customizationString = EMPTY_STRING;
+        }
+    }
+
+    public int getOutputLength()
+    {
+        return outputLength;
+    }
+
+    public byte[] getCustomizationString()
+    {
+        return Arrays.clone(customizationString);
+    }
+    
+    public ASN1Primitive toASN1Primitive()
+    {
+        ASN1EncodableVector v = new ASN1EncodableVector();
+
+        if (outputLength != DEF_LENGTH)
+        {
+            v.add(new ASN1Integer(outputLength));
+        }
+
+        if (customizationString.length != 0)
+        {
+            v.add(new DEROctetString(getCustomizationString()));
+        }
+
+        return new DERSequence(v);
+    }
+}
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/nist/NISTNamedCurves.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/nist/NISTNamedCurves.java
index 3e3e981..c75948b 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/nist/NISTNamedCurves.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/nist/NISTNamedCurves.java
@@ -8,6 +8,7 @@
 import com.android.org.bouncycastle.asn1.sec.SECNamedCurves;
 import com.android.org.bouncycastle.asn1.sec.SECObjectIdentifiers;
 import com.android.org.bouncycastle.asn1.x9.X9ECParameters;
+import com.android.org.bouncycastle.asn1.x9.X9ECParametersHolder;
 import com.android.org.bouncycastle.util.Strings;
 
 /**
@@ -44,17 +45,16 @@
         defineCurve("P-192", SECObjectIdentifiers.secp192r1);
     }
 
-    public static X9ECParameters getByName(
-        String  name)
+    public static X9ECParameters getByName(String name)
     {
-        ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier)objIds.get(Strings.toUpperCase(name));
+        ASN1ObjectIdentifier oid = getOID(name);
+        return null != oid ? SECNamedCurves.getByOID(oid) : null;
+    }
 
-        if (oid != null)
-        {
-            return getByOID(oid);
-        }
-
-        return null;
+    public static X9ECParametersHolder getByNameLazy(String name)
+    {
+        ASN1ObjectIdentifier oid = getOID(name);
+        return null != oid ? SECNamedCurves.getByOIDLazy(oid) : null;
     }
 
     /**
@@ -63,10 +63,14 @@
      *
      * @param oid an object identifier representing a named curve, if present.
      */
-    public static X9ECParameters getByOID(
-        ASN1ObjectIdentifier  oid)
+    public static X9ECParameters getByOID(ASN1ObjectIdentifier oid)
     {
-        return SECNamedCurves.getByOID(oid);
+        return names.containsKey(oid) ? SECNamedCurves.getByOID(oid) : null;
+    }
+
+    public static X9ECParametersHolder getByOIDLazy(ASN1ObjectIdentifier oid)
+    {
+        return names.containsKey(oid) ? SECNamedCurves.getByOIDLazy(oid) : null;
     }
 
     /**
@@ -75,8 +79,7 @@
      *
      * @return the object identifier associated with name, if present.
      */
-    public static ASN1ObjectIdentifier getOID(
-        String  name)
+    public static ASN1ObjectIdentifier getOID(String name)
     {
         return (ASN1ObjectIdentifier)objIds.get(Strings.toUpperCase(name));
     }
@@ -84,8 +87,7 @@
     /**
      * return the named curve name represented by the given object identifier.
      */
-    public static String getName(
-        ASN1ObjectIdentifier  oid)
+    public static String getName(ASN1ObjectIdentifier oid)
     {
         return (String)names.get(oid);
     }
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/nist/NISTObjectIdentifiers.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/nist/NISTObjectIdentifiers.java
index 3a1609e..128d5ef 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/nist/NISTObjectIdentifiers.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/nist/NISTObjectIdentifiers.java
@@ -60,69 +60,137 @@
     /** 2.16.840.1.101.3.4.2.19 */
     static final ASN1ObjectIdentifier    id_KmacWithSHAKE128 = hashAlgs.branch("19");
     /** 2.16.840.1.101.3.4.2.20 */
-    static final ASN1ObjectIdentifier    id_KmacWithSHAKE256 = hashAlgs.branch("20");
+    static final ASN1ObjectIdentifier id_KmacWithSHAKE256 = hashAlgs.branch("20");
 
-    /** 2.16.840.1.101.3.4.1 */
-    static final ASN1ObjectIdentifier    aes                     = nistAlgorithm.branch("1");
-    
-    /** 2.16.840.1.101.3.4.1.1 */
-    static final ASN1ObjectIdentifier    id_aes128_ECB           = aes.branch("1"); 
-    /** 2.16.840.1.101.3.4.1.2 */
-    static final ASN1ObjectIdentifier    id_aes128_CBC           = aes.branch("2");
-    /** 2.16.840.1.101.3.4.1.3 */
-    static final ASN1ObjectIdentifier    id_aes128_OFB           = aes.branch("3"); 
-    /** 2.16.840.1.101.3.4.1.4 */
-    static final ASN1ObjectIdentifier    id_aes128_CFB           = aes.branch("4"); 
-    /** 2.16.840.1.101.3.4.1.5 */
-    static final ASN1ObjectIdentifier    id_aes128_wrap          = aes.branch("5");
-    /** 2.16.840.1.101.3.4.1.6 */
-    static final ASN1ObjectIdentifier    id_aes128_GCM           = aes.branch("6");
-    /** 2.16.840.1.101.3.4.1.7 */
-    static final ASN1ObjectIdentifier    id_aes128_CCM           = aes.branch("7");
-    /** 2.16.840.1.101.3.4.1.28 */
-    static final ASN1ObjectIdentifier    id_aes128_wrap_pad      = aes.branch("8");
+    /**
+     * 2.16.840.1.101.3.4.1
+     */
+    static final ASN1ObjectIdentifier aes = nistAlgorithm.branch("1");
 
-    /** 2.16.840.1.101.3.4.1.21 */
-    static final ASN1ObjectIdentifier    id_aes192_ECB           = aes.branch("21"); 
-    /** 2.16.840.1.101.3.4.1.22 */
-    static final ASN1ObjectIdentifier    id_aes192_CBC           = aes.branch("22"); 
-    /** 2.16.840.1.101.3.4.1.23 */
-    static final ASN1ObjectIdentifier    id_aes192_OFB           = aes.branch("23"); 
-    /** 2.16.840.1.101.3.4.1.24 */
-    static final ASN1ObjectIdentifier    id_aes192_CFB           = aes.branch("24"); 
-    /** 2.16.840.1.101.3.4.1.25 */
-    static final ASN1ObjectIdentifier    id_aes192_wrap          = aes.branch("25");
-    /** 2.16.840.1.101.3.4.1.26 */
-    static final ASN1ObjectIdentifier    id_aes192_GCM           = aes.branch("26");
-    /** 2.16.840.1.101.3.4.1.27 */
-    static final ASN1ObjectIdentifier    id_aes192_CCM           = aes.branch("27");
-    /** 2.16.840.1.101.3.4.1.28 */
-    static final ASN1ObjectIdentifier    id_aes192_wrap_pad      = aes.branch("28");
+    /**
+     * 2.16.840.1.101.3.4.1.1
+     */
+    static final ASN1ObjectIdentifier id_aes128_ECB = aes.branch("1");
+    /**
+     * 2.16.840.1.101.3.4.1.2
+     */
+    static final ASN1ObjectIdentifier id_aes128_CBC = aes.branch("2");
+    /**
+     * 2.16.840.1.101.3.4.1.3
+     */
+    static final ASN1ObjectIdentifier id_aes128_OFB = aes.branch("3");
+    /**
+     * 2.16.840.1.101.3.4.1.4
+     */
+    static final ASN1ObjectIdentifier id_aes128_CFB = aes.branch("4");
+    /**
+     * 2.16.840.1.101.3.4.1.5
+     */
+    static final ASN1ObjectIdentifier id_aes128_wrap = aes.branch("5");
+    /**
+     * 2.16.840.1.101.3.4.1.6
+     */
+    static final ASN1ObjectIdentifier id_aes128_GCM = aes.branch("6");
+    /**
+     * 2.16.840.1.101.3.4.1.7
+     */
+    static final ASN1ObjectIdentifier id_aes128_CCM = aes.branch("7");
+    /**
+     * 2.16.840.1.101.3.4.1.8
+     */
+    static final ASN1ObjectIdentifier id_aes128_wrap_pad = aes.branch("8");
+    /**
+     * 2.16.840.1.101.3.4.1.9
+     */
+    static final ASN1ObjectIdentifier id_aes128_GMAC = aes.branch("9");
 
-    /** 2.16.840.1.101.3.4.1.41 */
-    static final ASN1ObjectIdentifier    id_aes256_ECB           = aes.branch("41"); 
-    /** 2.16.840.1.101.3.4.1.42 */
-    static final ASN1ObjectIdentifier    id_aes256_CBC           = aes.branch("42");
-    /** 2.16.840.1.101.3.4.1.43 */
-    static final ASN1ObjectIdentifier    id_aes256_OFB           = aes.branch("43"); 
-    /** 2.16.840.1.101.3.4.1.44 */
-    static final ASN1ObjectIdentifier    id_aes256_CFB           = aes.branch("44"); 
-    /** 2.16.840.1.101.3.4.1.45 */
-    static final ASN1ObjectIdentifier    id_aes256_wrap          = aes.branch("45"); 
-    /** 2.16.840.1.101.3.4.1.46 */
-    static final ASN1ObjectIdentifier    id_aes256_GCM           = aes.branch("46");
-    /** 2.16.840.1.101.3.4.1.47 */
-    static final ASN1ObjectIdentifier    id_aes256_CCM           = aes.branch("47");
-    /** 2.16.840.1.101.3.4.1.48 */
-    static final ASN1ObjectIdentifier    id_aes256_wrap_pad      = aes.branch("48");
+
+    /**
+     * 2.16.840.1.101.3.4.1.21
+     */
+    static final ASN1ObjectIdentifier id_aes192_ECB = aes.branch("21");
+    /**
+     * 2.16.840.1.101.3.4.1.22
+     */
+    static final ASN1ObjectIdentifier id_aes192_CBC = aes.branch("22");
+    /**
+     * 2.16.840.1.101.3.4.1.23
+     */
+    static final ASN1ObjectIdentifier id_aes192_OFB = aes.branch("23");
+    /**
+     * 2.16.840.1.101.3.4.1.24
+     */
+    static final ASN1ObjectIdentifier id_aes192_CFB = aes.branch("24");
+    /**
+     * 2.16.840.1.101.3.4.1.25
+     */
+    static final ASN1ObjectIdentifier id_aes192_wrap = aes.branch("25");
+    /**
+     * 2.16.840.1.101.3.4.1.26
+     */
+    static final ASN1ObjectIdentifier id_aes192_GCM = aes.branch("26");
+    /**
+     * 2.16.840.1.101.3.4.1.27
+     */
+    static final ASN1ObjectIdentifier id_aes192_CCM = aes.branch("27");
+    /**
+     * 2.16.840.1.101.3.4.1.28
+     */
+    static final ASN1ObjectIdentifier id_aes192_wrap_pad = aes.branch("28");
+
+    /**
+     * 2.16.840.1.101.3.4.1.29
+     */
+    static final ASN1ObjectIdentifier id_aes192_GMAC = aes.branch("29");
+
+
+    /**
+     * 2.16.840.1.101.3.4.1.41
+     */
+    static final ASN1ObjectIdentifier id_aes256_ECB = aes.branch("41");
+    /**
+     * 2.16.840.1.101.3.4.1.42
+     */
+    static final ASN1ObjectIdentifier id_aes256_CBC = aes.branch("42");
+    /**
+     * 2.16.840.1.101.3.4.1.43
+     */
+    static final ASN1ObjectIdentifier id_aes256_OFB = aes.branch("43");
+    /**
+     * 2.16.840.1.101.3.4.1.44
+     */
+    static final ASN1ObjectIdentifier id_aes256_CFB = aes.branch("44");
+    /**
+     * 2.16.840.1.101.3.4.1.45
+     */
+    static final ASN1ObjectIdentifier id_aes256_wrap = aes.branch("45");
+    /**
+     * 2.16.840.1.101.3.4.1.46
+     */
+    static final ASN1ObjectIdentifier id_aes256_GCM = aes.branch("46");
+    /**
+     * 2.16.840.1.101.3.4.1.47
+     */
+    static final ASN1ObjectIdentifier id_aes256_CCM = aes.branch("47");
+    /**
+     * 2.16.840.1.101.3.4.1.48
+     */
+    static final ASN1ObjectIdentifier id_aes256_wrap_pad = aes.branch("48");
+    /**
+     * 2.16.840.1.101.3.4.1.49
+     */
+    static final ASN1ObjectIdentifier id_aes256_GMAC = aes.branch("49");
+
 
     //
     // signatures
     //
-    /** 2.16.840.1.101.3.4.3 */
-    static final ASN1ObjectIdentifier    sigAlgs        = nistAlgorithm.branch("3");
+    /**
+     * 2.16.840.1.101.3.4.3
+     */
+    static final ASN1ObjectIdentifier sigAlgs = nistAlgorithm.branch("3");
 
-    static final ASN1ObjectIdentifier    id_dsa_with_sha2        = sigAlgs;
+    static final ASN1ObjectIdentifier id_dsa_with_sha2 = sigAlgs;
 
     /** 2.16.840.1.101.3.4.3.1 */
     static final ASN1ObjectIdentifier    dsa_with_sha224         = sigAlgs.branch("1");
@@ -152,12 +220,12 @@
     static final ASN1ObjectIdentifier    id_ecdsa_with_sha3_512       = sigAlgs.branch("12");
 
     // RSA PKCS #1 v1.5 Signature with SHA-3 family.
-    /** 2.16.840.1.101.3.4.3.9 */
+    /** 2.16.840.1.101.3.4.3.13 */
     static final ASN1ObjectIdentifier    id_rsassa_pkcs1_v1_5_with_sha3_224       = sigAlgs.branch("13");
-    /** 2.16.840.1.101.3.4.3.10 */
+    /** 2.16.840.1.101.3.4.3.14 */
     static final ASN1ObjectIdentifier    id_rsassa_pkcs1_v1_5_with_sha3_256       = sigAlgs.branch("14");
-    /** 2.16.840.1.101.3.4.3.11 */
+    /** 2.16.840.1.101.3.4.3.15 */
     static final ASN1ObjectIdentifier    id_rsassa_pkcs1_v1_5_with_sha3_384       = sigAlgs.branch("15");
-    /** 2.16.840.1.101.3.4.3.12 */
+    /** 2.16.840.1.101.3.4.3.16 */
     static final ASN1ObjectIdentifier    id_rsassa_pkcs1_v1_5_with_sha3_512       = sigAlgs.branch("16");
 }
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ocsp/BasicOCSPResponse.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ocsp/BasicOCSPResponse.java
new file mode 100644
index 0000000..aefc353
--- /dev/null
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ocsp/BasicOCSPResponse.java
@@ -0,0 +1,124 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.org.bouncycastle.asn1.ocsp;
+
+import com.android.org.bouncycastle.asn1.ASN1EncodableVector;
+import com.android.org.bouncycastle.asn1.ASN1Object;
+import com.android.org.bouncycastle.asn1.ASN1Primitive;
+import com.android.org.bouncycastle.asn1.ASN1Sequence;
+import com.android.org.bouncycastle.asn1.ASN1TaggedObject;
+import com.android.org.bouncycastle.asn1.DERBitString;
+import com.android.org.bouncycastle.asn1.DERSequence;
+import com.android.org.bouncycastle.asn1.DERTaggedObject;
+import com.android.org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+
+/**
+ * OCSP RFC 2560, RFC 6960
+ * <pre>
+ * BasicOCSPResponse       ::= SEQUENCE {
+ *    tbsResponseData      ResponseData,
+ *    signatureAlgorithm   AlgorithmIdentifier,
+ *    signature            BIT STRING,
+ *    certs                [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL }
+ * </pre>
+ * @hide This class is not part of the Android public SDK API
+ */
+public class BasicOCSPResponse
+    extends ASN1Object
+{
+    private ResponseData        tbsResponseData;
+    private AlgorithmIdentifier signatureAlgorithm;
+    private DERBitString        signature;
+    private ASN1Sequence        certs;
+
+    public BasicOCSPResponse(
+        ResponseData        tbsResponseData,
+        AlgorithmIdentifier signatureAlgorithm,
+        DERBitString        signature,
+        ASN1Sequence        certs)
+    {
+        this.tbsResponseData = tbsResponseData;
+        this.signatureAlgorithm = signatureAlgorithm;
+        this.signature = signature;
+        this.certs = certs;
+    }
+
+    private BasicOCSPResponse(
+        ASN1Sequence    seq)
+    {
+        this.tbsResponseData = ResponseData.getInstance(seq.getObjectAt(0));
+        this.signatureAlgorithm = AlgorithmIdentifier.getInstance(seq.getObjectAt(1));
+        this.signature = (DERBitString)seq.getObjectAt(2);
+
+        if (seq.size() > 3)
+        {
+            this.certs = ASN1Sequence.getInstance((ASN1TaggedObject)seq.getObjectAt(3), true);
+        }
+    }
+
+    public static BasicOCSPResponse getInstance(
+        ASN1TaggedObject obj,
+        boolean          explicit)
+    {
+        return getInstance(ASN1Sequence.getInstance(obj, explicit));
+    }
+
+    public static BasicOCSPResponse getInstance(
+        Object  obj)
+    {
+        if (obj instanceof BasicOCSPResponse)
+        {
+            return (BasicOCSPResponse)obj;
+        }
+        else if (obj != null)
+        {
+            return new BasicOCSPResponse(ASN1Sequence.getInstance(obj));
+        }
+
+        return null;
+    }
+
+    public ResponseData getTbsResponseData()
+    {
+        return tbsResponseData;
+    }
+
+    public AlgorithmIdentifier getSignatureAlgorithm()
+    {
+        return signatureAlgorithm;
+    }
+
+    public DERBitString getSignature()
+    {
+        return signature;
+    }
+
+    public ASN1Sequence getCerts()
+    {
+        return certs;
+    }
+
+    /**
+     * Produce an object suitable for an ASN1OutputStream.
+     * <pre>
+     * BasicOCSPResponse       ::= SEQUENCE {
+     *      tbsResponseData      ResponseData,
+     *      signatureAlgorithm   AlgorithmIdentifier,
+     *      signature            BIT STRING,
+     *      certs                [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL }
+     * </pre>
+     */
+    public ASN1Primitive toASN1Primitive()
+    {
+        ASN1EncodableVector v = new ASN1EncodableVector(4);
+
+        v.add(tbsResponseData);
+        v.add(signatureAlgorithm);
+        v.add(signature);
+        if (certs != null)
+        {
+            v.add(new DERTaggedObject(true, 0, certs));
+        }
+
+        return new DERSequence(v);
+    }
+}
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ocsp/CertID.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ocsp/CertID.java
new file mode 100644
index 0000000..5fc6d06
--- /dev/null
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ocsp/CertID.java
@@ -0,0 +1,109 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.org.bouncycastle.asn1.ocsp;
+
+import com.android.org.bouncycastle.asn1.ASN1EncodableVector;
+import com.android.org.bouncycastle.asn1.ASN1Integer;
+import com.android.org.bouncycastle.asn1.ASN1Object;
+import com.android.org.bouncycastle.asn1.ASN1OctetString;
+import com.android.org.bouncycastle.asn1.ASN1Primitive;
+import com.android.org.bouncycastle.asn1.ASN1Sequence;
+import com.android.org.bouncycastle.asn1.ASN1TaggedObject;
+import com.android.org.bouncycastle.asn1.DERSequence;
+import com.android.org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+public class CertID
+    extends ASN1Object
+{
+    AlgorithmIdentifier    hashAlgorithm;
+    ASN1OctetString        issuerNameHash;
+    ASN1OctetString        issuerKeyHash;
+    ASN1Integer             serialNumber;
+
+    public CertID(
+        AlgorithmIdentifier hashAlgorithm,
+        ASN1OctetString     issuerNameHash,
+        ASN1OctetString     issuerKeyHash,
+        ASN1Integer         serialNumber)
+    {
+        this.hashAlgorithm = hashAlgorithm;
+        this.issuerNameHash = issuerNameHash;
+        this.issuerKeyHash = issuerKeyHash;
+        this.serialNumber = serialNumber;
+    }
+
+    private CertID(
+        ASN1Sequence    seq)
+    {
+        hashAlgorithm = AlgorithmIdentifier.getInstance(seq.getObjectAt(0));
+        issuerNameHash = (ASN1OctetString)seq.getObjectAt(1);
+        issuerKeyHash = (ASN1OctetString)seq.getObjectAt(2);
+        serialNumber = (ASN1Integer)seq.getObjectAt(3);
+    }
+
+    public static CertID getInstance(
+        ASN1TaggedObject obj,
+        boolean          explicit)
+    {
+        return getInstance(ASN1Sequence.getInstance(obj, explicit));
+    }
+
+    public static CertID getInstance(
+        Object  obj)
+    {
+        if (obj instanceof CertID)
+        {
+            return (CertID)obj;
+        }
+        else if (obj != null)
+        {
+            return new CertID(ASN1Sequence.getInstance(obj));
+        }
+
+        return null;
+    }
+
+    public AlgorithmIdentifier getHashAlgorithm()
+    {
+        return hashAlgorithm;
+    }
+
+    public ASN1OctetString getIssuerNameHash()
+    {
+        return issuerNameHash;
+    }
+
+    public ASN1OctetString getIssuerKeyHash()
+    {
+        return issuerKeyHash;
+    }
+
+    public ASN1Integer getSerialNumber()
+    {
+        return serialNumber;
+    }
+
+    /**
+     * Produce an object suitable for an ASN1OutputStream.
+     * <pre>
+     * CertID          ::=     SEQUENCE {
+     *     hashAlgorithm       AlgorithmIdentifier,
+     *     issuerNameHash      OCTET STRING, -- Hash of Issuer's DN
+     *     issuerKeyHash       OCTET STRING, -- Hash of Issuers public key
+     *     serialNumber        CertificateSerialNumber }
+     * </pre>
+     */
+    public ASN1Primitive toASN1Primitive()
+    {
+        ASN1EncodableVector v = new ASN1EncodableVector(4);
+
+        v.add(hashAlgorithm);
+        v.add(issuerNameHash);
+        v.add(issuerKeyHash);
+        v.add(serialNumber);
+
+        return new DERSequence(v);
+    }
+}
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ocsp/CertStatus.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ocsp/CertStatus.java
new file mode 100644
index 0000000..4a76cb4
--- /dev/null
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ocsp/CertStatus.java
@@ -0,0 +1,117 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.org.bouncycastle.asn1.ocsp;
+
+import com.android.org.bouncycastle.asn1.ASN1Choice;
+import com.android.org.bouncycastle.asn1.ASN1Encodable;
+import com.android.org.bouncycastle.asn1.ASN1Null;
+import com.android.org.bouncycastle.asn1.ASN1Object;
+import com.android.org.bouncycastle.asn1.ASN1Primitive;
+import com.android.org.bouncycastle.asn1.ASN1TaggedObject;
+import com.android.org.bouncycastle.asn1.ASN1Util;
+import com.android.org.bouncycastle.asn1.DERNull;
+import com.android.org.bouncycastle.asn1.DERTaggedObject;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+public class CertStatus
+    extends ASN1Object
+    implements ASN1Choice
+{
+    private int             tagNo;
+    private ASN1Encodable    value;
+
+    /**
+     * create a CertStatus object with a tag of zero.
+     */
+    public CertStatus()
+    {
+        tagNo = 0;
+        value = DERNull.INSTANCE;
+    }
+
+    public CertStatus(
+        RevokedInfo info)
+    {
+        tagNo = 1;
+        value = info;
+    }
+
+    public CertStatus(
+        int tagNo,
+        ASN1Encodable    value)
+    {
+        this.tagNo = tagNo;
+        this.value = value;
+    }
+
+    private CertStatus(
+        ASN1TaggedObject    choice)
+    {
+        int tagNo = choice.getTagNo();
+
+        switch (tagNo)
+        {
+        case 0:
+            value = ASN1Null.getInstance(choice, false);
+            break;
+        case 1:
+            value = RevokedInfo.getInstance(choice, false);
+            break;
+        case 2:
+            // UnknownInfo ::= NULL
+            value = ASN1Null.getInstance(choice, false);
+            break;
+        default:
+            throw new IllegalArgumentException("Unknown tag encountered: " + ASN1Util.getTagText(choice));
+        }
+
+        this.tagNo = tagNo;
+    }
+
+    public static CertStatus getInstance(
+        Object  obj)
+    {
+        if (obj == null || obj instanceof CertStatus)
+        {
+            return (CertStatus)obj;
+        }
+        else if (obj instanceof ASN1TaggedObject)
+        {
+            return new CertStatus((ASN1TaggedObject)obj);
+        }
+
+        throw new IllegalArgumentException("unknown object in factory: " + obj.getClass().getName());
+    }
+
+    public static CertStatus getInstance(
+        ASN1TaggedObject obj,
+        boolean          explicit)
+    {
+        return getInstance(obj.getExplicitBaseTagged()); // must be explicitly tagged
+    }
+    
+    public int getTagNo()
+    {
+        return tagNo;
+    }
+
+    public ASN1Encodable getStatus()
+    {
+        return value;
+    }
+
+    /**
+     * Produce an object suitable for an ASN1OutputStream.
+     * <pre>
+     *  CertStatus ::= CHOICE {
+     *                  good        [0]     IMPLICIT NULL,
+     *                  revoked     [1]     IMPLICIT RevokedInfo,
+     *                  unknown     [2]     IMPLICIT UnknownInfo }
+     * </pre>
+     */
+    public ASN1Primitive toASN1Primitive()
+    {
+        return new DERTaggedObject(false, tagNo, value);
+    }
+}
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ocsp/CrlID.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ocsp/CrlID.java
new file mode 100644
index 0000000..becc59f
--- /dev/null
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ocsp/CrlID.java
@@ -0,0 +1,125 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.org.bouncycastle.asn1.ocsp;
+
+import java.util.Enumeration;
+
+import com.android.org.bouncycastle.asn1.ASN1EncodableVector;
+import com.android.org.bouncycastle.asn1.ASN1GeneralizedTime;
+import com.android.org.bouncycastle.asn1.ASN1IA5String;
+import com.android.org.bouncycastle.asn1.ASN1Integer;
+import com.android.org.bouncycastle.asn1.ASN1Object;
+import com.android.org.bouncycastle.asn1.ASN1Primitive;
+import com.android.org.bouncycastle.asn1.ASN1Sequence;
+import com.android.org.bouncycastle.asn1.ASN1TaggedObject;
+import com.android.org.bouncycastle.asn1.DERIA5String;
+import com.android.org.bouncycastle.asn1.DERSequence;
+import com.android.org.bouncycastle.asn1.DERTaggedObject;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+public class CrlID
+    extends ASN1Object
+{
+    private ASN1IA5String        crlUrl;
+    private ASN1Integer          crlNum;
+    private ASN1GeneralizedTime  crlTime;
+
+    private CrlID(
+        ASN1Sequence    seq)
+    {
+        Enumeration    e = seq.getObjects();
+
+        while (e.hasMoreElements())
+        {
+            ASN1TaggedObject    o = (ASN1TaggedObject)e.nextElement();
+
+            switch (o.getTagNo())
+            {
+            case 0:
+                crlUrl = ASN1IA5String.getInstance(o, true);
+                break;
+            case 1:
+                crlNum = ASN1Integer.getInstance(o, true);
+                break;
+            case 2:
+                crlTime = ASN1GeneralizedTime.getInstance(o, true);
+                break;
+            default:
+                throw new IllegalArgumentException(
+                        "unknown tag number: " + o.getTagNo());
+            }
+        }
+    }
+
+    public static CrlID getInstance(
+        Object  obj)
+    {
+        if (obj instanceof CrlID)
+        {
+            return (CrlID)obj;
+        }
+        else if (obj != null)
+        {
+            return new CrlID(ASN1Sequence.getInstance(obj));
+        }
+
+        return null;
+    }
+
+    /**
+     * @deprecated Use {@link #getCrlUrlIA5()} instead.
+     */
+    public DERIA5String getCrlUrl()
+    {
+        return null == crlUrl || crlUrl instanceof DERIA5String
+            ?   (DERIA5String)crlUrl
+            :   new DERIA5String(crlUrl.getString(), false);
+    }
+
+    public ASN1IA5String getCrlUrlIA5()
+    {
+        return crlUrl;
+    }
+
+    public ASN1Integer getCrlNum()
+    {
+        return crlNum;
+    }
+
+    public ASN1GeneralizedTime getCrlTime()
+    {
+        return crlTime;
+    }
+
+    /**
+     * Produce an object suitable for an ASN1OutputStream.
+     * <pre>
+     * CrlID ::= SEQUENCE {
+     *     crlUrl               [0]     EXPLICIT IA5String OPTIONAL,
+     *     crlNum               [1]     EXPLICIT INTEGER OPTIONAL,
+     *     crlTime              [2]     EXPLICIT GeneralizedTime OPTIONAL }
+     * </pre>
+     */
+    public ASN1Primitive toASN1Primitive()
+    {
+        ASN1EncodableVector v = new ASN1EncodableVector(3);
+
+        if (crlUrl != null)
+        {
+            v.add(new DERTaggedObject(true, 0, crlUrl));
+        }
+
+        if (crlNum != null)
+        {
+            v.add(new DERTaggedObject(true, 1, crlNum));
+        }
+
+        if (crlTime != null)
+        {
+            v.add(new DERTaggedObject(true, 2, crlTime));
+        }
+
+        return new DERSequence(v);
+    }
+}
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ocsp/OCSPObjectIdentifiers.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ocsp/OCSPObjectIdentifiers.java
new file mode 100644
index 0000000..ff065f5
--- /dev/null
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ocsp/OCSPObjectIdentifiers.java
@@ -0,0 +1,36 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.org.bouncycastle.asn1.ocsp;
+
+import com.android.org.bouncycastle.asn1.ASN1ObjectIdentifier;
+
+/**
+ * OIDs for <a href="https://tools.ietf.org/html/rfc2560">RFC 2560</a> and <a href="https://tools.ietf.org/html/rfc6960">RFC 6960</a>
+ * Online Certificate Status Protocol - OCSP.
+ * @hide This class is not part of the Android public SDK API
+ */
+public interface OCSPObjectIdentifiers
+{
+    /** OID: 1.3.6.1.5.5.7.48.1 */
+    static final ASN1ObjectIdentifier id_pkix_ocsp       = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.48.1");
+    /** OID: 1.3.6.1.5.5.7.48.1.1 */
+    static final ASN1ObjectIdentifier id_pkix_ocsp_basic = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.48.1.1");
+    
+    /** OID: 1.3.6.1.5.5.7.48.1.2 */
+    static final ASN1ObjectIdentifier id_pkix_ocsp_nonce = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.48.1.2");
+    /** OID: 1.3.6.1.5.5.7.48.1.3 */
+    static final ASN1ObjectIdentifier id_pkix_ocsp_crl   = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.48.1.3");
+    
+    /** OID: 1.3.6.1.5.5.7.48.1.4 */
+    static final ASN1ObjectIdentifier id_pkix_ocsp_response        = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.48.1.4");
+    /** OID: 1.3.6.1.5.5.7.48.1.5 */
+    static final ASN1ObjectIdentifier id_pkix_ocsp_nocheck         = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.48.1.5");
+    /** OID: 1.3.6.1.5.5.7.48.1.6 */
+    static final ASN1ObjectIdentifier id_pkix_ocsp_archive_cutoff  = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.48.1.6");
+    /** OID: 1.3.6.1.5.5.7.48.1.7 */
+    static final ASN1ObjectIdentifier id_pkix_ocsp_service_locator = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.48.1.7");
+
+
+    static final ASN1ObjectIdentifier id_pkix_ocsp_pref_sig_algs = id_pkix_ocsp.branch("8");
+
+    static final ASN1ObjectIdentifier id_pkix_ocsp_extended_revoke = id_pkix_ocsp.branch("9");
+}
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ocsp/OCSPRequest.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ocsp/OCSPRequest.java
new file mode 100644
index 0000000..bd2ddd0
--- /dev/null
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ocsp/OCSPRequest.java
@@ -0,0 +1,94 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.org.bouncycastle.asn1.ocsp;
+
+import com.android.org.bouncycastle.asn1.ASN1EncodableVector;
+import com.android.org.bouncycastle.asn1.ASN1Object;
+import com.android.org.bouncycastle.asn1.ASN1Primitive;
+import com.android.org.bouncycastle.asn1.ASN1Sequence;
+import com.android.org.bouncycastle.asn1.ASN1TaggedObject;
+import com.android.org.bouncycastle.asn1.DERSequence;
+import com.android.org.bouncycastle.asn1.DERTaggedObject;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+public class OCSPRequest
+    extends ASN1Object
+{
+    TBSRequest      tbsRequest;
+    Signature       optionalSignature;
+
+    public OCSPRequest(
+        TBSRequest  tbsRequest,
+        Signature   optionalSignature)
+    {
+        this.tbsRequest = tbsRequest;
+        this.optionalSignature = optionalSignature;
+    }
+
+    private OCSPRequest(
+        ASN1Sequence    seq)
+    {
+        tbsRequest = TBSRequest.getInstance(seq.getObjectAt(0));
+
+        if (seq.size() == 2)
+        {
+            optionalSignature = Signature.getInstance(
+                                (ASN1TaggedObject)seq.getObjectAt(1), true);
+        }
+    }
+    
+    public static OCSPRequest getInstance(
+        ASN1TaggedObject obj,
+        boolean          explicit)
+    {
+        return getInstance(ASN1Sequence.getInstance(obj, explicit));
+    }
+
+    public static OCSPRequest getInstance(
+        Object  obj)
+    {
+        if (obj instanceof OCSPRequest)
+        {
+            return (OCSPRequest)obj;
+        }
+        else if (obj != null)
+        {
+            return new OCSPRequest(ASN1Sequence.getInstance(obj));
+        }
+
+        return null;
+    }
+    
+    public TBSRequest getTbsRequest()
+    {
+        return tbsRequest;
+    }
+
+    public Signature getOptionalSignature()
+    {
+        return optionalSignature;
+    }
+
+    /**
+     * Produce an object suitable for an ASN1OutputStream.
+     * <pre>
+     * OCSPRequest     ::=     SEQUENCE {
+     *     tbsRequest                  TBSRequest,
+     *     optionalSignature   [0]     EXPLICIT Signature OPTIONAL }
+     * </pre>
+     */
+    public ASN1Primitive toASN1Primitive()
+    {
+        ASN1EncodableVector v = new ASN1EncodableVector(2);
+
+        v.add(tbsRequest);
+
+        if (optionalSignature != null)
+        {
+            v.add(new DERTaggedObject(true, 0, optionalSignature));
+        }
+
+        return new DERSequence(v);
+    }
+}
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ocsp/OCSPResponse.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ocsp/OCSPResponse.java
new file mode 100644
index 0000000..7bcf2f4
--- /dev/null
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ocsp/OCSPResponse.java
@@ -0,0 +1,103 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.org.bouncycastle.asn1.ocsp;
+
+import com.android.org.bouncycastle.asn1.ASN1EncodableVector;
+import com.android.org.bouncycastle.asn1.ASN1Object;
+import com.android.org.bouncycastle.asn1.ASN1Primitive;
+import com.android.org.bouncycastle.asn1.ASN1Sequence;
+import com.android.org.bouncycastle.asn1.ASN1TaggedObject;
+import com.android.org.bouncycastle.asn1.DERSequence;
+import com.android.org.bouncycastle.asn1.DERTaggedObject;
+
+/**
+ * OCSP RFC 2560, RFC 6960
+ * <pre>
+ * OCSPResponse ::= SEQUENCE {
+ *     responseStatus         OCSPResponseStatus,
+ *     responseBytes          [0] EXPLICIT ResponseBytes OPTIONAL }
+ * </pre>
+ * @see OCSPResponseStatus
+ * @see ResponseBytes
+ * @hide This class is not part of the Android public SDK API
+ */
+
+public class OCSPResponse
+    extends ASN1Object
+{
+    OCSPResponseStatus    responseStatus;
+    ResponseBytes        responseBytes;
+
+    public OCSPResponse(
+        OCSPResponseStatus  responseStatus,
+        ResponseBytes       responseBytes)
+    {
+        this.responseStatus = responseStatus;
+        this.responseBytes = responseBytes;
+    }
+
+    private OCSPResponse(
+        ASN1Sequence    seq)
+    {
+        responseStatus = OCSPResponseStatus.getInstance(seq.getObjectAt(0));
+
+        if (seq.size() == 2)
+        {
+            responseBytes = ResponseBytes.getInstance(
+                                (ASN1TaggedObject)seq.getObjectAt(1), true);
+        }
+    }
+
+    public static OCSPResponse getInstance(
+        ASN1TaggedObject obj,
+        boolean          explicit)
+    {
+        return getInstance(ASN1Sequence.getInstance(obj, explicit));
+    }
+
+    public static OCSPResponse getInstance(
+        Object  obj)
+    {
+        if (obj instanceof OCSPResponse)
+        {
+            return (OCSPResponse)obj;
+        }
+        else if (obj != null)
+        {
+            return new OCSPResponse(ASN1Sequence.getInstance(obj));
+        }
+
+        return null;
+    }
+
+    public OCSPResponseStatus getResponseStatus()
+    {
+        return responseStatus;
+    }
+
+    public ResponseBytes getResponseBytes()
+    {
+        return responseBytes;
+    }
+
+    /**
+     * Produce an object suitable for an ASN1OutputStream.
+     * <pre>
+     * OCSPResponse ::= SEQUENCE {
+     *     responseStatus         OCSPResponseStatus,
+     *     responseBytes          [0] EXPLICIT ResponseBytes OPTIONAL }
+     * </pre>
+     */
+    public ASN1Primitive toASN1Primitive()
+    {
+        ASN1EncodableVector v = new ASN1EncodableVector(2);
+
+        v.add(responseStatus);
+
+        if (responseBytes != null)
+        {
+            v.add(new DERTaggedObject(true, 0, responseBytes));
+        }
+
+        return new DERSequence(v);
+    }
+}
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ocsp/OCSPResponseStatus.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ocsp/OCSPResponseStatus.java
new file mode 100644
index 0000000..dcdd7f8
--- /dev/null
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ocsp/OCSPResponseStatus.java
@@ -0,0 +1,97 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.org.bouncycastle.asn1.ocsp;
+
+import java.math.BigInteger;
+
+import com.android.org.bouncycastle.asn1.ASN1Enumerated;
+import com.android.org.bouncycastle.asn1.ASN1Object;
+import com.android.org.bouncycastle.asn1.ASN1Primitive;
+
+
+/**
+ * OCSP RFC 2560, RFC 6960
+ * <p>
+ * The OCSPResponseStatus enumeration.
+ * <pre>
+ * OCSPResponseStatus ::= ENUMERATED {
+ *     successful            (0),  --Response has valid confirmations
+ *     malformedRequest      (1),  --Illegal confirmation request
+ *     internalError         (2),  --Internal error in issuer
+ *     tryLater              (3),  --Try again later
+ *                                 --(4) is not used
+ *     sigRequired           (5),  --Must sign the request
+ *     unauthorized          (6)   --Request unauthorized
+ * }
+ * </pre>
+ * @hide This class is not part of the Android public SDK API
+ */
+public class OCSPResponseStatus
+    extends ASN1Object
+{
+    public static final int SUCCESSFUL = 0;
+    public static final int MALFORMED_REQUEST = 1;
+    public static final int INTERNAL_ERROR = 2;
+    public static final int TRY_LATER = 3;
+    public static final int SIG_REQUIRED = 5;
+    public static final int UNAUTHORIZED = 6;
+
+    private ASN1Enumerated value;
+
+    /**
+     * RFC 2560, RFC 6960
+     * <p>
+     * The OCSPResponseStatus enumeration.
+     * <pre>
+     * OCSPResponseStatus ::= ENUMERATED {
+     *     successful            (0),  --Response has valid confirmations
+     *     malformedRequest      (1),  --Illegal confirmation request
+     *     internalError         (2),  --Internal error in issuer
+     *     tryLater              (3),  --Try again later
+     *                                 --(4) is not used
+     *     sigRequired           (5),  --Must sign the request
+     *     unauthorized          (6)   --Request unauthorized
+     * }
+     * </pre>
+     */
+    public OCSPResponseStatus(
+        int value)
+    {
+        this(new ASN1Enumerated(value));
+    }
+
+    private OCSPResponseStatus(
+        ASN1Enumerated value)
+    {
+        this.value = value;
+    }
+
+    public static OCSPResponseStatus getInstance(
+        Object  obj)
+    {
+        if (obj instanceof OCSPResponseStatus)
+        {
+            return (OCSPResponseStatus)obj;
+        }
+        else if (obj != null)
+        {
+            return new OCSPResponseStatus(ASN1Enumerated.getInstance(obj));
+        }
+
+        return null;
+    }
+
+    public int getIntValue()
+    {
+        return value.intValueExact();
+    }
+
+    public BigInteger getValue()
+    {
+        return value.getValue();
+    }
+
+    public ASN1Primitive toASN1Primitive()
+    {
+        return value;
+    }
+}
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ocsp/Request.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ocsp/Request.java
new file mode 100644
index 0000000..4883c02
--- /dev/null
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ocsp/Request.java
@@ -0,0 +1,95 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.org.bouncycastle.asn1.ocsp;
+
+import com.android.org.bouncycastle.asn1.ASN1EncodableVector;
+import com.android.org.bouncycastle.asn1.ASN1Object;
+import com.android.org.bouncycastle.asn1.ASN1Primitive;
+import com.android.org.bouncycastle.asn1.ASN1Sequence;
+import com.android.org.bouncycastle.asn1.ASN1TaggedObject;
+import com.android.org.bouncycastle.asn1.DERSequence;
+import com.android.org.bouncycastle.asn1.DERTaggedObject;
+import com.android.org.bouncycastle.asn1.x509.Extensions;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+public class Request
+    extends ASN1Object
+{
+    CertID            reqCert;
+    Extensions    singleRequestExtensions;
+
+    public Request(
+        CertID          reqCert,
+        Extensions singleRequestExtensions)
+    {
+        this.reqCert = reqCert;
+        this.singleRequestExtensions = singleRequestExtensions;
+    }
+
+    private Request(
+        ASN1Sequence    seq)
+    {
+        reqCert = CertID.getInstance(seq.getObjectAt(0));
+
+        if (seq.size() == 2)
+        {
+            singleRequestExtensions = Extensions.getInstance(
+                                (ASN1TaggedObject)seq.getObjectAt(1), true);
+        }
+    }
+
+    public static Request getInstance(
+        ASN1TaggedObject obj,
+        boolean          explicit)
+    {
+        return getInstance(ASN1Sequence.getInstance(obj, explicit));
+    }
+
+    public static Request getInstance(
+        Object  obj)
+    {
+        if (obj instanceof Request)
+        {
+            return (Request)obj;
+        }
+        else if (obj != null)
+        {
+            return new Request(ASN1Sequence.getInstance(obj));
+        }
+
+        return null;
+    }
+
+    public CertID getReqCert()
+    {
+        return reqCert;
+    }
+
+    public Extensions getSingleRequestExtensions()
+    {
+        return singleRequestExtensions;
+    }
+
+    /**
+     * Produce an object suitable for an ASN1OutputStream.
+     * <pre>
+     * Request         ::=     SEQUENCE {
+     *     reqCert                     CertID,
+     *     singleRequestExtensions     [0] EXPLICIT Extensions OPTIONAL }
+     * </pre>
+     */
+    public ASN1Primitive toASN1Primitive()
+    {
+        ASN1EncodableVector v = new ASN1EncodableVector(2);
+
+        v.add(reqCert);
+
+        if (singleRequestExtensions != null)
+        {
+            v.add(new DERTaggedObject(true, 0, singleRequestExtensions));
+        }
+
+        return new DERSequence(v);
+    }
+}
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ocsp/ResponderID.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ocsp/ResponderID.java
new file mode 100644
index 0000000..6724903
--- /dev/null
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ocsp/ResponderID.java
@@ -0,0 +1,113 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.org.bouncycastle.asn1.ocsp;
+
+import com.android.org.bouncycastle.asn1.ASN1Choice;
+import com.android.org.bouncycastle.asn1.ASN1Encodable;
+import com.android.org.bouncycastle.asn1.ASN1Object;
+import com.android.org.bouncycastle.asn1.ASN1OctetString;
+import com.android.org.bouncycastle.asn1.ASN1Primitive;
+import com.android.org.bouncycastle.asn1.ASN1TaggedObject;
+import com.android.org.bouncycastle.asn1.DEROctetString;
+import com.android.org.bouncycastle.asn1.DERTaggedObject;
+import com.android.org.bouncycastle.asn1.x500.X500Name;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+public class ResponderID
+    extends ASN1Object
+    implements ASN1Choice
+{
+    private ASN1Encodable    value;
+
+    public ResponderID(
+        ASN1OctetString    value)
+    {
+        this.value = value;
+    }
+
+    public ResponderID(
+        X500Name value)
+    {
+        this.value = value;
+    }
+
+    public static ResponderID getInstance(
+        Object  obj)
+    {
+        if (obj instanceof ResponderID)
+        {
+            return (ResponderID)obj;
+        }
+        else if (obj instanceof DEROctetString)
+        {
+            return new ResponderID((DEROctetString)obj);
+        }
+        else if (obj instanceof ASN1TaggedObject)
+        {
+            ASN1TaggedObject    o = (ASN1TaggedObject)obj;
+
+            if (o.getTagNo() == 1)
+            {
+                return new ResponderID(X500Name.getInstance(o, true));
+            }
+            else
+            {
+                return new ResponderID(ASN1OctetString.getInstance(o, true));
+            }
+        }
+
+        return new ResponderID(X500Name.getInstance(obj));
+    }
+
+    public static ResponderID getInstance(
+        ASN1TaggedObject obj,
+        boolean          explicit)
+    {
+        if (!explicit)
+        {
+            throw new IllegalArgumentException("choice item must be explicitly tagged");
+        }
+
+        return getInstance(obj.getExplicitBaseObject());
+    }
+
+    public byte[] getKeyHash()
+    {
+        if (this.value instanceof ASN1OctetString)
+        {
+            ASN1OctetString octetString = (ASN1OctetString)this.value;
+            return octetString.getOctets();
+        }
+
+        return null;
+    }
+
+    public X500Name getName()
+    {
+        if (this.value instanceof ASN1OctetString)
+        {
+            return null;
+        }
+
+        return X500Name.getInstance(value);
+    }
+
+    /**
+     * Produce an object suitable for an ASN1OutputStream.
+     * <pre>
+     * ResponderID ::= CHOICE {
+     *      byName          [1] Name,
+     *      byKey           [2] KeyHash }
+     * </pre>
+     */
+    public ASN1Primitive toASN1Primitive()
+    {
+        if (value instanceof ASN1OctetString)
+        {
+            return new DERTaggedObject(true, 2, value);
+        }
+
+        return new DERTaggedObject(true, 1, value);
+    }
+}
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ocsp/ResponseBytes.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ocsp/ResponseBytes.java
new file mode 100644
index 0000000..0ae80a4
--- /dev/null
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ocsp/ResponseBytes.java
@@ -0,0 +1,92 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.org.bouncycastle.asn1.ocsp;
+
+import com.android.org.bouncycastle.asn1.ASN1EncodableVector;
+import com.android.org.bouncycastle.asn1.ASN1Object;
+import com.android.org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import com.android.org.bouncycastle.asn1.ASN1OctetString;
+import com.android.org.bouncycastle.asn1.ASN1Primitive;
+import com.android.org.bouncycastle.asn1.ASN1Sequence;
+import com.android.org.bouncycastle.asn1.ASN1TaggedObject;
+import com.android.org.bouncycastle.asn1.DERSequence;
+
+/**
+ * OCSP RFC 2560, RFC 6960
+ * <pre>
+ * ResponseBytes ::=       SEQUENCE {
+ *     responseType   OBJECT IDENTIFIER,
+ *     response       OCTET STRING }
+ * </pre>
+ * @hide This class is not part of the Android public SDK API
+ */
+public class ResponseBytes
+    extends ASN1Object
+{
+    ASN1ObjectIdentifier    responseType;
+    ASN1OctetString        response;
+
+    public ResponseBytes(
+        ASN1ObjectIdentifier responseType,
+        ASN1OctetString     response)
+    {
+        this.responseType = responseType;
+        this.response = response;
+    }
+
+    private ResponseBytes(
+        ASN1Sequence    seq)
+    {
+        responseType = (ASN1ObjectIdentifier)seq.getObjectAt(0);
+        response = (ASN1OctetString)seq.getObjectAt(1);
+    }
+
+    public static ResponseBytes getInstance(
+        ASN1TaggedObject obj,
+        boolean          explicit)
+    {
+        return getInstance(ASN1Sequence.getInstance(obj, explicit));
+    }
+
+    public static ResponseBytes getInstance(
+        Object  obj)
+    {
+        if (obj instanceof ResponseBytes)
+        {
+            return (ResponseBytes)obj;
+        }
+        else if (obj != null)
+        {
+            return new ResponseBytes(ASN1Sequence.getInstance(obj));
+        }
+
+        return null;
+    }
+
+    public ASN1ObjectIdentifier getResponseType()
+    {
+        return responseType;
+    }
+
+    public ASN1OctetString getResponse()
+    {
+        return response;
+    }
+
+    /**
+     * Produce an object suitable for an ASN1OutputStream.
+     * <pre>
+     * ResponseBytes ::=       SEQUENCE {
+     *     responseType   OBJECT IDENTIFIER,
+     *     response       OCTET STRING }
+     * </pre>
+     */
+    public ASN1Primitive toASN1Primitive()
+    {
+        ASN1EncodableVector v = new ASN1EncodableVector(2);
+
+        v.add(responseType);
+        v.add(response);
+
+        return new DERSequence(v);
+    }
+}
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ocsp/ResponseData.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ocsp/ResponseData.java
new file mode 100644
index 0000000..b3964c9
--- /dev/null
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ocsp/ResponseData.java
@@ -0,0 +1,194 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.org.bouncycastle.asn1.ocsp;
+
+import com.android.org.bouncycastle.asn1.ASN1EncodableVector;
+import com.android.org.bouncycastle.asn1.ASN1GeneralizedTime;
+import com.android.org.bouncycastle.asn1.ASN1Integer;
+import com.android.org.bouncycastle.asn1.ASN1Object;
+import com.android.org.bouncycastle.asn1.ASN1Primitive;
+import com.android.org.bouncycastle.asn1.ASN1Sequence;
+import com.android.org.bouncycastle.asn1.ASN1TaggedObject;
+import com.android.org.bouncycastle.asn1.DERSequence;
+import com.android.org.bouncycastle.asn1.DERTaggedObject;
+import com.android.org.bouncycastle.asn1.x509.Extensions;
+import com.android.org.bouncycastle.asn1.x509.X509Extensions;
+
+/**
+ * OCSP RFC 2560, RFC 6960
+ * <pre>
+ * ResponseData ::= SEQUENCE {
+ *     version              [0] EXPLICIT Version DEFAULT v1,
+ *     responderID              ResponderID,
+ *     producedAt               GeneralizedTime,
+ *     responses                SEQUENCE OF SingleResponse,
+ *     responseExtensions   [1] EXPLICIT Extensions OPTIONAL }
+ * </pre>
+ * @hide This class is not part of the Android public SDK API
+ */
+public class ResponseData
+    extends ASN1Object
+{
+    private static final ASN1Integer V1 = new ASN1Integer(0);
+    
+    private boolean             versionPresent;
+    
+    private ASN1Integer          version;
+    private ResponderID         responderID;
+    private ASN1GeneralizedTime  producedAt;
+    private ASN1Sequence        responses;
+    private Extensions      responseExtensions;
+
+    public ResponseData(
+        ASN1Integer          version,
+        ResponderID         responderID,
+        ASN1GeneralizedTime  producedAt,
+        ASN1Sequence        responses,
+        Extensions      responseExtensions)
+    {
+        this.version = version;
+        this.responderID = responderID;
+        this.producedAt = producedAt;
+        this.responses = responses;
+        this.responseExtensions = responseExtensions;
+    }
+
+    /**
+     * @deprecated use method taking Extensions
+     * @param responderID
+     * @param producedAt
+     * @param responses
+     * @param responseExtensions
+     */
+    public ResponseData(
+        ResponderID         responderID,
+        ASN1GeneralizedTime  producedAt,
+        ASN1Sequence        responses,
+        X509Extensions responseExtensions)
+    {
+        this(V1, responderID, ASN1GeneralizedTime.getInstance(producedAt), responses, Extensions.getInstance(responseExtensions));
+    }
+
+    public ResponseData(
+        ResponderID         responderID,
+        ASN1GeneralizedTime  producedAt,
+        ASN1Sequence        responses,
+        Extensions      responseExtensions)
+    {
+        this(V1, responderID, producedAt, responses, responseExtensions);
+    }
+    
+    private ResponseData(
+        ASN1Sequence    seq)
+    {
+        int index = 0;
+
+        if (seq.getObjectAt(0) instanceof ASN1TaggedObject)
+        {
+            ASN1TaggedObject    o = (ASN1TaggedObject)seq.getObjectAt(0);
+
+            if (o.getTagNo() == 0)
+            {
+                this.versionPresent = true;
+                this.version = ASN1Integer.getInstance(
+                                (ASN1TaggedObject)seq.getObjectAt(0), true);
+                index++;
+            }
+            else
+            {
+                this.version = V1;
+            }
+        }
+        else
+        {
+            this.version = V1;
+        }
+
+        this.responderID = ResponderID.getInstance(seq.getObjectAt(index++));
+        this.producedAt = ASN1GeneralizedTime.getInstance(seq.getObjectAt(index++));
+        this.responses = (ASN1Sequence)seq.getObjectAt(index++);
+
+        if (seq.size() > index)
+        {
+            this.responseExtensions = Extensions.getInstance(
+                                (ASN1TaggedObject)seq.getObjectAt(index), true);
+        }
+    }
+
+    public static ResponseData getInstance(
+        ASN1TaggedObject obj,
+        boolean          explicit)
+    {
+        return getInstance(ASN1Sequence.getInstance(obj, explicit));
+    }
+
+    public static ResponseData getInstance(
+        Object  obj)
+    {
+        if (obj instanceof ResponseData)
+        {
+            return (ResponseData)obj;
+        }
+        else if (obj != null)
+        {
+            return new ResponseData(ASN1Sequence.getInstance(obj));
+        }
+
+        return null;
+    }
+
+    public ASN1Integer getVersion()
+    {
+        return version;
+    }
+
+    public ResponderID getResponderID()
+    {
+        return responderID;
+    }
+
+    public ASN1GeneralizedTime getProducedAt()
+    {
+        return producedAt;
+    }
+
+    public ASN1Sequence getResponses()
+    {
+        return responses;
+    }
+
+    public Extensions getResponseExtensions()
+    {
+        return responseExtensions;
+    }
+
+    /**
+     * Produce an object suitable for an ASN1OutputStream.
+     * <pre>
+     * ResponseData ::= SEQUENCE {
+     *     version              [0] EXPLICIT Version DEFAULT v1,
+     *     responderID              ResponderID,
+     *     producedAt               GeneralizedTime,
+     *     responses                SEQUENCE OF SingleResponse,
+     *     responseExtensions   [1] EXPLICIT Extensions OPTIONAL }
+     * </pre>
+     */
+    public ASN1Primitive toASN1Primitive()
+    {
+        ASN1EncodableVector v = new ASN1EncodableVector(5);
+
+        if (versionPresent || !version.equals(V1))
+        {
+            v.add(new DERTaggedObject(true, 0, version));
+        }
+
+        v.add(responderID);
+        v.add(producedAt);
+        v.add(responses);
+        if (responseExtensions != null)
+        {
+            v.add(new DERTaggedObject(true, 1, responseExtensions));
+        }
+
+        return new DERSequence(v);
+    }
+}
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ocsp/RevokedInfo.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ocsp/RevokedInfo.java
new file mode 100644
index 0000000..81c832b
--- /dev/null
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ocsp/RevokedInfo.java
@@ -0,0 +1,101 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.org.bouncycastle.asn1.ocsp;
+
+import com.android.org.bouncycastle.asn1.ASN1EncodableVector;
+import com.android.org.bouncycastle.asn1.ASN1Enumerated;
+import com.android.org.bouncycastle.asn1.ASN1GeneralizedTime;
+import com.android.org.bouncycastle.asn1.ASN1Object;
+import com.android.org.bouncycastle.asn1.ASN1Primitive;
+import com.android.org.bouncycastle.asn1.ASN1Sequence;
+import com.android.org.bouncycastle.asn1.ASN1TaggedObject;
+import com.android.org.bouncycastle.asn1.DERSequence;
+import com.android.org.bouncycastle.asn1.DERTaggedObject;
+import com.android.org.bouncycastle.asn1.x509.CRLReason;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+public class RevokedInfo
+    extends ASN1Object
+{
+    private ASN1GeneralizedTime  revocationTime;
+    private CRLReason           revocationReason;
+
+    public RevokedInfo(ASN1GeneralizedTime revocationTime)
+    {
+        this(revocationTime, null);
+    }
+
+    public RevokedInfo(
+        ASN1GeneralizedTime  revocationTime,
+        CRLReason           revocationReason)
+    {
+        this.revocationTime = revocationTime;
+        this.revocationReason = revocationReason;
+    }
+
+    private RevokedInfo(
+        ASN1Sequence    seq)
+    {
+        this.revocationTime = ASN1GeneralizedTime.getInstance(seq.getObjectAt(0));
+
+        if (seq.size() > 1)
+        {
+            this.revocationReason = CRLReason.getInstance(ASN1Enumerated.getInstance(
+                (ASN1TaggedObject)seq.getObjectAt(1), true));
+        }
+    }
+
+    public static RevokedInfo getInstance(
+        ASN1TaggedObject obj,
+        boolean          explicit)
+    {
+        return getInstance(ASN1Sequence.getInstance(obj, explicit));
+    }
+
+    public static RevokedInfo getInstance(
+        Object  obj)
+    {
+        if (obj instanceof RevokedInfo)
+        {
+            return (RevokedInfo)obj;
+        }
+        else if (obj != null)
+        {
+            return new RevokedInfo(ASN1Sequence.getInstance(obj));
+        }
+
+        return null;
+    }
+
+    public ASN1GeneralizedTime getRevocationTime()
+    {
+        return revocationTime;
+    }
+
+    public CRLReason getRevocationReason()
+    {
+        return revocationReason;
+    }
+
+    /**
+     * Produce an object suitable for an ASN1OutputStream.
+     * <pre>
+     * RevokedInfo ::= SEQUENCE {
+     *      revocationTime              GeneralizedTime,
+     *      revocationReason    [0]     EXPLICIT CRLReason OPTIONAL }
+     * </pre>
+     */
+    public ASN1Primitive toASN1Primitive()
+    {
+        ASN1EncodableVector v = new ASN1EncodableVector(2);
+
+        v.add(revocationTime);
+        if (revocationReason != null)
+        {
+            v.add(new DERTaggedObject(true, 0, revocationReason));
+        }
+
+        return new DERSequence(v);
+    }
+}
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ocsp/Signature.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ocsp/Signature.java
new file mode 100644
index 0000000..40fe942
--- /dev/null
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ocsp/Signature.java
@@ -0,0 +1,115 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.org.bouncycastle.asn1.ocsp;
+
+import com.android.org.bouncycastle.asn1.ASN1EncodableVector;
+import com.android.org.bouncycastle.asn1.ASN1Object;
+import com.android.org.bouncycastle.asn1.ASN1Primitive;
+import com.android.org.bouncycastle.asn1.ASN1Sequence;
+import com.android.org.bouncycastle.asn1.ASN1TaggedObject;
+import com.android.org.bouncycastle.asn1.DERBitString;
+import com.android.org.bouncycastle.asn1.DERSequence;
+import com.android.org.bouncycastle.asn1.DERTaggedObject;
+import com.android.org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+public class Signature
+    extends ASN1Object
+{
+    AlgorithmIdentifier signatureAlgorithm;
+    DERBitString        signature;
+    ASN1Sequence        certs;
+
+    public Signature(
+        AlgorithmIdentifier signatureAlgorithm,
+        DERBitString        signature)
+    {
+        this.signatureAlgorithm = signatureAlgorithm;
+        this.signature = signature;
+    }
+
+    public Signature(
+        AlgorithmIdentifier signatureAlgorithm,
+        DERBitString        signature,
+        ASN1Sequence        certs)
+    {
+        this.signatureAlgorithm = signatureAlgorithm;
+        this.signature = signature;
+        this.certs = certs;
+    }
+
+    private Signature(
+        ASN1Sequence    seq)
+    {
+        signatureAlgorithm  = AlgorithmIdentifier.getInstance(seq.getObjectAt(0));
+        signature = (DERBitString)seq.getObjectAt(1);
+
+        if (seq.size() == 3)
+        {
+            certs = ASN1Sequence.getInstance(
+                                (ASN1TaggedObject)seq.getObjectAt(2), true);
+        }
+    }
+
+    public static Signature getInstance(
+        ASN1TaggedObject obj,
+        boolean          explicit)
+    {
+        return getInstance(ASN1Sequence.getInstance(obj, explicit));
+    }
+
+    public static Signature getInstance(
+        Object  obj)
+    {
+        if (obj instanceof Signature)
+        {
+            return (Signature)obj;
+        }
+        else if (obj != null)
+        {
+            return new Signature(ASN1Sequence.getInstance(obj));
+        }
+
+        return null;
+    }
+
+    public AlgorithmIdentifier getSignatureAlgorithm()
+    {
+        return signatureAlgorithm;
+    }
+
+    public DERBitString getSignature()
+    {
+        return signature;
+    }
+
+    public ASN1Sequence getCerts()
+    {
+        return certs;
+    }
+
+    /**
+     * Produce an object suitable for an ASN1OutputStream.
+     * <pre>
+     * Signature       ::=     SEQUENCE {
+     *     signatureAlgorithm      AlgorithmIdentifier,
+     *     signature               BIT STRING,
+     *     certs               [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL}
+     * </pre>
+     */
+    public ASN1Primitive toASN1Primitive()
+    {
+        ASN1EncodableVector v = new ASN1EncodableVector(3);
+
+        v.add(signatureAlgorithm);
+        v.add(signature);
+
+        if (certs != null)
+        {
+            v.add(new DERTaggedObject(true, 0, certs));
+        }
+
+        return new DERSequence(v);
+    }
+}
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ocsp/SingleResponse.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ocsp/SingleResponse.java
new file mode 100644
index 0000000..85e8802
--- /dev/null
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ocsp/SingleResponse.java
@@ -0,0 +1,166 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.org.bouncycastle.asn1.ocsp;
+
+import com.android.org.bouncycastle.asn1.ASN1EncodableVector;
+import com.android.org.bouncycastle.asn1.ASN1GeneralizedTime;
+import com.android.org.bouncycastle.asn1.ASN1Object;
+import com.android.org.bouncycastle.asn1.ASN1Primitive;
+import com.android.org.bouncycastle.asn1.ASN1Sequence;
+import com.android.org.bouncycastle.asn1.ASN1TaggedObject;
+import com.android.org.bouncycastle.asn1.DERSequence;
+import com.android.org.bouncycastle.asn1.DERTaggedObject;
+import com.android.org.bouncycastle.asn1.x509.Extensions;
+import com.android.org.bouncycastle.asn1.x509.X509Extensions;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+public class SingleResponse
+    extends ASN1Object
+{
+    private CertID              certID;
+    private CertStatus          certStatus;
+    private ASN1GeneralizedTime  thisUpdate;
+    private ASN1GeneralizedTime  nextUpdate;
+    private Extensions      singleExtensions;
+
+    /**
+     * @deprecated use method taking ASN1GeneralizedTime and Extensions
+     * @param certID
+     * @param certStatus
+     * @param thisUpdate
+     * @param nextUpdate
+     * @param singleExtensions
+     */
+    public SingleResponse(
+        CertID              certID,
+        CertStatus          certStatus,
+        ASN1GeneralizedTime thisUpdate,
+        ASN1GeneralizedTime nextUpdate,
+        X509Extensions singleExtensions)
+    {
+        this(certID, certStatus, thisUpdate, nextUpdate, Extensions.getInstance(singleExtensions));
+    }
+
+    public SingleResponse(
+        CertID              certID,
+        CertStatus          certStatus,
+        ASN1GeneralizedTime thisUpdate,
+        ASN1GeneralizedTime nextUpdate,
+        Extensions          singleExtensions)
+    {
+        this.certID = certID;
+        this.certStatus = certStatus;
+        this.thisUpdate = thisUpdate;
+        this.nextUpdate = nextUpdate;
+        this.singleExtensions = singleExtensions;
+    }
+
+    private SingleResponse(
+        ASN1Sequence    seq)
+    {
+        this.certID = CertID.getInstance(seq.getObjectAt(0));
+        this.certStatus = CertStatus.getInstance(seq.getObjectAt(1));
+        this.thisUpdate = ASN1GeneralizedTime.getInstance(seq.getObjectAt(2));
+
+        if (seq.size() > 4)
+        {
+            this.nextUpdate = ASN1GeneralizedTime.getInstance(
+                                (ASN1TaggedObject)seq.getObjectAt(3), true);
+            this.singleExtensions = Extensions.getInstance(
+                                (ASN1TaggedObject)seq.getObjectAt(4), true);
+        }
+        else if (seq.size() > 3)
+        {
+            ASN1TaggedObject    o = (ASN1TaggedObject)seq.getObjectAt(3);
+
+            if (o.getTagNo() == 0)
+            {
+                this.nextUpdate = ASN1GeneralizedTime.getInstance(o, true);
+            }
+            else
+            {
+                this.singleExtensions = Extensions.getInstance(o, true);
+            }
+        }
+    }
+
+    public static SingleResponse getInstance(
+        ASN1TaggedObject obj,
+        boolean          explicit)
+    {
+        return getInstance(ASN1Sequence.getInstance(obj, explicit));
+    }
+
+    public static SingleResponse getInstance(
+        Object  obj)
+    {
+        if (obj instanceof SingleResponse)
+        {
+            return (SingleResponse)obj;
+        }
+        else if (obj != null)
+        {
+            return new SingleResponse(ASN1Sequence.getInstance(obj));
+        }
+
+        return null;
+    }
+
+    public CertID getCertID()
+    {
+        return certID;
+    }
+
+    public CertStatus getCertStatus()
+    {
+        return certStatus;
+    }
+
+    public ASN1GeneralizedTime getThisUpdate()
+    {
+        return thisUpdate;
+    }
+
+    public ASN1GeneralizedTime getNextUpdate()
+    {
+        return nextUpdate;
+    }
+
+    public Extensions getSingleExtensions()
+    {
+        return singleExtensions;
+    }
+
+    /**
+     * Produce an object suitable for an ASN1OutputStream.
+     * <pre>
+     *  SingleResponse ::= SEQUENCE {
+     *          certID                       CertID,
+     *          certStatus                   CertStatus,
+     *          thisUpdate                   GeneralizedTime,
+     *          nextUpdate         [0]       EXPLICIT GeneralizedTime OPTIONAL,
+     *          singleExtensions   [1]       EXPLICIT Extensions OPTIONAL }
+     * </pre>
+     */
+    public ASN1Primitive toASN1Primitive()
+    {
+        ASN1EncodableVector v = new ASN1EncodableVector(5);
+
+        v.add(certID);
+        v.add(certStatus);
+        v.add(thisUpdate);
+
+        if (nextUpdate != null)
+        {
+            v.add(new DERTaggedObject(true, 0, nextUpdate));
+        }
+
+        if (singleExtensions != null)
+        {
+            v.add(new DERTaggedObject(true, 1, singleExtensions));
+        }
+
+        return new DERSequence(v);
+    }
+}
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ocsp/TBSRequest.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ocsp/TBSRequest.java
new file mode 100644
index 0000000..839158f
--- /dev/null
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/ocsp/TBSRequest.java
@@ -0,0 +1,176 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.org.bouncycastle.asn1.ocsp;
+
+import com.android.org.bouncycastle.asn1.ASN1EncodableVector;
+import com.android.org.bouncycastle.asn1.ASN1Integer;
+import com.android.org.bouncycastle.asn1.ASN1Object;
+import com.android.org.bouncycastle.asn1.ASN1Primitive;
+import com.android.org.bouncycastle.asn1.ASN1Sequence;
+import com.android.org.bouncycastle.asn1.ASN1TaggedObject;
+import com.android.org.bouncycastle.asn1.DERSequence;
+import com.android.org.bouncycastle.asn1.DERTaggedObject;
+import com.android.org.bouncycastle.asn1.x509.Extensions;
+import com.android.org.bouncycastle.asn1.x509.GeneralName;
+import com.android.org.bouncycastle.asn1.x509.X509Extensions;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+public class TBSRequest
+    extends ASN1Object
+{
+    private static final ASN1Integer V1 = new ASN1Integer(0);
+    
+    ASN1Integer      version;
+    GeneralName     requestorName;
+    ASN1Sequence    requestList;
+    Extensions  requestExtensions;
+
+    boolean         versionSet;
+
+    /**
+     * @deprecated use method taking Extensions
+     * @param requestorName
+     * @param requestList
+     * @param requestExtensions
+     */
+    public TBSRequest(
+        GeneralName     requestorName,
+        ASN1Sequence    requestList,
+        X509Extensions requestExtensions)
+    {
+        this.version = V1;
+        this.requestorName = requestorName;
+        this.requestList = requestList;
+        this.requestExtensions = Extensions.getInstance(requestExtensions);
+    }
+
+    public TBSRequest(
+        GeneralName     requestorName,
+        ASN1Sequence    requestList,
+        Extensions  requestExtensions)
+    {
+        this.version = V1;
+        this.requestorName = requestorName;
+        this.requestList = requestList;
+        this.requestExtensions = requestExtensions;
+    }
+
+    private TBSRequest(
+        ASN1Sequence    seq)
+    {
+        int    index = 0;
+
+        if (seq.getObjectAt(0) instanceof ASN1TaggedObject)
+        {
+            ASN1TaggedObject    o = (ASN1TaggedObject)seq.getObjectAt(0);
+
+            if (o.getTagNo() == 0)
+            {
+                versionSet = true;
+                version = ASN1Integer.getInstance((ASN1TaggedObject)seq.getObjectAt(0), true);
+                index++;
+            }
+            else
+            {
+                version = V1;
+            }
+        }
+        else
+        {
+            version = V1;
+        }
+
+        if (seq.getObjectAt(index) instanceof ASN1TaggedObject)
+        {
+            requestorName = GeneralName.getInstance((ASN1TaggedObject)seq.getObjectAt(index++), true);
+        }
+        
+        requestList = (ASN1Sequence)seq.getObjectAt(index++);
+
+        if (seq.size() == (index + 1))
+        {
+            requestExtensions = Extensions.getInstance((ASN1TaggedObject)seq.getObjectAt(index), true);
+        }
+    }
+
+    public static TBSRequest getInstance(
+        ASN1TaggedObject obj,
+        boolean          explicit)
+    {
+        return getInstance(ASN1Sequence.getInstance(obj, explicit));
+    }
+
+    public static TBSRequest getInstance(
+        Object  obj)
+    {
+        if (obj instanceof TBSRequest)
+        {
+            return (TBSRequest)obj;
+        }
+        else if (obj != null)
+        {
+            return new TBSRequest(ASN1Sequence.getInstance(obj));
+        }
+
+        return null;
+    }
+
+    public ASN1Integer getVersion()
+    {
+        return version;
+    }
+
+    public GeneralName getRequestorName()
+    {
+        return requestorName;
+    }
+
+    public ASN1Sequence getRequestList()
+    {
+        return requestList;
+    }
+
+    public Extensions getRequestExtensions()
+    {
+        return requestExtensions;
+    }
+
+    /**
+     * Produce an object suitable for an ASN1OutputStream.
+     * <pre>
+     * TBSRequest      ::=     SEQUENCE {
+     *     version             [0]     EXPLICIT Version DEFAULT v1,
+     *     requestorName       [1]     EXPLICIT GeneralName OPTIONAL,
+     *     requestList                 SEQUENCE OF Request,
+     *     requestExtensions   [2]     EXPLICIT Extensions OPTIONAL }
+     * </pre>
+     */
+    public ASN1Primitive toASN1Primitive()
+    {
+        ASN1EncodableVector v = new ASN1EncodableVector(4);
+
+        //
+        // if default don't include - unless explicitly provided. Not strictly correct
+        // but required for some requests
+        //
+        if (!version.equals(V1) || versionSet)
+        {
+            v.add(new DERTaggedObject(true, 0, version));
+        }
+        
+        if (requestorName != null)
+        {
+            v.add(new DERTaggedObject(true, 1, requestorName));
+        }
+
+        v.add(requestList);
+
+        if (requestExtensions != null)
+        {
+            v.add(new DERTaggedObject(true, 2, requestExtensions));
+        }
+
+        return new DERSequence(v);
+    }
+}
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/pkcs/CRLBag.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/pkcs/CRLBag.java
index 441dfd1..5d97e80 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/pkcs/CRLBag.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/pkcs/CRLBag.java
@@ -24,8 +24,8 @@
     private CRLBag(
         ASN1Sequence seq)
     {
-        this.crlId = (ASN1ObjectIdentifier)seq.getObjectAt(0);
-        this.crlValue = ((ASN1TaggedObject)seq.getObjectAt(1)).getObject();
+        this.crlId = ASN1ObjectIdentifier.getInstance(seq.getObjectAt(0));
+        this.crlValue = ASN1TaggedObject.getInstance(seq.getObjectAt(1)).getExplicitBaseObject();
     }
 
     public static CRLBag getInstance(Object o)
@@ -69,7 +69,7 @@
      *
      * x509CRL BAG-TYPE ::= {OCTET STRING IDENTIFIED BY {certTypes 1}
      * -- DER-encoded X.509 CRL stored in OCTET STRING
-	 *
+     *
      * CRLTypes BAG-TYPE ::= {
      * x509CRL,
      * ... -- For future extensions
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/pkcs/CertBag.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/pkcs/CertBag.java
index 76fe51a..0501b4a 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/pkcs/CertBag.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/pkcs/CertBag.java
@@ -24,7 +24,7 @@
         ASN1Sequence    seq)
     {
         this.certId = ASN1ObjectIdentifier.getInstance(seq.getObjectAt(0));
-        this.certValue = ASN1TaggedObject.getInstance(seq.getObjectAt(1)).getObject();
+        this.certValue = ASN1TaggedObject.getInstance(seq.getObjectAt(1)).getExplicitBaseObject();
     }
 
     public static CertBag getInstance(Object o)
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/pkcs/CertificationRequest.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/pkcs/CertificationRequest.java
index b5e9091..4feb3da 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/pkcs/CertificationRequest.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/pkcs/CertificationRequest.java
@@ -1,6 +1,7 @@
 /* GENERATED SOURCE. DO NOT MODIFY. */
 package com.android.org.bouncycastle.asn1.pkcs;
 
+import com.android.org.bouncycastle.asn1.ASN1BitString;
 import com.android.org.bouncycastle.asn1.ASN1EncodableVector;
 import com.android.org.bouncycastle.asn1.ASN1Object;
 import com.android.org.bouncycastle.asn1.ASN1Primitive;
@@ -25,7 +26,7 @@
 {
     protected CertificationRequestInfo reqInfo = null;
     protected AlgorithmIdentifier sigAlgId = null;
-    protected DERBitString sigBits = null;
+    protected ASN1BitString sigBits = null;
 
     public static CertificationRequest getInstance(Object o)
     {
@@ -49,7 +50,7 @@
     public CertificationRequest(
         CertificationRequestInfo requestInfo,
         AlgorithmIdentifier     algorithm,
-        DERBitString            signature)
+        ASN1BitString            signature)
     {
         this.reqInfo = requestInfo;
         this.sigAlgId = algorithm;
@@ -77,7 +78,7 @@
         return sigAlgId;
     }
 
-    public DERBitString getSignature()
+    public ASN1BitString getSignature()
     {
         return sigBits;
     }
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/pkcs/CertificationRequestInfo.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/pkcs/CertificationRequestInfo.java
index fc3e97c..a58828c 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/pkcs/CertificationRequestInfo.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/pkcs/CertificationRequestInfo.java
@@ -99,10 +99,7 @@
         this(X500Name.getInstance(subject.toASN1Primitive()), pkInfo, attributes);
     }
 
-    /**
-     * @deprecated use getInstance().
-     */
-    public CertificationRequestInfo(
+    private CertificationRequestInfo(
         ASN1Sequence  seq)
     {
         version = (ASN1Integer)seq.getObjectAt(0);
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/pkcs/ContentInfo.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/pkcs/ContentInfo.java
index 2611ac7..41219ce 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/pkcs/ContentInfo.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/pkcs/ContentInfo.java
@@ -50,7 +50,7 @@
 
         if (e.hasMoreElements())
         {
-            content = ((ASN1TaggedObject)e.nextElement()).getObject();
+            content = ((ASN1TaggedObject)e.nextElement()).getExplicitBaseObject();
         }
 
         isBer = seq instanceof BERSequence;
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/pkcs/EncryptedData.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/pkcs/EncryptedData.java
index 87d7d06..4510a4f 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/pkcs/EncryptedData.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/pkcs/EncryptedData.java
@@ -57,9 +57,8 @@
     private EncryptedData(
         ASN1Sequence seq)
     {
-        int version = ((ASN1Integer)seq.getObjectAt(0)).intValueExact();
-
-        if (version != 0)
+        ASN1Integer version = (ASN1Integer)seq.getObjectAt(0);
+        if (!version.hasValue(0))
         {
             throw new IllegalArgumentException("sequence not version 0");
         }
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/pkcs/EncryptedPrivateKeyInfo.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/pkcs/EncryptedPrivateKeyInfo.java
index 1a72358..3b01891 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/pkcs/EncryptedPrivateKeyInfo.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/pkcs/EncryptedPrivateKeyInfo.java
@@ -11,6 +11,7 @@
 import com.android.org.bouncycastle.asn1.DEROctetString;
 import com.android.org.bouncycastle.asn1.DERSequence;
 import com.android.org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import com.android.org.bouncycastle.util.Arrays;
 
 /**
  * @hide This class is not part of the Android public SDK API
@@ -35,7 +36,7 @@
         byte[]              encoding)
     {
         this.algId = algId;
-        this.data = new DEROctetString(encoding);
+        this.data = new DEROctetString(Arrays.clone(encoding));
     }
 
     public static EncryptedPrivateKeyInfo getInstance(
@@ -60,7 +61,7 @@
 
     public byte[] getEncryptedData()
     {
-        return data.getOctets();
+        return Arrays.clone(data.getOctets());
     }
 
     /**
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/pkcs/PBKDF2Params.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/pkcs/PBKDF2Params.java
index 082284e..d974470 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/pkcs/PBKDF2Params.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/pkcs/PBKDF2Params.java
@@ -185,7 +185,7 @@
      */
     public byte[] getSalt()
     {
-        return octStr.getOctets();
+        return Arrays.clone(octStr.getOctets());
     }
 
     /**
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/pkcs/PBMAC1Params.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/pkcs/PBMAC1Params.java
new file mode 100644
index 0000000..842fc5e
--- /dev/null
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/pkcs/PBMAC1Params.java
@@ -0,0 +1,90 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.org.bouncycastle.asn1.pkcs;
+
+import java.util.Enumeration;
+
+import com.android.org.bouncycastle.asn1.ASN1Encodable;
+import com.android.org.bouncycastle.asn1.ASN1EncodableVector;
+import com.android.org.bouncycastle.asn1.ASN1Object;
+import com.android.org.bouncycastle.asn1.ASN1Primitive;
+import com.android.org.bouncycastle.asn1.ASN1Sequence;
+import com.android.org.bouncycastle.asn1.DERSequence;
+import com.android.org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+
+
+/**
+ * From https://datatracker.ietf.org/doc/html/rfc8018
+ *
+ * <pre>
+ * PBMAC1-params ::= SEQUENCE {
+ *     keyDerivationFunc AlgorithmIdentifier {{PBMAC1-KDFs}},
+ *     messageAuthScheme AlgorithmIdentifier {{PBMAC1-MACs}} }
+ * </pre>
+ * @hide This class is not part of the Android public SDK API
+ */
+public class PBMAC1Params
+    extends ASN1Object
+    implements PKCSObjectIdentifiers
+{
+    private AlgorithmIdentifier func;
+    private AlgorithmIdentifier scheme;
+
+    public static PBMAC1Params getInstance(
+        Object  obj)
+    {
+        if (obj instanceof PBMAC1Params)
+        {
+            return (PBMAC1Params)obj;
+        }
+        if (obj != null)
+        {
+            return new PBMAC1Params(ASN1Sequence.getInstance(obj));
+        }
+
+        return null;
+    }
+
+    public PBMAC1Params(AlgorithmIdentifier keyDevFunc, AlgorithmIdentifier encScheme)
+    {
+        this.func = keyDevFunc;
+        this.scheme = encScheme;
+    }
+
+    private PBMAC1Params(
+        ASN1Sequence  obj)
+    {
+        Enumeration e = obj.getObjects();
+        ASN1Sequence  funcSeq = ASN1Sequence.getInstance(((ASN1Encodable)e.nextElement()).toASN1Primitive());
+
+        if (funcSeq.getObjectAt(0).equals(id_PBKDF2))
+        {
+            func = new AlgorithmIdentifier(id_PBKDF2, PBKDF2Params.getInstance(funcSeq.getObjectAt(1)));
+        }
+        else
+        {
+            func = AlgorithmIdentifier.getInstance(funcSeq);
+        }
+
+        scheme = AlgorithmIdentifier.getInstance(e.nextElement());
+    }
+
+    public AlgorithmIdentifier getKeyDerivationFunc()
+    {
+        return func;
+    }
+
+    public AlgorithmIdentifier getMessageAuthScheme()
+    {
+        return scheme;
+    }
+
+    public ASN1Primitive toASN1Primitive()
+    {
+        ASN1EncodableVector v = new ASN1EncodableVector(2);
+
+        v.add(func);
+        v.add(scheme);
+
+        return new DERSequence(v);
+    }
+}
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/pkcs/PKCSObjectIdentifiers.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/pkcs/PKCSObjectIdentifiers.java
index 1e68d87..b243bc8 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/pkcs/PKCSObjectIdentifiers.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/pkcs/PKCSObjectIdentifiers.java
@@ -79,10 +79,12 @@
     ASN1ObjectIdentifier    pbeWithSHA1AndDES_CBC   = pkcs_5.branch("10");
     /** PKCS#5: 1.2.840.113549.1.5.11 */
     ASN1ObjectIdentifier    pbeWithSHA1AndRC2_CBC   = pkcs_5.branch("11");
-    /** PKCS#5: 1.2.840.113549.1.5.13 */
-    ASN1ObjectIdentifier    id_PBES2                = pkcs_5.branch("13");
     /** PKCS#5: 1.2.840.113549.1.5.12 */
     ASN1ObjectIdentifier    id_PBKDF2               = pkcs_5.branch("12");
+    /** PKCS#5: 1.2.840.113549.1.5.13 */
+    ASN1ObjectIdentifier    id_PBES2                = pkcs_5.branch("13");
+    /** PKCS#5: 1.2.840.113549.1.5.14 */
+    ASN1ObjectIdentifier    id_PBMAC1               = pkcs_5.branch("14");
 
     //
     // encryptionAlgorithm OBJECT IDENTIFIER ::= {
@@ -138,6 +140,10 @@
     ASN1ObjectIdentifier    id_hmacWithSHA384      = digestAlgorithm.branch("10").intern();
     /**  1.2.840.113549.2.11 */
     ASN1ObjectIdentifier    id_hmacWithSHA512      = digestAlgorithm.branch("11").intern();
+    /**  1.2.840.113549.2.12 */
+    ASN1ObjectIdentifier    id_hmacWithSHA512_224  = digestAlgorithm.branch("12").intern();
+    /**  1.2.840.113549.2.13 */
+    ASN1ObjectIdentifier    id_hmacWithSHA512_256  = digestAlgorithm.branch("13").intern();
 
     //
     // pkcs-7 OBJECT IDENTIFIER ::= {
@@ -192,6 +198,8 @@
     ASN1ObjectIdentifier    pkcs_9_at_smimeCapabilities  = pkcs_9.branch("15").intern();
     /** PKCS#9: 1.2.840.113549.1.9.16 */
     ASN1ObjectIdentifier    id_smime                     = pkcs_9.branch("16").intern();
+    /** PKCS#9: 1.2.840.113549.1.9.16.2.46 */
+    ASN1ObjectIdentifier    pkcs_9_at_binarySigningTime  = pkcs_9.branch("16.2.46").intern();
 
     /** PKCS#9: 1.2.840.113549.1.9.20 */
     ASN1ObjectIdentifier    pkcs_9_at_friendlyName  = pkcs_9.branch("20").intern();
@@ -250,8 +258,20 @@
 
     /** S/MIME: Algorithm Identifiers ; 1.2.840.113549.1.9.16.3 */
     ASN1ObjectIdentifier id_alg                  = id_smime.branch("3");
+
+    /** PKCS#9: 1.2.840.113549.1.9.16.3.5 */
+    ASN1ObjectIdentifier    id_alg_ESDH             = id_alg.branch("5");
+    /** PKCS#9: 1.2.840.113549.1.9.16.3.6 */
+    ASN1ObjectIdentifier    id_alg_CMS3DESwrap      = id_alg.branch("6");
+    /** PKCS#9: 1.2.840.113549.1.9.16.3.7 */
+    ASN1ObjectIdentifier    id_alg_CMSRC2wrap       = id_alg.branch("7");
+    /** PKCS#9: 1.2.840.113549.1.9.16.3.8 */
+    ASN1ObjectIdentifier id_alg_zlibCompress        = id_alg.branch("8");
     /** PKCS#9: 1.2.840.113549.1.9.16.3.9 */
-    ASN1ObjectIdentifier id_alg_PWRI_KEK         = id_alg.branch("9");
+    ASN1ObjectIdentifier id_alg_PWRI_KEK            = id_alg.branch("9");
+    /** PKCS#9: 1.2.840.113549.1.9.16.3.10 */
+    ASN1ObjectIdentifier    id_alg_SSDH             = id_alg.branch("10");
+
     /**
      * <pre>
      * -- RSA-KEM Key Transport Algorithm  RFC 5990
@@ -469,14 +489,5 @@
      * @deprecated use pbeWithSHAAnd40BitRC2_CBC
      */
     ASN1ObjectIdentifier    pbewithSHAAnd40BitRC2_CBC = pkcs_12PbeIds.branch("6");
-
-    /** PKCS#9: 1.2.840.113549.1.9.16.3.6 */
-    ASN1ObjectIdentifier    id_alg_CMS3DESwrap = new ASN1ObjectIdentifier("1.2.840.113549.1.9.16.3.6");
-    /** PKCS#9: 1.2.840.113549.1.9.16.3.7 */
-    ASN1ObjectIdentifier    id_alg_CMSRC2wrap  = new ASN1ObjectIdentifier("1.2.840.113549.1.9.16.3.7");
-    /** PKCS#9: 1.2.840.113549.1.9.16.3.5 */
-    ASN1ObjectIdentifier    id_alg_ESDH  = new ASN1ObjectIdentifier("1.2.840.113549.1.9.16.3.5");
-    /** PKCS#9: 1.2.840.113549.1.9.16.3.10 */
-    ASN1ObjectIdentifier    id_alg_SSDH  = new ASN1ObjectIdentifier("1.2.840.113549.1.9.16.3.10");
 }
 
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/pkcs/Pfx.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/pkcs/Pfx.java
index 01241cc..7a6217b 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/pkcs/Pfx.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/pkcs/Pfx.java
@@ -23,7 +23,7 @@
         ASN1Sequence   seq)
     {
         ASN1Integer version = ASN1Integer.getInstance(seq.getObjectAt(0));
-        if (version.intValueExact() != 3)
+        if (!version.hasValue(3))
         {
             throw new IllegalArgumentException("wrong version for PFX PDU");
         }
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/pkcs/PrivateKeyInfo.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/pkcs/PrivateKeyInfo.java
index c368321..1cb91be 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/pkcs/PrivateKeyInfo.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/pkcs/PrivateKeyInfo.java
@@ -166,7 +166,7 @@
                     throw new IllegalArgumentException("'publicKey' requires version v2(1) or later");
                 }
 
-                this.publicKey = DERBitString.getInstance(tagged, false);
+                this.publicKey = ASN1BitString.getInstance(tagged, false);
                 break;
             }
             default:
@@ -197,6 +197,11 @@
         return new DEROctetString(privateKey.getOctets());
     }
 
+    public int getPrivateKeyLength()
+    {
+        return privateKey.getOctetsLength();
+    }
+
     public ASN1Encodable parsePrivateKey()
         throws IOException
     {
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/pkcs/RSAESOAEPparams.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/pkcs/RSAESOAEPparams.java
index 2bdfbab..eedb57b 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/pkcs/RSAESOAEPparams.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/pkcs/RSAESOAEPparams.java
@@ -62,11 +62,7 @@
         this.pSourceAlgorithm = pSourceAlgorithm;
     }
 
-    /**
-     * @deprecated use getInstance()
-     * @param seq
-     */
-    public RSAESOAEPparams(
+    private RSAESOAEPparams(
         ASN1Sequence seq)
     {
         hashAlgorithm = DEFAULT_HASH_ALGORITHM;
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/pkcs/SafeBag.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/pkcs/SafeBag.java
index b093f6c..b07ab2f 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/pkcs/SafeBag.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/pkcs/SafeBag.java
@@ -61,7 +61,7 @@
         ASN1Sequence    seq)
     {
         this.bagId = (ASN1ObjectIdentifier)seq.getObjectAt(0);
-        this.bagValue = ((ASN1TaggedObject)seq.getObjectAt(1)).getObject();
+        this.bagValue = ((ASN1TaggedObject)seq.getObjectAt(1)).getExplicitBaseObject();
         if (seq.size() == 3)
         {
             this.bagAttributes = (ASN1Set)seq.getObjectAt(2);
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/sec/ECPrivateKey.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/sec/ECPrivateKey.java
index 07f0439..4ae47d8 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/sec/ECPrivateKey.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/sec/ECPrivateKey.java
@@ -4,6 +4,7 @@
 import java.math.BigInteger;
 import java.util.Enumeration;
 
+import com.android.org.bouncycastle.asn1.ASN1BitString;
 import com.android.org.bouncycastle.asn1.ASN1Encodable;
 import com.android.org.bouncycastle.asn1.ASN1EncodableVector;
 import com.android.org.bouncycastle.asn1.ASN1Integer;
@@ -12,7 +13,7 @@
 import com.android.org.bouncycastle.asn1.ASN1Primitive;
 import com.android.org.bouncycastle.asn1.ASN1Sequence;
 import com.android.org.bouncycastle.asn1.ASN1TaggedObject;
-import com.android.org.bouncycastle.asn1.DERBitString;
+import com.android.org.bouncycastle.asn1.BERTags;
 import com.android.org.bouncycastle.asn1.DEROctetString;
 import com.android.org.bouncycastle.asn1.DERSequence;
 import com.android.org.bouncycastle.asn1.DERTaggedObject;
@@ -93,7 +94,7 @@
      */
     public ECPrivateKey(
         BigInteger key,
-        DERBitString publicKey,
+        ASN1BitString publicKey,
         ASN1Encodable parameters)
     {
         this(key.bitLength(), key, publicKey, parameters);
@@ -110,7 +111,7 @@
     public ECPrivateKey(
         int orderBitLength,
         BigInteger key,
-        DERBitString publicKey,
+        ASN1BitString publicKey,
         ASN1Encodable parameters)
     {
         byte[] bytes = BigIntegers.asUnsignedByteArray((orderBitLength + 7) / 8, key);
@@ -139,18 +140,27 @@
 
         return new BigInteger(1, octs.getOctets());
     }
-
-    public DERBitString getPublicKey()
+    
+    public ASN1BitString getPublicKey()
     {
-        return (DERBitString)getObjectInTag(1);
+        return (ASN1BitString)getObjectInTag(1, BERTags.BIT_STRING);
     }
 
+    /**
+     * @deprecated Use {@link #getParametersObject()} instead and getInstance
+     *             methods or similar to get the object at the desired type.
+     */
     public ASN1Primitive getParameters()
     {
-        return getObjectInTag(0);
+        return getParametersObject().toASN1Primitive();
     }
 
-    private ASN1Primitive getObjectInTag(int tagNo)
+    public ASN1Object getParametersObject()
+    {
+        return getObjectInTag(0, -1);
+    }
+
+    private ASN1Object getObjectInTag(int tagNo, int baseTagNo)
     {
         Enumeration e = seq.getObjects();
 
@@ -161,9 +171,11 @@
             if (obj instanceof ASN1TaggedObject)
             {
                 ASN1TaggedObject tag = (ASN1TaggedObject)obj;
-                if (tag.getTagNo() == tagNo)
+                if (tag.hasContextTag(tagNo))
                 {
-                    return tag.getObject().toASN1Primitive();
+                    return baseTagNo < 0
+                        ?   tag.getExplicitBaseObject().toASN1Primitive()
+                        :   tag.getBaseUniversal(true, baseTagNo);
                 }
             }
         }
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/sec/ECPrivateKeyStructure.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/sec/ECPrivateKeyStructure.java
index dbef9bd..fe0e278 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/sec/ECPrivateKeyStructure.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/sec/ECPrivateKeyStructure.java
@@ -4,6 +4,7 @@
 import java.math.BigInteger;
 import java.util.Enumeration;
 
+import com.android.org.bouncycastle.asn1.ASN1BitString;
 import com.android.org.bouncycastle.asn1.ASN1Encodable;
 import com.android.org.bouncycastle.asn1.ASN1EncodableVector;
 import com.android.org.bouncycastle.asn1.ASN1Integer;
@@ -12,7 +13,6 @@
 import com.android.org.bouncycastle.asn1.ASN1Primitive;
 import com.android.org.bouncycastle.asn1.ASN1Sequence;
 import com.android.org.bouncycastle.asn1.ASN1TaggedObject;
-import com.android.org.bouncycastle.asn1.DERBitString;
 import com.android.org.bouncycastle.asn1.DEROctetString;
 import com.android.org.bouncycastle.asn1.DERSequence;
 import com.android.org.bouncycastle.asn1.DERTaggedObject;
@@ -56,7 +56,7 @@
 
     public ECPrivateKeyStructure(
         BigInteger    key,
-        DERBitString  publicKey,
+        ASN1BitString  publicKey,
         ASN1Encodable parameters)
     {
         byte[] bytes = BigIntegers.asUnsignedByteArray(key);
@@ -86,9 +86,9 @@
         return new BigInteger(1, octs.getOctets());
     }
 
-    public DERBitString getPublicKey()
+    public ASN1BitString getPublicKey()
     {
-        return (DERBitString)getObjectInTag(1);
+        return (ASN1BitString)getObjectInTag(1);
     }
 
     public ASN1Primitive getParameters()
@@ -109,7 +109,7 @@
                 ASN1TaggedObject tag = (ASN1TaggedObject)obj;
                 if (tag.getTagNo() == tagNo)
                 {
-                    return (ASN1Primitive)((ASN1Encodable)tag.getObject()).toASN1Primitive();
+                    return (ASN1Primitive)((ASN1Encodable)tag.getExplicitBaseObject()).toASN1Primitive();
                 }
             }
         }
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/sec/SECNamedCurves.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/sec/SECNamedCurves.java
index 2c67e25..7d1c56e 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/sec/SECNamedCurves.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/sec/SECNamedCurves.java
@@ -50,22 +50,27 @@
      */
     static X9ECParametersHolder secp112r1 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             // p = (2^128 - 3) / 76439
             BigInteger p = fromHex("DB7C2ABF62E35E668076BEAD208B");
             BigInteger a = fromHex("DB7C2ABF62E35E668076BEAD2088");
             BigInteger b = fromHex("659EF8BA043916EEDE8911702B22");
-            byte[] S = Hex.decodeStrict("00F50B028E4D696E676875615175290472783FB1");
             BigInteger n = fromHex("DB7C2ABF62E35E7628DFAC6561C5");
             BigInteger h = BigInteger.valueOf(1);
 
-            ECCurve curve = configureCurve(new ECCurve.Fp(p, a, b, n, h));
+            return configureCurve(new ECCurve.Fp(p, a, b, n, h, true));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = Hex.decodeStrict("00F50B028E4D696E676875615175290472783FB1");
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "0409487239995A5EE76B55F9C2F098A89CE5AF8724C0A23E0E0FF77500");
 
-            return new X9ECParameters(curve, G, n, h, S);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
@@ -74,22 +79,27 @@
      */
     static X9ECParametersHolder secp112r2 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             // p = (2^128 - 3) / 76439
             BigInteger p = fromHex("DB7C2ABF62E35E668076BEAD208B");
             BigInteger a = fromHex("6127C24C05F38A0AAAF65C0EF02C");
             BigInteger b = fromHex("51DEF1815DB5ED74FCC34C85D709");
-            byte[] S = Hex.decodeStrict("002757A1114D696E6768756151755316C05E0BD4");
             BigInteger n = fromHex("36DF0AAFD8B8D7597CA10520D04B");
             BigInteger h = BigInteger.valueOf(4);
 
-            ECCurve curve = configureCurve(new ECCurve.Fp(p, a, b, n, h));
+            return configureCurve(new ECCurve.Fp(p, a, b, n, h, true));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = Hex.decodeStrict("002757A1114D696E6768756151755316C05E0BD4");
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "044BA30AB5E892B4E1649DD0928643ADCD46F5882E3747DEF36E956E97");
 
-            return new X9ECParameters(curve, G, n, h, S);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
@@ -98,22 +108,27 @@
      */
     static X9ECParametersHolder secp128r1 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             // p = 2^128 - 2^97 - 1
             BigInteger p = fromHex("FFFFFFFDFFFFFFFFFFFFFFFFFFFFFFFF");
             BigInteger a = fromHex("FFFFFFFDFFFFFFFFFFFFFFFFFFFFFFFC");
             BigInteger b = fromHex("E87579C11079F43DD824993C2CEE5ED3");
-            byte[] S = Hex.decodeStrict("000E0D4D696E6768756151750CC03A4473D03679");
             BigInteger n = fromHex("FFFFFFFE0000000075A30D1B9038A115");
             BigInteger h = BigInteger.valueOf(1);
 
-            ECCurve curve = configureCurve(new ECCurve.Fp(p, a, b, n, h));
+            return configureCurve(new ECCurve.Fp(p, a, b, n, h, true));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = Hex.decodeStrict("000E0D4D696E6768756151750CC03A4473D03679");
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "04161FF7528B899B2D0C28607CA52C5B86CF5AC8395BAFEB13C02DA292DDED7A83");
 
-            return new X9ECParameters(curve, G, n, h, S);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
@@ -122,22 +137,27 @@
      */
     static X9ECParametersHolder secp128r2 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             // p = 2^128 - 2^97 - 1
             BigInteger p = fromHex("FFFFFFFDFFFFFFFFFFFFFFFFFFFFFFFF");
             BigInteger a = fromHex("D6031998D1B3BBFEBF59CC9BBFF9AEE1");
             BigInteger b = fromHex("5EEEFCA380D02919DC2C6558BB6D8A5D");
-            byte[] S = Hex.decodeStrict("004D696E67687561517512D8F03431FCE63B88F4");
             BigInteger n = fromHex("3FFFFFFF7FFFFFFFBE0024720613B5A3");
             BigInteger h = BigInteger.valueOf(4);
 
-            ECCurve curve = configureCurve(new ECCurve.Fp(p, a, b, n, h));
+            return configureCurve(new ECCurve.Fp(p, a, b, n, h, true));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = Hex.decodeStrict("004D696E67687561517512D8F03431FCE63B88F4");
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "047B6AA5D85E572983E6FB32A7CDEBC14027B6916A894D3AEE7106FE805FC34B44");
 
-            return new X9ECParameters(curve, G, n, h, S);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
@@ -146,13 +166,12 @@
      */
     static X9ECParametersHolder secp160k1 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             // p = 2^160 - 2^32 - 2^14 - 2^12 - 2^9 - 2^8 - 2^7 - 2^3 - 2^2 - 1
             BigInteger p = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFAC73");
             BigInteger a = ECConstants.ZERO;
             BigInteger b = BigInteger.valueOf(7);
-            byte[] S = null;
             BigInteger n = fromHex("0100000000000000000001B8FA16DFAB9ACA16B6B3");
             BigInteger h = BigInteger.valueOf(1);
 
@@ -170,12 +189,18 @@
                     new BigInteger("96341f1138933bc2f503fd44", 16),
                     176));
 
-            ECCurve curve = configureCurveGLV(new ECCurve.Fp(p, a, b, n, h), glv);
+            return configureCurveGLV(new ECCurve.Fp(p, a, b, n, h, true), glv);
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = null;
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "043B4C382CE37AA192A4019E763036F4F5DD4D7EBB938CF935318FDCED6BC28286531733C3F03C4FEE");
 
-            return new X9ECParameters(curve, G, n, h, S);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
@@ -184,22 +209,27 @@
      */
     static X9ECParametersHolder secp160r1 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             // p = 2^160 - 2^31 - 1
             BigInteger p = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7FFFFFFF");
             BigInteger a = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7FFFFFFC");
             BigInteger b = fromHex("1C97BEFC54BD7A8B65ACF89F81D4D4ADC565FA45");
-            byte[] S = Hex.decodeStrict("1053CDE42C14D696E67687561517533BF3F83345");
             BigInteger n = fromHex("0100000000000000000001F4C8F927AED3CA752257");
             BigInteger h = BigInteger.valueOf(1);
 
-            ECCurve curve = configureCurve(new ECCurve.Fp(p, a, b, n, h));
+            return configureCurve(new ECCurve.Fp(p, a, b, n, h, true));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = Hex.decodeStrict("1053CDE42C14D696E67687561517533BF3F83345");
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "044A96B5688EF573284664698968C38BB913CBFC8223A628553168947D59DCC912042351377AC5FB32");
 
-            return new X9ECParameters(curve, G, n, h, S);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
@@ -208,22 +238,27 @@
      */
     static X9ECParametersHolder secp160r2 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             // p = 2^160 - 2^32 - 2^14 - 2^12 - 2^9 - 2^8 - 2^7 - 2^3 - 2^2 - 1
             BigInteger p = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFAC73");
             BigInteger a = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFAC70");
             BigInteger b = fromHex("B4E134D3FB59EB8BAB57274904664D5AF50388BA");
-            byte[] S = Hex.decodeStrict("B99B99B099B323E02709A4D696E6768756151751");
             BigInteger n = fromHex("0100000000000000000000351EE786A818F3A1A16B");
             BigInteger h = BigInteger.valueOf(1);
 
-            ECCurve curve = configureCurve(new ECCurve.Fp(p, a, b, n, h));
+            return configureCurve(new ECCurve.Fp(p, a, b, n, h, true));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = Hex.decodeStrict("B99B99B099B323E02709A4D696E6768756151751");
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "0452DCB034293A117E1F4FF11B30F7199D3144CE6DFEAFFEF2E331F296E071FA0DF9982CFEA7D43F2E");
 
-            return new X9ECParameters(curve, G, n, h, S);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
@@ -232,13 +267,12 @@
      */
     static X9ECParametersHolder secp192k1 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             // p = 2^192 - 2^32 - 2^12 - 2^8 - 2^7 - 2^6 - 2^3 - 1
             BigInteger p = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFEE37");
             BigInteger a = ECConstants.ZERO;
             BigInteger b = BigInteger.valueOf(3);
-            byte[] S = null;
             BigInteger n = fromHex("FFFFFFFFFFFFFFFFFFFFFFFE26F2FC170F69466A74DEFD8D");
             BigInteger h = BigInteger.valueOf(1);
 
@@ -256,12 +290,18 @@
                     new BigInteger("b3fb3400dec5c4adceb8655d4c94", 16),
                     208));
 
-            ECCurve curve = configureCurveGLV(new ECCurve.Fp(p, a, b, n, h), glv);
+            return configureCurveGLV(new ECCurve.Fp(p, a, b, n, h, true), glv);
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = null;
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "04DB4FF10EC057E9AE26B07D0280B7F4341DA5D1B1EAE06C7D9B2F2F6D9C5628A7844163D015BE86344082AA88D95E2F9D");
 
-            return new X9ECParameters(curve, G, n, h, S);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
@@ -270,22 +310,27 @@
      */
     static X9ECParametersHolder secp192r1 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             // p = 2^192 - 2^64 - 1
             BigInteger p = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFF");
             BigInteger a = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFC");
             BigInteger b = fromHex("64210519E59C80E70FA7E9AB72243049FEB8DEECC146B9B1");
-            byte[] S = Hex.decodeStrict("3045AE6FC8422F64ED579528D38120EAE12196D5");
             BigInteger n = fromHex("FFFFFFFFFFFFFFFFFFFFFFFF99DEF836146BC9B1B4D22831");
             BigInteger h = BigInteger.valueOf(1);
 
-            ECCurve curve = configureCurve(new ECCurve.Fp(p, a, b, n, h));
+            return configureCurve(new ECCurve.Fp(p, a, b, n, h, true));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = Hex.decodeStrict("3045AE6FC8422F64ED579528D38120EAE12196D5");
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "04188DA80EB03090F67CBF20EB43A18800F4FF0AFD82FF101207192B95FFC8DA78631011ED6B24CDD573F977A11E794811");
 
-            return new X9ECParameters(curve, G, n, h, S);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
@@ -294,13 +339,12 @@
      */
     static X9ECParametersHolder secp224k1 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             // p = 2^224 - 2^32 - 2^12 - 2^11 - 2^9 - 2^7 - 2^4 - 2 - 1
             BigInteger p = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFE56D");
             BigInteger a = ECConstants.ZERO;
             BigInteger b = BigInteger.valueOf(5);
-            byte[] S = null;
             BigInteger n = fromHex("010000000000000000000000000001DCE8D2EC6184CAF0A971769FB1F7");
             BigInteger h = BigInteger.valueOf(1);
 
@@ -318,12 +362,18 @@
                     new BigInteger("b8adf1378a6eb73409fa6c9c637ba7f5", 16),
                     240));
 
-            ECCurve curve = configureCurveGLV(new ECCurve.Fp(p, a, b, n, h), glv);
+            return configureCurveGLV(new ECCurve.Fp(p, a, b, n, h, true), glv);
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = null;
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "04A1455B334DF099DF30FC28A169A467E9E47075A90F7E650EB6B7A45C7E089FED7FBA344282CAFBD6F7E319F7C0B0BD59E2CA4BDB556D61A5");
 
-            return new X9ECParameters(curve, G, n, h, S);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
@@ -332,22 +382,27 @@
      */
     static X9ECParametersHolder secp224r1 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             // p = 2^224 - 2^96 + 1
             BigInteger p = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000000000000001");
             BigInteger a = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFE");
             BigInteger b = fromHex("B4050A850C04B3ABF54132565044B0B7D7BFD8BA270B39432355FFB4");
-            byte[] S = Hex.decodeStrict("BD71344799D5C7FCDC45B59FA3B9AB8F6A948BC5");
             BigInteger n = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFF16A2E0B8F03E13DD29455C5C2A3D");
             BigInteger h = BigInteger.valueOf(1);
 
-            ECCurve curve = configureCurve(new ECCurve.Fp(p, a, b, n, h));
+            return configureCurve(new ECCurve.Fp(p, a, b, n, h, true));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = Hex.decodeStrict("BD71344799D5C7FCDC45B59FA3B9AB8F6A948BC5");
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "04B70E0CBD6BB4BF7F321390B94A03C1D356C21122343280D6115C1D21BD376388B5F723FB4C22DFE6CD4375A05A07476444D5819985007E34");
 
-            return new X9ECParameters(curve, G, n, h, S);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
@@ -356,13 +411,12 @@
      */
     static X9ECParametersHolder secp256k1 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             // p = 2^256 - 2^32 - 2^9 - 2^8 - 2^7 - 2^6 - 2^4 - 1
             BigInteger p = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F");
             BigInteger a = ECConstants.ZERO;
             BigInteger b = BigInteger.valueOf(7);
-            byte[] S = null;
             BigInteger n = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141");
             BigInteger h = BigInteger.valueOf(1);
 
@@ -380,12 +434,18 @@
                     new BigInteger("e4437ed6010e88286f547fa90abfe4c42212", 16),
                     272));
 
-            ECCurve curve = configureCurveGLV(new ECCurve.Fp(p, a, b, n, h), glv);
+            return configureCurveGLV(new ECCurve.Fp(p, a, b, n, h, true), glv);
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = null;
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "0479BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8");
 
-            return new X9ECParameters(curve, G, n, h, S);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
@@ -394,22 +454,27 @@
      */
     static X9ECParametersHolder secp256r1 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             // p = 2^224 (2^32 - 1) + 2^192 + 2^96 - 1
             BigInteger p = fromHex("FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF");
             BigInteger a = fromHex("FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC");
             BigInteger b = fromHex("5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B");
-            byte[] S = Hex.decodeStrict("C49D360886E704936A6678E1139D26B7819F7E90");
             BigInteger n = fromHex("FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551");
             BigInteger h = BigInteger.valueOf(1);
 
-            ECCurve curve = configureCurve(new ECCurve.Fp(p, a, b, n, h));
+            return configureCurve(new ECCurve.Fp(p, a, b, n, h, true));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = Hex.decodeStrict("C49D360886E704936A6678E1139D26B7819F7E90");
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "046B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C2964FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5");
 
-            return new X9ECParameters(curve, G, n, h, S);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
@@ -418,23 +483,28 @@
      */
     static X9ECParametersHolder secp384r1 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             // p = 2^384 - 2^128 - 2^96 + 2^32 - 1
             BigInteger p = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFF");
             BigInteger a = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFC");
             BigInteger b = fromHex("B3312FA7E23EE7E4988E056BE3F82D19181D9C6EFE8141120314088F5013875AC656398D8A2ED19D2A85C8EDD3EC2AEF");
-            byte[] S = Hex.decodeStrict("A335926AA319A27A1D00896A6773A4827ACDAC73");
             BigInteger n = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC7634D81F4372DDF581A0DB248B0A77AECEC196ACCC52973");
             BigInteger h = BigInteger.valueOf(1);
 
-            ECCurve curve = configureCurve(new ECCurve.Fp(p, a, b, n, h));
+            return configureCurve(new ECCurve.Fp(p, a, b, n, h, true));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = Hex.decodeStrict("A335926AA319A27A1D00896A6773A4827ACDAC73");
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve, "04"
                 + "AA87CA22BE8B05378EB1C71EF320AD746E1D3B628BA79B9859F741E082542A385502F25DBF55296C3A545E3872760AB7"
                 + "3617DE4A96262C6F5D9E98BF9292DC29F8F41DBD289A147CE9DA3113B5F0B8C00A60B1CE1D7E819D7A431D7C90EA0E5F");
 
-            return new X9ECParameters(curve, G, n, h, S);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
@@ -443,23 +513,28 @@
      */
     static X9ECParametersHolder secp521r1 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             // p = 2^521 - 1
             BigInteger p = fromHex("01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF");
             BigInteger a = fromHex("01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC");
             BigInteger b = fromHex("0051953EB9618E1C9A1F929A21A0B68540EEA2DA725B99B315F3B8B489918EF109E156193951EC7E937B1652C0BD3BB1BF073573DF883D2C34F1EF451FD46B503F00");
-            byte[] S = Hex.decodeStrict("D09E8800291CB85396CC6717393284AAA0DA64BA");
             BigInteger n = fromHex("01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA51868783BF2F966B7FCC0148F709A5D03BB5C9B8899C47AEBB6FB71E91386409");
             BigInteger h = BigInteger.valueOf(1);
 
-            ECCurve curve = configureCurve(new ECCurve.Fp(p, a, b, n, h));
+            return configureCurve(new ECCurve.Fp(p, a, b, n, h, true));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = Hex.decodeStrict("D09E8800291CB85396CC6717393284AAA0DA64BA");
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve, "04"
                 + "00C6858E06B70404E9CD9E3ECB662395B4429C648139053FB521F828AF606B4D3DBAA14B5E77EFE75928FE1DC127A2FFA8DE3348B3C1856A429BF97E7E31C2E5BD66"
                 + "011839296A789A3BC0045C8A5FB42C7D1BD998F54449579B446817AFBD17273E662C97EE72995EF42640C550B9013FAD0761353C7086A272C24088BE94769FD16650");
 
-            return new X9ECParameters(curve, G, n, h, S);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
@@ -468,23 +543,28 @@
      */
     static X9ECParametersHolder sect113r1 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             int m = 113;
             int k = 9;
 
             BigInteger a = fromHex("003088250CA6E7C7FE649CE85820F7");
             BigInteger b = fromHex("00E8BEE4D3E2260744188BE0E9C723");
-            byte[] S = Hex.decodeStrict("10E723AB14D696E6768756151756FEBF8FCB49A9");
             BigInteger n = fromHex("0100000000000000D9CCEC8A39E56F");
             BigInteger h = BigInteger.valueOf(2);
 
-            ECCurve curve = configureCurve(new ECCurve.F2m(m, k, a, b, n, h));
+            return configureCurve(new ECCurve.F2m(m, k, a, b, n, h));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = Hex.decodeStrict("10E723AB14D696E6768756151756FEBF8FCB49A9");
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "04009D73616F35F4AB1407D73562C10F00A52830277958EE84D1315ED31886");
 
-            return new X9ECParameters(curve, G, n, h, S);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
@@ -493,23 +573,28 @@
      */
     static X9ECParametersHolder sect113r2 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             int m = 113;
             int k = 9;
 
             BigInteger a = fromHex("00689918DBEC7E5A0DD6DFC0AA55C7");
             BigInteger b = fromHex("0095E9A9EC9B297BD4BF36E059184F");
-            byte[] S = Hex.decodeStrict("10C0FB15760860DEF1EEF4D696E676875615175D");
             BigInteger n = fromHex("010000000000000108789B2496AF93");
             BigInteger h = BigInteger.valueOf(2);
 
-            ECCurve curve = configureCurve(new ECCurve.F2m(m, k, a, b, n, h));
+            return configureCurve(new ECCurve.F2m(m, k, a, b, n, h));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = Hex.decodeStrict("10C0FB15760860DEF1EEF4D696E676875615175D");
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "0401A57A6A7B26CA5EF52FCDB816479700B3ADC94ED1FE674C06E695BABA1D");
 
-            return new X9ECParameters(curve, G, n, h, S);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
@@ -518,7 +603,7 @@
      */
     static X9ECParametersHolder sect131r1 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             int m = 131;
             int k1 = 2;
@@ -527,16 +612,21 @@
 
             BigInteger a = fromHex("07A11B09A76B562144418FF3FF8C2570B8");
             BigInteger b = fromHex("0217C05610884B63B9C6C7291678F9D341");
-            byte[] S = Hex.decodeStrict("4D696E676875615175985BD3ADBADA21B43A97E2");
             BigInteger n = fromHex("0400000000000000023123953A9464B54D");
             BigInteger h = BigInteger.valueOf(2);
 
-            ECCurve curve = configureCurve(new ECCurve.F2m(m, k1, k2, k3, a, b, n, h));
+            return configureCurve(new ECCurve.F2m(m, k1, k2, k3, a, b, n, h));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = Hex.decodeStrict("4D696E676875615175985BD3ADBADA21B43A97E2");
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "040081BAF91FDF9833C40F9C181343638399078C6E7EA38C001F73C8134B1B4EF9E150");
 
-            return new X9ECParameters(curve, G, n, h, S);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
@@ -545,7 +635,7 @@
      */
     static X9ECParametersHolder sect131r2 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             int m = 131;
             int k1 = 2;
@@ -554,16 +644,21 @@
 
             BigInteger a = fromHex("03E5A88919D7CAFCBF415F07C2176573B2");
             BigInteger b = fromHex("04B8266A46C55657AC734CE38F018F2192");
-            byte[] S = Hex.decodeStrict("985BD3ADBAD4D696E676875615175A21B43A97E3");
             BigInteger n = fromHex("0400000000000000016954A233049BA98F");
             BigInteger h = BigInteger.valueOf(2);
 
-            ECCurve curve = configureCurve(new ECCurve.F2m(m, k1, k2, k3, a, b, n, h));
+            return configureCurve(new ECCurve.F2m(m, k1, k2, k3, a, b, n, h));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = Hex.decodeStrict("985BD3ADBAD4D696E676875615175A21B43A97E3");
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "040356DCD8F2F95031AD652D23951BB366A80648F06D867940A5366D9E265DE9EB240F");
 
-            return new X9ECParameters(curve, G, n, h, S);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
@@ -572,7 +667,7 @@
      */
     static X9ECParametersHolder sect163k1 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             int m = 163;
             int k1 = 3;
@@ -581,16 +676,21 @@
 
             BigInteger a = BigInteger.valueOf(1);
             BigInteger b = BigInteger.valueOf(1);
-            byte[] S = null;
             BigInteger n = fromHex("04000000000000000000020108A2E0CC0D99F8A5EF");
             BigInteger h = BigInteger.valueOf(2);
 
-            ECCurve curve = configureCurve(new ECCurve.F2m(m, k1, k2, k3, a, b, n, h));
+            return configureCurve(new ECCurve.F2m(m, k1, k2, k3, a, b, n, h));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = null;
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "0402FE13C0537BBC11ACAA07D793DE4E6D5E5C94EEE80289070FB05D38FF58321F2E800536D538CCDAA3D9");
 
-            return new X9ECParameters(curve, G, n, h, S);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
@@ -599,7 +699,7 @@
      */
     static X9ECParametersHolder sect163r1 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             int m = 163;
             int k1 = 3;
@@ -608,16 +708,21 @@
 
             BigInteger a = fromHex("07B6882CAAEFA84F9554FF8428BD88E246D2782AE2");
             BigInteger b = fromHex("0713612DCDDCB40AAB946BDA29CA91F73AF958AFD9");
-            byte[] S = Hex.decodeStrict("24B7B137C8A14D696E6768756151756FD0DA2E5C");
             BigInteger n = fromHex("03FFFFFFFFFFFFFFFFFFFF48AAB689C29CA710279B");
             BigInteger h = BigInteger.valueOf(2);
 
-            ECCurve curve = configureCurve(new ECCurve.F2m(m, k1, k2, k3, a, b, n, h));
+            return configureCurve(new ECCurve.F2m(m, k1, k2, k3, a, b, n, h));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = Hex.decodeStrict("24B7B137C8A14D696E6768756151756FD0DA2E5C");
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "040369979697AB43897789566789567F787A7876A65400435EDB42EFAFB2989D51FEFCE3C80988F41FF883");
 
-            return new X9ECParameters(curve, G, n, h, S);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
@@ -626,7 +731,7 @@
      */
     static X9ECParametersHolder sect163r2 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             int m = 163;
             int k1 = 3;
@@ -635,16 +740,21 @@
 
             BigInteger a = BigInteger.valueOf(1);
             BigInteger b = fromHex("020A601907B8C953CA1481EB10512F78744A3205FD");
-            byte[] S = Hex.decodeStrict("85E25BFE5C86226CDB12016F7553F9D0E693A268");
             BigInteger n = fromHex("040000000000000000000292FE77E70C12A4234C33");
             BigInteger h = BigInteger.valueOf(2);
 
-            ECCurve curve = configureCurve(new ECCurve.F2m(m, k1, k2, k3, a, b, n, h));
+            return configureCurve(new ECCurve.F2m(m, k1, k2, k3, a, b, n, h));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = Hex.decodeStrict("85E25BFE5C86226CDB12016F7553F9D0E693A268");
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "0403F0EBA16286A2D57EA0991168D4994637E8343E3600D51FBC6C71A0094FA2CDD545B11C5C0C797324F1");
 
-            return new X9ECParameters(curve, G, n, h, S);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
@@ -653,23 +763,28 @@
      */
     static X9ECParametersHolder sect193r1 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             int m = 193;
             int k = 15;
 
             BigInteger a = fromHex("0017858FEB7A98975169E171F77B4087DE098AC8A911DF7B01");
             BigInteger b = fromHex("00FDFB49BFE6C3A89FACADAA7A1E5BBC7CC1C2E5D831478814");
-            byte[] S = Hex.decodeStrict("103FAEC74D696E676875615175777FC5B191EF30");
             BigInteger n = fromHex("01000000000000000000000000C7F34A778F443ACC920EBA49");
             BigInteger h = BigInteger.valueOf(2);
 
-            ECCurve curve = configureCurve(new ECCurve.F2m(m, k, a, b, n, h));
+            return configureCurve(new ECCurve.F2m(m, k, a, b, n, h));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = Hex.decodeStrict("103FAEC74D696E676875615175777FC5B191EF30");
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "0401F481BC5F0FF84A74AD6CDF6FDEF4BF6179625372D8C0C5E10025E399F2903712CCF3EA9E3A1AD17FB0B3201B6AF7CE1B05");
 
-            return new X9ECParameters(curve, G, n, h, S);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
@@ -678,23 +793,28 @@
      */
     static X9ECParametersHolder sect193r2 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             int m = 193;
             int k = 15;
 
             BigInteger a = fromHex("0163F35A5137C2CE3EA6ED8667190B0BC43ECD69977702709B");
             BigInteger b = fromHex("00C9BB9E8927D4D64C377E2AB2856A5B16E3EFB7F61D4316AE");
-            byte[] S = Hex.decodeStrict("10B7B4D696E676875615175137C8A16FD0DA2211");
             BigInteger n = fromHex("010000000000000000000000015AAB561B005413CCD4EE99D5");
             BigInteger h = BigInteger.valueOf(2);
 
-            ECCurve curve = configureCurve(new ECCurve.F2m(m, k, a, b, n, h));
+            return configureCurve(new ECCurve.F2m(m, k, a, b, n, h));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = Hex.decodeStrict("10B7B4D696E676875615175137C8A16FD0DA2211");
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "0400D9B67D192E0367C803F39E1A7E82CA14A651350AAE617E8F01CE94335607C304AC29E7DEFBD9CA01F596F927224CDECF6C");
 
-            return new X9ECParameters(curve, G, n, h, S);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
@@ -703,23 +823,28 @@
      */
     static X9ECParametersHolder sect233k1 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             int m = 233;
             int k = 74;
 
             BigInteger a = ECConstants.ZERO;
             BigInteger b = BigInteger.valueOf(1);
-            byte[] S = null;
             BigInteger n = fromHex("8000000000000000000000000000069D5BB915BCD46EFB1AD5F173ABDF");
             BigInteger h = BigInteger.valueOf(4);
 
-            ECCurve curve = configureCurve(new ECCurve.F2m(m, k, a, b, n, h));
+            return configureCurve(new ECCurve.F2m(m, k, a, b, n, h));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = null;
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "04017232BA853A7E731AF129F22FF4149563A419C26BF50A4C9D6EEFAD612601DB537DECE819B7F70F555A67C427A8CD9BF18AEB9B56E0C11056FAE6A3");
 
-            return new X9ECParameters(curve, G, n, h, S);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
@@ -728,23 +853,28 @@
      */
     static X9ECParametersHolder sect233r1 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             int m = 233;
             int k = 74;
 
             BigInteger a = BigInteger.valueOf(1);
             BigInteger b = fromHex("0066647EDE6C332C7F8C0923BB58213B333B20E9CE4281FE115F7D8F90AD");
-            byte[] S = Hex.decodeStrict("74D59FF07F6B413D0EA14B344B20A2DB049B50C3");
             BigInteger n = fromHex("01000000000000000000000000000013E974E72F8A6922031D2603CFE0D7");
             BigInteger h = BigInteger.valueOf(2);
 
-            ECCurve curve = configureCurve(new ECCurve.F2m(m, k, a, b, n, h));
+            return configureCurve(new ECCurve.F2m(m, k, a, b, n, h));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = Hex.decodeStrict("74D59FF07F6B413D0EA14B344B20A2DB049B50C3");
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "0400FAC9DFCBAC8313BB2139F1BB755FEF65BC391F8B36F8F8EB7371FD558B01006A08A41903350678E58528BEBF8A0BEFF867A7CA36716F7E01F81052");
 
-            return new X9ECParameters(curve, G, n, h, S);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
@@ -753,23 +883,28 @@
      */
     static X9ECParametersHolder sect239k1 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             int m = 239;
             int k = 158;
 
             BigInteger a = ECConstants.ZERO;
             BigInteger b = BigInteger.valueOf(1);
-            byte[] S = null;
             BigInteger n = fromHex("2000000000000000000000000000005A79FEC67CB6E91F1C1DA800E478A5");
             BigInteger h = BigInteger.valueOf(4);
 
-            ECCurve curve = configureCurve(new ECCurve.F2m(m, k, a, b, n, h));
+            return configureCurve(new ECCurve.F2m(m, k, a, b, n, h));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = null;
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "0429A0B6A887A983E9730988A68727A8B2D126C44CC2CC7B2A6555193035DC76310804F12E549BDB011C103089E73510ACB275FC312A5DC6B76553F0CA");
 
-            return new X9ECParameters(curve, G, n, h, S);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
@@ -778,7 +913,7 @@
      */
     static X9ECParametersHolder sect283k1 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             int m = 283;
             int k1 = 5;
@@ -787,17 +922,22 @@
 
             BigInteger a = ECConstants.ZERO;
             BigInteger b = BigInteger.valueOf(1);
-            byte[] S = null;
             BigInteger n = fromHex("01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE9AE2ED07577265DFF7F94451E061E163C61");
             BigInteger h = BigInteger.valueOf(4);
 
-            ECCurve curve = configureCurve(new ECCurve.F2m(m, k1, k2, k3, a, b, n, h));
+            return configureCurve(new ECCurve.F2m(m, k1, k2, k3, a, b, n, h));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = null;
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve, "04"
                 + "0503213F78CA44883F1A3B8162F188E553CD265F23C1567A16876913B0C2AC2458492836"
                 + "01CCDA380F1C9E318D90F95D07E5426FE87E45C0E8184698E45962364E34116177DD2259");
 
-            return new X9ECParameters(curve, G, n, h, S);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
@@ -806,7 +946,7 @@
      */
     static X9ECParametersHolder sect283r1 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             int m = 283;
             int k1 = 5;
@@ -815,17 +955,22 @@
 
             BigInteger a = BigInteger.valueOf(1);
             BigInteger b = fromHex("027B680AC8B8596DA5A4AF8A19A0303FCA97FD7645309FA2A581485AF6263E313B79A2F5");
-            byte[] S = Hex.decodeStrict("77E2B07370EB0F832A6DD5B62DFC88CD06BB84BE");
             BigInteger n = fromHex("03FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEF90399660FC938A90165B042A7CEFADB307");
             BigInteger h = BigInteger.valueOf(2);
 
-            ECCurve curve = configureCurve(new ECCurve.F2m(m, k1, k2, k3, a, b, n, h));
+            return configureCurve(new ECCurve.F2m(m, k1, k2, k3, a, b, n, h));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = Hex.decodeStrict("77E2B07370EB0F832A6DD5B62DFC88CD06BB84BE");
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve, "04"
                 + "05F939258DB7DD90E1934F8C70B0DFEC2EED25B8557EAC9C80E2E198F8CDBECD86B12053"
                 + "03676854FE24141CB98FE6D4B20D02B4516FF702350EDDB0826779C813F0DF45BE8112F4");
 
-            return new X9ECParameters(curve, G, n, h, S);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
@@ -834,24 +979,29 @@
      */
     static X9ECParametersHolder sect409k1 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             int m = 409;
             int k = 87;
 
             BigInteger a = ECConstants.ZERO;
             BigInteger b = BigInteger.valueOf(1);
-            byte[] S = null;
             BigInteger n = fromHex("7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE5F83B2D4EA20400EC4557D5ED3E3E7CA5B4B5C83B8E01E5FCF");
             BigInteger h = BigInteger.valueOf(4);
 
-            ECCurve curve = configureCurve(new ECCurve.F2m(m, k, a, b, n, h));
+            return configureCurve(new ECCurve.F2m(m, k, a, b, n, h));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = null;
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve, "04"
                 + "0060F05F658F49C1AD3AB1890F7184210EFD0987E307C84C27ACCFB8F9F67CC2C460189EB5AAAA62EE222EB1B35540CFE9023746"
                 + "01E369050B7C4E42ACBA1DACBF04299C3460782F918EA427E6325165E9EA10E3DA5F6C42E9C55215AA9CA27A5863EC48D8E0286B");
 
-            return new X9ECParameters(curve, G, n, h, S);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
@@ -860,24 +1010,29 @@
      */
     static X9ECParametersHolder sect409r1 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             int m = 409;
             int k = 87;
 
             BigInteger a = BigInteger.valueOf(1);
             BigInteger b = fromHex("0021A5C2C8EE9FEB5C4B9A753B7B476B7FD6422EF1F3DD674761FA99D6AC27C8A9A197B272822F6CD57A55AA4F50AE317B13545F");
-            byte[] S = Hex.decodeStrict("4099B5A457F9D69F79213D094C4BCD4D4262210B");
             BigInteger n = fromHex("010000000000000000000000000000000000000000000000000001E2AAD6A612F33307BE5FA47C3C9E052F838164CD37D9A21173");
             BigInteger h = BigInteger.valueOf(2);
 
-            ECCurve curve = configureCurve(new ECCurve.F2m(m, k, a, b, n, h));
+            return configureCurve(new ECCurve.F2m(m, k, a, b, n, h));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = Hex.decodeStrict("4099B5A457F9D69F79213D094C4BCD4D4262210B");
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve, "04"
                 + "015D4860D088DDB3496B0C6064756260441CDE4AF1771D4DB01FFE5B34E59703DC255A868A1180515603AEAB60794E54BB7996A7"
                 + "0061B1CFAB6BE5F32BBFA78324ED106A7636B9C5A7BD198D0158AA4F5488D08F38514F1FDF4B4F40D2181B3681C364BA0273C706");
 
-            return new X9ECParameters(curve, G, n, h, S);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
@@ -886,7 +1041,7 @@
      */
     static X9ECParametersHolder sect571k1 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             int m = 571;
             int k1 = 2;
@@ -895,17 +1050,22 @@
 
             BigInteger a = ECConstants.ZERO;
             BigInteger b = BigInteger.valueOf(1);
-            byte[] S = null;
             BigInteger n = fromHex("020000000000000000000000000000000000000000000000000000000000000000000000131850E1F19A63E4B391A8DB917F4138B630D84BE5D639381E91DEB45CFE778F637C1001");
             BigInteger h = BigInteger.valueOf(4);
 
-            ECCurve curve = configureCurve(new ECCurve.F2m(m, k1, k2, k3, a, b, n, h));
+            return configureCurve(new ECCurve.F2m(m, k1, k2, k3, a, b, n, h));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = null;
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve, "04"
                 + "026EB7A859923FBC82189631F8103FE4AC9CA2970012D5D46024804801841CA44370958493B205E647DA304DB4CEB08CBBD1BA39494776FB988B47174DCA88C7E2945283A01C8972"
                 + "0349DC807F4FBF374F4AEADE3BCA95314DD58CEC9F307A54FFC61EFC006D8A2C9D4979C0AC44AEA74FBEBBB9F772AEDCB620B01A7BA7AF1B320430C8591984F601CD4C143EF1C7A3");
 
-            return new X9ECParameters(curve, G, n, h, S);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
@@ -914,7 +1074,7 @@
      */
     static X9ECParametersHolder sect571r1 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             int m = 571;
             int k1 = 2;
@@ -923,17 +1083,22 @@
 
             BigInteger a = BigInteger.valueOf(1);
             BigInteger b = fromHex("02F40E7E2221F295DE297117B7F3D62F5C6A97FFCB8CEFF1CD6BA8CE4A9A18AD84FFABBD8EFA59332BE7AD6756A66E294AFD185A78FF12AA520E4DE739BACA0C7FFEFF7F2955727A");
-            byte[] S = Hex.decodeStrict("2AA058F73A0E33AB486B0F610410C53A7F132310");
             BigInteger n = fromHex("03FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE661CE18FF55987308059B186823851EC7DD9CA1161DE93D5174D66E8382E9BB2FE84E47");
             BigInteger h = BigInteger.valueOf(2);
 
-            ECCurve curve = configureCurve(new ECCurve.F2m(m, k1, k2, k3, a, b, n, h));
+            return configureCurve(new ECCurve.F2m(m, k1, k2, k3, a, b, n, h));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = Hex.decodeStrict("2AA058F73A0E33AB486B0F610410C53A7F132310");
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve, "04"
                 + "0303001D34B856296C16C0D40D3CD7750A93D1D2955FA80AA5F40FC8DB7B2ABDBDE53950F4C0D293CDD711A35B67FB1499AE60038614F1394ABFA3B4C850D927E1E7769C8EEC2D19"
                 + "037BF27342DA639B6DCCFFFEB73D69D78C6C27A6009CBBCA1980F8533921E8A684423E43BAB08A576291AF8F461BB2A8B3531D2F0485C19B16E2F1516E23DD3C1A4827AF1B8AC15B");
 
-            return new X9ECParameters(curve, G, n, h, S);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
@@ -987,26 +1152,35 @@
         defineCurve("sect571r1", SECObjectIdentifiers.sect571r1, sect571r1); 
     }
 
-    public static X9ECParameters getByName(
-        String name)
+    public static X9ECParameters getByName(String name)
     {
         ASN1ObjectIdentifier oid = getOID(name);
         return oid == null ? null : getByOID(oid);
     }
 
+    public static X9ECParametersHolder getByNameLazy(String name)
+    {
+        ASN1ObjectIdentifier oid = getOID(name);
+        return oid == null ? null : getByOIDLazy(oid);
+    }
+
     /**
      * return the X9ECParameters object for the named curve represented by
      * the passed in object identifier. Null if the curve isn't present.
      *
      * @param oid an object identifier representing a named curve, if present.
      */
-    public static X9ECParameters getByOID(
-        ASN1ObjectIdentifier oid)
+    public static X9ECParameters getByOID(ASN1ObjectIdentifier oid)
     {
-        X9ECParametersHolder holder = (X9ECParametersHolder)curves.get(oid);
+        X9ECParametersHolder holder = getByOIDLazy(oid);
         return holder == null ? null : holder.getParameters();
     }
 
+    public static X9ECParametersHolder getByOIDLazy(ASN1ObjectIdentifier oid)
+    {
+        return (X9ECParametersHolder)curves.get(oid);
+    }
+
     /**
      * return the object identifier signified by the passed in name. Null
      * if there is no object identifier associated with name.
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/util/ASN1Dump.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/util/ASN1Dump.java
index 91fd01f..9ddf89a 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/util/ASN1Dump.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/util/ASN1Dump.java
@@ -1,43 +1,42 @@
 /* GENERATED SOURCE. DO NOT MODIFY. */
 package com.android.org.bouncycastle.asn1.util;
 
-import java.io.IOException;
-import java.util.Enumeration;
-
-import com.android.org.bouncycastle.asn1.ASN1ApplicationSpecific;
+import com.android.org.bouncycastle.asn1.ASN1BMPString;
+import com.android.org.bouncycastle.asn1.ASN1BitString;
 import com.android.org.bouncycastle.asn1.ASN1Boolean;
 import com.android.org.bouncycastle.asn1.ASN1Encodable;
 import com.android.org.bouncycastle.asn1.ASN1Enumerated;
 import com.android.org.bouncycastle.asn1.ASN1External;
 import com.android.org.bouncycastle.asn1.ASN1GeneralizedTime;
+import com.android.org.bouncycastle.asn1.ASN1GraphicString;
+import com.android.org.bouncycastle.asn1.ASN1IA5String;
 import com.android.org.bouncycastle.asn1.ASN1Integer;
+import com.android.org.bouncycastle.asn1.ASN1Null;
+import com.android.org.bouncycastle.asn1.ASN1NumericString;
+import com.android.org.bouncycastle.asn1.ASN1ObjectDescriptor;
 import com.android.org.bouncycastle.asn1.ASN1ObjectIdentifier;
 import com.android.org.bouncycastle.asn1.ASN1OctetString;
 import com.android.org.bouncycastle.asn1.ASN1Primitive;
+import com.android.org.bouncycastle.asn1.ASN1PrintableString;
+import com.android.org.bouncycastle.asn1.ASN1RelativeOID;
 import com.android.org.bouncycastle.asn1.ASN1Sequence;
 import com.android.org.bouncycastle.asn1.ASN1Set;
+import com.android.org.bouncycastle.asn1.ASN1T61String;
 import com.android.org.bouncycastle.asn1.ASN1TaggedObject;
 import com.android.org.bouncycastle.asn1.ASN1UTCTime;
-import com.android.org.bouncycastle.asn1.BERApplicationSpecific;
+import com.android.org.bouncycastle.asn1.ASN1UTF8String;
+import com.android.org.bouncycastle.asn1.ASN1Util;
+import com.android.org.bouncycastle.asn1.ASN1VideotexString;
+import com.android.org.bouncycastle.asn1.ASN1VisibleString;
 import com.android.org.bouncycastle.asn1.BEROctetString;
 import com.android.org.bouncycastle.asn1.BERSequence;
 import com.android.org.bouncycastle.asn1.BERSet;
 import com.android.org.bouncycastle.asn1.BERTaggedObject;
-import com.android.org.bouncycastle.asn1.BERTags;
-import com.android.org.bouncycastle.asn1.DERApplicationSpecific;
-import com.android.org.bouncycastle.asn1.DERBMPString;
 import com.android.org.bouncycastle.asn1.DERBitString;
-import com.android.org.bouncycastle.asn1.DERGraphicString;
-import com.android.org.bouncycastle.asn1.DERIA5String;
-import com.android.org.bouncycastle.asn1.DERNull;
-import com.android.org.bouncycastle.asn1.DERPrintableString;
 import com.android.org.bouncycastle.asn1.DERSequence;
 import com.android.org.bouncycastle.asn1.DERSet;
-import com.android.org.bouncycastle.asn1.DERT61String;
-import com.android.org.bouncycastle.asn1.DERUTF8String;
-import com.android.org.bouncycastle.asn1.DERVideotexString;
-import com.android.org.bouncycastle.asn1.DERVisibleString;
-import com.android.org.bouncycastle.asn1.DLApplicationSpecific;
+import com.android.org.bouncycastle.asn1.DERTaggedObject;
+import com.android.org.bouncycastle.asn1.DLBitString;
 import com.android.org.bouncycastle.util.Strings;
 import com.android.org.bouncycastle.util.encoders.Hex;
 
@@ -62,11 +61,14 @@
         StringBuffer    buf)
     {
         String nl = Strings.lineSeparator();
-        if (obj instanceof ASN1Sequence)
+        if (obj instanceof ASN1Null)
         {
-            Enumeration     e = ((ASN1Sequence)obj).getObjects();
-            String          tab = indent + TAB;
-
+            buf.append(indent);
+            buf.append("NULL");
+            buf.append(nl);
+        }
+        else if (obj instanceof ASN1Sequence)
+        {
             buf.append(indent);
             if (obj instanceof BERSequence)
             {
@@ -80,64 +82,19 @@
             {
                 buf.append("Sequence");
             }
-
             buf.append(nl);
 
-            while (e.hasMoreElements())
+            ASN1Sequence sequence = (ASN1Sequence)obj;
+            String elementsIndent = indent + TAB;
+
+            for (int i = 0, count = sequence.size(); i < count; ++i)
             {
-                Object  o = e.nextElement();
-
-                if (o == null || o.equals(DERNull.INSTANCE))
-                {
-                    buf.append(tab);
-                    buf.append("NULL");
-                    buf.append(nl);
-                }
-                else if (o instanceof ASN1Primitive)
-                {
-                    _dumpAsString(tab, verbose, (ASN1Primitive)o, buf);
-                }
-                else
-                {
-                    _dumpAsString(tab, verbose, ((ASN1Encodable)o).toASN1Primitive(), buf);
-                }
+                _dumpAsString(elementsIndent, verbose, sequence.getObjectAt(i).toASN1Primitive(), buf);
             }
         }
-        else if (obj instanceof ASN1TaggedObject)
-        {
-            String          tab = indent + TAB;
-
-            buf.append(indent);
-            if (obj instanceof BERTaggedObject)
-            {
-                buf.append("BER Tagged [");
-            }
-            else
-            {
-                buf.append("Tagged [");
-            }
-
-            ASN1TaggedObject o = (ASN1TaggedObject)obj;
-
-            buf.append(Integer.toString(o.getTagNo()));
-            buf.append(']');
-
-            if (!o.isExplicit())
-            {
-                buf.append(" IMPLICIT ");
-            }
-
-            buf.append(nl);
-
-            _dumpAsString(tab, verbose, o.getObject(), buf);
-        }
         else if (obj instanceof ASN1Set)
         {
-            Enumeration     e = ((ASN1Set)obj).getObjects();
-            String          tab = indent + TAB;
-
             buf.append(indent);
-
             if (obj instanceof BERSet)
             {
                 buf.append("BER Set");
@@ -152,26 +109,45 @@
             }
             buf.append(nl);
 
-            while (e.hasMoreElements())
-            {
-                Object  o = e.nextElement();
+            ASN1Set set = (ASN1Set)obj;
+            String elementsIndent = indent + TAB;
 
-                if (o == null)
-                {
-                    buf.append(tab);
-                    buf.append("NULL");
-                    buf.append(nl);
-                }
-                else if (o instanceof ASN1Primitive)
-                {
-                    _dumpAsString(tab, verbose, (ASN1Primitive)o, buf);
-                }
-                else
-                {
-                    _dumpAsString(tab, verbose, ((ASN1Encodable)o).toASN1Primitive(), buf);
-                }
+            for (int i = 0, count = set.size(); i < count; ++i)
+            {
+                _dumpAsString(elementsIndent, verbose, set.getObjectAt(i).toASN1Primitive(), buf);
             }
         }
+        else if (obj instanceof ASN1TaggedObject)
+        {
+            buf.append(indent);
+            if (obj instanceof BERTaggedObject)
+            {
+                buf.append("BER Tagged ");
+            }
+            else if (obj instanceof DERTaggedObject)
+            {
+                buf.append("DER Tagged ");
+            }
+            else
+            {
+                buf.append("Tagged ");
+            }
+
+            ASN1TaggedObject o = (ASN1TaggedObject)obj;
+
+            buf.append(ASN1Util.getTagText(o));
+
+            if (!o.isExplicit())
+            {
+                buf.append(" IMPLICIT ");
+            }
+
+            buf.append(nl);
+
+            String baseIndent = indent + TAB;
+
+            _dumpAsString(baseIndent, verbose, o.getBaseObject().toASN1Primitive(), buf);
+        }
         else if (obj instanceof ASN1OctetString)
         {
             ASN1OctetString oct = (ASN1OctetString)obj;
@@ -197,6 +173,10 @@
         {
             buf.append(indent + "ObjectIdentifier(" + ((ASN1ObjectIdentifier)obj).getId() + ")" + nl);
         }
+        else if (obj instanceof ASN1RelativeOID)
+        {
+            buf.append(indent + "RelativeOID(" + ((ASN1RelativeOID)obj).getId() + ")" + nl);
+        }
         else if (obj instanceof ASN1Boolean)
         {
             buf.append(indent + "Boolean(" + ((ASN1Boolean)obj).isTrue() + ")" + nl);
@@ -205,50 +185,70 @@
         {
             buf.append(indent + "Integer(" + ((ASN1Integer)obj).getValue() + ")" + nl);
         }
-        else if (obj instanceof DERBitString)
+        else if (obj instanceof ASN1BitString)
         {
-            DERBitString bt = (DERBitString)obj;
-            buf.append(indent + "DER Bit String" + "[" + bt.getBytes().length + ", " + bt.getPadBits() + "] ");
+            ASN1BitString bitString = (ASN1BitString)obj;
+
+            byte[] bytes = bitString.getBytes();
+            int padBits = bitString.getPadBits();
+
+            if (bitString instanceof DERBitString)
+            {
+                buf.append(indent + "DER Bit String" + "[" + bytes.length + ", " + padBits + "] ");
+            }
+            else if (bitString instanceof DLBitString)
+            {
+                buf.append(indent + "DL Bit String" + "[" + bytes.length + ", " + padBits + "] ");
+            }
+            else
+            {
+                buf.append(indent + "BER Bit String" + "[" + bytes.length + ", " + padBits + "] ");
+            }
+
             if (verbose)
             {
-                buf.append(dumpBinaryDataAsString(indent, bt.getBytes()));
+                buf.append(dumpBinaryDataAsString(indent, bytes));
             }
             else
             {
                 buf.append(nl);
             }
         }
-        else if (obj instanceof DERIA5String)
+        else if (obj instanceof ASN1IA5String)
         {
-            buf.append(indent + "IA5String(" + ((DERIA5String)obj).getString() + ") " + nl);
+            buf.append(indent + "IA5String(" + ((ASN1IA5String)obj).getString() + ") " + nl);
         }
-        else if (obj instanceof DERUTF8String)
+        else if (obj instanceof ASN1UTF8String)
         {
-            buf.append(indent + "UTF8String(" + ((DERUTF8String)obj).getString() + ") " + nl);
+            buf.append(indent + "UTF8String(" + ((ASN1UTF8String)obj).getString() + ") " + nl);
         }
-        else if (obj instanceof DERPrintableString)
+        else if (obj instanceof ASN1NumericString)
         {
-            buf.append(indent + "PrintableString(" + ((DERPrintableString)obj).getString() + ") " + nl);
+            buf.append(indent + "NumericString(" + ((ASN1NumericString)obj).getString() + ") " + nl);
         }
-        else if (obj instanceof DERVisibleString)
+        else if (obj instanceof ASN1PrintableString)
         {
-            buf.append(indent + "VisibleString(" + ((DERVisibleString)obj).getString() + ") " + nl);
+            buf.append(indent + "PrintableString(" + ((ASN1PrintableString)obj).getString() + ") " + nl);
         }
-        else if (obj instanceof DERBMPString)
+        else if (obj instanceof ASN1VisibleString)
         {
-            buf.append(indent + "BMPString(" + ((DERBMPString)obj).getString() + ") " + nl);
+            buf.append(indent + "VisibleString(" + ((ASN1VisibleString)obj).getString() + ") " + nl);
         }
-        else if (obj instanceof DERT61String)
+        else if (obj instanceof ASN1BMPString)
         {
-            buf.append(indent + "T61String(" + ((DERT61String)obj).getString() + ") " + nl);
+            buf.append(indent + "BMPString(" + ((ASN1BMPString)obj).getString() + ") " + nl);
         }
-        else if (obj instanceof DERGraphicString)
+        else if (obj instanceof ASN1T61String)
         {
-            buf.append(indent + "GraphicString(" + ((DERGraphicString)obj).getString() + ") " + nl);
+            buf.append(indent + "T61String(" + ((ASN1T61String)obj).getString() + ") " + nl);
         }
-        else if (obj instanceof DERVideotexString)
+        else if (obj instanceof ASN1GraphicString)
         {
-            buf.append(indent + "VideotexString(" + ((DERVideotexString)obj).getString() + ") " + nl);
+            buf.append(indent + "GraphicString(" + ((ASN1GraphicString)obj).getString() + ") " + nl);
+        }
+        else if (obj instanceof ASN1VideotexString)
+        {
+            buf.append(indent + "VideotexString(" + ((ASN1VideotexString)obj).getString() + ") " + nl);
         }
         else if (obj instanceof ASN1UTCTime)
         {
@@ -258,23 +258,16 @@
         {
             buf.append(indent + "GeneralizedTime(" + ((ASN1GeneralizedTime)obj).getTime() + ") " + nl);
         }
-        else if (obj instanceof BERApplicationSpecific)
-        {
-            buf.append(outputApplicationSpecific("BER", indent, verbose, obj, nl));
-        }
-        else if (obj instanceof DERApplicationSpecific)
-        {
-            buf.append(outputApplicationSpecific("DER", indent, verbose, obj, nl));
-        }
-        else if (obj instanceof DLApplicationSpecific)
-        {
-            buf.append(outputApplicationSpecific("", indent, verbose, obj, nl));
-        }
         else if (obj instanceof ASN1Enumerated)
         {
             ASN1Enumerated en = (ASN1Enumerated) obj;
             buf.append(indent + "DER Enumerated(" + en.getValue() + ")" + nl);
         }
+        else if (obj instanceof ASN1ObjectDescriptor)
+        {
+            ASN1ObjectDescriptor od = (ASN1ObjectDescriptor)obj;
+            buf.append(indent + "ObjectDescriptor(" + od.getBaseGraphicString().getString() + ") " + nl);
+        }
         else if (obj instanceof ASN1External)
         {
             ASN1External ext = (ASN1External) obj;
@@ -300,32 +293,6 @@
             buf.append(indent + obj.toString() + nl);
         }
     }
-    
-    private static String outputApplicationSpecific(String type, String indent, boolean verbose, ASN1Primitive obj, String nl)
-    {
-        ASN1ApplicationSpecific app = ASN1ApplicationSpecific.getInstance(obj);
-        StringBuffer buf = new StringBuffer();
-
-        if (app.isConstructed())
-        {
-            try
-            {
-                ASN1Sequence s = ASN1Sequence.getInstance(app.getObject(BERTags.SEQUENCE));
-                buf.append(indent + type + " ApplicationSpecific[" + app.getApplicationTag() + "]" + nl);
-                for (Enumeration e = s.getObjects(); e.hasMoreElements();)
-                {
-                    _dumpAsString(indent + TAB, verbose, (ASN1Primitive)e.nextElement(), buf);
-                }
-            }
-            catch (IOException e)
-            {
-                buf.append(e);
-            }
-            return buf.toString();
-        }
-
-        return indent + type + " ApplicationSpecific[" + app.getApplicationTag() + "] (" + Strings.fromByteArray(Hex.encode(app.getContents())) + ")" + nl;
-    }
 
     /**
      * dump out a DER object as a formatted string, in non-verbose mode.
@@ -350,21 +317,22 @@
         Object   obj,
         boolean  verbose)
     {
-        StringBuffer buf = new StringBuffer();
-
+        ASN1Primitive primitive;
         if (obj instanceof ASN1Primitive)
         {
-            _dumpAsString("", verbose, (ASN1Primitive)obj, buf);
+            primitive = (ASN1Primitive)obj;
         }
         else if (obj instanceof ASN1Encodable)
         {
-            _dumpAsString("", verbose, ((ASN1Encodable)obj).toASN1Primitive(), buf);
+            primitive = ((ASN1Encodable)obj).toASN1Primitive();
         }
         else
         {
             return "unknown object type " + obj.toString();
         }
 
+        StringBuffer buf = new StringBuffer();
+        _dumpAsString("", verbose, primitive, buf);
         return buf.toString();
     }
 
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x500/DirectoryString.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x500/DirectoryString.java
index 568a09b..91b09dc 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x500/DirectoryString.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x500/DirectoryString.java
@@ -1,17 +1,18 @@
 /* GENERATED SOURCE. DO NOT MODIFY. */
 package com.android.org.bouncycastle.asn1.x500;
 
+import com.android.org.bouncycastle.asn1.ASN1BMPString;
 import com.android.org.bouncycastle.asn1.ASN1Choice;
 import com.android.org.bouncycastle.asn1.ASN1Encodable;
 import com.android.org.bouncycastle.asn1.ASN1Object;
 import com.android.org.bouncycastle.asn1.ASN1Primitive;
+import com.android.org.bouncycastle.asn1.ASN1PrintableString;
 import com.android.org.bouncycastle.asn1.ASN1String;
+import com.android.org.bouncycastle.asn1.ASN1T61String;
 import com.android.org.bouncycastle.asn1.ASN1TaggedObject;
-import com.android.org.bouncycastle.asn1.DERBMPString;
-import com.android.org.bouncycastle.asn1.DERPrintableString;
-import com.android.org.bouncycastle.asn1.DERT61String;
+import com.android.org.bouncycastle.asn1.ASN1UTF8String;
+import com.android.org.bouncycastle.asn1.ASN1UniversalString;
 import com.android.org.bouncycastle.asn1.DERUTF8String;
-import com.android.org.bouncycastle.asn1.DERUniversalString;
 
 /**
  * The DirectoryString CHOICE object.
@@ -30,29 +31,29 @@
             return (DirectoryString)o;
         }
 
-        if (o instanceof DERT61String)
+        if (o instanceof ASN1T61String)
         {
-            return new DirectoryString((DERT61String)o);
+            return new DirectoryString((ASN1T61String)o);
         }
 
-        if (o instanceof DERPrintableString)
+        if (o instanceof ASN1PrintableString)
         {
-            return new DirectoryString((DERPrintableString)o);
+            return new DirectoryString((ASN1PrintableString)o);
         }
 
-        if (o instanceof DERUniversalString)
+        if (o instanceof ASN1UniversalString)
         {
-            return new DirectoryString((DERUniversalString)o);
+            return new DirectoryString((ASN1UniversalString)o);
         }
 
-        if (o instanceof DERUTF8String)
+        if (o instanceof ASN1UTF8String)
         {
-            return new DirectoryString((DERUTF8String)o);
+            return new DirectoryString((ASN1UTF8String)o);
         }
 
-        if (o instanceof DERBMPString)
+        if (o instanceof ASN1BMPString)
         {
-            return new DirectoryString((DERBMPString)o);
+            return new DirectoryString((ASN1BMPString)o);
         }
 
         throw new IllegalArgumentException("illegal object in getInstance: " + o.getClass().getName());
@@ -65,35 +66,35 @@
             throw new IllegalArgumentException("choice item must be explicitly tagged");
         }
 
-        return getInstance(o.getObject());
+        return getInstance(o.getExplicitBaseObject());
     }
 
     private DirectoryString(
-        DERT61String string)
+        ASN1T61String string)
     {
         this.string = string;
     }
 
     private DirectoryString(
-        DERPrintableString string)
+        ASN1PrintableString string)
     {
         this.string = string;
     }
 
     private DirectoryString(
-        DERUniversalString string)
+        ASN1UniversalString string)
     {
         this.string = string;
     }
 
     private DirectoryString(
-        DERUTF8String string)
+        ASN1UTF8String string)
     {
         this.string = string;
     }
 
     private DirectoryString(
-        DERBMPString string)
+        ASN1BMPString string)
     {
         this.string = string;
     }
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x500/RDN.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x500/RDN.java
index 853c9ed..676b80c 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x500/RDN.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x500/RDN.java
@@ -2,12 +2,11 @@
 package com.android.org.bouncycastle.asn1.x500;
 
 import com.android.org.bouncycastle.asn1.ASN1Encodable;
-import com.android.org.bouncycastle.asn1.ASN1EncodableVector;
 import com.android.org.bouncycastle.asn1.ASN1Object;
 import com.android.org.bouncycastle.asn1.ASN1ObjectIdentifier;
 import com.android.org.bouncycastle.asn1.ASN1Primitive;
 import com.android.org.bouncycastle.asn1.ASN1Set;
-import com.android.org.bouncycastle.asn1.DERSequence;
+import com.android.org.bouncycastle.asn1.ASN1TaggedObject;
 import com.android.org.bouncycastle.asn1.DERSet;
 
 /**
@@ -21,6 +20,7 @@
 
     private RDN(ASN1Set values)
     {
+        // TODO Require minimum size of 1?
         this.values = values;
     }
 
@@ -38,6 +38,11 @@
         return null;
     }
 
+    public static RDN getInstance(ASN1TaggedObject taggedObject, boolean declaredExplicit)
+    {
+        return new RDN(ASN1Set.getInstance(taggedObject, declaredExplicit));
+    }
+
     /**
      * Create a single valued RDN.
      *
@@ -46,12 +51,7 @@
      */
     public RDN(ASN1ObjectIdentifier oid, ASN1Encodable value)
     {
-        ASN1EncodableVector v = new ASN1EncodableVector(2);
-
-        v.add(oid);
-        v.add(value);
-
-        this.values = new DERSet(new DERSequence(v));
+        this(new AttributeTypeAndValue(oid, value));
     }
 
     public RDN(AttributeTypeAndValue attrTAndV)
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x500/X500Name.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x500/X500Name.java
index 3f54005..067a461 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x500/X500Name.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x500/X500Name.java
@@ -1,8 +1,6 @@
 /* GENERATED SOURCE. DO NOT MODIFY. */
 package com.android.org.bouncycastle.asn1.x500;
 
-import java.util.Enumeration;
-
 import com.android.org.bouncycastle.asn1.ASN1Choice;
 import com.android.org.bouncycastle.asn1.ASN1Encodable;
 import com.android.org.bouncycastle.asn1.ASN1Object;
@@ -113,18 +111,18 @@
         X500NameStyle style,
         ASN1Sequence  seq)
     {
+        int count = seq.size();
+
         this.style = style;
-        this.rdns = new RDN[seq.size()];
+        this.rdns = new RDN[count];
 
         boolean inPlace = true;
-
-        int index = 0;
-        for (Enumeration e = seq.getObjects(); e.hasMoreElements();)
+        for (int index = 0; index < count; ++index)
         {
-            Object element = e.nextElement();
+            ASN1Encodable element = seq.getObjectAt(index);
             RDN rdn = RDN.getInstance(element);
             inPlace &= (rdn == element);
-            rdns[index++] = rdn;
+            rdns[index] = rdn;
         }
 
         if (inPlace)
@@ -231,6 +229,11 @@
         return res;
     }
 
+    public int size()
+    {
+        return rdns.length;
+    }
+
     public ASN1Primitive toASN1Primitive()
     {
         return rdnSeq;
@@ -267,14 +270,14 @@
         
         ASN1Primitive derO = ((ASN1Encodable)obj).toASN1Primitive();
 
-        if (this.toASN1Primitive().equals(derO))
+        if (toASN1Primitive().equals(derO))
         {
             return true;
         }
 
         try
         {
-            return style.areEqual(this, new X500Name(ASN1Sequence.getInstance(((ASN1Encodable)obj).toASN1Primitive())));
+            return style.areEqual(this, getInstance(obj));
         }
         catch (Exception e)
         {
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x500/style/AbstractX500NameStyle.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x500/style/AbstractX500NameStyle.java
index 083bff9..1f730da 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x500/style/AbstractX500NameStyle.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x500/style/AbstractX500NameStyle.java
@@ -133,19 +133,22 @@
 
     public boolean areEqual(X500Name name1, X500Name name2)
     {
-        RDN[] rdns1 = name1.getRDNs();
-        RDN[] rdns2 = name2.getRDNs();
-
-        if (rdns1.length != rdns2.length)
+        if (name1.size() != name2.size())
         {
             return false;
         }
 
+        RDN[] rdns1 = name1.getRDNs();
+        RDN[] rdns2 = name2.getRDNs();
+
         boolean reverse = false;
 
-        if (rdns1[0].getFirst() != null && rdns2[0].getFirst() != null)
+        AttributeTypeAndValue first1 = rdns1[0].getFirst();
+        AttributeTypeAndValue first2 = rdns2[0].getFirst();
+
+        if (first1 != null && first2 != null)
         {
-            reverse = !rdns1[0].getFirst().getType().equals(rdns2[0].getFirst().getType());  // guess forward
+            reverse = !first1.getType().equals(first2.getType());  // guess forward
         }
 
         for (int i = 0; i != rdns1.length; i++)
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x500/style/BCStrictStyle.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x500/style/BCStrictStyle.java
index be5c761..899d699 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x500/style/BCStrictStyle.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x500/style/BCStrictStyle.java
@@ -17,14 +17,14 @@
 
     public boolean areEqual(X500Name name1, X500Name name2)
     {
-        RDN[] rdns1 = name1.getRDNs();
-        RDN[] rdns2 = name2.getRDNs();
-
-        if (rdns1.length != rdns2.length)
+        if (name1.size() != name2.size())
         {
             return false;
         }
 
+        RDN[] rdns1 = name1.getRDNs();
+        RDN[] rdns2 = name2.getRDNs();
+
         for (int i = 0; i != rdns1.length; i++)
         {
             if (!rdnAreEqual(rdns1[i], rdns2[i]))
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x500/style/BCStyle.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x500/style/BCStyle.java
index a55bc29..953a410 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x500/style/BCStyle.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x500/style/BCStyle.java
@@ -293,9 +293,9 @@
         defaultLookUp = copyHashTable(DefaultLookUp);
     }
 
-    protected ASN1Encodable encodeStringValue(ASN1ObjectIdentifier oid,
-    		String value) {
-    	if (oid.equals(EmailAddress) || oid.equals(DC))
+    protected ASN1Encodable encodeStringValue(ASN1ObjectIdentifier oid, String value)
+    {
+        if (oid.equals(EmailAddress) || oid.equals(DC))
         {
             return new DERIA5String(value);
         }
@@ -303,18 +303,18 @@
         {
             return new ASN1GeneralizedTime(value);
         }
-        else if (oid.equals(C) || oid.equals(SN) || oid.equals(DN_QUALIFIER)
+        else if (oid.equals(C) || oid.equals(SERIALNUMBER) || oid.equals(DN_QUALIFIER)
             || oid.equals(TELEPHONE_NUMBER))
         {
             return new DERPrintableString(value);
         }
-    	
-    	return super.encodeStringValue(oid, value);
+        
+        return super.encodeStringValue(oid, value);
     }
 
     public String oidToDisplayName(ASN1ObjectIdentifier oid)
     {
-        return (String)DefaultSymbols.get(oid);
+        return (String)defaultSymbols.get(oid);
     }
 
     public String[] oidToAttrNames(ASN1ObjectIdentifier oid)
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x500/style/IETFUtils.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x500/style/IETFUtils.java
index 38a82a8..817753d 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x500/style/IETFUtils.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x500/style/IETFUtils.java
@@ -11,7 +11,7 @@
 import com.android.org.bouncycastle.asn1.ASN1ObjectIdentifier;
 import com.android.org.bouncycastle.asn1.ASN1Primitive;
 import com.android.org.bouncycastle.asn1.ASN1String;
-import com.android.org.bouncycastle.asn1.DERUniversalString;
+import com.android.org.bouncycastle.asn1.ASN1UniversalString;
 import com.android.org.bouncycastle.asn1.x500.AttributeTypeAndValue;
 import com.android.org.bouncycastle.asn1.x500.RDN;
 import com.android.org.bouncycastle.asn1.x500.X500NameBuilder;
@@ -26,12 +26,15 @@
 {
     private static String unescape(String elt)
     {
-        if (elt.length() == 0 || (elt.indexOf('\\') < 0 && elt.indexOf('"') < 0))
+        if (elt.length() == 0)
+        {
+            return elt;
+        }
+        if (elt.indexOf('\\') < 0 && elt.indexOf('"') < 0)
         {
             return elt.trim();
         }
 
-        char[] elts = elt.toCharArray();
         boolean escaped = false;
         boolean quoted = false;
         StringBuffer buf = new StringBuffer(elt.length());
@@ -39,9 +42,9 @@
 
         // if it's an escaped hash string and not an actual encoding in string form
         // we need to leave it escaped.
-        if (elts[0] == '\\')
+        if (elt.charAt(0) == '\\')
         {
-            if (elts[1] == '#')
+            if (elt.charAt(1) == '#')
             {
                 start = 2;
                 buf.append("\\#");
@@ -52,9 +55,9 @@
         int     lastEscaped = 0;
         char    hex1 = 0;
 
-        for (int i = start; i != elts.length; i++)
+        for (int i = start; i != elt.length(); i++)
         {
-            char c = elts[i];
+            char c = elt.charAt(i);
 
             if (c != ' ')
             {
@@ -70,8 +73,8 @@
                 else
                 {
                     buf.append(c);
+                    escaped = false;
                 }
-                escaped = false;
             }
             else if (c == '\\' && !(escaped || quoted))
             {
@@ -132,81 +135,93 @@
 
     public static RDN[] rDNsFromString(String name, X500NameStyle x500Style)
     {
-        X500NameTokenizer nTok = new X500NameTokenizer(name);
+        X500NameTokenizer tokenizer = new X500NameTokenizer(name);
         X500NameBuilder builder = new X500NameBuilder(x500Style);
 
-        while (nTok.hasMoreTokens())
+        addRDNs(x500Style, builder, tokenizer);
+
+        // TODO There's an unnecessary clone of the RDNs array happening here
+        return builder.build().getRDNs();
+    }
+
+    private static void addRDNs(X500NameStyle style, X500NameBuilder builder, X500NameTokenizer tokenizer)
+    {
+        String token;
+        while ((token = tokenizer.nextToken()) != null)
         {
-            String  token = nTok.nextToken();
-
-            if (token.indexOf('+') > 0)
+            if (token.indexOf('+') >= 0)
             {
-                X500NameTokenizer   pTok = new X500NameTokenizer(token, '+');
-                X500NameTokenizer   vTok = new X500NameTokenizer(pTok.nextToken(), '=');
-
-                String              attr = vTok.nextToken();
-
-                if (!vTok.hasMoreTokens())
-                {
-                    throw new IllegalArgumentException("badly formatted directory string");
-                }
-
-                String               value = vTok.nextToken();
-                ASN1ObjectIdentifier oid = x500Style.attrNameToOID(attr.trim());
-
-                if (pTok.hasMoreTokens())
-                {
-                    Vector oids = new Vector();
-                    Vector values = new Vector();
-
-                    oids.addElement(oid);
-                    values.addElement(unescape(value));
-
-                    while (pTok.hasMoreTokens())
-                    {
-                        vTok = new X500NameTokenizer(pTok.nextToken(), '=');
-
-                        attr = vTok.nextToken();
-
-                        if (!vTok.hasMoreTokens())
-                        {
-                            throw new IllegalArgumentException("badly formatted directory string");
-                        }
-
-                        value = vTok.nextToken();
-                        oid = x500Style.attrNameToOID(attr.trim());
-
-
-                        oids.addElement(oid);
-                        values.addElement(unescape(value));
-                    }
-
-                    builder.addMultiValuedRDN(toOIDArray(oids), toValueArray(values));
-                }
-                else
-                {
-                    builder.addRDN(oid, unescape(value));
-                }
+                addMultiValuedRDN(style, builder, new X500NameTokenizer(token, '+'));
             }
             else
             {
-                X500NameTokenizer   vTok = new X500NameTokenizer(token, '=');
-
-                String              attr = vTok.nextToken();
-
-                if (!vTok.hasMoreTokens())
-                {
-                    throw new IllegalArgumentException("badly formatted directory string");
-                }
-
-                String               value = vTok.nextToken();
-                ASN1ObjectIdentifier oid = x500Style.attrNameToOID(attr.trim());
-
-                builder.addRDN(oid, unescape(value));
+                addRDN(style, builder, token);
             }
         }
+    }
 
-        return builder.build().getRDNs();
+    private static void addMultiValuedRDN(X500NameStyle style, X500NameBuilder builder, X500NameTokenizer tokenizer)
+    {
+        String token = tokenizer.nextToken();
+        if (token == null)
+        {
+            throw new IllegalArgumentException("badly formatted directory string");
+        }
+
+        if (!tokenizer.hasMoreTokens())
+        {
+            addRDN(style, builder, token);
+            return;
+        }
+
+        Vector oids = new Vector();
+        Vector values = new Vector();
+
+        do
+        {
+            collectAttributeTypeAndValue(style, oids, values, token);
+            token = tokenizer.nextToken();
+        }
+        while (token != null);
+
+        builder.addMultiValuedRDN(toOIDArray(oids), toValueArray(values));
+    }
+
+    private static void addRDN(X500NameStyle style, X500NameBuilder builder, String token)
+    {
+        X500NameTokenizer tokenizer = new X500NameTokenizer(token, '=');
+
+        String typeToken = nextToken(tokenizer, true);
+        String valueToken = nextToken(tokenizer, false);
+
+        ASN1ObjectIdentifier oid = style.attrNameToOID(typeToken.trim());
+        String value = unescape(valueToken);
+
+        builder.addRDN(oid, value);
+    }
+
+    private static void collectAttributeTypeAndValue(X500NameStyle style, Vector oids, Vector values, String token)
+    {
+        X500NameTokenizer tokenizer = new X500NameTokenizer(token, '=');
+
+        String typeToken = nextToken(tokenizer, true);
+        String valueToken = nextToken(tokenizer, false);
+
+        ASN1ObjectIdentifier oid = style.attrNameToOID(typeToken.trim());
+        String value = unescape(valueToken);
+
+        oids.addElement(oid);
+        values.addElement(value);
+    }
+
+    private static String nextToken(X500NameTokenizer tokenizer, boolean expectMoreTokens)
+    {
+        String token = tokenizer.nextToken();
+        if (token == null || tokenizer.hasMoreTokens() != expectMoreTokens)
+        {
+            throw new IllegalArgumentException("badly formatted directory string");
+        }
+        return token;
     }
 
     private static String[] toValueArray(Vector values)
@@ -358,7 +373,7 @@
     {
         StringBuffer vBuf = new StringBuffer();
 
-        if (value instanceof ASN1String && !(value instanceof DERUniversalString))
+        if (value instanceof ASN1String && !(value instanceof ASN1UniversalString))
         {
             String v = ((ASN1String)value).getString();
             if (v.length() > 0 && v.charAt(0) == '#')
@@ -373,6 +388,7 @@
             try
             {
                 vBuf.append('#');
+                // -DM Hex.toHexString
                 vBuf.append(Hex.toHexString(value.toASN1Primitive().getEncoded(ASN1Encoding.DER)));
             }
             catch (IOException e)
@@ -427,7 +443,7 @@
 
         int endBuf = vBuf.length() - 1;
 
-        while (endBuf >= 0 && vBuf.charAt(endBuf) == ' ')
+        while (endBuf >= start && vBuf.charAt(endBuf) == ' ')
         {
             vBuf.insert(endBuf, '\\');
             endBuf--;
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x500/style/RFC4519Style.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x500/style/RFC4519Style.java
index 0499180..21381cd 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x500/style/RFC4519Style.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x500/style/RFC4519Style.java
@@ -15,7 +15,7 @@
  * @hide This class is not part of the Android public SDK API
  */
 public class RFC4519Style
-	extends AbstractX500NameStyle
+    extends AbstractX500NameStyle
 {
     public static final ASN1ObjectIdentifier businessCategory = new ASN1ObjectIdentifier("2.5.4.15").intern();
     public static final ASN1ObjectIdentifier c = new ASN1ObjectIdentifier("2.5.4.6").intern();
@@ -179,9 +179,9 @@
         defaultLookUp = copyHashTable(DefaultLookUp);
     }
 
-    protected ASN1Encodable encodeStringValue(ASN1ObjectIdentifier oid,
-    		String value) {
-    	if (oid.equals(dc))
+    protected ASN1Encodable encodeStringValue(ASN1ObjectIdentifier oid, String value)
+    {
+        if (oid.equals(dc))
         {
             return new DERIA5String(value);
         }
@@ -191,12 +191,12 @@
             return new DERPrintableString(value);
         }
 
-    	return super.encodeStringValue(oid, value);
+        return super.encodeStringValue(oid, value);
     }
 
     public String oidToDisplayName(ASN1ObjectIdentifier oid)
     {
-        return (String)DefaultSymbols.get(oid);
+        return (String)defaultSymbols.get(oid);
     }
 
     public String[] oidToAttrNames(ASN1ObjectIdentifier oid)
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x500/style/X500NameTokenizer.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x500/style/X500NameTokenizer.java
index 3f08dad..27ae360 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x500/style/X500NameTokenizer.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x500/style/X500NameTokenizer.java
@@ -10,83 +10,78 @@
  */
 public class X500NameTokenizer
 {
-    private String          value;
-    private int             index;
-    private char            separator;
-    private StringBuffer    buf = new StringBuffer();
+    private final String value;
+    private final char separator;
 
-    public X500NameTokenizer(
-        String  oid)
+    private int index;
+
+    public X500NameTokenizer(String oid)
     {
         this(oid, ',');
     }
-    
-    public X500NameTokenizer(
-        String  oid,
-        char    separator)
+
+    public X500NameTokenizer(String oid, char separator)
     {
+        if (oid == null)
+        {
+            throw new NullPointerException();
+        }
+        if (separator == '"' || separator == '\\')
+        {
+            throw new IllegalArgumentException("reserved separator character");
+        }
+
         this.value = oid;
-        this.index = -1;
         this.separator = separator;
+        this.index = oid.length() < 1 ? 0 : -1;
     }
 
     public boolean hasMoreTokens()
     {
-        return (index != value.length());
+        return index < value.length();
     }
 
     public String nextToken()
     {
-        if (index == value.length())
+        if (index >= value.length())
         {
             return null;
         }
 
-        int     end = index + 1;
         boolean quoted = false;
         boolean escaped = false;
 
-        buf.setLength(0);
-
-        while (end != value.length())
+        int beginIndex = index + 1;
+        while (++index < value.length())
         {
-            char    c = value.charAt(end);
+            char c = value.charAt(index);
 
-            if (c == '"')
+            if (escaped)
             {
-                if (!escaped)
-                {
-                    quoted = !quoted;
-                }
-                buf.append(c);
                 escaped = false;
             }
-            else
+            else if (c == '"')
             {
-                if (escaped || quoted)
-                {
-                    buf.append(c);
-                    escaped = false;
-                }
-                else if (c == '\\')
-                {
-                    buf.append(c);
-                    escaped = true;
-                }
-                else if (c == separator)
-                {
-                    break;
-                }
-                else
-                {
-                    buf.append(c);
-                }
+                quoted = !quoted;
             }
-            end++;
+            else if (quoted)
+            {
+            }
+            else if (c == '\\')
+            {
+                escaped = true;
+            }
+            else if (c == separator)
+            {
+                return value.substring(beginIndex, index);
+            }
         }
 
-        index = end;
+        if (escaped || quoted)
+        {
+            throw new IllegalArgumentException("badly formatted directory string");
+        }
 
-        return buf.toString();
+        return value.substring(beginIndex, index);
     }
 }
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/AltSignatureAlgorithm.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/AltSignatureAlgorithm.java
new file mode 100644
index 0000000..6f46b48
--- /dev/null
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/AltSignatureAlgorithm.java
@@ -0,0 +1,96 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.org.bouncycastle.asn1.x509;
+
+import com.android.org.bouncycastle.asn1.ASN1Encodable;
+import com.android.org.bouncycastle.asn1.ASN1Object;
+import com.android.org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import com.android.org.bouncycastle.asn1.ASN1Primitive;
+import com.android.org.bouncycastle.asn1.ASN1TaggedObject;
+
+/**
+ * X.509 Section 9.8.3.
+ * <br/>
+ * This extension may be used as a public-key certificate extension, a CRL extension or an AVL extension. It shall contain
+ * the algorithm identifier for the alternative digital signature algorithm used by the signer when creating an alternative
+ * digital signature and by the relying party when validating the alternative digital signature.
+ * <pre>
+ * altSignatureAlgorithm EXTENSION ::= {
+ *     SYNTAX AltSignatureAlgorithm
+ *     IDENTIFIED BY id-ce-altSignatureAlgorithm }
+ *
+ * AltSignatureAlgorithm ::= AlgorithmIdentifier{{SupportedAlgorithms}}
+ * </pre>
+ * When the altSignatureAlgorithm extension is included in a particular value that is an instance of a data type that
+ * supports extensions, the altSignatureValue extension shall also be included.
+ * <br/>
+ * NOTE 1 – By having a separate altSignatureAlgorithm extension, instead of having it combined with the
+ * altSignatureValue extension, the alternative digital signature algorithm is protected by the alternative signature.
+ * This extension may be flagged either as critical or as non-critical.
+ * <br/>
+ * NOTE 2 – It is recommended that it be flagged as non-critical. Flagging it as critical would require all relying parties to understand
+ * the extension and the alternative public-key algorithms
+ * @hide This class is not part of the Android public SDK API
+ */
+public class AltSignatureAlgorithm
+    extends ASN1Object
+{
+    private final AlgorithmIdentifier algorithm;
+
+    public static AltSignatureAlgorithm getInstance(
+        ASN1TaggedObject obj,
+        boolean          explicit)
+    {
+        return getInstance(AlgorithmIdentifier.getInstance(obj, explicit));
+    }
+
+    public static AltSignatureAlgorithm getInstance(
+        Object obj)
+    {
+        if (obj instanceof AltSignatureAlgorithm)
+        {
+            return (AltSignatureAlgorithm)obj;
+        }
+        else if (obj != null)
+        {
+            return new AltSignatureAlgorithm(AlgorithmIdentifier.getInstance(obj));
+        }
+
+        return null;
+    }
+
+    public static AltSignatureAlgorithm fromExtensions(Extensions extensions)
+    {
+        return getInstance(Extensions.getExtensionParsedValue(extensions, Extension.altSignatureAlgorithm));
+    }
+
+    public AltSignatureAlgorithm(AlgorithmIdentifier algorithm)
+    {
+        this.algorithm = algorithm;
+    }
+
+    public AltSignatureAlgorithm(ASN1ObjectIdentifier algorithm)
+    {
+        this(algorithm, null);
+    }
+
+    public AltSignatureAlgorithm(ASN1ObjectIdentifier algorithm, ASN1Encodable parameters)
+    {
+        this.algorithm = new AlgorithmIdentifier(algorithm, parameters);
+    }
+
+    /**
+     * Return the algorithm identifier representing the alternate signature algorithm
+     * used to generate the alternate signature algorithm value extension.
+     *
+     * @return alternate signature algorithm identifier.
+     */
+    public AlgorithmIdentifier getAlgorithm()
+    {
+        return algorithm;
+    }
+
+    public ASN1Primitive toASN1Primitive()
+    {
+        return algorithm.toASN1Primitive();
+    }
+}
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/AltSignatureValue.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/AltSignatureValue.java
new file mode 100644
index 0000000..46c307c
--- /dev/null
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/AltSignatureValue.java
@@ -0,0 +1,96 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.org.bouncycastle.asn1.x509;
+
+import com.android.org.bouncycastle.asn1.ASN1BitString;
+import com.android.org.bouncycastle.asn1.ASN1Object;
+import com.android.org.bouncycastle.asn1.ASN1Primitive;
+import com.android.org.bouncycastle.asn1.ASN1TaggedObject;
+import com.android.org.bouncycastle.asn1.DERBitString;
+
+/**
+ * X.509 Section 9.8.4.
+ * <br/>
+ * This extension may be used as a public-key certificate extension, a CRL extension or an AVL extension.
+ * This alternative signature shall be created by the issuer using its alternative private key, and it shall be verified using the
+ * alternative public key of the issuer.
+ * <pre>
+ * altSignatureValue EXTENSION ::= {
+ *     SYNTAX AltSignatureValue
+ *     IDENTIFIED BY id-ce-altSignatureValue }
+ *
+ * AltSignatureValue ::= BIT STRING
+ * </pre>
+ * This extension can only be created by a signer holding a multiple cryptographic algorithms public-key certificate. When
+ * creating the alternative digital signature on an issued public-key certificate or CRL, the signer shall use its alternative
+ * private key.
+ * <br/>
+ * The procedures for creating and validating alternative digital signatures are specified in:
+ * <ul>
+ * <li>clause 7.2.2 for public-key certificates;</li>
+ * <li>clause 7.10.3 for CRLs: and</li>
+ * <li>clause 11.4 for AVLs.</li>
+ * </ul>
+ * @hide This class is not part of the Android public SDK API
+ */
+public class AltSignatureValue
+    extends ASN1Object
+{
+    private final ASN1BitString signature;
+
+    public static AltSignatureValue getInstance(
+        ASN1TaggedObject obj,
+        boolean          explicit)
+    {
+        return getInstance(ASN1BitString.getInstance(obj, explicit));
+    }
+
+    public static AltSignatureValue getInstance(
+        Object obj)
+    {
+        if (obj instanceof AltSignatureValue)
+        {
+            return (AltSignatureValue)obj;
+        }
+        else if (obj != null)
+        {
+            return new AltSignatureValue(ASN1BitString.getInstance(obj));
+        }
+
+        return null;
+    }
+
+    public static AltSignatureValue fromExtensions(Extensions extensions)
+    {
+        return getInstance(Extensions.getExtensionParsedValue(extensions, Extension.altSignatureValue));
+    }
+
+    private AltSignatureValue(ASN1BitString signature)
+    {
+        this.signature = signature;
+    }
+
+    /**
+     * Base constructor.
+     *
+     * @param signature  a signature value, based on the enclosing certificate.
+     */
+    public AltSignatureValue(byte[] signature)
+    {
+        this.signature = new DERBitString(signature);
+    }
+
+    /**
+     * Return the alternate signature to verify the certificate.
+     *
+     * @return certificate's alternate signature.
+     */
+    public ASN1BitString getSignature()
+    {
+        return signature;
+    }
+
+    public ASN1Primitive toASN1Primitive()
+    {
+        return signature;
+    }
+}
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/AttCertIssuer.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/AttCertIssuer.java
index 7d343dd..acad7a4 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/AttCertIssuer.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/AttCertIssuer.java
@@ -50,7 +50,7 @@
         ASN1TaggedObject obj,
         boolean          explicit)
     {
-        return getInstance(obj.getObject()); // must be explicitly tagged
+        return getInstance(obj.getExplicitBaseObject()); // must be explicitly tagged
     }
 
     /**
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/AttributeCertificate.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/AttributeCertificate.java
index b5658e6..5ee2cd1 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/AttributeCertificate.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/AttributeCertificate.java
@@ -1,6 +1,7 @@
 /* GENERATED SOURCE. DO NOT MODIFY. */
 package com.android.org.bouncycastle.asn1.x509;
 
+import com.android.org.bouncycastle.asn1.ASN1BitString;
 import com.android.org.bouncycastle.asn1.ASN1EncodableVector;
 import com.android.org.bouncycastle.asn1.ASN1Object;
 import com.android.org.bouncycastle.asn1.ASN1Primitive;
@@ -16,7 +17,7 @@
 {
     AttributeCertificateInfo    acinfo;
     AlgorithmIdentifier         signatureAlgorithm;
-    DERBitString                signatureValue;
+    ASN1BitString               signatureValue;
 
     /**
      * @param obj
@@ -39,28 +40,23 @@
     public AttributeCertificate(
         AttributeCertificateInfo    acinfo,
         AlgorithmIdentifier         signatureAlgorithm,
-        DERBitString                signatureValue)
+        ASN1BitString               signatureValue)
     {
         this.acinfo = acinfo;
         this.signatureAlgorithm = signatureAlgorithm;
         this.signatureValue = signatureValue;
     }
 
-    /**
-     * @deprecated use getInstance() method.
-     */
-    public AttributeCertificate(
-        ASN1Sequence    seq)
+    private AttributeCertificate(ASN1Sequence seq)
     {
         if (seq.size() != 3)
         {
-            throw new IllegalArgumentException("Bad sequence size: "
-                    + seq.size());
+            throw new IllegalArgumentException("Bad sequence size: " + seq.size());
         }
 
         this.acinfo = AttributeCertificateInfo.getInstance(seq.getObjectAt(0));
         this.signatureAlgorithm = AlgorithmIdentifier.getInstance(seq.getObjectAt(1));
-        this.signatureValue = DERBitString.getInstance(seq.getObjectAt(2));
+        this.signatureValue = ASN1BitString.getInstance(seq.getObjectAt(2));
     }
     
     public AttributeCertificateInfo getAcinfo()
@@ -73,7 +69,7 @@
         return signatureAlgorithm;
     }
 
-    public DERBitString getSignatureValue()
+    public ASN1BitString getSignatureValue()
     {
         return signatureValue;
     }
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/AttributeCertificateInfo.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/AttributeCertificateInfo.java
index f8ee260..e7263d9 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/AttributeCertificateInfo.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/AttributeCertificateInfo.java
@@ -1,6 +1,7 @@
 /* GENERATED SOURCE. DO NOT MODIFY. */
 package com.android.org.bouncycastle.asn1.x509;
 
+import com.android.org.bouncycastle.asn1.ASN1BitString;
 import com.android.org.bouncycastle.asn1.ASN1Encodable;
 import com.android.org.bouncycastle.asn1.ASN1EncodableVector;
 import com.android.org.bouncycastle.asn1.ASN1Integer;
@@ -8,7 +9,6 @@
 import com.android.org.bouncycastle.asn1.ASN1Primitive;
 import com.android.org.bouncycastle.asn1.ASN1Sequence;
 import com.android.org.bouncycastle.asn1.ASN1TaggedObject;
-import com.android.org.bouncycastle.asn1.DERBitString;
 import com.android.org.bouncycastle.asn1.DERSequence;
 
 /**
@@ -21,10 +21,10 @@
     private Holder                  holder;
     private AttCertIssuer           issuer;
     private AlgorithmIdentifier     signature;
-    private ASN1Integer              serialNumber;
+    private ASN1Integer             serialNumber;
     private AttCertValidityPeriod   attrCertValidityPeriod;
     private ASN1Sequence            attributes;
-    private DERBitString            issuerUniqueID;
+    private ASN1BitString           issuerUniqueID;
     private Extensions              extensions;
 
     public static AttributeCertificateInfo getInstance(
@@ -80,9 +80,9 @@
         {
             ASN1Encodable    obj = seq.getObjectAt(i);
 
-            if (obj instanceof DERBitString)
+            if (obj instanceof ASN1BitString)
             {
-                this.issuerUniqueID = DERBitString.getInstance(seq.getObjectAt(i));
+                this.issuerUniqueID = ASN1BitString.getInstance(seq.getObjectAt(i));
             }
             else if (obj instanceof ASN1Sequence || obj instanceof Extensions)
             {
@@ -126,7 +126,7 @@
         return attributes;
     }
 
-    public DERBitString getIssuerUniqueID()
+    public ASN1BitString getIssuerUniqueID()
     {
         return issuerUniqueID;
     }
@@ -158,7 +158,7 @@
     {
         ASN1EncodableVector v = new ASN1EncodableVector(9);
 
-        if (version.intValueExact() != 0)
+        if (!version.hasValue(0))
         {
             v.add(version);
         }
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/AuthorityKeyIdentifier.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/AuthorityKeyIdentifier.java
index c4e2cf3..f4f45b5 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/AuthorityKeyIdentifier.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/AuthorityKeyIdentifier.java
@@ -18,6 +18,7 @@
 // Android-changed: Use Android digests
 // import org.bouncycastle.crypto.digests.SHA1Digest;
 import com.android.org.bouncycastle.crypto.digests.AndroidDigestFactory;
+import com.android.org.bouncycastle.util.Arrays;
 import com.android.org.bouncycastle.util.encoders.Hex;
 
 /**
@@ -175,7 +176,7 @@
         GeneralNames            name,
         BigInteger              serialNumber)
     {
-        this.keyidentifier = (keyIdentifier != null) ? new DEROctetString(keyIdentifier) : null;
+        this.keyidentifier = (keyIdentifier != null) ? new DEROctetString(Arrays.clone(keyIdentifier)) : null;
         this.certissuer = name;
         this.certserno = (serialNumber != null) ? new ASN1Integer(serialNumber) : null;
     }
@@ -232,6 +233,7 @@
 
     public String toString()
     {
+        // -DM Hex.toHexString
         String keyID = (keyidentifier != null) ? Hex.toHexString(keyidentifier.getOctets()) : "null";
 
         return "AuthorityKeyIdentifier: KeyID(" + keyID + ")";
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/BasicConstraints.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/BasicConstraints.java
index c0b8ef8..0975cda 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/BasicConstraints.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/BasicConstraints.java
@@ -126,6 +126,11 @@
         return null;
     }
 
+    public ASN1Integer getPathLenConstraintInteger()
+    {
+        return pathLenConstraint;
+    }
+
     /**
      * Produce an object suitable for an ASN1OutputStream.
      * <pre>
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/CRLNumber.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/CRLNumber.java
index c02cec2..433013c 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/CRLNumber.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/CRLNumber.java
@@ -6,6 +6,7 @@
 import com.android.org.bouncycastle.asn1.ASN1Integer;
 import com.android.org.bouncycastle.asn1.ASN1Object;
 import com.android.org.bouncycastle.asn1.ASN1Primitive;
+import com.android.org.bouncycastle.util.BigIntegers;
 
 /**
  * The CRLNumber object.
@@ -22,6 +23,10 @@
     public CRLNumber(
         BigInteger number)
     {
+        if (BigIntegers.ZERO.compareTo(number) > 0)
+        {
+            throw new IllegalArgumentException("Invalid CRL number : not in (0..MAX)");
+        }
         this.number = number;
     }
 
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/CRLReason.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/CRLReason.java
index 545c535..f370dfd 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/CRLReason.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/CRLReason.java
@@ -53,7 +53,7 @@
     /**
      * @deprecated use lower case version
      */
-    public static final int CESSATION_OF_OPERATION  = 5;
+    public static final int CESSATION_OF_OPERATION = 5;
     /**
      * @deprecated use lower case version
      */
@@ -76,7 +76,7 @@
     public static final int cACompromise = 2;
     public static final int affiliationChanged = 3;
     public static final int superseded = 4;
-    public static final int cessationOfOperation  = 5;
+    public static final int cessationOfOperation = 5;
     public static final int certificateHold = 6;
     // 7 -> unknown
     public static final int removeFromCRL = 8;
@@ -84,11 +84,11 @@
     public static final int aACompromise = 10;
 
     private static final String[] reasonString =
-    {
-        "unspecified", "keyCompromise", "cACompromise", "affiliationChanged",
-        "superseded", "cessationOfOperation", "certificateHold", "unknown",
-        "removeFromCRL", "privilegeWithdrawn", "aACompromise"
-    };
+        {
+            "unspecified", "keyCompromise", "cACompromise", "affiliationChanged",
+            "superseded", "cessationOfOperation", "certificateHold", "unknown",
+            "removeFromCRL", "privilegeWithdrawn", "aACompromise"
+        };
 
     private static final Hashtable table = new Hashtable();
 
@@ -111,6 +111,10 @@
     private CRLReason(
         int reason)
     {
+        if (reason < 0)
+        {
+            throw new IllegalArgumentException("Invalid CRL reason : not in (0..MAX)");
+        }
         value = new ASN1Enumerated(reason);
     }
 
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/Certificate.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/Certificate.java
index c5ce691..19f2fda 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/Certificate.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/Certificate.java
@@ -1,12 +1,12 @@
 /* GENERATED SOURCE. DO NOT MODIFY. */
 package com.android.org.bouncycastle.asn1.x509;
 
+import com.android.org.bouncycastle.asn1.ASN1BitString;
 import com.android.org.bouncycastle.asn1.ASN1Integer;
 import com.android.org.bouncycastle.asn1.ASN1Object;
 import com.android.org.bouncycastle.asn1.ASN1Primitive;
 import com.android.org.bouncycastle.asn1.ASN1Sequence;
 import com.android.org.bouncycastle.asn1.ASN1TaggedObject;
-import com.android.org.bouncycastle.asn1.DERBitString;
 import com.android.org.bouncycastle.asn1.x500.X500Name;
 
 /**
@@ -26,7 +26,7 @@
     ASN1Sequence  seq;
     TBSCertificate tbsCert;
     AlgorithmIdentifier     sigAlgId;
-    DERBitString            sig;
+    ASN1BitString            sig;
 
     public static Certificate getInstance(
         ASN1TaggedObject obj,
@@ -64,7 +64,7 @@
             tbsCert = TBSCertificate.getInstance(seq.getObjectAt(0));
             sigAlgId = AlgorithmIdentifier.getInstance(seq.getObjectAt(1));
 
-            sig = DERBitString.getInstance(seq.getObjectAt(2));
+            sig = ASN1BitString.getInstance(seq.getObjectAt(2));
         }
         else
         {
@@ -122,7 +122,7 @@
         return sigAlgId;
     }
 
-    public DERBitString getSignature()
+    public ASN1BitString getSignature()
     {
         return sig;
     }
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/CertificateList.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/CertificateList.java
index cc8a9db..546fb69 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/CertificateList.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/CertificateList.java
@@ -4,6 +4,7 @@
 
 import java.util.Enumeration;
 
+import com.android.org.bouncycastle.asn1.ASN1BitString;
 import com.android.org.bouncycastle.asn1.ASN1EncodableVector;
 import com.android.org.bouncycastle.asn1.ASN1Object;
 import com.android.org.bouncycastle.asn1.ASN1Primitive;
@@ -32,7 +33,7 @@
 {
     TBSCertList            tbsCertList;
     AlgorithmIdentifier    sigAlgId;
-    DERBitString           sig;
+    ASN1BitString          sig;
     boolean                isHashCodeSet = false;
     int                    hashCodeValue;
 
@@ -58,18 +59,14 @@
         return null;
     }
 
-    /**
-     * @deprecated use getInstance() method.
-     * @param seq
-     */
-    public CertificateList(
+    private CertificateList(
         ASN1Sequence seq)
     {
         if (seq.size() == 3)
         {
             tbsCertList = TBSCertList.getInstance(seq.getObjectAt(0));
             sigAlgId = AlgorithmIdentifier.getInstance(seq.getObjectAt(1));
-            sig = DERBitString.getInstance(seq.getObjectAt(2));
+            sig = ASN1BitString.getInstance(seq.getObjectAt(2));
         }
         else
         {
@@ -97,7 +94,7 @@
         return sigAlgId;
     }
 
-    public DERBitString getSignature()
+    public ASN1BitString getSignature()
     {
         return sig;
     }
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/DeltaCertificateDescriptor.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/DeltaCertificateDescriptor.java
new file mode 100644
index 0000000..8f87d68
--- /dev/null
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/DeltaCertificateDescriptor.java
@@ -0,0 +1,276 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.org.bouncycastle.asn1.x509;
+
+import java.util.Enumeration;
+
+import com.android.org.bouncycastle.asn1.ASN1BitString;
+import com.android.org.bouncycastle.asn1.ASN1Encodable;
+import com.android.org.bouncycastle.asn1.ASN1EncodableVector;
+import com.android.org.bouncycastle.asn1.ASN1Integer;
+import com.android.org.bouncycastle.asn1.ASN1Object;
+import com.android.org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import com.android.org.bouncycastle.asn1.ASN1Primitive;
+import com.android.org.bouncycastle.asn1.ASN1Sequence;
+import com.android.org.bouncycastle.asn1.ASN1TaggedObject;
+import com.android.org.bouncycastle.asn1.DERSequence;
+import com.android.org.bouncycastle.asn1.DERTaggedObject;
+import com.android.org.bouncycastle.asn1.x500.X500Name;
+
+/**
+ * <pre>
+ *     DeltaCertificateDescriptor ::= SEQUENCE {
+ *      serialNumber          CertificateSerialNumber,
+ *      signature             [0] IMPLICIT AlgorithmIdentifier
+ *           {SIGNATURE_ALGORITHM, {...}} OPTIONAL,
+ *      issuer                [1] IMPLICIT Name OPTIONAL,
+ *      validity              [2] IMPLICIT Validity OPTIONAL,
+ *      subject               [3] IMPLICIT Name OPTIONAL,
+ *      subjectPublicKeyInfo  SubjectPublicKeyInfo,
+ *      extensions            [4] IMPLICIT Extensions{CertExtensions}
+ *           OPTIONAL,
+ *      signatureValue        BIT STRING
+ *    }
+ *    </pre>
+ * @hide This class is not part of the Android public SDK API
+ */
+public class DeltaCertificateDescriptor
+    extends ASN1Object
+{
+    private final ASN1Integer serialNumber;
+
+    private AlgorithmIdentifier signature;
+    private X500Name issuer;
+    private ASN1Sequence validity;
+    private X500Name subject;
+    private SubjectPublicKeyInfo subjectPublicKeyInfo;
+    private Extensions extensions;
+
+    private final ASN1BitString signatureValue;
+
+    public static DeltaCertificateDescriptor getInstance(
+        Object  obj)
+    {
+        if (obj instanceof DeltaCertificateDescriptor)
+        {
+            return (DeltaCertificateDescriptor)obj;
+        }
+        else if (obj != null)
+        {
+            return new DeltaCertificateDescriptor(ASN1Sequence.getInstance(obj));
+        }
+
+        return null;
+    }
+
+    /**
+     * Retrieve a DeltaCertificateDescriptor for a passed in Extensions object, if present.
+     *
+     * @param extensions the extensions object to be examined.
+     * @return  the DeltaCertificateDescriptor, null if the extension is not present.
+     */
+    public static DeltaCertificateDescriptor fromExtensions(Extensions extensions)
+    {
+        return getInstance(Extensions.getExtensionParsedValue(extensions, Extension.deltaCertificateDescriptor));
+    }
+
+    private DeltaCertificateDescriptor(ASN1Sequence seq)
+    {
+        this.serialNumber = ASN1Integer.getInstance(seq.getObjectAt(0));
+
+        int idx = 1;
+        ASN1Encodable next = seq.getObjectAt(idx);
+        while (next instanceof ASN1TaggedObject)
+        {
+            ASN1TaggedObject tagged = ASN1TaggedObject.getInstance(next);
+            switch (tagged.getTagNo())
+            {
+            case 0:
+                signature = AlgorithmIdentifier.getInstance(tagged, false);
+                break;
+            case 1:
+                issuer = X500Name.getInstance(tagged, true);   // issuer
+                break;
+            case 2:
+                validity = ASN1Sequence.getInstance(tagged, false);
+                break;
+            case 3:
+                subject = X500Name.getInstance(tagged, true);   // subject
+                break;
+            }
+            next = seq.getObjectAt(idx++);
+        }
+
+        subjectPublicKeyInfo = subjectPublicKeyInfo.getInstance(next);
+
+        next = seq.getObjectAt(idx);
+        while (next instanceof ASN1TaggedObject)
+        {
+            ASN1TaggedObject tagged = ASN1TaggedObject.getInstance(next);
+            switch (tagged.getTagNo())
+            {
+            case 4:
+                extensions = Extensions.getInstance(tagged, false); 
+                break;
+            }
+            next = seq.getObjectAt(idx++);
+        }
+
+        signatureValue = ASN1BitString.getInstance(next);
+    }
+
+    public ASN1Integer getSerialNumber()
+    {
+        return serialNumber;
+    }
+
+    public AlgorithmIdentifier getSignature()
+    {
+        return signature;
+    }
+
+    public X500Name getIssuer()
+    {
+        return issuer;
+    }
+
+    public ASN1Sequence getValidity()
+    {
+        return validity;
+    }
+
+    public X500Name getSubject()
+    {
+        return subject;
+    }
+
+    public SubjectPublicKeyInfo getSubjectPublicKeyInfo()
+    {
+        return subjectPublicKeyInfo;
+    }
+
+    public Extensions getExtensions()
+    {
+        return extensions;
+    }
+
+    public ASN1BitString getSignatureValue()
+    {
+        return signatureValue;
+    }
+
+    public DeltaCertificateDescriptor trimTo(TBSCertificate baseTbsCertificate, Extensions tbsExtensions)
+    {
+        AlgorithmIdentifier signature = baseTbsCertificate.signature;
+        X500Name issuer = baseTbsCertificate.issuer;
+        ASN1Sequence validity = new DERSequence(new ASN1Encodable[]
+        {
+            baseTbsCertificate.startDate, baseTbsCertificate.endDate
+        });
+        X500Name subject = baseTbsCertificate.subject;
+        ASN1Sequence s = ASN1Sequence.getInstance(toASN1Primitive());
+        ASN1EncodableVector v = new ASN1EncodableVector();
+
+        Enumeration en = s.getObjects();
+        v.add((ASN1Encodable)en.nextElement());
+
+        ASN1Encodable next = (ASN1Encodable)en.nextElement();
+        while (next instanceof ASN1TaggedObject)
+        {
+            ASN1TaggedObject tagged = ASN1TaggedObject.getInstance(next);
+            switch (tagged.getTagNo())
+            {
+            case 0:
+                AlgorithmIdentifier sig = AlgorithmIdentifier.getInstance(tagged, false);
+                if (!sig.equals(signature))
+                {
+                    v.add(next);
+                }
+                break;
+            case 1:
+                X500Name iss = X500Name.getInstance(tagged, true);   // issuer
+                if (!iss.equals(issuer))
+                {
+                    v.add(next);
+                }
+                break;
+            case 2:
+                ASN1Sequence val = ASN1Sequence.getInstance(tagged, false);
+                if (!val.equals(validity))
+                {
+                    v.add(next);
+                }
+                break;
+            case 3:
+                X500Name sub = X500Name.getInstance(tagged, true);   // subject
+                if (!sub.equals(subject))
+                {
+                    v.add(next);
+                }
+                break;
+            }
+            next = (ASN1Encodable)en.nextElement();
+        }
+
+        v.add(next);
+
+        next = (ASN1Encodable)en.nextElement();
+        while (next instanceof ASN1TaggedObject)
+        {
+            ASN1TaggedObject tagged = ASN1TaggedObject.getInstance(next);
+            switch (tagged.getTagNo())
+            {
+            case 4:
+                Extensions deltaExts = Extensions.getInstance(tagged, false);
+                ExtensionsGenerator deltaExtGen = new ExtensionsGenerator();
+                for (Enumeration extEn = deltaExts.oids(); extEn.hasMoreElements(); )
+                {
+                    Extension deltaExt = deltaExts.getExtension((ASN1ObjectIdentifier)extEn.nextElement());
+                    Extension primaryExt = tbsExtensions.getExtension(deltaExt.getExtnId());
+
+                    if (primaryExt != null)
+                    {
+                        if (!deltaExt.equals(primaryExt))
+                        {
+                            deltaExtGen.addExtension(deltaExt);
+                        }
+                    }
+                }
+
+                DeltaCertificateDescriptor trimmedDeltaCertDesc;
+                if (!deltaExtGen.isEmpty())
+                {
+                    v.add(new DERTaggedObject(false, 4, deltaExtGen.generate()));
+                }
+            }
+            next = (ASN1Encodable)en.nextElement();
+        }
+
+        v.add(next);
+
+        return new DeltaCertificateDescriptor(new DERSequence(v));
+    }
+
+    private void addOptional(ASN1EncodableVector v, int tag, boolean explicit, ASN1Object obj)
+    {
+        if (obj != null)
+        {
+             v.add(new DERTaggedObject(explicit, tag, obj));
+        }
+    }
+
+    public ASN1Primitive toASN1Primitive()
+    {
+        ASN1EncodableVector v = new ASN1EncodableVector(7);
+
+        v.add(serialNumber);
+        addOptional(v, 0, false, signature);
+        addOptional(v, 1, true, issuer); // CHOICE
+        addOptional(v, 2, false, validity);
+        addOptional(v, 3, true, subject);  // CHOICE
+        v.add(subjectPublicKeyInfo);
+        addOptional(v, 4, false, extensions);
+        v.add(signatureValue);
+
+        return new DERSequence(v);
+    }
+}
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/DistributionPoint.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/DistributionPoint.java
index f37199e..86dd71a 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/DistributionPoint.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/DistributionPoint.java
@@ -1,12 +1,12 @@
 /* GENERATED SOURCE. DO NOT MODIFY. */
 package com.android.org.bouncycastle.asn1.x509;
 
+import com.android.org.bouncycastle.asn1.ASN1BitString;
 import com.android.org.bouncycastle.asn1.ASN1EncodableVector;
 import com.android.org.bouncycastle.asn1.ASN1Object;
 import com.android.org.bouncycastle.asn1.ASN1Primitive;
 import com.android.org.bouncycastle.asn1.ASN1Sequence;
 import com.android.org.bouncycastle.asn1.ASN1TaggedObject;
-import com.android.org.bouncycastle.asn1.DERBitString;
 import com.android.org.bouncycastle.asn1.DERSequence;
 import com.android.org.bouncycastle.asn1.DERTaggedObject;
 import com.android.org.bouncycastle.util.Strings;
@@ -61,10 +61,11 @@
             switch (t.getTagNo())
             {
             case 0:
+                // CHOICE so explicit
                 distributionPoint = DistributionPointName.getInstance(t, true);
                 break;
             case 1:
-                reasons = new ReasonFlags(DERBitString.getInstance(t, false));
+                reasons = new ReasonFlags(ASN1BitString.getInstance(t, false));
                 break;
             case 2:
                 cRLIssuer = GeneralNames.getInstance(t, false);
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/Extension.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/Extension.java
index 3ae3404..45adc5c 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/Extension.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/Extension.java
@@ -13,6 +13,7 @@
 import com.android.org.bouncycastle.asn1.ASN1Sequence;
 import com.android.org.bouncycastle.asn1.DEROctetString;
 import com.android.org.bouncycastle.asn1.DERSequence;
+import com.android.org.bouncycastle.util.Arrays;
 
 /**
  * an object for the elements in the X.509 V3 extension block.
@@ -181,6 +182,26 @@
      */
     public static final ASN1ObjectIdentifier expiredCertsOnCRL = new ASN1ObjectIdentifier("2.5.29.60").intern();
 
+    /**
+     * the subject’s alternative public key information
+     */
+    public static final ASN1ObjectIdentifier subjectAltPublicKeyInfo = new ASN1ObjectIdentifier("2.5.29.72").intern();
+
+    /**
+     * the algorithm identifier for the alternative digital signature algorithm.
+     */
+    public static final ASN1ObjectIdentifier altSignatureAlgorithm = new ASN1ObjectIdentifier("2.5.29.73").intern();
+
+    /**
+     * alternative signature shall be created by the issuer using its alternative private key.
+     */
+    public static final ASN1ObjectIdentifier altSignatureValue = new ASN1ObjectIdentifier("2.5.29.74").intern();
+
+    /**
+     * delta certificate extension - prototype value will change!
+     */
+    public static final ASN1ObjectIdentifier deltaCertificateDescriptor = new ASN1ObjectIdentifier("2.16.840.1.114027.80.6.1");
+
     private ASN1ObjectIdentifier extnId;
     private boolean             critical;
     private ASN1OctetString      value;
@@ -212,7 +233,7 @@
         boolean critical,
         byte[] value)
     {
-        this(extnId, critical, new DEROctetString(value));
+        this(extnId, critical, new DEROctetString(Arrays.clone(value)));
     }
 
     /**
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/Extensions.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/Extensions.java
index c721c9c..4166c83 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/Extensions.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/Extensions.java
@@ -13,6 +13,7 @@
 import com.android.org.bouncycastle.asn1.ASN1Sequence;
 import com.android.org.bouncycastle.asn1.ASN1TaggedObject;
 import com.android.org.bouncycastle.asn1.DERSequence;
+import com.android.org.bouncycastle.util.Properties;
 
 /**
  * <pre>
@@ -72,6 +73,11 @@
     private Extensions(
         ASN1Sequence seq)
     {
+        if (seq.size() == 0)
+        {
+            throw new IllegalArgumentException("empty extension sequence found");
+        }
+
         Enumeration e = seq.getObjects();
 
         while (e.hasMoreElements())
@@ -80,7 +86,10 @@
 
             if (extensions.containsKey(ext.getExtnId()))
             {
-                throw new IllegalArgumentException("repeated extension found: " + ext.getExtnId());
+                if (!Properties.isOverrideSet("com.android.org.bouncycastle.x509.ignore_repeated_extensions"))
+                {
+                    throw new IllegalArgumentException("repeated extension found: " + ext.getExtnId());
+                }
             }
             
             extensions.put(ext.getExtnId(), ext);
@@ -108,6 +117,11 @@
     public Extensions(
         Extension[] extensions)
     {
+        if (extensions == null || extensions.length == 0)
+        {
+            throw new IllegalArgumentException("extension array cannot be null or empty");
+        }
+
         for (int i = 0; i != extensions.length; i++)
         {
             Extension ext = extensions[i];
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/ExtensionsGenerator.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/ExtensionsGenerator.java
index 2720ba2..31bde19 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/ExtensionsGenerator.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/ExtensionsGenerator.java
@@ -2,13 +2,22 @@
 package com.android.org.bouncycastle.asn1.x509;
 
 import java.io.IOException;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashSet;
 import java.util.Hashtable;
+import java.util.Set;
 import java.util.Vector;
 
 import com.android.org.bouncycastle.asn1.ASN1Encodable;
+import com.android.org.bouncycastle.asn1.ASN1EncodableVector;
 import com.android.org.bouncycastle.asn1.ASN1Encoding;
 import com.android.org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import com.android.org.bouncycastle.asn1.ASN1ParsingException;
+import com.android.org.bouncycastle.asn1.ASN1Sequence;
 import com.android.org.bouncycastle.asn1.DEROctetString;
+import com.android.org.bouncycastle.asn1.DERSequence;
+import com.android.org.bouncycastle.util.Arrays;
 
 /**
  * Generator for X.509 extensions
@@ -18,6 +27,18 @@
 {
     private Hashtable extensions = new Hashtable();
     private Vector extOrdering = new Vector();
+    private static final Set dupsAllowed;
+
+
+    static
+    {
+        Set dups = new HashSet();
+        dups.add(Extension.subjectAlternativeName);
+        dups.add(Extension.issuerAlternativeName);
+        dups.add(Extension.subjectDirectoryAttributes);
+        dups.add(Extension.certificateIssuer);
+        dupsAllowed = Collections.unmodifiableSet(dups);
+    }
 
     /**
      * Reset the generator
@@ -32,14 +53,14 @@
      * Add an extension with the given oid and the passed in value to be included
      * in the OCTET STRING associated with the extension.
      *
-     * @param oid  OID for the extension.
-     * @param critical  true if critical, false otherwise.
-     * @param value the ASN.1 object to be included in the extension.
+     * @param oid      OID for the extension.
+     * @param critical true if critical, false otherwise.
+     * @param value    the ASN.1 object to be included in the extension.
      */
     public void addExtension(
         ASN1ObjectIdentifier oid,
-        boolean              critical,
-        ASN1Encodable        value)
+        boolean critical,
+        ASN1Encodable value)
         throws IOException
     {
         this.addExtension(oid, critical, value.toASN1Primitive().getEncoded(ASN1Encoding.DER));
@@ -49,22 +70,52 @@
      * Add an extension with the given oid and the passed in byte array to be wrapped in the
      * OCTET STRING associated with the extension.
      *
-     * @param oid OID for the extension.
+     * @param oid      OID for the extension.
      * @param critical true if critical, false otherwise.
-     * @param value the byte array to be wrapped.
+     * @param value    the byte array to be wrapped.
      */
     public void addExtension(
         ASN1ObjectIdentifier oid,
-        boolean             critical,
-        byte[]              value)
+        boolean critical,
+        byte[] value)
     {
         if (extensions.containsKey(oid))
         {
-            throw new IllegalArgumentException("extension " + oid + " already added");
-        }
+            if (dupsAllowed.contains(oid))
+            {
+                Extension existingExtension = (Extension)extensions.get(oid);
+                ASN1Sequence seq1 = ASN1Sequence.getInstance(DEROctetString.getInstance(existingExtension.getExtnValue()).getOctets());
+                ASN1Sequence seq2 = ASN1Sequence.getInstance(value);
 
-        extOrdering.addElement(oid);
-        extensions.put(oid, new Extension(oid, critical, new DEROctetString(value)));
+                ASN1EncodableVector items = new ASN1EncodableVector(seq1.size() + seq2.size());
+                for (Enumeration en = seq1.getObjects(); en.hasMoreElements();)
+                {
+                    items.add((ASN1Encodable)en.nextElement());
+                }
+                for (Enumeration en = seq2.getObjects(); en.hasMoreElements();)
+                {
+                    items.add((ASN1Encodable)en.nextElement());
+                }
+                
+                try
+                {
+                    extensions.put(oid, new Extension(oid, critical, new DERSequence(items).getEncoded()));
+                }
+                catch (IOException e)
+                {
+                    throw new ASN1ParsingException(e.getMessage(), e);
+                }
+            }
+            else
+            {
+                throw new IllegalArgumentException("extension " + oid + " already added");
+            }
+        }
+        else
+        {
+            extOrdering.addElement(oid);
+            extensions.put(oid, new Extension(oid, critical, new DEROctetString(Arrays.clone(value))));
+        }
     }
 
     /**
@@ -88,14 +139,14 @@
      * Replace an extension with the given oid and the passed in value to be included
      * in the OCTET STRING associated with the extension.
      *
-     * @param oid  OID for the extension.
-     * @param critical  true if critical, false otherwise.
-     * @param value the ASN.1 object to be included in the extension.
+     * @param oid      OID for the extension.
+     * @param critical true if critical, false otherwise.
+     * @param value    the ASN.1 object to be included in the extension.
      */
     public void replaceExtension(
         ASN1ObjectIdentifier oid,
-        boolean              critical,
-        ASN1Encodable        value)
+        boolean critical,
+        ASN1Encodable value)
         throws IOException
     {
         this.replaceExtension(oid, critical, value.toASN1Primitive().getEncoded(ASN1Encoding.DER));
@@ -105,14 +156,14 @@
      * Replace an extension with the given oid and the passed in byte array to be wrapped in the
      * OCTET STRING associated with the extension.
      *
-     * @param oid OID for the extension.
+     * @param oid      OID for the extension.
      * @param critical true if critical, false otherwise.
-     * @param value the byte array to be wrapped.
+     * @param value    the byte array to be wrapped.
      */
     public void replaceExtension(
         ASN1ObjectIdentifier oid,
-        boolean             critical,
-        byte[]              value)
+        boolean critical,
+        byte[] value)
     {
         this.replaceExtension(new Extension(oid, critical, value));
     }
@@ -158,7 +209,7 @@
      */
     public boolean hasExtension(ASN1ObjectIdentifier oid)
     {
-         return extensions.containsKey(oid);
+        return extensions.containsKey(oid);
     }
 
     /**
@@ -169,7 +220,7 @@
      */
     public Extension getExtension(ASN1ObjectIdentifier oid)
     {
-         return (Extension)extensions.get(oid);
+        return (Extension)extensions.get(oid);
     }
 
     /**
@@ -185,7 +236,7 @@
     /**
      * Generate an Extensions object based on the current state of the generator.
      *
-     * @return  an X09Extensions object.
+     * @return an X09Extensions object.
      */
     public Extensions generate()
     {
@@ -198,4 +249,15 @@
 
         return new Extensions(exts);
     }
+
+    public void addExtension(Extensions extensions)
+    {
+        ASN1ObjectIdentifier[] oids = extensions.getExtensionOIDs();
+        for (int i = 0; i != oids.length; i++)
+        {
+            ASN1ObjectIdentifier ident = oids[i];
+            Extension ext = extensions.getExtension(ident);
+            addExtension(ASN1ObjectIdentifier.getInstance(ident), ext.isCritical(), ext.getExtnValue().getOctets());
+        }
+    }
 }
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/GeneralName.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/GeneralName.java
index 982782b..f0dbfa2 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/GeneralName.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/GeneralName.java
@@ -6,6 +6,7 @@
 
 import com.android.org.bouncycastle.asn1.ASN1Choice;
 import com.android.org.bouncycastle.asn1.ASN1Encodable;
+import com.android.org.bouncycastle.asn1.ASN1IA5String;
 import com.android.org.bouncycastle.asn1.ASN1Object;
 import com.android.org.bouncycastle.asn1.ASN1ObjectIdentifier;
 import com.android.org.bouncycastle.asn1.ASN1OctetString;
@@ -196,7 +197,7 @@
             case dNSName:
             case rfc822Name:
             case uniformResourceIdentifier:
-                return new GeneralName(tag, DERIA5String.getInstance(tagObj, false));
+                return new GeneralName(tag, ASN1IA5String.getInstance(tagObj, false));
 
             case directoryName:
                 return new GeneralName(tag, X500Name.getInstance(tagObj, true));
@@ -229,6 +230,11 @@
         ASN1TaggedObject tagObj,
         boolean          explicit)
     {
+        if (!explicit)
+        {
+            throw new IllegalArgumentException("choice item must be explicitly tagged");
+        }
+
         return GeneralName.getInstance(ASN1TaggedObject.getInstance(tagObj, true));
     }
 
@@ -253,7 +259,7 @@
         case rfc822Name:
         case dNSName:
         case uniformResourceIdentifier:
-            buf.append(DERIA5String.getInstance(obj).getString());
+            buf.append(ASN1IA5String.getInstance(obj).getString());
             break;
         case directoryName:
             buf.append(X500Name.getInstance(obj).toString());
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/GeneralSubtree.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/GeneralSubtree.java
index 6830ad5..d67a44b 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/GeneralSubtree.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/GeneralSubtree.java
@@ -205,7 +205,7 @@
 
         v.add(base);
 
-        if (minimum != null && !minimum.hasValue(ZERO))
+        if (minimum != null && !minimum.hasValue(0))
         {
             v.add(new DERTaggedObject(false, 0, minimum));
         }
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/Holder.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/Holder.java
index 16b280a..f31c025 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/Holder.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/Holder.java
@@ -89,7 +89,7 @@
         default:
             throw new IllegalArgumentException("unknown tag in Holder");
         }
-        version = 0;
+        version = V1_CERTIFICATE_HOLDER;
     }
 
     /**
@@ -125,7 +125,7 @@
                 throw new IllegalArgumentException("unknown tag in Holder");
             }
         }
-        version = 1;
+        version = V2_CERTIFICATE_HOLDER;
     }
 
     public Holder(IssuerSerial baseCertificateID)
@@ -211,7 +211,7 @@
 
     public ASN1Primitive toASN1Primitive()
     {
-        if (version == 1)
+        if (version == V2_CERTIFICATE_HOLDER)
         {
             ASN1EncodableVector v = new ASN1EncodableVector(3);
 
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/IssuerSerial.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/IssuerSerial.java
index b92639d..fdd59c2 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/IssuerSerial.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/IssuerSerial.java
@@ -3,13 +3,13 @@
 
 import java.math.BigInteger;
 
+import com.android.org.bouncycastle.asn1.ASN1BitString;
 import com.android.org.bouncycastle.asn1.ASN1EncodableVector;
 import com.android.org.bouncycastle.asn1.ASN1Integer;
 import com.android.org.bouncycastle.asn1.ASN1Object;
 import com.android.org.bouncycastle.asn1.ASN1Primitive;
 import com.android.org.bouncycastle.asn1.ASN1Sequence;
 import com.android.org.bouncycastle.asn1.ASN1TaggedObject;
-import com.android.org.bouncycastle.asn1.DERBitString;
 import com.android.org.bouncycastle.asn1.DERSequence;
 import com.android.org.bouncycastle.asn1.x500.X500Name;
 
@@ -19,9 +19,9 @@
 public class IssuerSerial
     extends ASN1Object
 {
-    GeneralNames            issuer;
-    ASN1Integer              serial;
-    DERBitString            issuerUID;
+    GeneralNames  issuer;
+    ASN1Integer   serial;
+    ASN1BitString issuerUID;
 
     public static IssuerSerial getInstance(
             Object  obj)
@@ -59,7 +59,7 @@
 
         if (seq.size() == 3)
         {
-            issuerUID = DERBitString.getInstance(seq.getObjectAt(2));
+            issuerUID = ASN1BitString.getInstance(seq.getObjectAt(2));
         }
     }
 
@@ -95,7 +95,7 @@
         return serial;
     }
 
-    public DERBitString getIssuerUID()
+    public ASN1BitString getIssuerUID()
     {
         return issuerUID;
     }
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/IssuingDistributionPoint.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/IssuingDistributionPoint.java
index 62fafaf..6a89df0 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/IssuingDistributionPoint.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/IssuingDistributionPoint.java
@@ -1,6 +1,7 @@
 /* GENERATED SOURCE. DO NOT MODIFY. */
 package com.android.org.bouncycastle.asn1.x509;
 
+import com.android.org.bouncycastle.asn1.ASN1BitString;
 import com.android.org.bouncycastle.asn1.ASN1Boolean;
 import com.android.org.bouncycastle.asn1.ASN1EncodableVector;
 import com.android.org.bouncycastle.asn1.ASN1Object;
@@ -154,7 +155,7 @@
             switch (o.getTagNo())
             {
             case 0:
-                                                    // CHOICE so explicit
+                // CHOICE so explicit
                 distributionPoint = DistributionPointName.getInstance(o, true);
                 break;
             case 1:
@@ -164,7 +165,7 @@
                 onlyContainsCACerts = ASN1Boolean.getInstance(o, false).isTrue();
                 break;
             case 3:
-                onlySomeReasons = new ReasonFlags(ReasonFlags.getInstance(o, false));
+                onlySomeReasons = new ReasonFlags(ASN1BitString.getInstance(o, false));
                 break;
             case 4:
                 indirectCRL = ASN1Boolean.getInstance(o, false).isTrue();
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/KeyPurposeId.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/KeyPurposeId.java
index 8f464a1..6f6206b 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/KeyPurposeId.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/KeyPurposeId.java
@@ -109,6 +109,30 @@
      */
     public static final KeyPurposeId id_kp_capwapWTP = new KeyPurposeId(id_kp.branch("19"));
 
+
+    /**
+     * id-kp-cmcCA OBJECT IDENTIFIER ::= {
+     *          iso(1) identified-organization(3) dod(6) internet(1)
+     *          security(5) mechanisms(5) pkix(7) kp(3) 27 }
+     */
+    public static final KeyPurposeId id_kp_cmcCA = new KeyPurposeId(id_kp.branch("27"));
+
+    /**
+     * id-kp-cmcRA OBJECT IDENTIFIER ::= {
+     *          iso(1) identified-organization(3) dod(6) internet(1)
+     *          security(5) mechanisms(5) pkix(7) kp(3) 28 }
+     */
+    public static final KeyPurposeId id_kp_cmcRA = new KeyPurposeId(id_kp.branch("28"));
+
+    /**
+     * id-kp-cmKGA OBJECT IDENTIFIER ::= {
+     *          iso(1) identified-organization(3) dod(6) internet(1)
+     *          security(5) mechanisms(5) pkix(7) kp(3) 32 }
+     */
+    public static final KeyPurposeId id_kp_cmKGA = new KeyPurposeId(id_kp.branch("32"));
+
+
+
     //
     // microsoft key purpose ids
     //
@@ -142,15 +166,6 @@
         this.id = id;
     }
 
-    /**
-     * @param id string representation of an OID.
-     * @deprecated use getInstance and an OID or one of the constants above.
-     */
-    public KeyPurposeId(String id)
-    {
-        this(new ASN1ObjectIdentifier(id));
-    }
-
     public static KeyPurposeId getInstance(Object o)
     {
         if (o instanceof KeyPurposeId)
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/KeyUsage.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/KeyUsage.java
index 12d2d07..b3d7a6f 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/KeyUsage.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/KeyUsage.java
@@ -1,6 +1,7 @@
 /* GENERATED SOURCE. DO NOT MODIFY. */
 package com.android.org.bouncycastle.asn1.x509;
 
+import com.android.org.bouncycastle.asn1.ASN1BitString;
 import com.android.org.bouncycastle.asn1.ASN1Object;
 import com.android.org.bouncycastle.asn1.ASN1Primitive;
 import com.android.org.bouncycastle.asn1.DERBitString;
@@ -36,7 +37,7 @@
     public static final int        encipherOnly     = (1 << 0);
     public static final int        decipherOnly     = (1 << 15);
 
-    private DERBitString bitString;
+    private ASN1BitString bitString;
 
     public static KeyUsage getInstance(Object obj)   // needs to be DERBitString for other VMs
     {
@@ -46,7 +47,7 @@
         }
         else if (obj != null)
         {
-            return new KeyUsage(DERBitString.getInstance(obj));
+            return new KeyUsage(ASN1BitString.getInstance(obj));
         }
 
         return null;
@@ -71,7 +72,7 @@
     }
 
     private KeyUsage(
-        DERBitString bitString)
+        ASN1BitString bitString)
     {
         this.bitString = bitString;
     }
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/ObjectDigestInfo.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/ObjectDigestInfo.java
index a5478ca..6b41325 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/ObjectDigestInfo.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/ObjectDigestInfo.java
@@ -1,6 +1,7 @@
 /* GENERATED SOURCE. DO NOT MODIFY. */
 package com.android.org.bouncycastle.asn1.x509;
 
+import com.android.org.bouncycastle.asn1.ASN1BitString;
 import com.android.org.bouncycastle.asn1.ASN1EncodableVector;
 import com.android.org.bouncycastle.asn1.ASN1Enumerated;
 import com.android.org.bouncycastle.asn1.ASN1Object;
@@ -56,7 +57,7 @@
 
     AlgorithmIdentifier digestAlgorithm;
 
-    DERBitString objectDigest;
+    ASN1BitString objectDigest;
 
     public static ObjectDigestInfo getInstance(
         Object obj)
@@ -131,7 +132,7 @@
 
         digestAlgorithm = AlgorithmIdentifier.getInstance(seq.getObjectAt(1 + offset));
 
-        objectDigest = DERBitString.getInstance(seq.getObjectAt(2 + offset));
+        objectDigest = ASN1BitString.getInstance(seq.getObjectAt(2 + offset));
     }
 
     public ASN1Enumerated getDigestedObjectType()
@@ -149,7 +150,7 @@
         return digestAlgorithm;
     }
 
-    public DERBitString getObjectDigest()
+    public ASN1BitString getObjectDigest()
     {
         return objectDigest;
     }
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/OtherName.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/OtherName.java
index 0a91d49..9693ad9 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/OtherName.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/OtherName.java
@@ -69,7 +69,7 @@
     private OtherName(ASN1Sequence seq)
     {
         this.typeID = ASN1ObjectIdentifier.getInstance(seq.getObjectAt(0));
-        this.value = ASN1TaggedObject.getInstance(seq.getObjectAt(1)).getObject(); // explicitly tagged
+        this.value = ASN1TaggedObject.getInstance(seq.getObjectAt(1)).getExplicitBaseObject();
     }
 
     public ASN1ObjectIdentifier getTypeID()
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/PKIXNameConstraintValidator.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/PKIXNameConstraintValidator.java
index ed2ded2..b7c29cf 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/PKIXNameConstraintValidator.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/PKIXNameConstraintValidator.java
@@ -10,9 +10,9 @@
 import java.util.Map;
 import java.util.Set;
 
+import com.android.org.bouncycastle.asn1.ASN1IA5String;
 import com.android.org.bouncycastle.asn1.ASN1OctetString;
 import com.android.org.bouncycastle.asn1.ASN1Sequence;
-import com.android.org.bouncycastle.asn1.DERIA5String;
 import com.android.org.bouncycastle.asn1.x500.RDN;
 import com.android.org.bouncycastle.asn1.x500.X500Name;
 import com.android.org.bouncycastle.asn1.x500.style.IETFUtils;
@@ -1826,22 +1826,22 @@
     private static String extractHostFromURL(String url)
     {
         // see RFC 1738
-        // remove ':' after protocol, e.g. http:
+        // remove ':' after protocol, e.g. https:
         String sub = url.substring(url.indexOf(':') + 1);
-        // extract host from Common Internet Scheme Syntax, e.g. http://
+        // extract host from Common Internet Scheme Syntax, e.g. https://
         if (sub.indexOf("//") != -1)
         {
             sub = sub.substring(sub.indexOf("//") + 2);
         }
-        // first remove port, e.g. http://test.com:21
+        // first remove port, e.g. https://test.com:21
         if (sub.lastIndexOf(':') != -1)
         {
             sub = sub.substring(0, sub.lastIndexOf(':'));
         }
-        // remove user and password, e.g. http://john:[email protected]
+        // remove user and password, e.g. https://john:[email protected]
         sub = sub.substring(sub.indexOf(':') + 1);
         sub = sub.substring(sub.indexOf('@') + 1);
-        // remove local parts, e.g. http://test.com/bla
+        // remove local parts, e.g. https://test.com/bla
         if (sub.indexOf('/') != -1)
         {
             sub = sub.substring(0, sub.indexOf('/'));
@@ -1851,7 +1851,7 @@
 
     private String extractNameAsString(GeneralName name)
     {
-        return DERIA5String.getInstance(name.getName()).getString();
+        return ASN1IA5String.getInstance(name.getName()).getString();
     }
 
     /**
@@ -2080,6 +2080,7 @@
             temp.append(":");
             try
             {
+                // -DM Hex.toHexString
                 temp.append(Hex.toHexString(name.getValue().toASN1Primitive().getEncoded()));
             }
             catch (IOException e)
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/PolicyQualifierInfo.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/PolicyQualifierInfo.java
index 933cc5f..de3ff86 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/PolicyQualifierInfo.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/PolicyQualifierInfo.java
@@ -116,4 +116,9 @@
 
       return new DERSequence(dev);
    }
+
+    public String toString()
+    {
+        return "PolicyQualifierInfo[" + policyQualifierId + ", " + qualifier + "]";
+    }
 }
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/ReasonFlags.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/ReasonFlags.java
index 79d22f6..ad7b1ef 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/ReasonFlags.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/ReasonFlags.java
@@ -1,6 +1,7 @@
 /* GENERATED SOURCE. DO NOT MODIFY. */
 package com.android.org.bouncycastle.asn1.x509;
 
+import com.android.org.bouncycastle.asn1.ASN1BitString;
 import com.android.org.bouncycastle.asn1.DERBitString;
 
 /**
@@ -80,7 +81,7 @@
     }
 
     public ReasonFlags(
-        DERBitString reasons)
+        ASN1BitString reasons)
     {
         super(reasons.getBytes(), reasons.getPadBits());
     }
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/SubjectAltPublicKeyInfo.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/SubjectAltPublicKeyInfo.java
new file mode 100644
index 0000000..d473897
--- /dev/null
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/SubjectAltPublicKeyInfo.java
@@ -0,0 +1,110 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.org.bouncycastle.asn1.x509;
+
+import com.android.org.bouncycastle.asn1.ASN1BitString;
+import com.android.org.bouncycastle.asn1.ASN1EncodableVector;
+import com.android.org.bouncycastle.asn1.ASN1Object;
+import com.android.org.bouncycastle.asn1.ASN1Primitive;
+import com.android.org.bouncycastle.asn1.ASN1Sequence;
+import com.android.org.bouncycastle.asn1.ASN1TaggedObject;
+import com.android.org.bouncycastle.asn1.DERSequence;
+
+/**
+ * X.509 Section 9.8.2.
+ * <br/>
+ * This public-key certificate extension, when present, shall contain the subject’s alternative public key information
+ * <pre>
+ * subjectAltPublicKeyInfo EXTENSION ::= {
+ *      SYNTAX SubjectAltPublicKeyInfo
+ *      IDENTIFIED BY id-ce-subjectAltPublicKeyInfo }
+ *
+ * SubjectAltPublicKeyInfo ::= SEQUENCE {
+ *     algorithm AlgorithmIdentifier{{SupportedAlgorithms}},
+ *     subjectAltPublicKey BIT STRING }
+ * </pre>
+ * The SubjectAltPublicKeyInfo data type has the following components:
+ * <ul>
+ * <li>the algorithm subcomponent, which shall hold the algorithm that this public key is an instance of</li>
+ * <li>the subjectAltPublicKey subcomponent, which shall hold the alternative public key</li>
+ * </ul>
+ * This extension may be flagged as critical or as non-critical.
+ * <br/>
+ * NOTE – It is recommended that it be flagged as non-critical. Flagging it as critical would require relying parties to understand this
+ * extension and the alternative public-key algorithm.
+ * @hide This class is not part of the Android public SDK API
+ */
+public class SubjectAltPublicKeyInfo
+    extends ASN1Object
+{
+    private AlgorithmIdentifier algorithm;
+    private ASN1BitString subjectAltPublicKey;
+
+    public static SubjectAltPublicKeyInfo getInstance(
+        ASN1TaggedObject obj,
+        boolean          explicit)
+    {
+        return getInstance(ASN1Sequence.getInstance(obj, explicit));
+    }
+
+    public static SubjectAltPublicKeyInfo getInstance(
+        Object obj)
+    {
+        if (obj instanceof SubjectAltPublicKeyInfo)
+        {
+            return (SubjectAltPublicKeyInfo)obj;
+        }
+        else if (obj != null)
+        {
+            return new SubjectAltPublicKeyInfo(ASN1Sequence.getInstance(obj));
+        }
+
+        return null;
+    }
+
+    public static SubjectAltPublicKeyInfo fromExtensions(Extensions extensions)
+    {
+        return getInstance(Extensions.getExtensionParsedValue(extensions, Extension.subjectAltPublicKeyInfo));
+    }
+
+    private SubjectAltPublicKeyInfo(ASN1Sequence s)
+    {
+        if (s.size() != 2)
+        {
+            throw new IllegalArgumentException("extension should contain only 2 elements");
+        }
+        algorithm = AlgorithmIdentifier.getInstance(s.getObjectAt(0));
+        subjectAltPublicKey = ASN1BitString.getInstance(s.getObjectAt(1));
+    }
+
+    public SubjectAltPublicKeyInfo(AlgorithmIdentifier algorithm, ASN1BitString subjectAltPublicKey)
+    {
+        this.algorithm = algorithm;
+        this.subjectAltPublicKey = subjectAltPublicKey;
+    }
+
+    public SubjectAltPublicKeyInfo(SubjectPublicKeyInfo subjectPublicKeyInfo)
+    {
+        this.algorithm = subjectPublicKeyInfo.getAlgorithm();
+        this.subjectAltPublicKey = subjectPublicKeyInfo.getPublicKeyData();
+    }
+
+    public AlgorithmIdentifier getAlgorithm()
+    {
+        return algorithm;
+    }
+
+    public ASN1BitString getSubjectAltPublicKey()
+    {
+        return subjectAltPublicKey;
+    }
+
+    public ASN1Primitive toASN1Primitive()
+    {
+        ASN1EncodableVector v = new ASN1EncodableVector();
+
+        v.add(algorithm);
+        v.add(subjectAltPublicKey);
+
+        return new DERSequence(v);
+    }
+}
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/SubjectPublicKeyInfo.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/SubjectPublicKeyInfo.java
index 66f0369..99aad0b 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/SubjectPublicKeyInfo.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/SubjectPublicKeyInfo.java
@@ -4,6 +4,7 @@
 import java.io.IOException;
 import java.util.Enumeration;
 
+import com.android.org.bouncycastle.asn1.ASN1BitString;
 import com.android.org.bouncycastle.asn1.ASN1Encodable;
 import com.android.org.bouncycastle.asn1.ASN1EncodableVector;
 import com.android.org.bouncycastle.asn1.ASN1Object;
@@ -24,7 +25,7 @@
     extends ASN1Object
 {
     private AlgorithmIdentifier     algId;
-    private DERBitString            keyData;
+    private ASN1BitString           keyData;
 
     public static SubjectPublicKeyInfo getInstance(
         ASN1TaggedObject obj,
@@ -51,6 +52,14 @@
 
     public SubjectPublicKeyInfo(
         AlgorithmIdentifier algId,
+        ASN1BitString publicKey)
+    {
+        this.keyData = publicKey;
+        this.algId = algId;
+    }
+
+    public SubjectPublicKeyInfo(
+        AlgorithmIdentifier algId,
         ASN1Encodable       publicKey)
         throws IOException
     {
@@ -78,10 +87,10 @@
                     + seq.size());
         }
 
-        Enumeration         e = seq.getObjects();
+        Enumeration e = seq.getObjects();
 
         this.algId = AlgorithmIdentifier.getInstance(e.nextElement());
-        this.keyData = DERBitString.getInstance(e.nextElement());
+        this.keyData = ASN1BitString.getInstance(e.nextElement());
     }
 
     public AlgorithmIdentifier getAlgorithm()
@@ -132,7 +141,7 @@
      *
      * @return the public key as the raw bit string...
      */
-    public DERBitString getPublicKeyData()
+    public ASN1BitString getPublicKeyData()
     {
         return keyData;
     }
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/TBSCertList.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/TBSCertList.java
index c25f87a..dd701a5 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/TBSCertList.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/TBSCertList.java
@@ -107,7 +107,7 @@
         }
     }
 
-    private class RevokedCertificatesEnumeration
+    private static class RevokedCertificatesEnumeration
         implements Enumeration
     {
         private final Enumeration en;
@@ -128,7 +128,7 @@
         }
     }
 
-    private class EmptyEnumeration
+    private static class EmptyEnumeration
         implements Enumeration
     {
         public boolean hasMoreElements()
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/TBSCertificate.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/TBSCertificate.java
index efce6aa..5545c0c 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/TBSCertificate.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/TBSCertificate.java
@@ -1,19 +1,16 @@
 /* GENERATED SOURCE. DO NOT MODIFY. */
 package com.android.org.bouncycastle.asn1.x509;
 
-import java.math.BigInteger;
-
+import com.android.org.bouncycastle.asn1.ASN1BitString;
 import com.android.org.bouncycastle.asn1.ASN1EncodableVector;
 import com.android.org.bouncycastle.asn1.ASN1Integer;
 import com.android.org.bouncycastle.asn1.ASN1Object;
 import com.android.org.bouncycastle.asn1.ASN1Primitive;
 import com.android.org.bouncycastle.asn1.ASN1Sequence;
 import com.android.org.bouncycastle.asn1.ASN1TaggedObject;
-import com.android.org.bouncycastle.asn1.DERBitString;
 import com.android.org.bouncycastle.asn1.DERSequence;
 import com.android.org.bouncycastle.asn1.DERTaggedObject;
 import com.android.org.bouncycastle.asn1.x500.X500Name;
-import com.android.org.bouncycastle.util.BigIntegers;
 import com.android.org.bouncycastle.util.Properties;
 
 /**
@@ -49,8 +46,8 @@
     Time                    startDate, endDate;
     X500Name                subject;
     SubjectPublicKeyInfo    subjectPublicKeyInfo;
-    DERBitString            issuerUniqueId;
-    DERBitString            subjectUniqueId;
+    ASN1BitString           issuerUniqueId;
+    ASN1BitString           subjectUniqueId;
     Extensions              extensions;
 
     public static TBSCertificate getInstance(
@@ -98,15 +95,15 @@
         boolean isV1 = false;
         boolean isV2 = false;
  
-        if (version.hasValue(BigInteger.valueOf(0)))
+        if (version.hasValue(0))
         {
             isV1 = true;
         }
-        else if (version.hasValue(BigInteger.valueOf(1)))
+        else if (version.hasValue(1))
         {
             isV2 = true;
         }
-        else if (!version.hasValue(BigInteger.valueOf(2)))
+        else if (!version.hasValue(2))
         {
             throw new IllegalArgumentException("version number not recognised");
         }
@@ -144,10 +141,10 @@
             switch (extra.getTagNo())
             {
             case 1:
-                issuerUniqueId = DERBitString.getInstance(extra, false);
+                issuerUniqueId = ASN1BitString.getInstance(extra, false);
                 break;
             case 2:
-                subjectUniqueId = DERBitString.getInstance(extra, false);
+                subjectUniqueId = ASN1BitString.getInstance(extra, false);
                 break;
             case 3:
                 if (isV2)
@@ -208,12 +205,12 @@
         return subjectPublicKeyInfo;
     }
 
-    public DERBitString getIssuerUniqueId()
+    public ASN1BitString getIssuerUniqueId()
     {
         return issuerUniqueId;
     }
 
-    public DERBitString getSubjectUniqueId()
+    public ASN1BitString getSubjectUniqueId()
     {
         return subjectUniqueId;
     }
@@ -240,7 +237,7 @@
         ASN1EncodableVector v = new ASN1EncodableVector();
 
         // DEFAULT Zero
-        if (!version.hasValue(BigIntegers.ZERO))
+        if (!version.hasValue(0))
         {
             v.add(new DERTaggedObject(true, 0, version));
         }
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/TBSCertificateStructure.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/TBSCertificateStructure.java
index fc5aa92..9fef389 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/TBSCertificateStructure.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/TBSCertificateStructure.java
@@ -1,12 +1,12 @@
 /* GENERATED SOURCE. DO NOT MODIFY. */
 package com.android.org.bouncycastle.asn1.x509;
 
+import com.android.org.bouncycastle.asn1.ASN1BitString;
 import com.android.org.bouncycastle.asn1.ASN1Integer;
 import com.android.org.bouncycastle.asn1.ASN1Object;
 import com.android.org.bouncycastle.asn1.ASN1Primitive;
 import com.android.org.bouncycastle.asn1.ASN1Sequence;
 import com.android.org.bouncycastle.asn1.ASN1TaggedObject;
-import com.android.org.bouncycastle.asn1.DERBitString;
 import com.android.org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
 import com.android.org.bouncycastle.asn1.x500.X500Name;
 
@@ -45,8 +45,8 @@
     Time                    startDate, endDate;
     X500Name                subject;
     SubjectPublicKeyInfo    subjectPublicKeyInfo;
-    DERBitString            issuerUniqueId;
-    DERBitString            subjectUniqueId;
+    ASN1BitString           issuerUniqueId;
+    ASN1BitString           subjectUniqueId;
     X509Extensions          extensions;
 
     public static TBSCertificateStructure getInstance(
@@ -118,10 +118,10 @@
             switch (extra.getTagNo())
             {
             case 1:
-                issuerUniqueId = DERBitString.getInstance(extra, false);
+                issuerUniqueId = ASN1BitString.getInstance(extra, false);
                 break;
             case 2:
-                subjectUniqueId = DERBitString.getInstance(extra, false);
+                subjectUniqueId = ASN1BitString.getInstance(extra, false);
                 break;
             case 3:
                 extensions = X509Extensions.getInstance(extra);
@@ -174,12 +174,12 @@
         return subjectPublicKeyInfo;
     }
 
-    public DERBitString getIssuerUniqueId()
+    public ASN1BitString getIssuerUniqueId()
     {
         return issuerUniqueId;
     }
 
-    public DERBitString getSubjectUniqueId()
+    public ASN1BitString getSubjectUniqueId()
     {
         return subjectUniqueId;
     }
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/Time.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/Time.java
index 2b3a122..c556273 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/Time.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/Time.java
@@ -17,6 +17,7 @@
 import com.android.org.bouncycastle.asn1.ASN1UTCTime;
 import com.android.org.bouncycastle.asn1.DERGeneralizedTime;
 import com.android.org.bouncycastle.asn1.DERUTCTime;
+import com.android.org.bouncycastle.asn1.LocaleUtil;
 
 /**
  * @hide This class is not part of the Android public SDK API
@@ -31,7 +32,12 @@
         ASN1TaggedObject obj,
         boolean          explicit)
     {
-        return getInstance(obj.getObject()); // must be explicitly tagged
+        if (!explicit)
+        {
+            throw new IllegalArgumentException("choice item must be explicitly tagged");
+        }
+
+        return getInstance(obj.getExplicitBaseObject());
     }
 
     public Time(
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/V2Form.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/V2Form.java
index cab4fb4..1f7af22 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/V2Form.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/V2Form.java
@@ -71,10 +71,7 @@
         this.objectDigestInfo = objectDigestInfo;
     }
 
-    /**
-     * @deprecated use getInstance().
-     */
-    public V2Form(
+    private V2Form(
         ASN1Sequence seq)
     {
         if (seq.size() > 3)
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/V3TBSCertificateGenerator.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/V3TBSCertificateGenerator.java
index 1c7d69a..576c06f 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/V3TBSCertificateGenerator.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/V3TBSCertificateGenerator.java
@@ -3,6 +3,8 @@
 
 import com.android.org.bouncycastle.asn1.ASN1EncodableVector;
 import com.android.org.bouncycastle.asn1.ASN1Integer;
+import com.android.org.bouncycastle.asn1.ASN1Object;
+import com.android.org.bouncycastle.asn1.ASN1Sequence;
 import com.android.org.bouncycastle.asn1.ASN1UTCTime;
 import com.android.org.bouncycastle.asn1.DERBitString;
 import com.android.org.bouncycastle.asn1.DERSequence;
@@ -165,21 +167,34 @@
         }
     }
 
-    @android.compat.annotation.UnsupportedAppUsage
-    public TBSCertificate generateTBSCertificate()
+    public ASN1Sequence generatePreTBSCertificate()
     {
-        if ((serialNumber == null) || (signature == null)
+        if (signature != null)
+        {
+            throw new IllegalStateException("signature field should not be set in PreTBSCertificate");
+        }
+        if ((serialNumber == null)
             || (issuer == null) || (startDate == null) || (endDate == null)
             || (subject == null && !altNamePresentAndCritical) || (subjectPublicKeyInfo == null))
         {
             throw new IllegalStateException("not all mandatory fields set in V3 TBScertificate generator");
         }
 
+        return generateTBSStructure();
+    }
+
+    private ASN1Sequence generateTBSStructure()
+    {
         ASN1EncodableVector v = new ASN1EncodableVector(10);
 
         v.add(version);
         v.add(serialNumber);
-        v.add(signature);
+
+        if (signature != null)
+        {
+            v.add(signature);
+        }
+        
         v.add(issuer);
 
         //
@@ -219,6 +234,19 @@
             v.add(new DERTaggedObject(true, 3, extensions));
         }
 
-        return TBSCertificate.getInstance(new DERSequence(v));
+        return new DERSequence(v);
+    }
+
+    @android.compat.annotation.UnsupportedAppUsage
+    public TBSCertificate generateTBSCertificate()
+    {
+        if ((serialNumber == null) || (signature == null)
+            || (issuer == null) || (startDate == null) || (endDate == null)
+            || (subject == null && !altNamePresentAndCritical) || (subjectPublicKeyInfo == null))
+        {
+            throw new IllegalStateException("not all mandatory fields set in V3 TBScertificate generator");
+        }
+
+        return TBSCertificate.getInstance(generateTBSStructure());
     }
 }
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/X509CertificateStructure.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/X509CertificateStructure.java
index 658d6cc..04b559b 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/X509CertificateStructure.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/X509CertificateStructure.java
@@ -1,12 +1,12 @@
 /* GENERATED SOURCE. DO NOT MODIFY. */
 package com.android.org.bouncycastle.asn1.x509;
 
+import com.android.org.bouncycastle.asn1.ASN1BitString;
 import com.android.org.bouncycastle.asn1.ASN1Integer;
 import com.android.org.bouncycastle.asn1.ASN1Object;
 import com.android.org.bouncycastle.asn1.ASN1Primitive;
 import com.android.org.bouncycastle.asn1.ASN1Sequence;
 import com.android.org.bouncycastle.asn1.ASN1TaggedObject;
-import com.android.org.bouncycastle.asn1.DERBitString;
 import com.android.org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
 import com.android.org.bouncycastle.asn1.x500.X500Name;
 
@@ -29,7 +29,7 @@
     ASN1Sequence  seq;
     TBSCertificateStructure tbsCert;
     AlgorithmIdentifier     sigAlgId;
-    DERBitString            sig;
+    ASN1BitString sig;
 
     public static X509CertificateStructure getInstance(
         ASN1TaggedObject obj,
@@ -66,7 +66,7 @@
             tbsCert = TBSCertificateStructure.getInstance(seq.getObjectAt(0));
             sigAlgId = AlgorithmIdentifier.getInstance(seq.getObjectAt(1));
 
-            sig = DERBitString.getInstance(seq.getObjectAt(2));
+            sig = ASN1BitString.getInstance(seq.getObjectAt(2));
         }
         else
         {
@@ -119,7 +119,7 @@
         return sigAlgId;
     }
 
-    public DERBitString getSignature()
+    public ASN1BitString getSignature()
     {
         return sig;
     }
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/X509DefaultEntryConverter.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/X509DefaultEntryConverter.java
index 3a0d261..a67b1c9 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/X509DefaultEntryConverter.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/X509DefaultEntryConverter.java
@@ -9,6 +9,7 @@
 import com.android.org.bouncycastle.asn1.DERIA5String;
 import com.android.org.bouncycastle.asn1.DERPrintableString;
 import com.android.org.bouncycastle.asn1.DERUTF8String;
+import com.android.org.bouncycastle.asn1.x500.style.BCStyle;
 
 /**
  * The default converter for X509 DN entries when going from their
@@ -47,16 +48,16 @@
             {
                 value = value.substring(1);
             }
-            if (oid.equals(X509Name.EmailAddress) || oid.equals(X509Name.DC))
+            if (oid.equals(BCStyle.EmailAddress) || oid.equals(BCStyle.DC))
             {
                 return new DERIA5String(value);
             }
-            else if (oid.equals(X509Name.DATE_OF_BIRTH))  // accept time string as well as # (for compatibility)
+            else if (oid.equals(BCStyle.DATE_OF_BIRTH))  // accept time string as well as # (for compatibility)
             {
                 return new DERGeneralizedTime(value);
             }
-            else if (oid.equals(X509Name.C) || oid.equals(X509Name.SN) || oid.equals(X509Name.DN_QUALIFIER)
-                || oid.equals(X509Name.TELEPHONE_NUMBER))
+            else if (oid.equals(BCStyle.C) || oid.equals(BCStyle.SERIALNUMBER) || oid.equals(BCStyle.DN_QUALIFIER)
+                || oid.equals(BCStyle.TELEPHONE_NUMBER))
             {
                  return new DERPrintableString(value);
             }
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/X509Extensions.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/X509Extensions.java
index f93dc33..a45feee 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/X509Extensions.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/X509Extensions.java
@@ -13,10 +13,11 @@
 import com.android.org.bouncycastle.asn1.ASN1Primitive;
 import com.android.org.bouncycastle.asn1.ASN1Sequence;
 import com.android.org.bouncycastle.asn1.ASN1TaggedObject;
+import com.android.org.bouncycastle.asn1.BERTags;
 import com.android.org.bouncycastle.asn1.DERSequence;
 
 /**
- * @deprecated use {@link Extensions}
+ * @deprecated use {@link Extension} and  {@link Extensions}
  * @hide This class is not part of the Android public SDK API
  */
 public class X509Extensions
@@ -238,7 +239,9 @@
 
         if (obj instanceof ASN1TaggedObject)
         {
-            return getInstance(((ASN1TaggedObject)obj).getObject());
+            ASN1TaggedObject taggedObject = ASN1TaggedObject.getInstance(obj, BERTags.CONTEXT_SPECIFIC);
+
+            return getInstance(taggedObject.getBaseObject().toASN1Primitive());
         }
 
         throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/X509Name.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/X509Name.java
index 58fd9b7..fdc2dc4 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/X509Name.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/X509Name.java
@@ -16,9 +16,9 @@
 import com.android.org.bouncycastle.asn1.ASN1Set;
 import com.android.org.bouncycastle.asn1.ASN1String;
 import com.android.org.bouncycastle.asn1.ASN1TaggedObject;
+import com.android.org.bouncycastle.asn1.ASN1UniversalString;
 import com.android.org.bouncycastle.asn1.DERSequence;
 import com.android.org.bouncycastle.asn1.DERSet;
-import com.android.org.bouncycastle.asn1.DERUniversalString;
 import com.android.org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
 import com.android.org.bouncycastle.asn1.x500.X500Name;
 import com.android.org.bouncycastle.util.Strings;
@@ -429,7 +429,7 @@
                    ordering.addElement(ASN1ObjectIdentifier.getInstance(s.getObjectAt(0)));
                    
                    ASN1Encodable value = s.getObjectAt(1);
-                   if (value instanceof ASN1String && !(value instanceof DERUniversalString))
+                   if (value instanceof ASN1String && !(value instanceof ASN1UniversalString))
                    {
                        String v = ((ASN1String)value).getString();
                        if (v.length() > 0 && v.charAt(0) == '#')
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/X509NameEntryConverter.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/X509NameEntryConverter.java
index 2d48ac0..99ff23e 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/X509NameEntryConverter.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/X509NameEntryConverter.java
@@ -5,7 +5,7 @@
 
 import com.android.org.bouncycastle.asn1.ASN1ObjectIdentifier;
 import com.android.org.bouncycastle.asn1.ASN1Primitive;
-import com.android.org.bouncycastle.asn1.DERPrintableString;
+import com.android.org.bouncycastle.asn1.ASN1PrintableString;
 import com.android.org.bouncycastle.util.encoders.Hex;
 
 /**
@@ -73,7 +73,7 @@
     protected boolean canBePrintable(
         String  str)
     {
-        return DERPrintableString.isPrintableString(str);
+        return ASN1PrintableString.isPrintableString(str);
     }
     
     /**
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/X509ObjectIdentifiers.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/X509ObjectIdentifiers.java
index 45c4582..74ce730 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/X509ObjectIdentifiers.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x509/X509ObjectIdentifiers.java
@@ -111,4 +111,11 @@
     static final ASN1ObjectIdentifier ocspAccessMethod = id_ad_ocsp;
     /** OID for crl uri in AuthorityInformationAccess extension */
     static final ASN1ObjectIdentifier crlAccessMethod  = id_ad_caIssuers;
+
+
+    /**
+     *  id-PasswordBasedMac OBJECT IDENTIFIER ::= { iso(1) member-body(2)
+     *          us(840) nt(113533) nsn(7) algorithms(66) 13 }
+     */
+    static final ASN1ObjectIdentifier id_PasswordBasedMac = new ASN1ObjectIdentifier("1.2.840.113533.7.66.13");
 }
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x9/DHValidationParms.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x9/DHValidationParms.java
index 01bdf6e..8e72e3f 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x9/DHValidationParms.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x9/DHValidationParms.java
@@ -1,13 +1,13 @@
 /* GENERATED SOURCE. DO NOT MODIFY. */
 package com.android.org.bouncycastle.asn1.x9;
 
+import com.android.org.bouncycastle.asn1.ASN1BitString;
 import com.android.org.bouncycastle.asn1.ASN1EncodableVector;
 import com.android.org.bouncycastle.asn1.ASN1Integer;
 import com.android.org.bouncycastle.asn1.ASN1Object;
 import com.android.org.bouncycastle.asn1.ASN1Primitive;
 import com.android.org.bouncycastle.asn1.ASN1Sequence;
 import com.android.org.bouncycastle.asn1.ASN1TaggedObject;
-import com.android.org.bouncycastle.asn1.DERBitString;
 import com.android.org.bouncycastle.asn1.DERSequence;
 
 /**
@@ -16,7 +16,7 @@
  */
 public class DHValidationParms extends ASN1Object
 {
-    private DERBitString seed;
+    private ASN1BitString seed;
     private ASN1Integer pgenCounter;
 
     public static DHValidationParms getInstance(ASN1TaggedObject obj, boolean explicit)
@@ -38,7 +38,7 @@
         return null;
     }
 
-    public DHValidationParms(DERBitString seed, ASN1Integer pgenCounter)
+    public DHValidationParms(ASN1BitString seed, ASN1Integer pgenCounter)
     {
         if (seed == null)
         {
@@ -60,11 +60,11 @@
             throw new IllegalArgumentException("Bad sequence size: " + seq.size());
         }
 
-        this.seed = DERBitString.getInstance(seq.getObjectAt(0));
+        this.seed = ASN1BitString.getInstance(seq.getObjectAt(0));
         this.pgenCounter = ASN1Integer.getInstance(seq.getObjectAt(1));
     }
 
-    public DERBitString getSeed()
+    public ASN1BitString getSeed()
     {
         return this.seed;
     }
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x9/ECNamedCurveTable.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x9/ECNamedCurveTable.java
index 19018b1..5f351aa 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x9/ECNamedCurveTable.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x9/ECNamedCurveTable.java
@@ -71,6 +71,47 @@
         return ecP;
     }
 
+    public static X9ECParametersHolder getByNameLazy(String name)
+    {
+        X9ECParametersHolder holder = X962NamedCurves.getByNameLazy(name);
+
+        if (null == holder)
+        {
+            holder = SECNamedCurves.getByNameLazy(name);
+        }
+
+        if (null == holder)
+        {
+            holder = NISTNamedCurves.getByNameLazy(name);
+        }
+
+        // BEGIN Android-removed: Unsupported curves
+        /*
+        if (null == holder)
+        {
+            holder = TeleTrusTNamedCurves.getByNameLazy(name);
+        }
+
+        if (null == holder)
+        {
+            holder = ANSSINamedCurves.getByNameLazy(name);
+        }
+
+        if (null == holder)
+        {
+            holder = ECGOST3410NamedCurves.getByNameLazy(name);
+        }
+
+        if (null == holder)
+        {
+            holder = GMNamedCurves.getByNameLazy(name);
+        }
+        */
+        // END Android-removed: Unsupported curves
+
+        return holder;
+    }
+
     /**
      * return the object identifier signified by the passed in name. Null
      * if there is no object identifier associated with name.
@@ -224,6 +265,44 @@
         return ecP;
     }
 
+    public static X9ECParametersHolder getByOIDLazy(ASN1ObjectIdentifier oid)
+    {
+        X9ECParametersHolder holder = X962NamedCurves.getByOIDLazy(oid);
+
+        if (null == holder)
+        {
+            holder = SECNamedCurves.getByOIDLazy(oid);
+        }
+
+        // NOTE: All the NIST curves are currently from SEC, so no point in redundant OID lookup
+
+        // BEGIN Android-removed: Unsupported curves
+        /*
+        if (null == holder)
+        {
+            holder = TeleTrusTNamedCurves.getByOIDLazy(oid);
+        }
+
+        if (null == holder)
+        {
+            holder = ANSSINamedCurves.getByOIDLazy(oid);
+        }
+
+        if (null == holder)
+        {
+            holder = ECGOST3410NamedCurves.getByOIDLazy(oid);
+        }
+
+        if (null == holder)
+        {
+            holder = GMNamedCurves.getByOIDLazy(oid);
+        }
+        */
+        // END Android-removed: Unsupported curves
+
+        return holder;
+    }
+
     /**
      * return an enumeration of the names of the available curves.
      *
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x9/ValidationParams.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x9/ValidationParams.java
index deac4bc..0a03fce 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x9/ValidationParams.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x9/ValidationParams.java
@@ -3,6 +3,7 @@
 
 import java.math.BigInteger;
 
+import com.android.org.bouncycastle.asn1.ASN1BitString;
 import com.android.org.bouncycastle.asn1.ASN1EncodableVector;
 import com.android.org.bouncycastle.asn1.ASN1Integer;
 import com.android.org.bouncycastle.asn1.ASN1Object;
@@ -25,7 +26,7 @@
 public class ValidationParams
     extends ASN1Object
 {
-    private DERBitString seed;
+    private ASN1BitString seed;
     private ASN1Integer pgenCounter;
 
     public static ValidationParams getInstance(ASN1TaggedObject obj, boolean explicit)
@@ -80,7 +81,7 @@
             throw new IllegalArgumentException("Bad sequence size: " + seq.size());
         }
 
-        this.seed = DERBitString.getInstance(seq.getObjectAt(0));
+        this.seed = ASN1BitString.getInstance(seq.getObjectAt(0));
         this.pgenCounter = ASN1Integer.getInstance(seq.getObjectAt(1));
     }
 
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x9/X962NamedCurves.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x9/X962NamedCurves.java
index 70e96e2..d461d24 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x9/X962NamedCurves.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x9/X962NamedCurves.java
@@ -37,141 +37,183 @@
 
     static X9ECParametersHolder prime192v1 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             BigInteger n = fromHex("ffffffffffffffffffffffff99def836146bc9b1b4d22831");
             BigInteger h = BigInteger.valueOf(1);
 
-            ECCurve curve = configureCurve(new ECCurve.Fp(
+            return configureCurve(new ECCurve.Fp(
                 fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFF"),
                 fromHex("fffffffffffffffffffffffffffffffefffffffffffffffc"),
                 fromHex("64210519e59c80e70fa7e9ab72243049feb8deecc146b9b1"),
-                n, h));
+                n, h, true));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = Hex.decodeStrict("3045AE6FC8422f64ED579528D38120EAE12196D5");
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "03188da80eb03090f67cbf20eb43a18800f4ff0afd82ff1012");
 
-            return new X9ECParameters(curve, G, n, h, Hex.decodeStrict("3045AE6FC8422f64ED579528D38120EAE12196D5"));
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
     static X9ECParametersHolder prime192v2 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             BigInteger n = fromHex("fffffffffffffffffffffffe5fb1a724dc80418648d8dd31");
             BigInteger h = BigInteger.valueOf(1);
 
-            ECCurve curve = configureCurve(new ECCurve.Fp(
+            return configureCurve(new ECCurve.Fp(
                 fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFF"),
                 fromHex("fffffffffffffffffffffffffffffffefffffffffffffffc"),
                 fromHex("cc22d6dfb95c6b25e49c0d6364a4e5980c393aa21668d953"),
-                n, h));
+                n, h, true));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = Hex.decodeStrict("31a92ee2029fd10d901b113e990710f0d21ac6b6");
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "03eea2bae7e1497842f2de7769cfe9c989c072ad696f48034a");
 
-            return new X9ECParameters(curve, G, n, h, Hex.decodeStrict("31a92ee2029fd10d901b113e990710f0d21ac6b6"));
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
     static X9ECParametersHolder prime192v3 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             BigInteger n = fromHex("ffffffffffffffffffffffff7a62d031c83f4294f640ec13");
             BigInteger h = BigInteger.valueOf(1);
 
-            ECCurve curve = configureCurve(new ECCurve.Fp(
+            return configureCurve(new ECCurve.Fp(
                 fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFF"),
                 fromHex("fffffffffffffffffffffffffffffffefffffffffffffffc"),
                 fromHex("22123dc2395a05caa7423daeccc94760a7d462256bd56916"),
-                n, h));
+                n, h, true));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = Hex.decodeStrict("c469684435deb378c4b65ca9591e2a5763059a2e");
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "027d29778100c65a1da1783716588dce2b8b4aee8e228f1896");
 
-            return new X9ECParameters(curve, G, n, h, Hex.decodeStrict("c469684435deb378c4b65ca9591e2a5763059a2e"));
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
     static X9ECParametersHolder prime239v1 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             BigInteger n = fromHex("7fffffffffffffffffffffff7fffff9e5e9a9f5d9071fbd1522688909d0b");
             BigInteger h = BigInteger.valueOf(1);
 
-            ECCurve curve = configureCurve(new ECCurve.Fp(
+            return configureCurve(new ECCurve.Fp(
                 new BigInteger("883423532389192164791648750360308885314476597252960362792450860609699839"),
                 fromHex("7fffffffffffffffffffffff7fffffffffff8000000000007ffffffffffc"),
                 fromHex("6b016c3bdcf18941d0d654921475ca71a9db2fb27d1d37796185c2942c0a"),
-                n, h));
+                n, h, true));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = Hex.decodeStrict("e43bb460f0b80cc0c0b075798e948060f8321b7d");
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "020ffa963cdca8816ccc33b8642bedf905c3d358573d3f27fbbd3b3cb9aaaf");
 
-            return new X9ECParameters(curve, G, n, h, Hex.decodeStrict("e43bb460f0b80cc0c0b075798e948060f8321b7d"));
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
     static X9ECParametersHolder prime239v2 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             BigInteger n = fromHex("7fffffffffffffffffffffff800000cfa7e8594377d414c03821bc582063");
             BigInteger h = BigInteger.valueOf(1);
 
-            ECCurve curve = configureCurve(new ECCurve.Fp(
+            return configureCurve(new ECCurve.Fp(
                 new BigInteger("883423532389192164791648750360308885314476597252960362792450860609699839"),
                 fromHex("7fffffffffffffffffffffff7fffffffffff8000000000007ffffffffffc"),
                 fromHex("617fab6832576cbbfed50d99f0249c3fee58b94ba0038c7ae84c8c832f2c"),
-                n, h));
+                n, h, true));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = Hex.decodeStrict("e8b4011604095303ca3b8099982be09fcb9ae616");
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "0238af09d98727705120c921bb5e9e26296a3cdcf2f35757a0eafd87b830e7");
 
-            return new X9ECParameters(curve, G, n, h, Hex.decodeStrict("e8b4011604095303ca3b8099982be09fcb9ae616"));
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
     static X9ECParametersHolder prime239v3 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             BigInteger n = fromHex("7fffffffffffffffffffffff7fffff975deb41b3a6057c3c432146526551");
             BigInteger h = BigInteger.valueOf(1);
 
-            ECCurve curve = configureCurve(new ECCurve.Fp(
+            return configureCurve(new ECCurve.Fp(
                 new BigInteger("883423532389192164791648750360308885314476597252960362792450860609699839"),
                 fromHex("7fffffffffffffffffffffff7fffffffffff8000000000007ffffffffffc"),
                 fromHex("255705fa2a306654b1f4cb03d6a750a30c250102d4988717d9ba15ab6d3e"),
-                n, h));
+                n, h, true));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = Hex.decodeStrict("7d7374168ffe3471b60a857686a19475d3bfa2ff");
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "036768ae8e18bb92cfcf005c949aa2c6d94853d0e660bbf854b1c9505fe95a");
 
-            return new X9ECParameters(curve, G, n, h, Hex.decodeStrict("7d7374168ffe3471b60a857686a19475d3bfa2ff"));
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
     static X9ECParametersHolder prime256v1 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             BigInteger n = fromHex("ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551");
             BigInteger h = BigInteger.valueOf(1);
 
-            ECCurve curve = configureCurve(new ECCurve.Fp(
+            return configureCurve(new ECCurve.Fp(
                 new BigInteger("115792089210356248762697446949407573530086143415290314195533631308867097853951"),
                 fromHex("ffffffff00000001000000000000000000000000fffffffffffffffffffffffc"),
                 fromHex("5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b"),
-                n, h));
+                n, h, true));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = Hex.decodeStrict("c49d360886e704936a6678e1139d26b7819f7e90");
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "036b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296");
 
-            return new X9ECParameters(curve, G, n, h, Hex.decodeStrict("c49d360886e704936a6678e1139d26b7819f7e90"));
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
@@ -180,337 +222,433 @@
      */
     static X9ECParametersHolder c2pnb163v1 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             BigInteger n = fromHex("0400000000000000000001E60FC8821CC74DAEAFC1");
             BigInteger h = BigInteger.valueOf(2);
 
-            ECCurve curve = configureCurve(new ECCurve.F2m(
+            return configureCurve(new ECCurve.F2m(
                 163,
                 1, 2, 8,
                 fromHex("072546B5435234A422E0789675F432C89435DE5242"),
                 fromHex("00C9517D06D5240D3CFF38C74B20B6CD4D6F9DD4D9"),
                 n, h));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = Hex.decodeStrict("D2C0FB15760860DEF1EEF4D696E6768756151754");
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "0307AF69989546103D79329FCC3D74880F33BBE803CB");
 
-            return new X9ECParameters(curve, G, n, h, Hex.decodeStrict("D2C0FB15760860DEF1EEF4D696E6768756151754"));
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
     static X9ECParametersHolder c2pnb163v2 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             BigInteger n = fromHex("03FFFFFFFFFFFFFFFFFFFDF64DE1151ADBB78F10A7");
             BigInteger h = BigInteger.valueOf(2);
 
-            ECCurve curve = configureCurve(new ECCurve.F2m(
+            return configureCurve(new ECCurve.F2m(
                 163,
                 1, 2, 8,
                 fromHex("0108B39E77C4B108BED981ED0E890E117C511CF072"),
                 fromHex("0667ACEB38AF4E488C407433FFAE4F1C811638DF20"),
                 n, h));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = null;
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "030024266E4EB5106D0A964D92C4860E2671DB9B6CC5");
 
-            return new X9ECParameters(curve, G, n, h);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
     static X9ECParametersHolder c2pnb163v3 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             BigInteger n = fromHex("03FFFFFFFFFFFFFFFFFFFE1AEE140F110AFF961309");
             BigInteger h = BigInteger.valueOf(2);
 
-            ECCurve curve = configureCurve(new ECCurve.F2m(
+            return configureCurve(new ECCurve.F2m(
                 163,
                 1, 2, 8,
                 fromHex("07A526C63D3E25A256A007699F5447E32AE456B50E"),
                 fromHex("03F7061798EB99E238FD6F1BF95B48FEEB4854252B"),
                 n, h));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = null;
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "0202F9F87B7C574D0BDECF8A22E6524775F98CDEBDCB");
 
-            return new X9ECParameters(curve, G, n, h);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
     static X9ECParametersHolder c2pnb176w1 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             BigInteger n = fromHex("010092537397ECA4F6145799D62B0A19CE06FE26AD");
             BigInteger h = BigInteger.valueOf(0xFF6E);
 
-            ECCurve curve = configureCurve(new ECCurve.F2m(
+            return configureCurve(new ECCurve.F2m(
                 176,
                 1, 2, 43,
                 fromHex("E4E6DB2995065C407D9D39B8D0967B96704BA8E9C90B"),
                 fromHex("5DDA470ABE6414DE8EC133AE28E9BBD7FCEC0AE0FFF2"),
                 n, h));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = null;
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "038D16C2866798B600F9F08BB4A8E860F3298CE04A5798");
 
-            return new X9ECParameters(curve, G, n, h);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
     static X9ECParametersHolder c2tnb191v1 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             BigInteger n = fromHex("40000000000000000000000004A20E90C39067C893BBB9A5");
             BigInteger h = BigInteger.valueOf(2);
 
-            ECCurve curve = configureCurve(new ECCurve.F2m(
+            return configureCurve(new ECCurve.F2m(
                 191,
                 9,
                 fromHex("2866537B676752636A68F56554E12640276B649EF7526267"),
                 fromHex("2E45EF571F00786F67B0081B9495A3D95462F5DE0AA185EC"),
                 n, h));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = Hex.decodeStrict("4E13CA542744D696E67687561517552F279A8C84");
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "0236B3DAF8A23206F9C4F299D7B21A9C369137F2C84AE1AA0D");
 
-            return new X9ECParameters(curve, G, n, h, Hex.decodeStrict("4E13CA542744D696E67687561517552F279A8C84"));
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
     static X9ECParametersHolder c2tnb191v2 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             BigInteger n = fromHex("20000000000000000000000050508CB89F652824E06B8173");
             BigInteger h = BigInteger.valueOf(4);
 
-            ECCurve curve = configureCurve(new ECCurve.F2m(
+            return configureCurve(new ECCurve.F2m(
                 191,
                 9,
                 fromHex("401028774D7777C7B7666D1366EA432071274F89FF01E718"),
                 fromHex("0620048D28BCBD03B6249C99182B7C8CD19700C362C46A01"),
                 n, h));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = null;
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "023809B2B7CC1B28CC5A87926AAD83FD28789E81E2C9E3BF10");
 
-            return new X9ECParameters(curve, G, n, h);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
     static X9ECParametersHolder c2tnb191v3 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             BigInteger n = fromHex("155555555555555555555555610C0B196812BFB6288A3EA3");
             BigInteger h = BigInteger.valueOf(6);
 
-            ECCurve curve = configureCurve(new ECCurve.F2m(
+            return configureCurve(new ECCurve.F2m(
                 191,
                 9,
                 fromHex("6C01074756099122221056911C77D77E77A777E7E7E77FCB"),
                 fromHex("71FE1AF926CF847989EFEF8DB459F66394D90F32AD3F15E8"),
                 n, h));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = null;
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "03375D4CE24FDE434489DE8746E71786015009E66E38A926DD");
 
-            return new X9ECParameters(curve, G, n, h);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
     static X9ECParametersHolder c2pnb208w1 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             BigInteger n = fromHex("0101BAF95C9723C57B6C21DA2EFF2D5ED588BDD5717E212F9D");
             BigInteger h = BigInteger.valueOf(0xFE48);
 
-            ECCurve curve = configureCurve(new ECCurve.F2m(
+            return configureCurve(new ECCurve.F2m(
                 208,
                 1, 2, 83,
                 BigInteger.valueOf(0),
                 fromHex("C8619ED45A62E6212E1160349E2BFA844439FAFC2A3FD1638F9E"),
                 n, h));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = null;
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "0289FDFBE4ABE193DF9559ECF07AC0CE78554E2784EB8C1ED1A57A");
 
-            return new X9ECParameters(curve, G, n, h);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
     static X9ECParametersHolder c2tnb239v1 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             BigInteger n = fromHex("2000000000000000000000000000000F4D42FFE1492A4993F1CAD666E447");
             BigInteger h = BigInteger.valueOf(4);
 
-            ECCurve curve = configureCurve(new ECCurve.F2m(
+            return configureCurve(new ECCurve.F2m(
                 239,
                 36,
                 fromHex("32010857077C5431123A46B808906756F543423E8D27877578125778AC76"),
                 fromHex("790408F2EEDAF392B012EDEFB3392F30F4327C0CA3F31FC383C422AA8C16"),
                 n, h));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = null;
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "0257927098FA932E7C0A96D3FD5B706EF7E5F5C156E16B7E7C86038552E91D");
 
-            return new X9ECParameters(curve, G, n, h);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
     static X9ECParametersHolder c2tnb239v2 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             BigInteger n = fromHex("1555555555555555555555555555553C6F2885259C31E3FCDF154624522D");
             BigInteger h = BigInteger.valueOf(6);
 
-            ECCurve curve = configureCurve(new ECCurve.F2m(
+            return configureCurve(new ECCurve.F2m(
                 239,
                 36,
                 fromHex("4230017757A767FAE42398569B746325D45313AF0766266479B75654E65F"),
                 fromHex("5037EA654196CFF0CD82B2C14A2FCF2E3FF8775285B545722F03EACDB74B"),
                 n, h));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = null;
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "0228F9D04E900069C8DC47A08534FE76D2B900B7D7EF31F5709F200C4CA205");
 
-            return new X9ECParameters(curve, G, n, h);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
     static X9ECParametersHolder c2tnb239v3 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             BigInteger n = fromHex("0CCCCCCCCCCCCCCCCCCCCCCCCCCCCCAC4912D2D9DF903EF9888B8A0E4CFF");
             BigInteger h = BigInteger.valueOf(10);
 
-            ECCurve curve = configureCurve(new ECCurve.F2m(
+            return configureCurve(new ECCurve.F2m(
                 239,
                 36,
                 fromHex("01238774666A67766D6676F778E676B66999176666E687666D8766C66A9F"),
                 fromHex("6A941977BA9F6A435199ACFC51067ED587F519C5ECB541B8E44111DE1D40"),
                 n, h));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = null;
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "0370F6E9D04D289C4E89913CE3530BFDE903977D42B146D539BF1BDE4E9C92");
 
-            return new X9ECParameters(curve, G, n, h);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
     static X9ECParametersHolder c2pnb272w1 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             BigInteger n = fromHex("0100FAF51354E0E39E4892DF6E319C72C8161603FA45AA7B998A167B8F1E629521");
             BigInteger h = BigInteger.valueOf(0xFF06);
 
-            ECCurve curve = configureCurve(new ECCurve.F2m(
+            return configureCurve(new ECCurve.F2m(
                 272,
                 1, 3, 56,
                 fromHex("91A091F03B5FBA4AB2CCF49C4EDD220FB028712D42BE752B2C40094DBACDB586FB20"),
                 fromHex("7167EFC92BB2E3CE7C8AAAFF34E12A9C557003D7C73A6FAF003F99F6CC8482E540F7"),
                 n, h));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = null;
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "026108BABB2CEEBCF787058A056CBE0CFE622D7723A289E08A07AE13EF0D10D171DD8D");
 
-            return new X9ECParameters(curve, G, n, h);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
     static X9ECParametersHolder c2pnb304w1 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             BigInteger n = fromHex("0101D556572AABAC800101D556572AABAC8001022D5C91DD173F8FB561DA6899164443051D");
             BigInteger h = BigInteger.valueOf(0xFE2E);
 
-            ECCurve curve = configureCurve(new ECCurve.F2m(
+            return configureCurve(new ECCurve.F2m(
                 304,
                 1, 2, 11,
                 fromHex("FD0D693149A118F651E6DCE6802085377E5F882D1B510B44160074C1288078365A0396C8E681"),
                 fromHex("BDDB97E555A50A908E43B01C798EA5DAA6788F1EA2794EFCF57166B8C14039601E55827340BE"),
                 n, h));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = null;
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "02197B07845E9BE2D96ADB0F5F3C7F2CFFBD7A3EB8B6FEC35C7FD67F26DDF6285A644F740A2614");
 
-            return new X9ECParameters(curve, G, n, h);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
     static X9ECParametersHolder c2tnb359v1 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             BigInteger n = fromHex("01AF286BCA1AF286BCA1AF286BCA1AF286BCA1AF286BC9FB8F6B85C556892C20A7EB964FE7719E74F490758D3B");
             BigInteger h = BigInteger.valueOf(0x4C);
 
-            ECCurve curve = configureCurve(new ECCurve.F2m(
+            return configureCurve(new ECCurve.F2m(
                 359,
                 68,
                 fromHex("5667676A654B20754F356EA92017D946567C46675556F19556A04616B567D223A5E05656FB549016A96656A557"),
                 fromHex("2472E2D0197C49363F1FE7F5B6DB075D52B6947D135D8CA445805D39BC345626089687742B6329E70680231988"),
                 n, h));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = null;
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "033C258EF3047767E7EDE0F1FDAA79DAEE3841366A132E163ACED4ED2401DF9C6BDCDE98E8E707C07A2239B1B097");
 
-            return new X9ECParameters(curve, G, n, h);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
     static X9ECParametersHolder c2pnb368w1 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             BigInteger n = fromHex("010090512DA9AF72B08349D98A5DD4C7B0532ECA51CE03E2D10F3B7AC579BD87E909AE40A6F131E9CFCE5BD967");
             BigInteger h = BigInteger.valueOf(0xFF70);
 
-            ECCurve curve = configureCurve(new ECCurve.F2m(
+            return configureCurve(new ECCurve.F2m(
                 368,
                 1, 2, 85,
                 fromHex("E0D2EE25095206F5E2A4F9ED229F1F256E79A0E2B455970D8D0D865BD94778C576D62F0AB7519CCD2A1A906AE30D"),
                 fromHex("FC1217D4320A90452C760A58EDCD30C8DD069B3C34453837A34ED50CB54917E1C2112D84D164F444F8F74786046A"),
                 n, h));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = null;
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "021085E2755381DCCCE3C1557AFA10C2F0C0C2825646C5B34A394CBCFA8BC16B22E7E789E927BE216F02E1FB136A5F");
 
-            return new X9ECParameters(curve, G, n, h);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
     static X9ECParametersHolder c2tnb431r1 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             BigInteger n = fromHex("0340340340340340340340340340340340340340340340340340340323C313FAB50589703B5EC68D3587FEC60D161CC149C1AD4A91");
             BigInteger h = BigInteger.valueOf(0x2760);
 
-            ECCurve curve = configureCurve(new ECCurve.F2m(
+            return configureCurve(new ECCurve.F2m(
                 431,
                 120,
                 fromHex("1A827EF00DD6FC0E234CAF046C6A5D8A85395B236CC4AD2CF32A0CADBDC9DDF620B0EB9906D0957F6C6FEACD615468DF104DE296CD8F"),
                 fromHex("10D9B4A3D9047D8B154359ABFB1B7F5485B04CEB868237DDC9DEDA982A679A5A919B626D4E50A8DD731B107A9962381FB5D807BF2618"),
                 n, h));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = null;
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "02120FC05D3C67A99DE161D2F4092622FECA701BE4F50F4758714E8A87BBF2A658EF8C21E7C5EFE965361F6C2999C0C247B0DBD70CE6B7");
 
-            return new X9ECParameters(curve, G, n, h);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
@@ -553,17 +691,16 @@
         defineCurve("c2tnb431r1", X9ObjectIdentifiers.c2tnb431r1, c2tnb431r1);
     }
 
-    public static X9ECParameters getByName(
-        String name)
+    public static X9ECParameters getByName(String name)
     {
-        ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier)objIds.get(Strings.toLowerCase(name));
+        ASN1ObjectIdentifier oid = getOID(name);
+        return oid == null ? null : getByOID(oid);
+    }
 
-        if (oid != null)
-        {
-            return getByOID(oid);
-        }
-
-        return null;
+    public static X9ECParametersHolder getByNameLazy(String name)
+    {
+        ASN1ObjectIdentifier oid = getOID(name);
+        return oid == null ? null : getByOIDLazy(oid);
     }
 
     /**
@@ -572,17 +709,15 @@
      *
      * @param oid an object identifier representing a named curve, if present.
      */
-    public static X9ECParameters getByOID(
-        ASN1ObjectIdentifier oid)
+    public static X9ECParameters getByOID(ASN1ObjectIdentifier oid)
     {
-        X9ECParametersHolder holder = (X9ECParametersHolder)curves.get(oid);
+        X9ECParametersHolder holder = getByOIDLazy(oid);
+        return holder == null ? null : holder.getParameters();
+    }
 
-        if (holder != null)
-        {
-            return holder.getParameters();
-        }
-
-        return null;
+    public static X9ECParametersHolder getByOIDLazy(ASN1ObjectIdentifier oid)
+    {
+        return (X9ECParametersHolder)curves.get(oid);
     }
 
     /**
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x9/X962Parameters.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x9/X962Parameters.java
index fac3bdd..579c562 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x9/X962Parameters.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x9/X962Parameters.java
@@ -50,7 +50,12 @@
         ASN1TaggedObject obj,
         boolean          explicit)
     {
-        return getInstance(obj.getObject()); // must be explicitly tagged
+        if (!explicit)
+        {
+            throw new IllegalArgumentException("choice item must be explicitly tagged");
+        }
+
+        return getInstance(obj.getExplicitBaseObject());
     }
     
     public X962Parameters(
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x9/X9ECParameters.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x9/X9ECParameters.java
index d2a48d1..9fe2d7d 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x9/X9ECParameters.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x9/X9ECParameters.java
@@ -38,7 +38,7 @@
         ASN1Sequence  seq)
     {
         if (!(seq.getObjectAt(0) instanceof ASN1Integer)
-            || !((ASN1Integer)seq.getObjectAt(0)).hasValue(ONE))
+            || !((ASN1Integer)seq.getObjectAt(0)).hasValue(1))
         {
             throw new IllegalArgumentException("bad version in X9ECParameters");
         }
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x9/X9ECParametersHolder.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x9/X9ECParametersHolder.java
index ddc5700..2582d93 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x9/X9ECParametersHolder.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x9/X9ECParametersHolder.java
@@ -1,14 +1,27 @@
 /* GENERATED SOURCE. DO NOT MODIFY. */
 package com.android.org.bouncycastle.asn1.x9;
 
+import com.android.org.bouncycastle.math.ec.ECCurve;
+
 /**
  * A holding class that allows for X9ECParameters to be lazily constructed.
  * @hide This class is not part of the Android public SDK API
  */
 public abstract class X9ECParametersHolder
 {
+    private ECCurve curve;
     private X9ECParameters params;
 
+    public synchronized ECCurve getCurve()
+    {
+        if (curve == null)
+        {
+            curve = createCurve();
+        }
+
+        return curve;
+    }
+
     public synchronized X9ECParameters getParameters()
     {
         if (params == null)
@@ -19,5 +32,10 @@
         return params;
     }
 
+    protected ECCurve createCurve()
+    {
+        return createParameters().getCurve();
+    }
+
     protected abstract X9ECParameters createParameters();
 }
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x9/X9FieldElement.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x9/X9FieldElement.java
index 53f51e6..1e0c609 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x9/X9FieldElement.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/x9/X9FieldElement.java
@@ -1,10 +1,7 @@
 /* GENERATED SOURCE. DO NOT MODIFY. */
 package com.android.org.bouncycastle.asn1.x9;
 
-import java.math.BigInteger;
-
 import com.android.org.bouncycastle.asn1.ASN1Object;
-import com.android.org.bouncycastle.asn1.ASN1OctetString;
 import com.android.org.bouncycastle.asn1.ASN1Primitive;
 import com.android.org.bouncycastle.asn1.DEROctetString;
 import com.android.org.bouncycastle.math.ec.ECFieldElement;
@@ -25,22 +22,6 @@
         this.f = f;
     }
 
-    /**
-     * @deprecated Will be removed
-     */
-    public X9FieldElement(BigInteger p, ASN1OctetString s)
-    {
-        this(new ECFieldElement.Fp(p, new BigInteger(1, s.getOctets())));
-    }
-
-    /**
-     * @deprecated Will be removed
-     */
-    public X9FieldElement(int m, int k1, int k2, int k3, ASN1OctetString s)
-    {
-        this(new ECFieldElement.F2m(m, k1, k2, k3, new BigInteger(1, s.getOctets())));
-    }
-
     public ECFieldElement getValue()
     {
         return f;
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/AlphabetMapper.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/AlphabetMapper.java
new file mode 100644
index 0000000..038ee71
--- /dev/null
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/AlphabetMapper.java
@@ -0,0 +1,34 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.org.bouncycastle.crypto;
+
+/**
+ * Base interface for mapping from an alphabet to a set of indexes
+ * suitable for use with FPE.
+ * @hide This class is not part of the Android public SDK API
+ */
+public interface AlphabetMapper
+{
+    /**
+     * Return the number of characters in the alphabet.
+     *
+     * @return the radix for the alphabet.
+     */
+    int getRadix();
+
+    /**
+     * Return the passed in char[] as a byte array of indexes (indexes
+     * can be more than 1 byte)
+     *
+     * @param input characters to be mapped.
+     * @return an index array.
+     */
+    byte[] convertToIndexes(char[] input);
+
+    /**
+     * Return a char[] for this alphabet based on the indexes passed.
+     *
+     * @param input input array of indexes.
+     * @return an array of char corresponding to the index values.
+     */
+    char[] convertToChars(byte[] input);
+}
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/BlockCipher.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/BlockCipher.java
index eaf9803..1789d21 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/BlockCipher.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/BlockCipher.java
@@ -38,16 +38,16 @@
      * Process one block of input from the array in and write it to
      * the out array.
      *
-     * @param in the array containing the input data.
+     * @param input the array containing the input data.
      * @param inOff offset into the in array the data starts at.
-     * @param out the array the output data will be copied into.
+     * @param output the array the output data will be copied into.
      * @param outOff the offset into the out array the output will start at.
-     * @exception DataLengthException if there isn't enough data in in, or
+     * @exception DataLengthException if there isn't enough data in input , or
      * space in out.
      * @exception IllegalStateException if the cipher isn't initialised.
      * @return the number of bytes processed and produced.
      */
-    public int processBlock(byte[] in, int inOff, byte[] out, int outOff)
+    public int processBlock(byte[] input, int inOff, byte[] output, int outOff)
         throws DataLengthException, IllegalStateException;
 
     /**
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/BufferedBlockCipher.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/BufferedBlockCipher.java
index f3cfea8..0cde747 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/BufferedBlockCipher.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/BufferedBlockCipher.java
@@ -16,8 +16,9 @@
     protected byte[]        buf;
     protected int           bufOff;
 
-    protected boolean       forEncryption;
-    protected BlockCipher   cipher;
+    protected boolean          forEncryption;
+    protected BlockCipher      cipher;
+    protected MultiBlockCipher mbCipher;
 
     protected boolean       partialBlockOkay;
     protected boolean       pgpCFB;
@@ -25,7 +26,7 @@
     /**
      * constructor for subclasses
      */
-    protected BufferedBlockCipher()
+    BufferedBlockCipher()
     {
     }
 
@@ -33,13 +34,24 @@
      * Create a buffered block cipher without padding.
      *
      * @param cipher the underlying block cipher this buffering object wraps.
+     * @deprecated use the constructor on DefaultBufferedBlockCipher.
      */
     public BufferedBlockCipher(
         BlockCipher     cipher)
     {
         this.cipher = cipher;
 
-        buf = new byte[cipher.getBlockSize()];
+        if (cipher instanceof MultiBlockCipher)
+        {
+            this.mbCipher = (MultiBlockCipher)cipher;
+            buf = new byte[mbCipher.getMultiBlockSize()];
+        }
+        else
+        {
+            this.mbCipher = null;
+            buf = new byte[cipher.getBlockSize()];
+        }
+
         bufOff = 0;
 
         //
@@ -145,6 +157,11 @@
     public int getOutputSize(
         int length)
     {
+        if (pgpCFB && forEncryption)
+        {
+            return length + bufOff + (cipher.getBlockSize() + 2);
+        }
+
         // Note: Can assume partialBlockOkay is true for purposes of this calculation
         return length + bufOff;
     }
@@ -227,12 +244,29 @@
             len -= gapLen;
             inOff += gapLen;
 
-            while (len > buf.length)
+            if (mbCipher != null)
             {
-                resultLen += cipher.processBlock(in, inOff, out, outOff + resultLen);
+                int blockCount = len / mbCipher.getMultiBlockSize();
 
-                len -= blockSize;
-                inOff += blockSize;
+                if (blockCount > 0)
+                {
+                    resultLen += mbCipher.processBlocks(in, inOff, blockCount, out, outOff + resultLen);
+
+                    int processed = blockCount * mbCipher.getMultiBlockSize();
+
+                    len -= processed;
+                    inOff += processed;
+                }
+            }
+            else
+            {
+                while (len > buf.length)
+                {
+                    resultLen += cipher.processBlock(in, inOff, out, outOff + resultLen);
+
+                    len -= blockSize;
+                    inOff += blockSize;
+                }
             }
         }
 
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/CipherKeyGenerator.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/CipherKeyGenerator.java
index a7212c9..eae4741 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/CipherKeyGenerator.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/CipherKeyGenerator.java
@@ -3,6 +3,8 @@
 
 import java.security.SecureRandom;
 
+import com.android.org.bouncycastle.crypto.constraints.DefaultServiceProperties;
+
 /**
  * The base class for symmetric, or secret, cipher key generators.
  * @hide This class is not part of the Android public SDK API
@@ -22,6 +24,8 @@
     {
         this.random = param.getRandom();
         this.strength = (param.getStrength() + 7) / 8;
+
+        CryptoServicesRegistrar.checkConstraints(new DefaultServiceProperties("SymKeyGen", param.getStrength()));
     }
 
     /**
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/CryptoServiceConstraintsException.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/CryptoServiceConstraintsException.java
new file mode 100644
index 0000000..33f9e09
--- /dev/null
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/CryptoServiceConstraintsException.java
@@ -0,0 +1,14 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.org.bouncycastle.crypto;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+public class CryptoServiceConstraintsException
+    extends RuntimeException
+{
+    public CryptoServiceConstraintsException(String msg)
+    {
+        super(msg);
+    }
+}
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/CryptoServiceProperties.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/CryptoServiceProperties.java
new file mode 100644
index 0000000..d80043a
--- /dev/null
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/CryptoServiceProperties.java
@@ -0,0 +1,16 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.org.bouncycastle.crypto;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+public interface CryptoServiceProperties
+{
+    int bitsOfSecurity();
+
+    String getServiceName();
+
+    CryptoServicePurpose getPurpose();
+
+    Object getParams();
+}
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/CryptoServicePurpose.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/CryptoServicePurpose.java
new file mode 100644
index 0000000..76b81ea
--- /dev/null
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/CryptoServicePurpose.java
@@ -0,0 +1,19 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.org.bouncycastle.crypto;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+public enum CryptoServicePurpose
+{
+    AGREEMENT,
+    ENCRYPTION,
+    DECRYPTION,
+    KEYGEN,
+    SIGNING,         // for signatures (and digests)
+    VERIFYING,
+    AUTHENTICATION,  // for MACs (and digests)
+    VERIFICATION,
+    PRF,
+    ANY
+}
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/CryptoServicesConstraints.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/CryptoServicesConstraints.java
new file mode 100644
index 0000000..6c90e31
--- /dev/null
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/CryptoServicesConstraints.java
@@ -0,0 +1,10 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.org.bouncycastle.crypto;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+public interface CryptoServicesConstraints
+{
+    void check(CryptoServiceProperties service);
+}
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/CryptoServicesPermission.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/CryptoServicesPermission.java
index c393a4d..3a977ef 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/CryptoServicesPermission.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/CryptoServicesPermission.java
@@ -27,6 +27,11 @@
      */
     public static final String DEFAULT_RANDOM = "defaultRandomConfig";
 
+    /**
+     * Enable the setting of the constraints.
+     */
+    public static final String CONSTRAINTS = "constraints";
+
     private final Set<String> actions = new HashSet<String>();
 
     public CryptoServicesPermission(String name)
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/CryptoServicesRegistrar.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/CryptoServicesRegistrar.java
index 920e33c..81c47d3 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/CryptoServicesRegistrar.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/CryptoServicesRegistrar.java
@@ -9,12 +9,16 @@
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.Map;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.logging.Logger;
 
 import com.android.org.bouncycastle.asn1.x9.X9ECParameters;
 import com.android.org.bouncycastle.crypto.params.DHParameters;
 import com.android.org.bouncycastle.crypto.params.DHValidationParameters;
 import com.android.org.bouncycastle.crypto.params.DSAParameters;
 import com.android.org.bouncycastle.crypto.params.DSAValidationParameters;
+import com.android.org.bouncycastle.util.Properties;
+import com.android.org.bouncycastle.util.Strings;
 import com.android.org.bouncycastle.util.encoders.Hex;
 
 /**
@@ -23,15 +27,28 @@
  */
 public final class CryptoServicesRegistrar
 {
+    private static final Logger LOG = Logger.getLogger(CryptoServicesRegistrar.class.getName());
+
     private static final Permission CanSetDefaultProperty = new CryptoServicesPermission(CryptoServicesPermission.GLOBAL_CONFIG);
     private static final Permission CanSetThreadProperty = new CryptoServicesPermission(CryptoServicesPermission.THREAD_LOCAL_CONFIG);
     private static final Permission CanSetDefaultRandom = new CryptoServicesPermission(CryptoServicesPermission.DEFAULT_RANDOM);
+    private static final Permission CanSetConstraints = new CryptoServicesPermission(CryptoServicesPermission.CONSTRAINTS);
 
     private static final ThreadLocal<Map<String, Object[]>> threadProperties = new ThreadLocal<Map<String, Object[]>>();
     private static final Map<String, Object[]> globalProperties = Collections.synchronizedMap(new HashMap<String, Object[]>());
+    private static final SecureRandomProvider defaultRandomProviderImpl = new ThreadLocalSecureRandomProvider();
 
-    private static final Object cacheLock = new Object();
-    private static SecureRandom defaultSecureRandom;
+    private static final CryptoServicesConstraints noConstraintsImpl = new CryptoServicesConstraints()
+    {
+        public void check(CryptoServiceProperties service)
+        {
+            // anything goes.
+        }
+    };
+
+    private static final AtomicReference<SecureRandomProvider> defaultSecureRandomProvider = new AtomicReference<SecureRandomProvider>();
+    private static final boolean preconfiguredConstraints;
+    private static final AtomicReference<CryptoServicesConstraints> servicesConstraints = new AtomicReference<CryptoServicesConstraints>();
 
     static
     {
@@ -45,56 +62,59 @@
 
         DSAParameters def768Params = new DSAParameters(
             new BigInteger("e9e642599d355f37c97ffd3567120b8e25c9cd43e927b3a9670fbec5" +
-                           "d890141922d2c3b3ad2480093799869d1e846aab49fab0ad26d2ce6a" +
-                           "22219d470bce7d777d4a21fbe9c270b57f607002f3cef8393694cf45" +
-                           "ee3688c11a8c56ab127a3daf", 16),
+                "d890141922d2c3b3ad2480093799869d1e846aab49fab0ad26d2ce6a" +
+                "22219d470bce7d777d4a21fbe9c270b57f607002f3cef8393694cf45" +
+                "ee3688c11a8c56ab127a3daf", 16),
             new BigInteger("9cdbd84c9f1ac2f38d0f80f42ab952e7338bf511", 16),
             new BigInteger("30470ad5a005fb14ce2d9dcd87e38bc7d1b1c5facbaecbe95f190aa7" +
-                           "a31d23c4dbbcbe06174544401a5b2c020965d8c2bd2171d366844577" +
-                           "1f74ba084d2029d83c1c158547f3a9f1a2715be23d51ae4d3e5a1f6a" +
-                           "7064f316933a346d3f529252", 16),
+                "a31d23c4dbbcbe06174544401a5b2c020965d8c2bd2171d366844577" +
+                "1f74ba084d2029d83c1c158547f3a9f1a2715be23d51ae4d3e5a1f6a" +
+                "7064f316933a346d3f529252", 16),
             new DSAValidationParameters(Hex.decodeStrict("77d0f8c4dad15eb8c4f2f8d6726cefd96d5bb399"), 263));
 
         DSAParameters def1024Params = new DSAParameters(
             new BigInteger("fd7f53811d75122952df4a9c2eece4e7f611b7523cef4400c31e3f80" +
-                            "b6512669455d402251fb593d8d58fabfc5f5ba30f6cb9b556cd7813b" +
-                            "801d346ff26660b76b9950a5a49f9fe8047b1022c24fbba9d7feb7c6" +
-                            "1bf83b57e7c6a8a6150f04fb83f6d3c51ec3023554135a169132f675" +
-                            "f3ae2b61d72aeff22203199dd14801c7", 16),
+                "b6512669455d402251fb593d8d58fabfc5f5ba30f6cb9b556cd7813b" +
+                "801d346ff26660b76b9950a5a49f9fe8047b1022c24fbba9d7feb7c6" +
+                "1bf83b57e7c6a8a6150f04fb83f6d3c51ec3023554135a169132f675" +
+                "f3ae2b61d72aeff22203199dd14801c7", 16),
             new BigInteger("9760508f15230bccb292b982a2eb840bf0581cf5", 16),
             new BigInteger("f7e1a085d69b3ddecbbcab5c36b857b97994afbbfa3aea82f9574c0b" +
-                            "3d0782675159578ebad4594fe67107108180b449167123e84c281613" +
-                            "b7cf09328cc8a6e13c167a8b547c8d28e0a3ae1e2bb3a675916ea37f" +
-                            "0bfa213562f1fb627a01243bcca4f1bea8519089a883dfe15ae59f06" +
-                            "928b665e807b552564014c3bfecf492a", 16),
+                "3d0782675159578ebad4594fe67107108180b449167123e84c281613" +
+                "b7cf09328cc8a6e13c167a8b547c8d28e0a3ae1e2bb3a675916ea37f" +
+                "0bfa213562f1fb627a01243bcca4f1bea8519089a883dfe15ae59f06" +
+                "928b665e807b552564014c3bfecf492a", 16),
             new DSAValidationParameters(Hex.decodeStrict("8d5155894229d5e689ee01e6018a237e2cae64cd"), 92));
 
         DSAParameters def2048Params = new DSAParameters(
             new BigInteger("95475cf5d93e596c3fcd1d902add02f427f5f3c7210313bb45fb4d5b" +
-                            "b2e5fe1cbd678cd4bbdd84c9836be1f31c0777725aeb6c2fc38b85f4" +
-                            "8076fa76bcd8146cc89a6fb2f706dd719898c2083dc8d896f84062e2" +
-                            "c9c94d137b054a8d8096adb8d51952398eeca852a0af12df83e475aa" +
-                            "65d4ec0c38a9560d5661186ff98b9fc9eb60eee8b030376b236bc73b" +
-                            "e3acdbd74fd61c1d2475fa3077b8f080467881ff7e1ca56fee066d79" +
-                            "506ade51edbb5443a563927dbc4ba520086746175c8885925ebc64c6" +
-                            "147906773496990cb714ec667304e261faee33b3cbdf008e0c3fa906" +
-                            "50d97d3909c9275bf4ac86ffcb3d03e6dfc8ada5934242dd6d3bcca2" +
-                            "a406cb0b", 16),
+                "b2e5fe1cbd678cd4bbdd84c9836be1f31c0777725aeb6c2fc38b85f4" +
+                "8076fa76bcd8146cc89a6fb2f706dd719898c2083dc8d896f84062e2" +
+                "c9c94d137b054a8d8096adb8d51952398eeca852a0af12df83e475aa" +
+                "65d4ec0c38a9560d5661186ff98b9fc9eb60eee8b030376b236bc73b" +
+                "e3acdbd74fd61c1d2475fa3077b8f080467881ff7e1ca56fee066d79" +
+                "506ade51edbb5443a563927dbc4ba520086746175c8885925ebc64c6" +
+                "147906773496990cb714ec667304e261faee33b3cbdf008e0c3fa906" +
+                "50d97d3909c9275bf4ac86ffcb3d03e6dfc8ada5934242dd6d3bcca2" +
+                "a406cb0b", 16),
             new BigInteger("f8183668ba5fc5bb06b5981e6d8b795d30b8978d43ca0ec572e37e09939a9773", 16),
             new BigInteger("42debb9da5b3d88cc956e08787ec3f3a09bba5f48b889a74aaf53174" +
-                            "aa0fbe7e3c5b8fcd7a53bef563b0e98560328960a9517f4014d3325f" +
-                            "c7962bf1e049370d76d1314a76137e792f3f0db859d095e4a5b93202" +
-                            "4f079ecf2ef09c797452b0770e1350782ed57ddf794979dcef23cb96" +
-                            "f183061965c4ebc93c9c71c56b925955a75f94cccf1449ac43d586d0" +
-                            "beee43251b0b2287349d68de0d144403f13e802f4146d882e057af19" +
-                            "b6f6275c6676c8fa0e3ca2713a3257fd1b27d0639f695e347d8d1cf9" +
-                            "ac819a26ca9b04cb0eb9b7b035988d15bbac65212a55239cfc7e58fa" +
-                            "e38d7250ab9991ffbc97134025fe8ce04c4399ad96569be91a546f49" +
-                            "78693c7a", 16),
+                "aa0fbe7e3c5b8fcd7a53bef563b0e98560328960a9517f4014d3325f" +
+                "c7962bf1e049370d76d1314a76137e792f3f0db859d095e4a5b93202" +
+                "4f079ecf2ef09c797452b0770e1350782ed57ddf794979dcef23cb96" +
+                "f183061965c4ebc93c9c71c56b925955a75f94cccf1449ac43d586d0" +
+                "beee43251b0b2287349d68de0d144403f13e802f4146d882e057af19" +
+                "b6f6275c6676c8fa0e3ca2713a3257fd1b27d0639f695e347d8d1cf9" +
+                "ac819a26ca9b04cb0eb9b7b035988d15bbac65212a55239cfc7e58fa" +
+                "e38d7250ab9991ffbc97134025fe8ce04c4399ad96569be91a546f49" +
+                "78693c7a", 16),
             new DSAValidationParameters(Hex.decodeStrict("b0b4417601b59cbc9d8ac8f935cadaec4f5fbb2f23785609ae466748d9b5a536"), 497));
 
         localSetGlobalProperty(Property.DSA_DEFAULT_PARAMS, def512Params, def768Params, def1024Params, def2048Params);
         localSetGlobalProperty(Property.DH_DEFAULT_PARAMS, toDH(def512Params), toDH(def768Params), toDH(def1024Params), toDH(def2048Params));
+
+        servicesConstraints.set(getDefaultConstraints());
+        preconfiguredConstraints = (servicesConstraints.get() != noConstraintsImpl);
     }
 
     private CryptoServicesRegistrar()
@@ -109,25 +129,9 @@
      */
     public static SecureRandom getSecureRandom()
     {
-        synchronized (cacheLock)
-        {
-            if (null != defaultSecureRandom)
-            {
-                return defaultSecureRandom;
-            }
-        }
+        defaultSecureRandomProvider.compareAndSet(null, defaultRandomProviderImpl);
 
-        SecureRandom tmp = new SecureRandom();
-
-        synchronized (cacheLock)
-        {
-            if (null == defaultSecureRandom)
-            {
-                defaultSecureRandom = tmp;
-            }
-
-            return defaultSecureRandom;
-        }
+        return defaultSecureRandomProvider.get().get();
     }
 
     /**
@@ -146,13 +150,83 @@
      *
      * @param secureRandom the SecureRandom to use as the default.
      */
-    public static void setSecureRandom(SecureRandom secureRandom)
+    public static void setSecureRandom(final SecureRandom secureRandom)
     {
         checkPermission(CanSetDefaultRandom);
 
-        synchronized (cacheLock)
+        if (secureRandom == null)
         {
-            defaultSecureRandom = secureRandom;
+            defaultSecureRandomProvider.set(defaultRandomProviderImpl);
+        }
+        else
+        {
+            defaultSecureRandomProvider.set(new SecureRandomProvider()
+            {
+                public SecureRandom get()
+                {
+                    return secureRandom;
+                }
+            });
+        }
+    }
+
+    /**
+     * Set a default secure random provider to be used where none is otherwise provided.
+     *
+     * @param secureRandomProvider a provider SecureRandom to use when a default SecureRandom is requested.
+     */
+    public static void setSecureRandomProvider(SecureRandomProvider secureRandomProvider)
+    {
+        checkPermission(CanSetDefaultRandom);
+
+        defaultSecureRandomProvider.set(secureRandomProvider);
+    }
+
+    /**
+     * Return the current algorithm/services constraints.
+     *
+     * @return the algorithm/services constraints.
+     */
+    public static CryptoServicesConstraints getServicesConstraints()
+    {
+        return servicesConstraints.get();
+    }
+
+    /**
+     * Check a service to make sure it meets the current constraints.
+     *
+     * @param cryptoService the service to be checked.
+     * @throws CryptoServiceConstraintsException if the service violates the current constraints.
+     */
+    public static void checkConstraints(CryptoServiceProperties cryptoService)
+    {
+        servicesConstraints.get().check(cryptoService);
+    }
+
+    /**
+     * Set the current algorithm constraints.
+     */
+    public static void setServicesConstraints(CryptoServicesConstraints constraints)
+    {
+        checkPermission(CanSetConstraints);
+
+        CryptoServicesConstraints newConstraints = (constraints == null) ? noConstraintsImpl : constraints;
+
+        if (preconfiguredConstraints)
+        {
+            if (Properties.isOverrideSet("com.android.org.bouncycastle.constraints.allow_override"))
+            {
+                servicesConstraints.set(newConstraints);
+            }
+            else
+            {
+                LOG.warning("attempt to override pre-configured constraints ignored");
+            }
+        }
+        else
+        {
+            // TODO: should this only be allowed once?
+            servicesConstraints.set(newConstraints);
         }
     }
 
@@ -161,7 +235,7 @@
      * configuration first and then on the global configuration in no local configuration exists.
      *
      * @param property the property to look up.
-     * @param <T> the type to be returned
+     * @param <T>      the type to be returned
      * @return null if the property is not set, the default value otherwise,
      */
     public static <T> T getProperty(Property property)
@@ -197,7 +271,7 @@
      * DSA_DEFAULT_PARAMS.
      *
      * @param property the name of the property to look up.
-     * @param <T> the base type of the array to be returned.
+     * @param <T>      the base type of the array to be returned.
      * @return null if the property is not set, an array of the current values otherwise.
      */
     public static <T> T[] getSizedProperty(Property property)
@@ -217,8 +291,8 @@
      * DSA_DEFAULT_PARAMS.
      *
      * @param property the name of the property to look up.
-     * @param size the size (in bits) of the defining value in the property type.
-     * @param <T> the type of the value to be returned.
+     * @param size     the size (in bits) of the defining value in the property type.
+     * @param <T>      the type of the value to be returned.
      * @return the current value for the size, null if there is no value set,
      */
     public static <T> T getSizedProperty(Property property, int size)
@@ -263,9 +337,9 @@
      * one value can be passed in for a sized property. If more than one value is provided the
      * first value in the argument list becomes the default value.
      *
-     * @param property the name of the property to set.
+     * @param property      the name of the property to set.
      * @param propertyValue the values to assign to the property.
-     * @param <T> the base type of the property value.
+     * @param <T>           the base type of the property value.
      */
     public static <T> void setThreadProperty(Property property, T... propertyValue)
     {
@@ -284,9 +358,9 @@
      * one value can be passed in for a sized property. If more than one value is provided the
      * first value in the argument list becomes the default value.
      *
-     * @param property the name of the property to set.
+     * @param property      the name of the property to set.
      * @param propertyValue the values to assign to the property.
-     * @param <T> the base type of the property value.
+     * @param <T>           the base type of the property value.
      */
     public static <T> void setGlobalProperty(Property property, T... propertyValue)
     {
@@ -325,7 +399,7 @@
      * Clear the global value for the passed in property.
      *
      * @param property the property to be cleared.
-     * @param <T> the base type of the property value
+     * @param <T>      the base type of the property value
      * @return an array of T if a value was previously set, null otherwise.
      */
     public static <T> T[] clearGlobalProperty(Property property)
@@ -342,7 +416,7 @@
      * Clear the thread local value for the passed in property.
      *
      * @param property the property to be cleared.
-     * @param <T> the base type of the property value
+     * @param <T>      the base type of the property value
      * @return an array of T if a value was previously set, null otherwise.
      */
     public static <T> T[] clearThreadProperty(Property property)
@@ -417,6 +491,13 @@
         return m;
     }
 
+    private static CryptoServicesConstraints getDefaultConstraints()
+    {
+        // TODO: return one based on system/security properties if set.
+
+        return noConstraintsImpl;
+    }
+
     /**
      * Available properties that can be set.
      * @hide This class is not part of the Android public SDK API
@@ -430,11 +511,11 @@
         /**
          * The default parameters for a particular size of Diffie-Hellman key.This is a sized property.
          */
-        public static final Property DH_DEFAULT_PARAMS= new Property("dhDefaultParams", DHParameters.class);
+        public static final Property DH_DEFAULT_PARAMS = new Property("dhDefaultParams", DHParameters.class);
         /**
          * The default parameters for a particular size of DSA key. This is a sized property.
          */
-        public static final Property DSA_DEFAULT_PARAMS= new Property("dsaDefaultParams", DSAParameters.class);
+        public static final Property DSA_DEFAULT_PARAMS = new Property("dsaDefaultParams", DSAParameters.class);
         private final String name;
         private final Class type;
 
@@ -444,4 +525,20 @@
             this.type = type;
         }
     }
+
+    private static class ThreadLocalSecureRandomProvider
+        implements SecureRandomProvider
+    {
+        final ThreadLocal<SecureRandom> defaultRandoms = new ThreadLocal<SecureRandom>();
+
+        public SecureRandom get()
+        {
+            if (defaultRandoms.get() == null)
+            {
+                defaultRandoms.set(new SecureRandom());
+            }
+
+            return defaultRandoms.get();
+        }
+    }
 }
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/DefaultBufferedBlockCipher.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/DefaultBufferedBlockCipher.java
new file mode 100644
index 0000000..2107405
--- /dev/null
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/DefaultBufferedBlockCipher.java
@@ -0,0 +1,358 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.org.bouncycastle.crypto;
+
+
+/**
+ * A wrapper class that allows block ciphers to be used to process data in
+ * a piecemeal fashion. The BufferedBlockCipher outputs a block only when the
+ * buffer is full and more data is being added, or on a doFinal.
+ * <p>
+ * Note: in the case where the underlying cipher is either a CFB cipher or an
+ * OFB one the last block may not be a multiple of the block size. Use this class
+ * for construction rather than BufferedBlockCipher as BufferedBlockCipher will eventually
+ * turn into an interface.
+ * @hide This class is not part of the Android public SDK API
+ */
+public class DefaultBufferedBlockCipher
+    extends BufferedBlockCipher
+{
+    protected byte[]        buf;
+    protected int           bufOff;
+
+    protected boolean          forEncryption;
+    protected BlockCipher      cipher;
+    protected MultiBlockCipher mbCipher;
+
+    protected boolean       partialBlockOkay;
+    protected boolean       pgpCFB;
+
+    /**
+     * constructor for subclasses
+     */
+    protected DefaultBufferedBlockCipher()
+    {
+    }
+
+    /**
+     * Create a buffered block cipher without padding.
+     *
+     * @param cipher the underlying block cipher this buffering object wraps.
+     */
+    public DefaultBufferedBlockCipher(
+        BlockCipher     cipher)
+    {
+        this.cipher = cipher;
+
+        if (cipher instanceof MultiBlockCipher)
+        {
+            this.mbCipher = (MultiBlockCipher)cipher;
+            buf = new byte[mbCipher.getMultiBlockSize()];
+        }
+        else
+        {
+            this.mbCipher = null;
+            buf = new byte[cipher.getBlockSize()];
+        }
+
+        bufOff = 0;
+
+        //
+        // check if we can handle partial blocks on doFinal.
+        //
+        String  name = cipher.getAlgorithmName();
+        int     idx = name.indexOf('/') + 1;
+
+        pgpCFB = (idx > 0 && name.startsWith("PGP", idx));
+
+        if (pgpCFB || cipher instanceof StreamCipher)
+        {
+            partialBlockOkay = true;
+        }
+        else
+        {
+            partialBlockOkay = (idx > 0 && (name.startsWith("OpenPGP", idx)));
+        }
+    }
+
+    /**
+     * return the cipher this object wraps.
+     *
+     * @return the cipher this object wraps.
+     */
+    public BlockCipher getUnderlyingCipher()
+    {
+        return cipher;
+    }
+
+    /**
+     * initialise the cipher.
+     *
+     * @param forEncryption if true the cipher is initialised for
+     *  encryption, if false for decryption.
+     * @param params the key and other data required by the cipher.
+     * @exception IllegalArgumentException if the params argument is
+     * inappropriate.
+     */
+    public void init(
+        boolean             forEncryption,
+        CipherParameters    params)
+        throws IllegalArgumentException
+    {
+        this.forEncryption = forEncryption;
+
+        reset();
+
+        cipher.init(forEncryption, params);
+    }
+
+    /**
+     * return the blocksize for the underlying cipher.
+     *
+     * @return the blocksize for the underlying cipher.
+     */
+    public int getBlockSize()
+    {
+        return cipher.getBlockSize();
+    }
+
+    /**
+     * return the size of the output buffer required for an update 
+     * an input of len bytes.
+     *
+     * @param len the length of the input.
+     * @return the space required to accommodate a call to update
+     * with len bytes of input.
+     */
+    public int getUpdateOutputSize(
+        int len)
+    {
+        int total       = len + bufOff;
+        int leftOver;
+
+        if (pgpCFB)
+        {
+            if (forEncryption)
+            {
+                leftOver = total % buf.length - (cipher.getBlockSize() + 2);
+            }
+            else
+            {
+                leftOver = total % buf.length;
+            }
+        }
+        else
+        {
+            leftOver    = total % buf.length;
+        }
+
+        return total - leftOver;
+    }
+
+    /**
+     * return the size of the output buffer required for an update plus a
+     * doFinal with an input of 'length' bytes.
+     *
+     * @param length the length of the input.
+     * @return the space required to accommodate a call to update and doFinal
+     * with 'length' bytes of input.
+     */
+    public int getOutputSize(
+        int length)
+    {
+        if (pgpCFB && forEncryption)
+        {
+            return length + bufOff + (cipher.getBlockSize() + 2);
+        }
+
+        // Note: Can assume partialBlockOkay is true for purposes of this calculation
+        return length + bufOff;
+    }
+
+    /**
+     * process a single byte, producing an output block if necessary.
+     *
+     * @param in the input byte.
+     * @param out the space for any output that might be produced.
+     * @param outOff the offset from which the output will be copied.
+     * @return the number of output bytes copied to out.
+     * @exception DataLengthException if there isn't enough space in out.
+     * @exception IllegalStateException if the cipher isn't initialised.
+     */
+    public int processByte(
+        byte        in,
+        byte[]      out,
+        int         outOff)
+        throws DataLengthException, IllegalStateException
+    {
+        int         resultLen = 0;
+
+        buf[bufOff++] = in;
+
+        if (bufOff == buf.length)
+        {
+            resultLen = cipher.processBlock(buf, 0, out, outOff);
+            bufOff = 0;
+        }
+
+        return resultLen;
+    }
+
+    /**
+     * process an array of bytes, producing output if necessary.
+     *
+     * @param in the input byte array.
+     * @param inOff the offset at which the input data starts.
+     * @param len the number of bytes to be copied out of the input array.
+     * @param out the space for any output that might be produced.
+     * @param outOff the offset from which the output will be copied.
+     * @return the number of output bytes copied to out.
+     * @exception DataLengthException if there isn't enough space in out.
+     * @exception IllegalStateException if the cipher isn't initialised.
+     */
+    public int processBytes(
+        byte[]      in,
+        int         inOff,
+        int         len,
+        byte[]      out,
+        int         outOff)
+        throws DataLengthException, IllegalStateException
+    {
+        if (len < 0)
+        {
+            throw new IllegalArgumentException("Can't have a negative input length!");
+        }
+
+        int blockSize   = getBlockSize();
+        int length      = getUpdateOutputSize(len);
+        
+        if (length > 0)
+        {
+            if ((outOff + length) > out.length)
+            {
+                throw new OutputLengthException("output buffer too short");
+            }
+        }
+
+        int resultLen = 0;
+        int gapLen = buf.length - bufOff;
+
+        if (len > gapLen)
+        {
+            System.arraycopy(in, inOff, buf, bufOff, gapLen);
+
+            resultLen += cipher.processBlock(buf, 0, out, outOff);
+
+            bufOff = 0;
+            len -= gapLen;
+            inOff += gapLen;
+
+            if (mbCipher != null)
+            {
+                int blockCount = len / mbCipher.getMultiBlockSize();
+
+                if (blockCount > 0)
+                {
+                    resultLen += mbCipher.processBlocks(in, inOff, blockCount, out, outOff + resultLen);
+
+                    int processed = blockCount * mbCipher.getMultiBlockSize();
+
+                    len -= processed;
+                    inOff += processed;
+                }
+            }
+            else
+            {
+                while (len > buf.length)
+                {
+                    resultLen += cipher.processBlock(in, inOff, out, outOff + resultLen);
+
+                    len -= blockSize;
+                    inOff += blockSize;
+                }
+            }
+        }
+
+        System.arraycopy(in, inOff, buf, bufOff, len);
+
+        bufOff += len;
+
+        if (bufOff == buf.length)
+        {
+            resultLen += cipher.processBlock(buf, 0, out, outOff + resultLen);
+            bufOff = 0;
+        }
+
+        return resultLen;
+    }
+
+    /**
+     * Process the last block in the buffer.
+     *
+     * @param out the array the block currently being held is copied into.
+     * @param outOff the offset at which the copying starts.
+     * @return the number of output bytes copied to out.
+     * @exception DataLengthException if there is insufficient space in out for
+     * the output, or the input is not block size aligned and should be.
+     * @exception IllegalStateException if the underlying cipher is not
+     * initialised.
+     * @exception InvalidCipherTextException if padding is expected and not found.
+     * @exception DataLengthException if the input is not block size
+     * aligned.
+     */
+    public int doFinal(
+        byte[]  out,
+        int     outOff)
+        throws DataLengthException, IllegalStateException, InvalidCipherTextException
+    {
+        try
+        {
+            int resultLen = 0;
+
+            if (outOff + bufOff > out.length)
+            {
+                throw new OutputLengthException("output buffer too short for doFinal()");
+            }
+
+            if (bufOff != 0)
+            {
+                if (!partialBlockOkay)
+                {
+                    throw new DataLengthException("data not block size aligned");
+                }
+
+                cipher.processBlock(buf, 0, buf, 0);
+                resultLen = bufOff;
+                bufOff = 0;
+                System.arraycopy(buf, 0, out, outOff, resultLen);
+            }
+
+            return resultLen;
+        }
+        finally
+        {
+            reset();
+        }
+    }
+
+    /**
+     * Reset the buffer and cipher. After resetting the object is in the same
+     * state as it was after the last init (if there was one).
+     */
+    public void reset()
+    {
+        //
+        // clean the buffer.
+        //
+        for (int i = 0; i < buf.length; i++)
+        {
+            buf[i] = 0;
+        }
+
+        bufOff = 0;
+
+        //
+        // reset the underlying cipher.
+        //
+        cipher.reset();
+    }
+}
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/DefaultMultiBlockCipher.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/DefaultMultiBlockCipher.java
new file mode 100644
index 0000000..bf1de96
--- /dev/null
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/DefaultMultiBlockCipher.java
@@ -0,0 +1,37 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.org.bouncycastle.crypto;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+public abstract class DefaultMultiBlockCipher
+    implements MultiBlockCipher
+{
+    protected DefaultMultiBlockCipher()
+    {
+    }
+
+    public int getMultiBlockSize()
+    {
+        return this.getBlockSize();
+    }
+
+    public int processBlocks(byte[] in, int inOff, int blockCount, byte[] out, int outOff)
+        throws DataLengthException, IllegalStateException
+    {
+
+        // TODO check if the underlying cipher supports the multiblock interface and call it directly?
+
+        int resultLen = 0;
+        int blockSize = this.getMultiBlockSize();
+        
+        for (int i = 0; i != blockCount; i++)
+        {
+            resultLen += this.processBlock(in, inOff, out, outOff + resultLen);
+
+            inOff += blockSize;
+        }
+
+        return resultLen;
+    }
+}
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/EncapsulatedSecretExtractor.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/EncapsulatedSecretExtractor.java
new file mode 100644
index 0000000..215dc09
--- /dev/null
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/EncapsulatedSecretExtractor.java
@@ -0,0 +1,22 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.org.bouncycastle.crypto;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+public interface EncapsulatedSecretExtractor
+{
+    /**
+     * Extract the secret based on the recipient private key.
+     *
+     * @param encapsulation the encapsulated secret.
+     */
+    byte[] extractSecret(byte[] encapsulation);
+
+    /**
+     * Return the length in bytes of the encapsulation.
+     *
+     * @return length in bytes of an encapsulation for this parameter set.
+     */
+    int getEncapsulationLength();
+}
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/EncapsulatedSecretGenerator.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/EncapsulatedSecretGenerator.java
new file mode 100644
index 0000000..d94b70b
--- /dev/null
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/EncapsulatedSecretGenerator.java
@@ -0,0 +1,17 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.org.bouncycastle.crypto;
+
+import com.android.org.bouncycastle.crypto.params.AsymmetricKeyParameter;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+public interface EncapsulatedSecretGenerator
+{
+    /**
+     * Generate secret/encapsulation based on the recipient public key.
+     *
+     * @return An SecretWithEncapsulation derived from the recipient public key.
+     */
+    SecretWithEncapsulation generateEncapsulated(AsymmetricKeyParameter recipientKey);
+}
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/MultiBlockCipher.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/MultiBlockCipher.java
new file mode 100644
index 0000000..128bc4b
--- /dev/null
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/MultiBlockCipher.java
@@ -0,0 +1,33 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.org.bouncycastle.crypto;
+
+/**
+ * Base interface for a cipher engine capable of processing multiple blocks at a time.
+ * @hide This class is not part of the Android public SDK API
+ */
+public interface MultiBlockCipher
+    extends BlockCipher
+{
+    /**
+     * Return the multi-block size for this cipher (in bytes).
+     *
+     * @return the multi-block size for this cipher in bytes.
+     */
+    int getMultiBlockSize();
+
+    /**
+     * Process blockCount blocks from input in offset inOff and place the output in
+     * out from offset outOff.
+     *
+     * @param in input data array.
+     * @param inOff start of input data in in.
+     * @param blockCount number of blocks to be processed.
+     * @param out output data array.
+     * @param outOff start position for output data.
+     * @return number of bytes written to out.
+     * @throws DataLengthException
+     * @throws IllegalStateException
+     */
+    int processBlocks(byte[] in, int inOff, int blockCount, byte[] out, int outOff)
+        throws DataLengthException, IllegalStateException;
+}
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/SavableDigest.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/SavableDigest.java
new file mode 100644
index 0000000..1d03a68
--- /dev/null
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/SavableDigest.java
@@ -0,0 +1,15 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.org.bouncycastle.crypto;
+
+import com.android.org.bouncycastle.crypto.digests.EncodableDigest;
+import com.android.org.bouncycastle.util.Memoable;
+
+/**
+ * Extended digest which provides the ability to store state and
+ * provide an encoding.
+ * @hide This class is not part of the Android public SDK API
+ */
+public interface SavableDigest
+    extends ExtendedDigest, EncodableDigest, Memoable
+{
+}
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/SecretWithEncapsulation.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/SecretWithEncapsulation.java
new file mode 100644
index 0000000..78debf9
--- /dev/null
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/SecretWithEncapsulation.java
@@ -0,0 +1,26 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.org.bouncycastle.crypto;
+
+import javax.security.auth.Destroyable;
+
+/**
+ * Interface describing secret with encapsulation details.
+ * @hide This class is not part of the Android public SDK API
+ */
+public interface SecretWithEncapsulation
+    extends Destroyable
+{
+    /**
+     * Return the secret associated with the encapsulation.
+     *
+     * @return the secret the encapsulation is for.
+     */
+    byte[] getSecret();
+
+    /**
+     * Return the data that carries the secret in its encapsulated form.
+     *
+     * @return the encapsulation of the secret.
+     */
+    byte[] getEncapsulation();
+}
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/SecureRandomProvider.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/SecureRandomProvider.java
new file mode 100644
index 0000000..b837cc2
--- /dev/null
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/SecureRandomProvider.java
@@ -0,0 +1,17 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.org.bouncycastle.crypto;
+
+import java.security.SecureRandom;
+
+/**
+ * Source provider for SecureRandom implementations.
+ * @hide This class is not part of the Android public SDK API
+ */
+public interface SecureRandomProvider
+{
+    /**
+     * Return a SecureRandom instance.
+     * @return a SecureRandom
+     */
+    SecureRandom get();
+}
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/StreamBlockCipher.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/StreamBlockCipher.java
index 513ca36..7835b6b 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/StreamBlockCipher.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/StreamBlockCipher.java
@@ -7,7 +7,8 @@
  * @hide This class is not part of the Android public SDK API
  */
 public abstract class StreamBlockCipher
-    implements BlockCipher, StreamCipher
+    extends DefaultMultiBlockCipher
+    implements StreamCipher
 {
     private final BlockCipher cipher;
 
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/agreement/DHBasicAgreement.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/agreement/DHBasicAgreement.java
index 87d4b77..fe57b37 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/agreement/DHBasicAgreement.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/agreement/DHBasicAgreement.java
@@ -5,6 +5,7 @@
 
 import com.android.org.bouncycastle.crypto.BasicAgreement;
 import com.android.org.bouncycastle.crypto.CipherParameters;
+import com.android.org.bouncycastle.crypto.CryptoServicesRegistrar;
 import com.android.org.bouncycastle.crypto.params.AsymmetricKeyParameter;
 import com.android.org.bouncycastle.crypto.params.DHParameters;
 import com.android.org.bouncycastle.crypto.params.DHPrivateKeyParameters;
@@ -49,6 +50,8 @@
 
         this.key = (DHPrivateKeyParameters)kParam;
         this.dhParams = key.getParameters();
+
+        CryptoServicesRegistrar.checkConstraints(Utils.getDefaultProperties("DHB", key));
     }
 
     public int getFieldSize()
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/agreement/ECDHBasicAgreement.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/agreement/ECDHBasicAgreement.java
index 824dc4b..fcba1aa 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/agreement/ECDHBasicAgreement.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/agreement/ECDHBasicAgreement.java
@@ -5,6 +5,7 @@
 
 import com.android.org.bouncycastle.crypto.BasicAgreement;
 import com.android.org.bouncycastle.crypto.CipherParameters;
+import com.android.org.bouncycastle.crypto.CryptoServicesRegistrar;
 import com.android.org.bouncycastle.crypto.params.ECDomainParameters;
 import com.android.org.bouncycastle.crypto.params.ECPrivateKeyParameters;
 import com.android.org.bouncycastle.crypto.params.ECPublicKeyParameters;
@@ -36,6 +37,8 @@
         CipherParameters key)
     {
         this.key = (ECPrivateKeyParameters)key;
+
+        CryptoServicesRegistrar.checkConstraints(Utils.getDefaultProperties("ECDH", this.key));
     }
 
     public int getFieldSize()
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/agreement/Utils.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/agreement/Utils.java
new file mode 100644
index 0000000..329b7db
--- /dev/null
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/agreement/Utils.java
@@ -0,0 +1,41 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.org.bouncycastle.crypto.agreement;
+
+import com.android.org.bouncycastle.crypto.CryptoServiceProperties;
+import com.android.org.bouncycastle.crypto.CryptoServicePurpose;
+import com.android.org.bouncycastle.crypto.constraints.ConstraintUtils;
+import com.android.org.bouncycastle.crypto.constraints.DefaultServiceProperties;
+import com.android.org.bouncycastle.crypto.params.DHKeyParameters;
+import com.android.org.bouncycastle.crypto.params.ECKeyParameters;
+// Android-removed: unsupported algorithms
+// import org.bouncycastle.crypto.params.X25519PrivateKeyParameters;
+// import org.bouncycastle.crypto.params.X448PrivateKeyParameters;
+
+class Utils
+{
+    static CryptoServiceProperties getDefaultProperties(String algorithm, ECKeyParameters k)
+    {
+        return new DefaultServiceProperties(algorithm, ConstraintUtils.bitsOfSecurityFor(k.getParameters().getCurve()), k, CryptoServicePurpose.AGREEMENT);
+    }
+
+    static CryptoServiceProperties getDefaultProperties(String algorithm, DHKeyParameters k)
+    {
+        return new DefaultServiceProperties(algorithm, ConstraintUtils.bitsOfSecurityFor(k.getParameters().getP()), k, CryptoServicePurpose.AGREEMENT);
+    }
+
+    // BEGIN Android-removed: unsupported algorithms
+    /*
+    static CryptoServiceProperties getDefaultProperties(String algorithm, X448PrivateKeyParameters k)
+    {
+        return new DefaultServiceProperties(algorithm, 224, k, CryptoServicePurpose.AGREEMENT);
+    }
+
+    static CryptoServiceProperties getDefaultProperties(String algorithm, X25519PrivateKeyParameters k)
+    {
+        return new DefaultServiceProperties(algorithm, 128, k, CryptoServicePurpose.AGREEMENT);
+    }
+    */
+    // END Android-removed: unsupported algorithms
+}
+
+
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/constraints/ConstraintUtils.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/constraints/ConstraintUtils.java
new file mode 100644
index 0000000..24eb1c2
--- /dev/null
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/constraints/ConstraintUtils.java
@@ -0,0 +1,51 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.org.bouncycastle.crypto.constraints;
+
+import java.math.BigInteger;
+
+import com.android.org.bouncycastle.math.ec.ECCurve;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+public class ConstraintUtils
+{
+    /**
+     * Return the bits of security for the passed in RSA modulus or DH/DSA group value.
+     *
+     * @param p a modulus or group value
+     * @return the security strength in bits.
+     */
+    public static int bitsOfSecurityFor(BigInteger p)
+    {
+        return bitsOfSecurityForFF(p.bitLength());
+    }
+
+    /**
+     * Return the bits of security for the passed in Elliptic Curve.
+     *
+     * @param curve the ECCurve of interest.
+     * @return the security strength in bits.
+     */
+    public static int bitsOfSecurityFor(ECCurve curve)
+    {
+        int sBits = (curve.getFieldSize() + 1) / 2;
+
+        return (sBits > 256) ? 256 : sBits;
+    }
+
+    public static int bitsOfSecurityForFF(int strength)
+    {
+        if (strength >= 2048)
+        {
+            return (strength >= 3072) ?
+                        ((strength >= 7680) ?
+                            ((strength >= 15360) ? 256
+                            : 192)
+                        : 128)
+                   : 112;
+        }
+
+        return (strength >= 1024) ? 80 : 20;      // TODO: possibly a bit harsh...
+    }
+}
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/constraints/DefaultServiceProperties.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/constraints/DefaultServiceProperties.java
new file mode 100644
index 0000000..21fe865
--- /dev/null
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/constraints/DefaultServiceProperties.java
@@ -0,0 +1,59 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.org.bouncycastle.crypto.constraints;
+
+import com.android.org.bouncycastle.crypto.CryptoServiceProperties;
+import com.android.org.bouncycastle.crypto.CryptoServicePurpose;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+public class DefaultServiceProperties
+    implements CryptoServiceProperties
+{
+    private final String algorithm;
+    private final int bitsOfSecurity;
+    private final Object params;
+    private final CryptoServicePurpose purpose;
+
+    public DefaultServiceProperties(String algorithm, int bitsOfSecurity)
+    {
+        this(algorithm, bitsOfSecurity, null, CryptoServicePurpose.ANY);
+    }
+
+    public DefaultServiceProperties(String algorithm, int bitsOfSecurity, Object params)
+    {
+        this(algorithm, bitsOfSecurity, params, CryptoServicePurpose.ANY);
+    }
+
+    public DefaultServiceProperties(String algorithm, int bitsOfSecurity, Object params, CryptoServicePurpose purpose)
+    {
+        this.algorithm = algorithm;
+        this.bitsOfSecurity = bitsOfSecurity;
+        this.params = params;
+        if (params instanceof CryptoServicePurpose)
+        {
+            throw new IllegalArgumentException("params should not be CryptoServicePurpose");
+        }
+        this.purpose = purpose;
+    }
+
+    public int bitsOfSecurity()
+    {
+        return bitsOfSecurity;
+    }
+
+    public String getServiceName()
+    {
+        return algorithm;
+    }
+
+    public CryptoServicePurpose getPurpose()
+    {
+        return purpose;
+    }
+
+    public Object getParams()
+    {
+        return params;
+    }
+}
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/digests/AsconDigest.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/digests/AsconDigest.java
new file mode 100644
index 0000000..7e4f2b8
--- /dev/null
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/digests/AsconDigest.java
@@ -0,0 +1,222 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.org.bouncycastle.crypto.digests;
+
+import java.io.ByteArrayOutputStream;
+
+import com.android.org.bouncycastle.crypto.DataLengthException;
+import com.android.org.bouncycastle.crypto.ExtendedDigest;
+import com.android.org.bouncycastle.crypto.OutputLengthException;
+
+/* ASCON v1.2 Digest, https://ascon.iaik.tugraz.at/ .
+ * <p>
+ * https://csrc.nist.gov/CSRC/media/Projects/lightweight-cryptography/documents/finalist-round/updated-spec-doc/ascon-spec-final.pdf
+ * <p>
+ * ASCON v1.2 Digest with reference to C Reference Impl from: https://github.com/ascon/ascon-c .
+ */
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+public class AsconDigest
+    implements ExtendedDigest
+{
+    /**
+     * @hide This class is not part of the Android public SDK API
+     */
+    public enum AsconParameters
+    {
+        AsconHash,
+        AsconHashA,
+    }
+
+    AsconParameters asconParameters;
+
+    public AsconDigest(AsconParameters parameters)
+    {
+        this.asconParameters = parameters;
+        switch (parameters)
+        {
+        case AsconHash:
+            ASCON_PB_ROUNDS = 12;
+            algorithmName = "Ascon-Hash";
+            break;
+        case AsconHashA:
+            ASCON_PB_ROUNDS = 8;
+            algorithmName = "Ascon-HashA";
+            break;
+        default:
+            throw new IllegalArgumentException("Invalid parameter settings for Ascon Hash");
+        }
+        reset();
+    }
+
+    private final String algorithmName;
+    private final ByteArrayOutputStream buffer = new ByteArrayOutputStream();
+    private long x0;
+    private long x1;
+    private long x2;
+    private long x3;
+    private long x4;
+    private final int CRYPTO_BYTES = 32;
+    private final int ASCON_PB_ROUNDS;
+
+    private long ROR(long x, int n)
+    {
+        return x >>> n | x << (64 - n);
+    }
+
+    private void ROUND(long C)
+    {
+        long t0 = x0 ^ x1 ^ x2 ^ x3 ^ C ^ (x1 & (x0 ^ x2 ^ x4 ^ C));
+        long t1 = x0 ^ x2 ^ x3 ^ x4 ^ C ^ ((x1 ^ x2 ^ C) & (x1 ^ x3));
+        long t2 = x1 ^ x2 ^ x4 ^ C ^ (x3 & x4);
+        long t3 = x0 ^ x1 ^ x2 ^ C ^ ((~x0) & (x3 ^ x4));
+        long t4 = x1 ^ x3 ^ x4 ^ ((x0 ^ x4) & x1);
+        x0 = t0 ^ ROR(t0, 19) ^ ROR(t0, 28);
+        x1 = t1 ^ ROR(t1, 39) ^ ROR(t1, 61);
+        x2 = ~(t2 ^ ROR(t2, 1) ^ ROR(t2, 6));
+        x3 = t3 ^ ROR(t3, 10) ^ ROR(t3, 17);
+        x4 = t4 ^ ROR(t4, 7) ^ ROR(t4, 41);
+    }
+
+    private void P(int nr)
+    {
+        if (nr == 12)
+        {
+            ROUND(0xf0L);
+            ROUND(0xe1L);
+            ROUND(0xd2L);
+            ROUND(0xc3L);
+        }
+        if (nr >= 8)
+        {
+            ROUND(0xb4L);
+            ROUND(0xa5L);
+        }
+        ROUND(0x96L);
+        ROUND(0x87L);
+        ROUND(0x78L);
+        ROUND(0x69L);
+        ROUND(0x5aL);
+        ROUND(0x4bL);
+    }
+
+    private long PAD(int i)
+    {
+        return 0x80L << (56 - (i << 3));
+    }
+
+    private long LOADBYTES(final byte[] bytes, int inOff, int n)
+    {
+        long x = 0;
+        for (int i = 0; i < n; ++i)
+        {
+            x |= (bytes[i + inOff] & 0xFFL) << ((7 - i) << 3);
+        }
+        return x;
+    }
+
+    private void STOREBYTES(byte[] bytes, int inOff, long w, int n)
+    {
+        for (int i = 0; i < n; ++i)
+        {
+            bytes[i + inOff] = (byte)(w >>> ((7 - i) << 3));
+        }
+    }
+
+    @Override
+    public String getAlgorithmName()
+    {
+        return algorithmName;
+    }
+
+    @Override
+    public int getDigestSize()
+    {
+        return CRYPTO_BYTES;
+    }
+
+    @Override
+    public int getByteLength()
+    {
+        return 8;
+    }
+
+    @Override
+    public void update(byte in)
+    {
+        buffer.write(in);
+    }
+
+    @Override
+    public void update(byte[] input, int inOff, int len)
+    {
+        if ((inOff + len) > input.length)
+        {
+            throw new DataLengthException("input buffer too short");
+        }
+        buffer.write(input, inOff, len);
+    }
+
+    @Override
+    public int doFinal(byte[] output, int outOff)
+    {
+        if (CRYPTO_BYTES + outOff > output.length)
+        {
+            throw new OutputLengthException("output buffer is too short");
+        }
+        byte[] input = buffer.toByteArray();
+        int len = buffer.size();
+        int inOff = 0;
+        /* absorb full plaintext blocks */
+        int ASCON_HASH_RATE = 8;
+        while (len >= ASCON_HASH_RATE)
+        {
+            x0 ^= LOADBYTES(input, inOff, 8);
+            P(ASCON_PB_ROUNDS);
+            inOff += ASCON_HASH_RATE;
+            len -= ASCON_HASH_RATE;
+        }
+        /* absorb final plaintext block */
+        x0 ^= LOADBYTES(input, inOff, len);
+        x0 ^= PAD(len);
+        int ASCON_PA_ROUNDS = 12;
+        P(ASCON_PA_ROUNDS);
+        /* squeeze full output blocks */
+        len = CRYPTO_BYTES;
+        while (len > ASCON_HASH_RATE)
+        {
+            STOREBYTES(output, outOff, x0, 8);
+            P(ASCON_PB_ROUNDS);
+            outOff += ASCON_HASH_RATE;
+            len -= ASCON_HASH_RATE;
+        }
+        /* squeeze final output block */
+        STOREBYTES(output, outOff, x0, len);
+        reset();
+        return CRYPTO_BYTES;
+    }
+
+    @Override
+    public void reset()
+    {
+        buffer.reset();
+        /* initialize */
+        switch (asconParameters)
+        {
+        case AsconHashA:
+            x0 = 92044056785660070L;
+            x1 = 8326807761760157607L;
+            x2 = 3371194088139667532L;
+            x3 = -2956994353054992515L;
+            x4 = -6828509670848688761L;
+            break;
+        case AsconHash:
+            x0 = -1255492011513352131L;
+            x1 = -8380609354527731710L;
+            x2 = -5437372128236807582L;
+            x3 = 4834782570098516968L;
+            x4 = 3787428097924915520L;
+            break;
+        }
+    }
+}
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/digests/GeneralDigest.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/digests/GeneralDigest.java
index c075dce..9a1b9bf 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/digests/GeneralDigest.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/digests/GeneralDigest.java
@@ -1,4 +1,5 @@
 /* GENERATED SOURCE. DO NOT MODIFY. */
+// BEGIN Android-changed: maintain old behaviour
 package com.android.org.bouncycastle.crypto.digests;
 
 import com.android.org.bouncycastle.crypto.ExtendedDigest;
@@ -160,3 +161,4 @@
 
     protected abstract void processBlock();
 }
+// END Android-changed: maintain old behaviour
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/digests/Haraka256Digest.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/digests/Haraka256Digest.java
new file mode 100644
index 0000000..61fa962
--- /dev/null
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/digests/Haraka256Digest.java
@@ -0,0 +1,156 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.org.bouncycastle.crypto.digests;
+
+import com.android.org.bouncycastle.crypto.CryptoServicePurpose;
+import com.android.org.bouncycastle.crypto.CryptoServicesRegistrar;
+import com.android.org.bouncycastle.util.Arrays;
+import com.android.org.bouncycastle.util.Bytes;
+
+/**
+ * Haraka-256 v2, https://eprint.iacr.org/2016/098.pdf
+ * <p>
+ * Haraka256-256 with reference to Python Reference Impl from: https://github.com/kste/haraka
+ * </p>
+ * @hide This class is not part of the Android public SDK API
+ */
+public class Haraka256Digest
+    extends HarakaBase
+{
+    private void mix256(byte[][] s1, byte[][] s2)
+    {
+        System.arraycopy(s1[0], 0, s2[0], 0, 4);
+        System.arraycopy(s1[1], 0, s2[0], 4, 4);
+        System.arraycopy(s1[0], 4, s2[0], 8, 4);
+        System.arraycopy(s1[1], 4, s2[0], 12, 4);
+
+        System.arraycopy(s1[0], 8, s2[1], 0, 4);
+        System.arraycopy(s1[1], 8, s2[1], 4, 4);
+        System.arraycopy(s1[0], 12, s2[1], 8, 4);
+        System.arraycopy(s1[1], 12, s2[1], 12, 4);
+    }
+
+    private int haraka256256(byte[] msg, byte[] out, int outOff)
+    {
+        byte[][] s1 = new byte[2][16];
+        byte[][] s2 = new byte[2][16];
+
+        System.arraycopy(msg, 0, s1[0], 0, 16);
+        System.arraycopy(msg, 16, s1[1], 0, 16);
+
+        s1[0] = aesEnc(s1[0], RC[0]);
+        s1[1] = aesEnc(s1[1], RC[1]);
+        s1[0] = aesEnc(s1[0], RC[2]);
+        s1[1] = aesEnc(s1[1], RC[3]);
+        mix256(s1, s2);
+
+        s1[0] = aesEnc(s2[0], RC[4]);
+        s1[1] = aesEnc(s2[1], RC[5]);
+        s1[0] = aesEnc(s1[0], RC[6]);
+        s1[1] = aesEnc(s1[1], RC[7]);
+        mix256(s1, s2);
+
+        s1[0] = aesEnc(s2[0], RC[8]);
+        s1[1] = aesEnc(s2[1], RC[9]);
+        s1[0] = aesEnc(s1[0], RC[10]);
+        s1[1] = aesEnc(s1[1], RC[11]);
+        mix256(s1, s2);
+
+        s1[0] = aesEnc(s2[0], RC[12]);
+        s1[1] = aesEnc(s2[1], RC[13]);
+        s1[0] = aesEnc(s1[0], RC[14]);
+        s1[1] = aesEnc(s1[1], RC[15]);
+        mix256(s1, s2);
+
+        s1[0] = aesEnc(s2[0], RC[16]);
+        s1[1] = aesEnc(s2[1], RC[17]);
+        s1[0] = aesEnc(s1[0], RC[18]);
+        s1[1] = aesEnc(s1[1], RC[19]);
+        mix256(s1, s2);
+
+        Bytes.xor(16, s2[0], 0, msg,  0, out, outOff);
+        Bytes.xor(16, s2[1], 0, msg, 16, out, outOff + 16);
+
+        return DIGEST_SIZE;
+    }
+
+    private final byte[] buffer;
+    private int off;
+
+    private final CryptoServicePurpose purpose;
+
+
+    public Haraka256Digest()
+    {
+        this(CryptoServicePurpose.ANY);
+    }
+
+    public Haraka256Digest(CryptoServicePurpose purpose)
+    {
+        this.purpose = purpose;
+
+        this.buffer = new byte[32];
+
+        CryptoServicesRegistrar.checkConstraints(Utils.getDefaultProperties(this, getDigestSize()*4, purpose));
+    }
+
+    public Haraka256Digest(Haraka256Digest digest)
+    {
+        this.purpose = digest.purpose;
+
+        this.buffer = Arrays.clone(digest.buffer);
+        this.off = digest.off;
+
+        CryptoServicesRegistrar.checkConstraints(Utils.getDefaultProperties(this, getDigestSize()*4, purpose));
+    }
+
+    public String getAlgorithmName()
+    {
+        return "Haraka-256";
+    }
+
+    public void update(byte in)
+    {
+        if (off > 32 - 1)
+        {
+            throw new IllegalArgumentException("total input cannot be more than 32 bytes");
+        }
+
+        buffer[off++] = in;
+    }
+
+    public void update(byte[] in, int inOff, int len)
+    {
+        if (off > 32 - len)
+        {
+            throw new IllegalArgumentException("total input cannot be more than 32 bytes");
+        }
+
+        System.arraycopy(in, inOff, buffer, off, len);
+        off += len;
+    }
+
+    public int doFinal(byte[] out, int outOff)
+    {
+        if (off != 32)
+        {
+            throw new IllegalStateException("input must be exactly 32 bytes");
+        }
+
+        if (out.length - outOff < 32)
+        {
+            throw new IllegalArgumentException("output too short to receive digest");
+        }
+
+        int rv = haraka256256(buffer, out, outOff);
+
+        reset();
+
+        return rv;
+    }
+
+    public void reset()
+    {
+        off = 0;
+        Arrays.clear(buffer);
+    }
+}
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/digests/Haraka512Digest.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/digests/Haraka512Digest.java
new file mode 100644
index 0000000..5035ed3
--- /dev/null
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/digests/Haraka512Digest.java
@@ -0,0 +1,191 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.org.bouncycastle.crypto.digests;
+
+import com.android.org.bouncycastle.crypto.CryptoServicePurpose;
+import com.android.org.bouncycastle.util.Arrays;
+import com.android.org.bouncycastle.util.Bytes;
+
+/**
+ * Haraka-512 v2, https://eprint.iacr.org/2016/098.pdf
+ * <p>
+ * Haraka512-256 with reference to Python Reference Impl from: https://github.com/kste/haraka
+ * </p>
+ * @hide This class is not part of the Android public SDK API
+ */
+public class Haraka512Digest
+    extends HarakaBase
+{
+    private final byte[] buffer;
+    private int off;
+
+    private final CryptoServicePurpose purpose;
+
+
+    public Haraka512Digest()
+    {
+        this(CryptoServicePurpose.ANY);
+    }
+    public Haraka512Digest(CryptoServicePurpose purpose)
+    {
+        this.purpose = purpose;
+
+        this.buffer = new byte[64];
+    }
+
+    public Haraka512Digest(Haraka512Digest digest)
+    {
+        this.purpose = digest.purpose;
+
+        this.buffer = Arrays.clone(digest.buffer);
+        this.off = digest.off;
+    }
+
+    private void mix512(byte[][] s1, byte[][] s2)
+    {
+        System.arraycopy(s1[0], 12, s2[0], 0, 4);
+        System.arraycopy(s1[2], 12, s2[0], 4, 4);
+        System.arraycopy(s1[1], 12, s2[0], 8, 4);
+        System.arraycopy(s1[3], 12, s2[0], 12, 4);
+
+        System.arraycopy(s1[2], 0, s2[1], 0, 4);
+        System.arraycopy(s1[0], 0, s2[1], 4, 4);
+        System.arraycopy(s1[3], 0, s2[1], 8, 4);
+        System.arraycopy(s1[1], 0, s2[1], 12, 4);
+
+        System.arraycopy(s1[2], 4, s2[2], 0, 4);
+        System.arraycopy(s1[0], 4, s2[2], 4, 4);
+        System.arraycopy(s1[3], 4, s2[2], 8, 4);
+        System.arraycopy(s1[1], 4, s2[2], 12, 4);
+
+        System.arraycopy(s1[0], 8, s2[3], 0, 4);
+        System.arraycopy(s1[2], 8, s2[3], 4, 4);
+        System.arraycopy(s1[1], 8, s2[3], 8, 4);
+        System.arraycopy(s1[3], 8, s2[3], 12, 4);
+    }
+
+    private int haraka512256(byte[] msg, byte[] out, int outOff)
+    {
+        byte[][] s1 = new byte[4][16];
+        byte[][] s2 = new byte[4][16];
+
+        //-- Unrolled version of above.
+
+        System.arraycopy(msg, 0, s1[0], 0, 16);
+        System.arraycopy(msg, 16, s1[1], 0, 16);
+        System.arraycopy(msg, 32, s1[2], 0, 16);
+        System.arraycopy(msg, 48, s1[3], 0, 16);
+
+        s1[0] = aesEnc(s1[0], RC[0]);
+        s1[1] = aesEnc(s1[1], RC[1]);
+        s1[2] = aesEnc(s1[2], RC[2]);
+        s1[3] = aesEnc(s1[3], RC[3]);
+        s1[0] = aesEnc(s1[0], RC[4]);
+        s1[1] = aesEnc(s1[1], RC[5]);
+        s1[2] = aesEnc(s1[2], RC[6]);
+        s1[3] = aesEnc(s1[3], RC[7]);
+        mix512(s1, s2);
+
+        s1[0] = aesEnc(s2[0], RC[8]);
+        s1[1] = aesEnc(s2[1], RC[9]);
+        s1[2] = aesEnc(s2[2], RC[10]);
+        s1[3] = aesEnc(s2[3], RC[11]);
+        s1[0] = aesEnc(s1[0], RC[12]);
+        s1[1] = aesEnc(s1[1], RC[13]);
+        s1[2] = aesEnc(s1[2], RC[14]);
+        s1[3] = aesEnc(s1[3], RC[15]);
+        mix512(s1, s2);
+
+        s1[0] = aesEnc(s2[0], RC[16]);
+        s1[1] = aesEnc(s2[1], RC[17]);
+        s1[2] = aesEnc(s2[2], RC[18]);
+        s1[3] = aesEnc(s2[3], RC[19]);
+        s1[0] = aesEnc(s1[0], RC[20]);
+        s1[1] = aesEnc(s1[1], RC[21]);
+        s1[2] = aesEnc(s1[2], RC[22]);
+        s1[3] = aesEnc(s1[3], RC[23]);
+        mix512(s1, s2);
+
+        s1[0] = aesEnc(s2[0], RC[24]);
+        s1[1] = aesEnc(s2[1], RC[25]);
+        s1[2] = aesEnc(s2[2], RC[26]);
+        s1[3] = aesEnc(s2[3], RC[27]);
+        s1[0] = aesEnc(s1[0], RC[28]);
+        s1[1] = aesEnc(s1[1], RC[29]);
+        s1[2] = aesEnc(s1[2], RC[30]);
+        s1[3] = aesEnc(s1[3], RC[31]);
+        mix512(s1, s2);
+
+        s1[0] = aesEnc(s2[0], RC[32]);
+        s1[1] = aesEnc(s2[1], RC[33]);
+        s1[2] = aesEnc(s2[2], RC[34]);
+        s1[3] = aesEnc(s2[3], RC[35]);
+        s1[0] = aesEnc(s1[0], RC[36]);
+        s1[1] = aesEnc(s1[1], RC[37]);
+        s1[2] = aesEnc(s1[2], RC[38]);
+        s1[3] = aesEnc(s1[3], RC[39]);
+        mix512(s1, s2);
+
+        Bytes.xor(16, s2[0], 0, msg,  0, s1[0], 0);
+        Bytes.xor(16, s2[1], 0, msg, 16, s1[1], 0);
+        Bytes.xor(16, s2[2], 0, msg, 32, s1[2], 0);
+        Bytes.xor(16, s2[3], 0, msg, 48, s1[3], 0);
+
+        System.arraycopy(s1[0], 8, out, outOff, 8);
+        System.arraycopy(s1[1], 8, out, outOff + 8, 8);
+        System.arraycopy(s1[2], 0, out, outOff + 16, 8);
+        System.arraycopy(s1[3], 0, out, outOff + 24, 8);
+
+        return DIGEST_SIZE;
+    }
+
+    public String getAlgorithmName()
+    {
+        return "Haraka-512";
+    }
+
+    public void update(byte in)
+    {
+        if (off > 64 - 1)
+        {
+            throw new IllegalArgumentException("total input cannot be more than 64 bytes");
+        }
+
+        buffer[off++] = in;
+    }
+
+    public void update(byte[] in, int inOff, int len)
+    {
+        if (off > 64 - len)
+        {
+            throw new IllegalArgumentException("total input cannot be more than 64 bytes");
+        }
+
+        System.arraycopy(in, inOff, buffer, off, len);
+        off += len;
+    }
+
+    public int doFinal(byte[] out, int outOff)
+    {
+        if (off != 64)
+        {
+            throw new IllegalStateException("input must be exactly 64 bytes");
+        }
+
+        if (out.length - outOff < 32)
+        {
+            throw new IllegalArgumentException("output too short to receive digest");
+        }
+
+        int rv = haraka512256(buffer, out, outOff);
+
+        reset();
+
+        return rv;
+    }
+
+    public void reset()
+    {
+        off = 0;
+        Arrays.clear(buffer);
+    }
+}
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/digests/HarakaBase.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/digests/HarakaBase.java
new file mode 100644
index 0000000..cd7f508
--- /dev/null
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/digests/HarakaBase.java
@@ -0,0 +1,149 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.org.bouncycastle.crypto.digests;
+
+import com.android.org.bouncycastle.crypto.Digest;
+import com.android.org.bouncycastle.util.Bytes;
+
+/**
+ * Base class for Haraka v2, https://eprint.iacr.org/2016/098.pdf
+ * @hide This class is not part of the Android public SDK API
+ */
+public abstract class HarakaBase
+    implements Digest
+{
+    protected static final int DIGEST_SIZE = 32;
+
+    // Haraka round constants
+    static final byte[][] RC = new byte[][]
+    {
+        new byte[]{ (byte)0x9D, (byte)0x7B, (byte)0x81, (byte)0x75, (byte)0xF0, (byte)0xFE, (byte)0xC5, (byte)0xB2, (byte)0x0A, (byte)0xC0, (byte)0x20, (byte)0xE6, (byte)0x4C, (byte)0x70, (byte)0x84, (byte)0x06 },
+        new byte[]{ (byte)0x17, (byte)0xF7, (byte)0x08, (byte)0x2F, (byte)0xA4, (byte)0x6B, (byte)0x0F, (byte)0x64, (byte)0x6B, (byte)0xA0, (byte)0xF3, (byte)0x88, (byte)0xE1, (byte)0xB4, (byte)0x66, (byte)0x8B },
+        new byte[]{ (byte)0x14, (byte)0x91, (byte)0x02, (byte)0x9F, (byte)0x60, (byte)0x9D, (byte)0x02, (byte)0xCF, (byte)0x98, (byte)0x84, (byte)0xF2, (byte)0x53, (byte)0x2D, (byte)0xDE, (byte)0x02, (byte)0x34 },
+        new byte[]{ (byte)0x79, (byte)0x4F, (byte)0x5B, (byte)0xFD, (byte)0xAF, (byte)0xBC, (byte)0xF3, (byte)0xBB, (byte)0x08, (byte)0x4F, (byte)0x7B, (byte)0x2E, (byte)0xE6, (byte)0xEA, (byte)0xD6, (byte)0x0E },
+        new byte[]{ (byte)0x44, (byte)0x70, (byte)0x39, (byte)0xBE, (byte)0x1C, (byte)0xCD, (byte)0xEE, (byte)0x79, (byte)0x8B, (byte)0x44, (byte)0x72, (byte)0x48, (byte)0xCB, (byte)0xB0, (byte)0xCF, (byte)0xCB },
+        new byte[]{ (byte)0x7B, (byte)0x05, (byte)0x8A, (byte)0x2B, (byte)0xED, (byte)0x35, (byte)0x53, (byte)0x8D, (byte)0xB7, (byte)0x32, (byte)0x90, (byte)0x6E, (byte)0xEE, (byte)0xCD, (byte)0xEA, (byte)0x7E },
+        new byte[]{ (byte)0x1B, (byte)0xEF, (byte)0x4F, (byte)0xDA, (byte)0x61, (byte)0x27, (byte)0x41, (byte)0xE2, (byte)0xD0, (byte)0x7C, (byte)0x2E, (byte)0x5E, (byte)0x43, (byte)0x8F, (byte)0xC2, (byte)0x67 },
+        new byte[]{ (byte)0x3B, (byte)0x0B, (byte)0xC7, (byte)0x1F, (byte)0xE2, (byte)0xFD, (byte)0x5F, (byte)0x67, (byte)0x07, (byte)0xCC, (byte)0xCA, (byte)0xAF, (byte)0xB0, (byte)0xD9, (byte)0x24, (byte)0x29 },
+        new byte[]{ (byte)0xEE, (byte)0x65, (byte)0xD4, (byte)0xB9, (byte)0xCA, (byte)0x8F, (byte)0xDB, (byte)0xEC, (byte)0xE9, (byte)0x7F, (byte)0x86, (byte)0xE6, (byte)0xF1, (byte)0x63, (byte)0x4D, (byte)0xAB },
+        new byte[]{ (byte)0x33, (byte)0x7E, (byte)0x03, (byte)0xAD, (byte)0x4F, (byte)0x40, (byte)0x2A, (byte)0x5B, (byte)0x64, (byte)0xCD, (byte)0xB7, (byte)0xD4, (byte)0x84, (byte)0xBF, (byte)0x30, (byte)0x1C },
+        new byte[]{ (byte)0x00, (byte)0x98, (byte)0xF6, (byte)0x8D, (byte)0x2E, (byte)0x8B, (byte)0x02, (byte)0x69, (byte)0xBF, (byte)0x23, (byte)0x17, (byte)0x94, (byte)0xB9, (byte)0x0B, (byte)0xCC, (byte)0xB2 },
+        new byte[]{ (byte)0x8A, (byte)0x2D, (byte)0x9D, (byte)0x5C, (byte)0xC8, (byte)0x9E, (byte)0xAA, (byte)0x4A, (byte)0x72, (byte)0x55, (byte)0x6F, (byte)0xDE, (byte)0xA6, (byte)0x78, (byte)0x04, (byte)0xFA },
+        new byte[]{ (byte)0xD4, (byte)0x9F, (byte)0x12, (byte)0x29, (byte)0x2E, (byte)0x4F, (byte)0xFA, (byte)0x0E, (byte)0x12, (byte)0x2A, (byte)0x77, (byte)0x6B, (byte)0x2B, (byte)0x9F, (byte)0xB4, (byte)0xDF },
+        new byte[]{ (byte)0xEE, (byte)0x12, (byte)0x6A, (byte)0xBB, (byte)0xAE, (byte)0x11, (byte)0xD6, (byte)0x32, (byte)0x36, (byte)0xA2, (byte)0x49, (byte)0xF4, (byte)0x44, (byte)0x03, (byte)0xA1, (byte)0x1E },
+        new byte[]{ (byte)0xA6, (byte)0xEC, (byte)0xA8, (byte)0x9C, (byte)0xC9, (byte)0x00, (byte)0x96, (byte)0x5F, (byte)0x84, (byte)0x00, (byte)0x05, (byte)0x4B, (byte)0x88, (byte)0x49, (byte)0x04, (byte)0xAF },
+        new byte[]{ (byte)0xEC, (byte)0x93, (byte)0xE5, (byte)0x27, (byte)0xE3, (byte)0xC7, (byte)0xA2, (byte)0x78, (byte)0x4F, (byte)0x9C, (byte)0x19, (byte)0x9D, (byte)0xD8, (byte)0x5E, (byte)0x02, (byte)0x21 },
+        new byte[]{ (byte)0x73, (byte)0x01, (byte)0xD4, (byte)0x82, (byte)0xCD, (byte)0x2E, (byte)0x28, (byte)0xB9, (byte)0xB7, (byte)0xC9, (byte)0x59, (byte)0xA7, (byte)0xF8, (byte)0xAA, (byte)0x3A, (byte)0xBF },
+        new byte[]{ (byte)0x6B, (byte)0x7D, (byte)0x30, (byte)0x10, (byte)0xD9, (byte)0xEF, (byte)0xF2, (byte)0x37, (byte)0x17, (byte)0xB0, (byte)0x86, (byte)0x61, (byte)0x0D, (byte)0x70, (byte)0x60, (byte)0x62 },
+        new byte[]{ (byte)0xC6, (byte)0x9A, (byte)0xFC, (byte)0xF6, (byte)0x53, (byte)0x91, (byte)0xC2, (byte)0x81, (byte)0x43, (byte)0x04, (byte)0x30, (byte)0x21, (byte)0xC2, (byte)0x45, (byte)0xCA, (byte)0x5A },
+        new byte[]{ (byte)0x3A, (byte)0x94, (byte)0xD1, (byte)0x36, (byte)0xE8, (byte)0x92, (byte)0xAF, (byte)0x2C, (byte)0xBB, (byte)0x68, (byte)0x6B, (byte)0x22, (byte)0x3C, (byte)0x97, (byte)0x23, (byte)0x92 },
+        new byte[]{ (byte)0xB4, (byte)0x71, (byte)0x10, (byte)0xE5, (byte)0x58, (byte)0xB9, (byte)0xBA, (byte)0x6C, (byte)0xEB, (byte)0x86, (byte)0x58, (byte)0x22, (byte)0x38, (byte)0x92, (byte)0xBF, (byte)0xD3 },
+        new byte[]{ (byte)0x8D, (byte)0x12, (byte)0xE1, (byte)0x24, (byte)0xDD, (byte)0xFD, (byte)0x3D, (byte)0x93, (byte)0x77, (byte)0xC6, (byte)0xF0, (byte)0xAE, (byte)0xE5, (byte)0x3C, (byte)0x86, (byte)0xDB },
+        new byte[]{ (byte)0xB1, (byte)0x12, (byte)0x22, (byte)0xCB, (byte)0xE3, (byte)0x8D, (byte)0xE4, (byte)0x83, (byte)0x9C, (byte)0xA0, (byte)0xEB, (byte)0xFF, (byte)0x68, (byte)0x62, (byte)0x60, (byte)0xBB },
+        new byte[]{ (byte)0x7D, (byte)0xF7, (byte)0x2B, (byte)0xC7, (byte)0x4E, (byte)0x1A, (byte)0xB9, (byte)0x2D, (byte)0x9C, (byte)0xD1, (byte)0xE4, (byte)0xE2, (byte)0xDC, (byte)0xD3, (byte)0x4B, (byte)0x73 },
+        new byte[]{ (byte)0x4E, (byte)0x92, (byte)0xB3, (byte)0x2C, (byte)0xC4, (byte)0x15, (byte)0x14, (byte)0x4B, (byte)0x43, (byte)0x1B, (byte)0x30, (byte)0x61, (byte)0xC3, (byte)0x47, (byte)0xBB, (byte)0x43 },
+        new byte[]{ (byte)0x99, (byte)0x68, (byte)0xEB, (byte)0x16, (byte)0xDD, (byte)0x31, (byte)0xB2, (byte)0x03, (byte)0xF6, (byte)0xEF, (byte)0x07, (byte)0xE7, (byte)0xA8, (byte)0x75, (byte)0xA7, (byte)0xDB },
+        new byte[]{ (byte)0x2C, (byte)0x47, (byte)0xCA, (byte)0x7E, (byte)0x02, (byte)0x23, (byte)0x5E, (byte)0x8E, (byte)0x77, (byte)0x59, (byte)0x75, (byte)0x3C, (byte)0x4B, (byte)0x61, (byte)0xF3, (byte)0x6D },
+        new byte[]{ (byte)0xF9, (byte)0x17, (byte)0x86, (byte)0xB8, (byte)0xB9, (byte)0xE5, (byte)0x1B, (byte)0x6D, (byte)0x77, (byte)0x7D, (byte)0xDE, (byte)0xD6, (byte)0x17, (byte)0x5A, (byte)0xA7, (byte)0xCD },
+        new byte[]{ (byte)0x5D, (byte)0xEE, (byte)0x46, (byte)0xA9, (byte)0x9D, (byte)0x06, (byte)0x6C, (byte)0x9D, (byte)0xAA, (byte)0xE9, (byte)0xA8, (byte)0x6B, (byte)0xF0, (byte)0x43, (byte)0x6B, (byte)0xEC },
+        new byte[]{ (byte)0xC1, (byte)0x27, (byte)0xF3, (byte)0x3B, (byte)0x59, (byte)0x11, (byte)0x53, (byte)0xA2, (byte)0x2B, (byte)0x33, (byte)0x57, (byte)0xF9, (byte)0x50, (byte)0x69, (byte)0x1E, (byte)0xCB },
+        new byte[]{ (byte)0xD9, (byte)0xD0, (byte)0x0E, (byte)0x60, (byte)0x53, (byte)0x03, (byte)0xED, (byte)0xE4, (byte)0x9C, (byte)0x61, (byte)0xDA, (byte)0x00, (byte)0x75, (byte)0x0C, (byte)0xEE, (byte)0x2C },
+        new byte[]{ (byte)0x50, (byte)0xA3, (byte)0xA4, (byte)0x63, (byte)0xBC, (byte)0xBA, (byte)0xBB, (byte)0x80, (byte)0xAB, (byte)0x0C, (byte)0xE9, (byte)0x96, (byte)0xA1, (byte)0xA5, (byte)0xB1, (byte)0xF0 },
+        new byte[]{ (byte)0x39, (byte)0xCA, (byte)0x8D, (byte)0x93, (byte)0x30, (byte)0xDE, (byte)0x0D, (byte)0xAB, (byte)0x88, (byte)0x29, (byte)0x96, (byte)0x5E, (byte)0x02, (byte)0xB1, (byte)0x3D, (byte)0xAE },
+        new byte[]{ (byte)0x42, (byte)0xB4, (byte)0x75, (byte)0x2E, (byte)0xA8, (byte)0xF3, (byte)0x14, (byte)0x88, (byte)0x0B, (byte)0xA4, (byte)0x54, (byte)0xD5, (byte)0x38, (byte)0x8F, (byte)0xBB, (byte)0x17 },
+        new byte[]{ (byte)0xF6, (byte)0x16, (byte)0x0A, (byte)0x36, (byte)0x79, (byte)0xB7, (byte)0xB6, (byte)0xAE, (byte)0xD7, (byte)0x7F, (byte)0x42, (byte)0x5F, (byte)0x5B, (byte)0x8A, (byte)0xBB, (byte)0x34 },
+        new byte[]{ (byte)0xDE, (byte)0xAF, (byte)0xBA, (byte)0xFF, (byte)0x18, (byte)0x59, (byte)0xCE, (byte)0x43, (byte)0x38, (byte)0x54, (byte)0xE5, (byte)0xCB, (byte)0x41, (byte)0x52, (byte)0xF6, (byte)0x26 },
+        new byte[]{ (byte)0x78, (byte)0xC9, (byte)0x9E, (byte)0x83, (byte)0xF7, (byte)0x9C, (byte)0xCA, (byte)0xA2, (byte)0x6A, (byte)0x02, (byte)0xF3, (byte)0xB9, (byte)0x54, (byte)0x9A, (byte)0xE9, (byte)0x4C },
+        new byte[]{ (byte)0x35, (byte)0x12, (byte)0x90, (byte)0x22, (byte)0x28, (byte)0x6E, (byte)0xC0, (byte)0x40, (byte)0xBE, (byte)0xF7, (byte)0xDF, (byte)0x1B, (byte)0x1A, (byte)0xA5, (byte)0x51, (byte)0xAE },
+        new byte[]{ (byte)0xCF, (byte)0x59, (byte)0xA6, (byte)0x48, (byte)0x0F, (byte)0xBC, (byte)0x73, (byte)0xC1, (byte)0x2B, (byte)0xD2, (byte)0x7E, (byte)0xBA, (byte)0x3C, (byte)0x61, (byte)0xC1, (byte)0xA0 },
+        new byte[]{ (byte)0xA1, (byte)0x9D, (byte)0xC5, (byte)0xE9, (byte)0xFD, (byte)0xBD, (byte)0xD6, (byte)0x4A, (byte)0x88, (byte)0x82, (byte)0x28, (byte)0x02, (byte)0x03, (byte)0xCC, (byte)0x6A, (byte)0x75 },
+    };
+
+    private static final byte[][] S = new byte[][]{
+        { (byte)0x63, (byte)0x7C, (byte)0x77, (byte)0x7B, (byte)0xF2, (byte)0x6B, (byte)0x6F, (byte)0xC5, (byte)0x30, (byte)0x01, (byte)0x67, (byte)0x2B, (byte)0xFE, (byte)0xD7, (byte)0xAB, (byte)0x76 },
+        { (byte)0xCA, (byte)0x82, (byte)0xC9, (byte)0x7D, (byte)0xFA, (byte)0x59, (byte)0x47, (byte)0xF0, (byte)0xAD, (byte)0xD4, (byte)0xA2, (byte)0xAF, (byte)0x9C, (byte)0xA4, (byte)0x72, (byte)0xC0 },
+        { (byte)0xB7, (byte)0xFD, (byte)0x93, (byte)0x26, (byte)0x36, (byte)0x3F, (byte)0xF7, (byte)0xCC, (byte)0x34, (byte)0xA5, (byte)0xE5, (byte)0xF1, (byte)0x71, (byte)0xD8, (byte)0x31, (byte)0x15 },
+        { (byte)0x04, (byte)0xC7, (byte)0x23, (byte)0xC3, (byte)0x18, (byte)0x96, (byte)0x05, (byte)0x9A, (byte)0x07, (byte)0x12, (byte)0x80, (byte)0xE2, (byte)0xEB, (byte)0x27, (byte)0xB2, (byte)0x75 },
+        { (byte)0x09, (byte)0x83, (byte)0x2C, (byte)0x1A, (byte)0x1B, (byte)0x6E, (byte)0x5A, (byte)0xA0, (byte)0x52, (byte)0x3B, (byte)0xD6, (byte)0xB3, (byte)0x29, (byte)0xE3, (byte)0x2F, (byte)0x84 },
+        { (byte)0x53, (byte)0xD1, (byte)0x00, (byte)0xED, (byte)0x20, (byte)0xFC, (byte)0xB1, (byte)0x5B, (byte)0x6A, (byte)0xCB, (byte)0xBE, (byte)0x39, (byte)0x4A, (byte)0x4C, (byte)0x58, (byte)0xCF },
+        { (byte)0xD0, (byte)0xEF, (byte)0xAA, (byte)0xFB, (byte)0x43, (byte)0x4D, (byte)0x33, (byte)0x85, (byte)0x45, (byte)0xF9, (byte)0x02, (byte)0x7F, (byte)0x50, (byte)0x3C, (byte)0x9F, (byte)0xA8 },
+        { (byte)0x51, (byte)0xA3, (byte)0x40, (byte)0x8F, (byte)0x92, (byte)0x9D, (byte)0x38, (byte)0xF5, (byte)0xBC, (byte)0xB6, (byte)0xDA, (byte)0x21, (byte)0x10, (byte)0xFF, (byte)0xF3, (byte)0xD2 },
+        { (byte)0xCD, (byte)0x0C, (byte)0x13, (byte)0xEC, (byte)0x5F, (byte)0x97, (byte)0x44, (byte)0x17, (byte)0xC4, (byte)0xA7, (byte)0x7E, (byte)0x3D, (byte)0x64, (byte)0x5D, (byte)0x19, (byte)0x73 },
+        { (byte)0x60, (byte)0x81, (byte)0x4F, (byte)0xDC, (byte)0x22, (byte)0x2A, (byte)0x90, (byte)0x88, (byte)0x46, (byte)0xEE, (byte)0xB8, (byte)0x14, (byte)0xDE, (byte)0x5E, (byte)0x0B, (byte)0xDB },
+        { (byte)0xE0, (byte)0x32, (byte)0x3A, (byte)0x0A, (byte)0x49, (byte)0x06, (byte)0x24, (byte)0x5C, (byte)0xC2, (byte)0xD3, (byte)0xAC, (byte)0x62, (byte)0x91, (byte)0x95, (byte)0xE4, (byte)0x79 },
+        { (byte)0xE7, (byte)0xC8, (byte)0x37, (byte)0x6D, (byte)0x8D, (byte)0xD5, (byte)0x4E, (byte)0xA9, (byte)0x6C, (byte)0x56, (byte)0xF4, (byte)0xEA, (byte)0x65, (byte)0x7A, (byte)0xAE, (byte)0x08 },
+        { (byte)0xBA, (byte)0x78, (byte)0x25, (byte)0x2E, (byte)0x1C, (byte)0xA6, (byte)0xB4, (byte)0xC6, (byte)0xE8, (byte)0xDD, (byte)0x74, (byte)0x1F, (byte)0x4B, (byte)0xBD, (byte)0x8B, (byte)0x8A },
+        { (byte)0x70, (byte)0x3E, (byte)0xB5, (byte)0x66, (byte)0x48, (byte)0x03, (byte)0xF6, (byte)0x0E, (byte)0x61, (byte)0x35, (byte)0x57, (byte)0xB9, (byte)0x86, (byte)0xC1, (byte)0x1D, (byte)0x9E },
+        { (byte)0xE1, (byte)0xF8, (byte)0x98, (byte)0x11, (byte)0x69, (byte)0xD9, (byte)0x8E, (byte)0x94, (byte)0x9B, (byte)0x1E, (byte)0x87, (byte)0xE9, (byte)0xCE, (byte)0x55, (byte)0x28, (byte)0xDF },
+        { (byte)0x8C, (byte)0xA1, (byte)0x89, (byte)0x0D, (byte)0xBF, (byte)0xE6, (byte)0x42, (byte)0x68, (byte)0x41, (byte)0x99, (byte)0x2D, (byte)0x0F, (byte)0xB0, (byte)0x54, (byte)0xBB, (byte)0x16 },
+    };
+
+    static byte sBox(byte x)
+    {
+        return S[(((x & 0xFF) >>> 4))][x & 0xF];
+    }
+
+    static byte[] subBytes(byte[] s)
+    {
+        byte[] out = new byte[s.length];
+        out[0] = sBox(s[0]);
+        out[1] = sBox(s[1]);
+        out[2] = sBox(s[2]);
+        out[3] = sBox(s[3]);
+        out[4] = sBox(s[4]);
+        out[5] = sBox(s[5]);
+        out[6] = sBox(s[6]);
+        out[7] = sBox(s[7]);
+        out[8] = sBox(s[8]);
+        out[9] = sBox(s[9]);
+        out[10] = sBox(s[10]);
+        out[11] = sBox(s[11]);
+        out[12] = sBox(s[12]);
+        out[13] = sBox(s[13]);
+        out[14] = sBox(s[14]);
+        out[15] = sBox(s[15]);
+        return out;
+    }
+
+    static byte[] shiftRows(byte[] s)
+    {
+        return new byte[]{
+            s[0], s[5], s[10], s[15],
+            s[4], s[9], s[14], s[3],
+            s[8], s[13], s[2], s[7],
+            s[12], s[1], s[6], s[11]
+        };
+    }
+
+    static byte[] aesEnc(byte[] s, byte[] rk)
+    {
+        s = subBytes(s);
+        s = shiftRows(s);
+        s = mixColumns(s);
+        Bytes.xorTo(16, rk, s);
+        return s;
+    }
+
+    static byte mulX(byte p)
+    {
+        return (byte)(((p & 0x7F) << 1) ^ (((p & 0x80) >> 7) * 0x1B));
+    }
+
+    private static byte[] mixColumns(byte[] s)
+    {
+        byte[] out = new byte[s.length];
+        int j = 0;
+        for (int i = 0; i < 4; i++)
+        {
+            out[j++] = (byte)(mulX(s[4 * i]) ^ mulX(s[4 * i + 1]) ^ s[4 * i + 1] ^ s[4 * i + 2] ^ s[4 * i + 3]);
+            out[j++] = (byte)(s[4 * i] ^ mulX(s[4 * i + 1]) ^ mulX(s[4 * i + 2]) ^ s[4 * i + 2] ^ s[4 * i + 3]);
+            out[j++] = (byte)(s[4 * i] ^ s[4 * i + 1] ^ mulX(s[4 * i + 2]) ^ mulX(s[4 * i + 3]) ^ s[4 * i + 3]);
+            out[j++] = (byte)(mulX(s[4 * i]) ^ s[4 * i] ^ s[4 * i + 1] ^ s[4 * i + 2] ^ mulX(s[4 * i + 3]));
+        }
+        return out;
+    }
+
+    public int getDigestSize()
+    {
+        return DIGEST_SIZE;
+    }
+}
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/digests/ISAPDigest.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/digests/ISAPDigest.java
new file mode 100644
index 0000000..26bf5f2
--- /dev/null
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/digests/ISAPDigest.java
@@ -0,0 +1,148 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.org.bouncycastle.crypto.digests;
+
+import java.io.ByteArrayOutputStream;
+
+import com.android.org.bouncycastle.crypto.DataLengthException;
+import com.android.org.bouncycastle.crypto.Digest;
+import com.android.org.bouncycastle.crypto.OutputLengthException;
+import com.android.org.bouncycastle.util.Pack;
+
+/**
+ * ISAP Hash v2, https://isap.iaik.tugraz.at/
+ * https://csrc.nist.gov/CSRC/media/Projects/lightweight-cryptography/documents/finalist-round/updated-spec-doc/isap-spec-final.pdf
+ * <p>
+ * ISAP Hash v2 with reference to C Reference Impl from: https://github.com/isap-lwc/isap-code-package
+ * </p>
+ * @hide This class is not part of the Android public SDK API
+ */
+
+public class ISAPDigest
+    implements Digest
+{
+    private long x0, x1, x2, x3, x4;
+    private long t0, t1, t2, t3, t4;
+    private ByteArrayOutputStream buffer = new ByteArrayOutputStream();
+
+    private void ROUND(long C)
+    {
+        t0 = x0 ^ x1 ^ x2 ^ x3 ^ C ^ (x1 & (x0 ^ x2 ^ x4 ^ C));
+        t1 = x0 ^ x2 ^ x3 ^ x4 ^ C ^ ((x1 ^ x2 ^ C) & (x1 ^ x3));
+        t2 = x1 ^ x2 ^ x4 ^ C ^ (x3 & x4);
+        t3 = x0 ^ x1 ^ x2 ^ C ^ ((~x0) & (x3 ^ x4));
+        t4 = x1 ^ x3 ^ x4 ^ ((x0 ^ x4) & x1);
+        x0 = t0 ^ ROTR(t0, 19) ^ ROTR(t0, 28);
+        x1 = t1 ^ ROTR(t1, 39) ^ ROTR(t1, 61);
+        x2 = ~(t2 ^ ROTR(t2, 1) ^ ROTR(t2, 6));
+        x3 = t3 ^ ROTR(t3, 10) ^ ROTR(t3, 17);
+        x4 = t4 ^ ROTR(t4, 7) ^ ROTR(t4, 41);
+    }
+
+    private void P12()
+    {
+        ROUND(0xf0);
+        ROUND(0xe1);
+        ROUND(0xd2);
+        ROUND(0xc3);
+        ROUND(0xb4);
+        ROUND(0xa5);
+        ROUND(0x96);
+        ROUND(0x87);
+        ROUND(0x78);
+        ROUND(0x69);
+        ROUND(0x5a);
+        ROUND(0x4b);
+    }
+
+    private long ROTR(long x, long n)
+    {
+        return (x >>> n) | (x << (64 - n));
+    }
+
+    protected long U64BIG(long x)
+    {
+        return ((ROTR(x, 8) & (0xFF000000FF000000L)) | (ROTR(x, 24) & (0x00FF000000FF0000L)) |
+            (ROTR(x, 40) & (0x0000FF000000FF00L)) | (ROTR(x, 56) & (0x000000FF000000FFL)));
+    }
+
+    @Override
+    public String getAlgorithmName()
+    {
+        return "ISAP Hash";
+    }
+
+    @Override
+    public int getDigestSize()
+    {
+        return 32;
+    }
+
+    @Override
+    public void update(byte input)
+    {
+        buffer.write(input);
+    }
+
+    @Override
+    public void update(byte[] input, int inOff, int len)
+    {
+        if ((inOff + len) > input.length)
+        {
+            throw new DataLengthException("input buffer too short");
+        }
+        buffer.write(input, inOff, len);
+    }
+
+    @Override
+    public int doFinal(byte[] out, int outOff)
+    {
+        if (32 + outOff > out.length)
+        {
+            throw new OutputLengthException("output buffer is too short");
+        }
+        t0 = t1 = t2 = t3 = t4 = 0;
+        /* init state */
+        x0 = -1255492011513352131L;
+        x1 = -8380609354527731710L;
+        x2 = -5437372128236807582L;
+        x3 = 4834782570098516968L;
+        x4 = 3787428097924915520L;
+        /* absorb */
+        byte[] input = buffer.toByteArray();
+        int len = input.length;
+        long[] in64 = new long[len >> 3];
+        Pack.littleEndianToLong(input, 0, in64, 0, in64.length);
+        int idx = 0;
+        while (len >= 8)
+        {
+            x0 ^= U64BIG(in64[idx++]);
+            P12();
+            len -= 8;
+        }
+        /* absorb final input block */
+        x0 ^= 0x80L << ((7 - len) << 3);
+        while (len > 0)
+        {
+            x0 ^= (input[(idx << 3) + --len] & 0xFFL) << ((7 - len) << 3);
+        }
+        P12();
+        // squeeze
+        long[] out64 = new long[4];
+        for (idx = 0; idx < 3; ++idx)
+        {
+            out64[idx] = U64BIG(x0);
+            P12();
+        }
+        /* squeeze final output block */
+        out64[idx] = U64BIG(x0);
+        Pack.longToLittleEndian(out64, out, outOff);
+        buffer.reset();
+        return 32;
+    }
+
+    @Override
+    public void reset()
+    {
+        buffer.reset();
+    }
+}
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/digests/LongDigest.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/digests/LongDigest.java
index 2c9aa03..ed94702 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/digests/LongDigest.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/digests/LongDigest.java
@@ -1,6 +1,8 @@
 /* GENERATED SOURCE. DO NOT MODIFY. */
 package com.android.org.bouncycastle.crypto.digests;
 
+import com.android.org.bouncycastle.crypto.CryptoServiceProperties;
+import com.android.org.bouncycastle.crypto.CryptoServicePurpose;
 import com.android.org.bouncycastle.crypto.ExtendedDigest;
 import com.android.org.bouncycastle.util.Memoable;
 import com.android.org.bouncycastle.util.Pack;
@@ -14,6 +16,8 @@
 {
     private static final int BYTE_LENGTH = 128;
 
+    protected final CryptoServicePurpose purpose;
+
     private byte[] xBuf = new byte[8];
     private int     xBufOff;
 
@@ -30,6 +34,16 @@
      */
     protected LongDigest()
     {
+        this(CryptoServicePurpose.ANY);
+    }
+
+    /**
+     * Constructor for variable length word
+     */
+    protected LongDigest(CryptoServicePurpose purpose)
+    {
+        this.purpose = purpose;
+
         xBufOff = 0;
 
         reset();
@@ -42,6 +56,8 @@
      */
     protected LongDigest(LongDigest t)
     {
+        this.purpose = t.purpose;
+
         copyIn(t);
     }
 
@@ -149,7 +165,7 @@
         //
         // process whole words.
         //
-        while (len > xBuf.length)
+        while (len >= xBuf.length)
         {
             processWord(in, inOff);
 
@@ -408,4 +424,5 @@
 0x4cc5d4becb3e42b6L, 0x597f299cfc657e2aL, 0x5fcb6fab3ad6faecL, 0x6c44198c4a475817L
     };
 
+    protected abstract CryptoServiceProperties cryptoServiceProperties();
 }
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/digests/MD4Digest.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/digests/MD4Digest.java
index 19d2fe7..8e30270 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/digests/MD4Digest.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/digests/MD4Digest.java
@@ -1,4 +1,5 @@
 /* GENERATED SOURCE. DO NOT MODIFY. */
+// BEGIN Android-changed: maintain old behaviour
 package com.android.org.bouncycastle.crypto.digests;
 
 
@@ -291,3 +292,4 @@
         copyIn(d);
     }
 }
+// END Android-changed: maintain old behaviour
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/digests/MD5Digest.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/digests/MD5Digest.java
index ab7b875..a764061 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/digests/MD5Digest.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/digests/MD5Digest.java
@@ -1,4 +1,5 @@
 /* GENERATED SOURCE. DO NOT MODIFY. */
+// BEGIN Android-changed: maintain old behaviour
 package com.android.org.bouncycastle.crypto.digests;
 
 
@@ -361,3 +362,4 @@
         return state;
     }
 }
+// END Android-changed: maintain old behaviour
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/digests/PhotonBeetleDigest.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/digests/PhotonBeetleDigest.java
new file mode 100644
index 0000000..77c46db
--- /dev/null
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/digests/PhotonBeetleDigest.java
@@ -0,0 +1,221 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.org.bouncycastle.crypto.digests;
+
+import java.io.ByteArrayOutputStream;
+
+import com.android.org.bouncycastle.crypto.DataLengthException;
+import com.android.org.bouncycastle.crypto.Digest;
+import com.android.org.bouncycastle.crypto.OutputLengthException;
+import com.android.org.bouncycastle.util.Arrays;
+import com.android.org.bouncycastle.util.Bytes;
+
+/**
+ * Photon-Beetle, https://www.isical.ac.in/~lightweight/beetle/
+ * https://csrc.nist.gov/CSRC/media/Projects/lightweight-cryptography/documents/finalist-round/updated-spec-doc/photon-beetle-spec-final.pdf
+ * <p>
+ * Photon-Beetle with reference to C Reference Impl from: https://github.com/PHOTON-Beetle/Software
+ * </p>
+ * @hide This class is not part of the Android public SDK API
+ */
+public class PhotonBeetleDigest
+    implements Digest
+{
+    private byte[] state;
+    private byte[][] state_2d;
+    private ByteArrayOutputStream buffer = new ByteArrayOutputStream();
+    private final int INITIAL_RATE_INBYTES = 16;
+    private int RATE_INBYTES = 4;
+    private int SQUEEZE_RATE_INBYTES = 16;
+    private int STATE_INBYTES = 32;
+    private int TAG_INBYTES = 32;
+    private int LAST_THREE_BITS_OFFSET = 5;
+    private int ROUND = 12;
+    private int D = 8;
+    private int Dq = 3;
+    private int Dr = 7;
+    private int DSquare = 64;
+    private int S = 4;
+    private int S_1 = 3;
+    private byte[][] RC = {//[D][12]
+        {1, 3, 7, 14, 13, 11, 6, 12, 9, 2, 5, 10},
+        {0, 2, 6, 15, 12, 10, 7, 13, 8, 3, 4, 11},
+        {2, 0, 4, 13, 14, 8, 5, 15, 10, 1, 6, 9},
+        {6, 4, 0, 9, 10, 12, 1, 11, 14, 5, 2, 13},
+        {14, 12, 8, 1, 2, 4, 9, 3, 6, 13, 10, 5},
+        {15, 13, 9, 0, 3, 5, 8, 2, 7, 12, 11, 4},
+        {13, 15, 11, 2, 1, 7, 10, 0, 5, 14, 9, 6},
+        {9, 11, 15, 6, 5, 3, 14, 4, 1, 10, 13, 2}
+    };
+    private byte[][] MixColMatrix = { //[D][D]
+        {2, 4, 2, 11, 2, 8, 5, 6},
+        {12, 9, 8, 13, 7, 7, 5, 2},
+        {4, 4, 13, 13, 9, 4, 13, 9},
+        {1, 6, 5, 1, 12, 13, 15, 14},
+        {15, 12, 9, 13, 14, 5, 14, 13},
+        {9, 14, 5, 15, 4, 12, 9, 6},
+        {12, 2, 2, 10, 3, 1, 1, 14},
+        {15, 1, 13, 10, 5, 10, 2, 3}
+    };
+
+    private byte[] sbox = {12, 5, 6, 11, 9, 0, 10, 13, 3, 14, 15, 8, 4, 7, 1, 2};
+
+    public PhotonBeetleDigest()
+    {
+        state = new byte[STATE_INBYTES];
+        state_2d = new byte[D][D];
+    }
+
+    @Override
+    public String getAlgorithmName()
+    {
+        return "Photon-Beetle Hash";
+    }
+
+    @Override
+    public int getDigestSize()
+    {
+        return TAG_INBYTES;
+    }
+
+    @Override
+    public void update(byte input)
+    {
+        buffer.write(input);
+    }
+
+    @Override
+    public void update(byte[] input, int inOff, int len)
+    {
+        if ((inOff + len) > input.length)
+        {
+            throw new DataLengthException("input buffer too short");
+        }
+        buffer.write(input, inOff, len);
+    }
+
+    @Override
+    public int doFinal(byte[] output, int outOff)
+    {
+        if (32 + outOff > output.length)
+        {
+            throw new OutputLengthException("output buffer is too short");
+        }
+        byte[] input = buffer.toByteArray();
+        int inlen = input.length;
+        if (inlen == 0)
+        {
+            state[STATE_INBYTES - 1] ^= 1 << LAST_THREE_BITS_OFFSET;
+        }
+        else if (inlen <= INITIAL_RATE_INBYTES)
+        {
+            System.arraycopy(input, 0, state, 0, inlen);
+            if (inlen < INITIAL_RATE_INBYTES)
+            {
+                state[inlen] ^= 0x01; // ozs
+            }
+            state[STATE_INBYTES - 1] ^= (inlen < INITIAL_RATE_INBYTES ? (byte)1 : (byte)2) << LAST_THREE_BITS_OFFSET;
+        }
+        else
+        {
+            System.arraycopy(input, 0, state, 0, INITIAL_RATE_INBYTES);
+            inlen -= INITIAL_RATE_INBYTES;
+            int Dlen_inblocks = (inlen + RATE_INBYTES - 1) / RATE_INBYTES;
+            int i, LastDBlocklen;
+            for (i = 0; i < Dlen_inblocks - 1; i++)
+            {
+                PHOTON_Permutation();
+                Bytes.xorTo(RATE_INBYTES, input, INITIAL_RATE_INBYTES + i * RATE_INBYTES, state, 0);
+            }
+            PHOTON_Permutation();
+            LastDBlocklen = inlen - i * RATE_INBYTES;
+            Bytes.xorTo(LastDBlocklen, input, INITIAL_RATE_INBYTES + i * RATE_INBYTES, state, 0);
+            if (LastDBlocklen < RATE_INBYTES)
+            {
+                state[LastDBlocklen] ^= 0x01; // ozs
+            }
+            state[STATE_INBYTES - 1] ^= (inlen % RATE_INBYTES == 0 ? (byte)1 : (byte)2) << LAST_THREE_BITS_OFFSET;
+        }
+        PHOTON_Permutation();
+        System.arraycopy(state, 0, output, outOff, SQUEEZE_RATE_INBYTES);
+        PHOTON_Permutation();
+        System.arraycopy(state, 0, output, outOff + SQUEEZE_RATE_INBYTES, TAG_INBYTES - SQUEEZE_RATE_INBYTES);
+        return TAG_INBYTES;
+    }
+
+    @Override
+    public void reset()
+    {
+        buffer.reset();
+        Arrays.fill(state, (byte)0);
+    }
+
+    void PHOTON_Permutation()
+    {
+        int i, j, k, l;
+        for (i = 0; i < DSquare; i++)
+        {
+            state_2d[i >>> Dq][i & Dr] = (byte)(((state[i >> 1] & 0xFF) >>> (4 * (i & 1))) & 0xf);
+        }
+        for (int round = 0; round < ROUND; round++)
+        {
+            //AddKey
+            for (i = 0; i < D; i++)
+            {
+                state_2d[i][0] ^= RC[i][round];
+            }
+            //SubCell
+            for (i = 0; i < D; i++)
+            {
+                for (j = 0; j < D; j++)
+                {
+                    state_2d[i][j] = sbox[state_2d[i][j]];
+                }
+            }
+            //ShiftRow
+            for (i = 1; i < D; i++)
+            {
+                System.arraycopy(state_2d[i], 0, state, 0, D);
+                System.arraycopy(state, i, state_2d[i], 0, D - i);
+                System.arraycopy(state, 0, state_2d[i], D - i, i);
+            }
+            //MixColumn
+            for (j = 0; j < D; j++)
+            {
+                for (i = 0; i < D; i++)
+                {
+                    byte sum = 0;
+                    for (k = 0; k < D; k++)
+                    {
+                        int x = MixColMatrix[i][k], ret = 0, b = state_2d[k][j];
+                        for (l = 0; l < S; l++)
+                        {
+                            if (((b >>> l) & 1) != 0)
+                            {
+                                ret ^= x;
+                            }
+                            if (((x >>> S_1) & 1) != 0)
+                            {
+                                x <<= 1;
+                                x ^= 0x3;
+                            }
+                            else
+                            {
+                                x <<= 1;
+                            }
+                        }
+                        sum ^= ret & 15;
+                    }
+                    state[i] = sum;
+                }
+                for (i = 0; i < D; i++)
+                {
+                    state_2d[i][j] = state[i];
+                }
+            }
+        }
+        for (i = 0; i < DSquare; i += 2)
+        {
+            state[i >>> 1] = (byte)(((state_2d[i >>> Dq][i & Dr] & 0xf)) | ((state_2d[i >>> Dq][(i + 1) & Dr] & 0xf) << 4));
+        }
+    }
+}
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/digests/SHA1Digest.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/digests/SHA1Digest.java
index ebc5bfe..fcb56ad 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/digests/SHA1Digest.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/digests/SHA1Digest.java
@@ -1,4 +1,5 @@
 /* GENERATED SOURCE. DO NOT MODIFY. */
+// BEGIN Android-changed: maintain old behaviour
 package com.android.org.bouncycastle.crypto.digests;
 
 import com.android.org.bouncycastle.util.Memoable;
@@ -350,6 +351,4 @@
     }
 }
 
-
-
-
+// END Android-changed: maintain old behaviour
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/digests/SHA224Digest.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/digests/SHA224Digest.java
index b17a503..0f60f69 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/digests/SHA224Digest.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/digests/SHA224Digest.java
@@ -1,4 +1,5 @@
 /* GENERATED SOURCE. DO NOT MODIFY. */
+// BEGIN Android-changed: maintain old behaviour
 package com.android.org.bouncycastle.crypto.digests;
 
 
@@ -360,4 +361,4 @@
         return state;
     }
 }
-
+// END Android-changed: maintain old behaviour
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/digests/SHA256Digest.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/digests/SHA256Digest.java
index 25eda9d..16d0dce 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/digests/SHA256Digest.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/digests/SHA256Digest.java
@@ -1,7 +1,13 @@
 /* GENERATED SOURCE. DO NOT MODIFY. */
+// BEGIN Android-changed: adapt to old version of GeneralDigest
 package com.android.org.bouncycastle.crypto.digests;
 
 
+import com.android.org.bouncycastle.crypto.CryptoServiceProperties;
+import com.android.org.bouncycastle.crypto.CryptoServicePurpose;
+import com.android.org.bouncycastle.crypto.CryptoServicesRegistrar;
+import com.android.org.bouncycastle.crypto.Digest;
+import com.android.org.bouncycastle.crypto.SavableDigest;
 import com.android.org.bouncycastle.util.Memoable;
 import com.android.org.bouncycastle.util.Pack;
 
@@ -20,20 +26,60 @@
  */
 public class SHA256Digest
     extends GeneralDigest
-    implements EncodableDigest
+    implements SavableDigest
 {
     private static final int    DIGEST_LENGTH = 32;
+    protected final CryptoServicePurpose purpose;
 
     private int     H1, H2, H3, H4, H5, H6, H7, H8;
 
     private int[]   X = new int[64];
     private int     xOff;
 
+    public static SavableDigest newInstance()
+    {
+        return new SHA256Digest();
+    }
+
+    public static SavableDigest newInstance(CryptoServicePurpose purpose)
+    {
+        return new SHA256Digest(purpose);
+    }
+
+    public static SavableDigest newInstance(Digest digest)
+    {
+        if (digest instanceof SHA256Digest)
+        {
+            return new SHA256Digest((SHA256Digest) digest);
+        }
+
+        throw new IllegalArgumentException("receiver digest not available for input type " + (digest != null ? digest.getClass().getName() : "null"));
+    }
+
+    public static SavableDigest newInstance(byte[] encoded)
+    {
+        return new SHA256Digest(encoded);
+    }
+
     /**
      * Standard constructor
      */
     public SHA256Digest()
     {
+        this(CryptoServicePurpose.ANY);
+    }
+
+    /**
+     * Standard constructor, with purpose
+     */
+    public SHA256Digest(CryptoServicePurpose purpose)
+    {
+        super();
+
+        this.purpose = purpose;
+
+        CryptoServicesRegistrar.checkConstraints(cryptoServiceProperties());
+
         reset();
     }
 
@@ -45,6 +91,8 @@
     {
         super(t);
 
+        this.purpose = CryptoServicePurpose.ANY;
+
         copyIn(t);
     }
 
@@ -74,6 +122,8 @@
     {
         super(encodedState);
 
+        this.purpose = CryptoServicePurpose.ANY;
+
         H1 = Pack.bigEndianToInt(encodedState, 16);
         H2 = Pack.bigEndianToInt(encodedState, 20);
         H3 = Pack.bigEndianToInt(encodedState, 24);
@@ -105,13 +155,7 @@
         byte[]  in,
         int     inOff)
     {
-        // Note: Inlined for performance
-//        X[xOff] = Pack.bigEndianToInt(in, inOff);
-        int n = in[inOff] << 24;
-        n |= (in[++inOff] & 0xff) << 16;
-        n |= (in[++inOff] & 0xff) << 8;
-        n |= (in[++inOff] & 0xff);
-        X[xOff] = n;
+        X[xOff] = Pack.bigEndianToInt(in, inOff);
 
         if (++xOff == 16)
         {
@@ -131,9 +175,7 @@
         X[15] = (int)(bitLength & 0xffffffff);
     }
 
-    public int doFinal(
-        byte[]  out,
-        int     outOff)
+    public int doFinal(byte[] out, int outOff)
     {
         finish();
 
@@ -201,7 +243,7 @@
         int     g = H7;
         int     h = H8;
 
-        int t = 0;     
+        int t = 0;
         for(int i = 0; i < 8; i ++)
         {
             // t = 8 * i
@@ -334,7 +376,7 @@
 
     public byte[] getEncodedState()
     {
-        byte[] state = new byte[52 + xOff * 4];
+        byte[] state = new byte[52 + xOff * 4 + 1];
 
         super.populateState(state);
 
@@ -353,7 +395,14 @@
             Pack.intToBigEndian(X[i], state, 52 + (i * 4));
         }
 
+        state[state.length - 1] = (byte)purpose.ordinal();
+
         return state;
     }
-}
 
+    protected CryptoServiceProperties cryptoServiceProperties()
+    {
+        return Utils.getDefaultProperties(this, 256, purpose);
+    }
+}
+// END Android-changed: adapt to old version of GeneralDigest
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/digests/SHA384Digest.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/digests/SHA384Digest.java
index 8ff6764..ca25f05 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/digests/SHA384Digest.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/digests/SHA384Digest.java
@@ -1,6 +1,9 @@
 /* GENERATED SOURCE. DO NOT MODIFY. */
 package com.android.org.bouncycastle.crypto.digests;
 
+import com.android.org.bouncycastle.crypto.CryptoServiceProperties;
+import com.android.org.bouncycastle.crypto.CryptoServicePurpose;
+import com.android.org.bouncycastle.crypto.CryptoServicesRegistrar;
 import com.android.org.bouncycastle.util.Memoable;
 import com.android.org.bouncycastle.util.Pack;
 
@@ -27,6 +30,19 @@
      */
     public SHA384Digest()
     {
+        this(CryptoServicePurpose.ANY);
+    }
+
+    /**
+     * Standard constructor, with purpose
+     */
+    public SHA384Digest(CryptoServicePurpose purpose)
+    {
+        super(purpose);
+
+        CryptoServicesRegistrar.checkConstraints(cryptoServiceProperties());
+
+        reset();
     }
 
     /**
@@ -36,6 +52,8 @@
     public SHA384Digest(SHA384Digest t)
     {
         super(t);
+
+        CryptoServicesRegistrar.checkConstraints(cryptoServiceProperties());
     }
 
     /**
@@ -45,7 +63,11 @@
      */
     public SHA384Digest(byte[] encodedState)
     {
+        super(CryptoServicePurpose.values()[encodedState[encodedState.length - 1]]);
+
         restoreState(encodedState);
+
+        CryptoServicesRegistrar.checkConstraints(cryptoServiceProperties());
     }
 
     public String getAlgorithmName()
@@ -111,8 +133,16 @@
 
     public byte[] getEncodedState()
     {
-        byte[] encoded = new byte[getEncodedStateSize()];
+        byte[] encoded = new byte[getEncodedStateSize() + 1];
         super.populateState(encoded);
+
+        encoded[encoded.length - 1] = (byte)purpose.ordinal();
+
         return encoded;
     }
+
+    protected CryptoServiceProperties cryptoServiceProperties()
+    {
+        return Utils.getDefaultProperties(this, 256, purpose);
+    }
 }
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/digests/SHA512Digest.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/digests/SHA512Digest.java
index 882b671..bb89ed7 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/digests/SHA512Digest.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/digests/SHA512Digest.java
@@ -1,6 +1,9 @@
 /* GENERATED SOURCE. DO NOT MODIFY. */
 package com.android.org.bouncycastle.crypto.digests;
 
+import com.android.org.bouncycastle.crypto.CryptoServiceProperties;
+import com.android.org.bouncycastle.crypto.CryptoServicePurpose;
+import com.android.org.bouncycastle.crypto.CryptoServicesRegistrar;
 import com.android.org.bouncycastle.util.Memoable;
 import com.android.org.bouncycastle.util.Pack;
 
@@ -27,6 +30,19 @@
      */
     public SHA512Digest()
     {
+        this(CryptoServicePurpose.ANY);
+    }
+
+    /**
+     * Standard constructor, with purpose
+     */
+    public SHA512Digest(CryptoServicePurpose purpose)
+    {
+        super(purpose);
+
+        CryptoServicesRegistrar.checkConstraints(cryptoServiceProperties());
+
+        reset();
     }
 
     /**
@@ -36,6 +52,8 @@
     public SHA512Digest(SHA512Digest t)
     {
         super(t);
+
+        CryptoServicesRegistrar.checkConstraints(cryptoServiceProperties());
     }
 
     /**
@@ -45,7 +63,11 @@
      */
     public SHA512Digest(byte[] encodedState)
     {
+        super(CryptoServicePurpose.values()[encodedState[encodedState.length - 1]]);
+
         restoreState(encodedState);
+
+        CryptoServicesRegistrar.checkConstraints(cryptoServiceProperties());
     }
 
     public String getAlgorithmName()
@@ -113,9 +135,17 @@
 
     public byte[] getEncodedState()
     {
-        byte[] encoded = new byte[getEncodedStateSize()];
+        byte[] encoded = new byte[getEncodedStateSize() + 1];
         super.populateState(encoded);
+
+        encoded[encoded.length - 1] = (byte)purpose.ordinal();
+
         return encoded;
     }
+
+    protected CryptoServiceProperties cryptoServiceProperties()
+    {
+        return Utils.getDefaultProperties(this, 256, purpose);
+    }
 }
 
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/digests/Utils.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/digests/Utils.java
new file mode 100644
index 0000000..c6bd5bb
--- /dev/null
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/digests/Utils.java
@@ -0,0 +1,98 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.org.bouncycastle.crypto.digests;
+
+import com.android.org.bouncycastle.crypto.CryptoServiceProperties;
+import com.android.org.bouncycastle.crypto.CryptoServicePurpose;
+import com.android.org.bouncycastle.crypto.Digest;
+
+class Utils
+{
+    static CryptoServiceProperties getDefaultProperties(Digest digest, CryptoServicePurpose purpose)
+    {
+        return new DefaultProperties(digest.getDigestSize() * 4, digest.getAlgorithmName(), purpose);
+    }
+
+    static CryptoServiceProperties getDefaultProperties(Digest digest, int prfBitsOfSecurity, CryptoServicePurpose purpose)
+    {
+        return new DefaultPropertiesWithPRF(digest.getDigestSize() * 4, prfBitsOfSecurity, digest.getAlgorithmName(), purpose);
+    }
+
+    // Service Definitions
+    private static class DefaultPropertiesWithPRF
+        implements CryptoServiceProperties
+    {
+        private final int bitsOfSecurity;
+        private final int prfBitsOfSecurity;
+        private final String algorithmName;
+        private final CryptoServicePurpose purpose;
+
+        public DefaultPropertiesWithPRF(int bitsOfSecurity, int prfBitsOfSecurity, String algorithmName, CryptoServicePurpose purpose)
+        {
+            this.bitsOfSecurity = bitsOfSecurity;
+            this.prfBitsOfSecurity = prfBitsOfSecurity;
+            this.algorithmName = algorithmName;
+            this.purpose = purpose;
+        }
+
+        public int bitsOfSecurity()
+        {
+            if (purpose == CryptoServicePurpose.PRF)
+            {
+                return prfBitsOfSecurity;
+            }
+
+            return bitsOfSecurity;
+        }
+
+        public String getServiceName()
+        {
+            return algorithmName;
+        }
+
+        public CryptoServicePurpose getPurpose()
+        {
+            return purpose;
+        }
+
+        public Object getParams()
+        {
+            return null;
+        }
+    }
+
+    // Service Definitions
+    private static class DefaultProperties
+        implements CryptoServiceProperties
+    {
+        private final int bitsOfSecurity;
+        private final String algorithmName;
+        private final CryptoServicePurpose purpose;
+
+        public DefaultProperties(int bitsOfSecurity, String algorithmName, CryptoServicePurpose purpose)
+        {
+            this.bitsOfSecurity = bitsOfSecurity;
+            this.algorithmName = algorithmName;
+            this.purpose = purpose;
+        }
+
+        public int bitsOfSecurity()
+        {
+            return bitsOfSecurity;
+        }
+
+        public String getServiceName()
+        {
+            return algorithmName;
+        }
+
+        public CryptoServicePurpose getPurpose()
+        {
+            return purpose;
+        }
+
+        public Object getParams()
+        {
+            return null;
+        }
+    }
+}
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/digests/XofUtils.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/digests/XofUtils.java
index 13a053d..f5ba10f 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/digests/XofUtils.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/digests/XofUtils.java
@@ -1,6 +1,8 @@
 /* GENERATED SOURCE. DO NOT MODIFY. */
 package com.android.org.bouncycastle.crypto.digests;
 
+import com.android.org.bouncycastle.util.Arrays;
+
 /**
  * @hide This class is not part of the Android public SDK API
  */
@@ -8,24 +10,24 @@
 {
     public static byte[] leftEncode(long strLen)
     {
-    	byte n = 1;
+        byte n = 1;
 
         long v = strLen;
-    	while ((v >>= 8) != 0)
+        while ((v >>= 8) != 0)
         {
-    		n++;
-    	}
+            n++;
+        }
 
         byte[] b = new byte[n + 1];
 
-    	b[0] = n;
+        b[0] = n;
 
-    	for (int i = 1; i <= n; i++)
-    	{
-    		b[i] = (byte)(strLen >> (8 * (n - i)));
-    	}
+        for (int i = 1; i <= n; i++)
+        {
+            b[i] = (byte)(strLen >> (8 * (n - i)));
+        }
 
-    	return b;
+        return b;
     }
 
     public static byte[] rightEncode(long strLen)
@@ -49,4 +51,18 @@
 
         return b;
     }
+
+    static byte[] encode(byte X)
+    {
+        return Arrays.concatenate(XofUtils.leftEncode(8), new byte[] { X });
+    }
+
+    static byte[] encode(byte[] in, int inOff, int len)
+    {
+        if (in.length == len)
+        {
+            return Arrays.concatenate(XofUtils.leftEncode(len * 8), in);
+        }
+        return Arrays.concatenate(XofUtils.leftEncode(len * 8), Arrays.copyOfRange(in, inOff, inOff + len));
+    }
 }
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/digests/XoodyakDigest.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/digests/XoodyakDigest.java
new file mode 100644
index 0000000..3fa6856
--- /dev/null
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/digests/XoodyakDigest.java
@@ -0,0 +1,208 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.org.bouncycastle.crypto.digests;
+
+import java.io.ByteArrayOutputStream;
+
+import com.android.org.bouncycastle.crypto.DataLengthException;
+import com.android.org.bouncycastle.crypto.Digest;
+import com.android.org.bouncycastle.crypto.OutputLengthException;
+import com.android.org.bouncycastle.util.Arrays;
+import com.android.org.bouncycastle.util.Pack;
+
+/**
+ * Xoodyak v1, https://csrc.nist.gov/CSRC/media/Projects/lightweight-cryptography/documents/finalist-round/updated-spec-doc/xoodyak-spec-final.pdf
+ * <p>
+ * Xoodyak with reference to C Reference Impl from: https://github.com/XKCP/XKCP
+ * </p>
+ * @hide This class is not part of the Android public SDK API
+ */
+
+public class XoodyakDigest
+    implements Digest
+{
+    private byte[] state;
+    private int phase;
+    private MODE mode;
+    private int Rabsorb;
+    private final int f_bPrime = 48;
+    private final int Rhash = 16;
+    private final int PhaseDown = 1;
+    private final int PhaseUp = 2;
+    private final int NLANES = 12;
+    private final int NROWS = 3;
+    private final int NCOLUMS = 4;
+    private final int MAXROUNDS = 12;
+    private final int TAGLEN = 16;
+    private final int[] RC = {0x00000058, 0x00000038, 0x000003C0, 0x000000D0, 0x00000120, 0x00000014, 0x00000060,
+        0x0000002C, 0x00000380, 0x000000F0, 0x000001A0, 0x00000012};
+    private final ByteArrayOutputStream buffer = new ByteArrayOutputStream();
+
+    enum MODE
+    {
+        ModeHash,
+        ModeKeyed
+    }
+
+    public XoodyakDigest()
+    {
+        state = new byte[48];
+        reset();
+    }
+
+    @Override
+    public String getAlgorithmName()
+    {
+        return "Xoodyak Hash";
+    }
+
+    @Override
+    public int getDigestSize()
+    {
+        return 32;
+    }
+
+    @Override
+    public void update(byte input)
+    {
+        buffer.write(input);
+    }
+
+    @Override
+    public void update(byte[] input, int inOff, int len)
+    {
+        if ((inOff + len) > input.length)
+        {
+            throw new DataLengthException("input buffer too short");
+        }
+        buffer.write(input, inOff, len);
+
+    }
+
+    @Override
+    public int doFinal(byte[] output, int outOff)
+    {
+        if (32 + outOff > output.length)
+        {
+            throw new OutputLengthException("output buffer is too short");
+        }
+        byte[] input = buffer.toByteArray();
+        int inOff = 0;
+        int len = buffer.size();
+        int Cd = 0x03;
+        int splitLen;
+        do
+        {
+            if (phase != PhaseUp)
+            {
+                Up(null, 0, 0, 0);
+            }
+            splitLen = Math.min(len, Rabsorb);
+            Down(input, inOff, splitLen, Cd);
+            Cd = 0;
+            inOff += splitLen;
+            len -= splitLen;
+        }
+        while (len != 0);
+        Up(output, outOff, TAGLEN, 0x40);
+        Down(null, 0, 0, 0);
+        Up(output, outOff + TAGLEN, TAGLEN, 0);
+        return 32;
+    }
+
+    @Override
+    public void reset()
+    {
+        Arrays.fill(state, (byte)0);
+        phase = PhaseUp;
+        mode = MODE.ModeHash;
+        Rabsorb = Rhash;
+        buffer.reset();
+    }
+
+    private void Up(byte[] Yi, int YiOff, int YiLen, int Cu)
+    {
+        if (mode != MODE.ModeHash)
+        {
+            state[f_bPrime - 1] ^= Cu;
+        }
+        int[] a = new int[NLANES];
+        Pack.littleEndianToInt(state, 0, a, 0, a.length);
+        int x, y;
+        int[] b = new int[NLANES];
+        int[] p = new int[NCOLUMS];
+        int[] e = new int[NCOLUMS];
+        for (int i = 0; i < MAXROUNDS; ++i)
+        {
+            /* Theta: Column Parity Mixer */
+            for (x = 0; x < NCOLUMS; ++x)
+            {
+                p[x] = a[index(x, 0)] ^ a[index(x, 1)] ^ a[index(x, 2)];
+            }
+            for (x = 0; x < NCOLUMS; ++x)
+            {
+                y = p[(x + 3) & 3];
+                e[x] = ROTL32(y, 5) ^ ROTL32(y, 14);
+            }
+            for (x = 0; x < NCOLUMS; ++x)
+            {
+                for (y = 0; y < NROWS; ++y)
+                {
+                    a[index(x, y)] ^= e[x];
+                }
+            }
+            /* Rho-west: plane shift */
+            for (x = 0; x < NCOLUMS; ++x)
+            {
+                b[index(x, 0)] = a[index(x, 0)];
+                b[index(x, 1)] = a[index(x + 3, 1)];
+                b[index(x, 2)] = ROTL32(a[index(x, 2)], 11);
+            }
+            /* Iota: round ant */
+            b[0] ^= RC[i];
+            /* Chi: non linear layer */
+            for (x = 0; x < NCOLUMS; ++x)
+            {
+                for (y = 0; y < NROWS; ++y)
+                {
+                    a[index(x, y)] = b[index(x, y)] ^ (~b[index(x, y + 1)] & b[index(x, y + 2)]);
+                }
+            }
+            /* Rho-east: plane shift */
+            for (x = 0; x < NCOLUMS; ++x)
+            {
+                b[index(x, 0)] = a[index(x, 0)];
+                b[index(x, 1)] = ROTL32(a[index(x, 1)], 1);
+                b[index(x, 2)] = ROTL32(a[index(x + 2, 2)], 8);
+            }
+            System.arraycopy(b, 0, a, 0, NLANES);
+        }
+        Pack.intToLittleEndian(a, 0, a.length, state, 0);
+        phase = PhaseUp;
+        if (Yi != null)
+        {
+            System.arraycopy(state, 0, Yi, YiOff, YiLen);
+        }
+    }
+
+    void Down(byte[] Xi, int XiOff, int XiLen, int Cd)
+    {
+        for (int i = 0; i < XiLen; i++)
+        {
+            state[i] ^= Xi[XiOff++];
+        }
+        state[XiLen] ^= 0x01;
+        state[f_bPrime - 1] ^= (mode == MODE.ModeHash) ? (Cd & 0x01) : Cd;
+        phase = PhaseDown;
+    }
+
+    private int index(int x, int y)
+    {
+        return (((y % NROWS) * NCOLUMS) + ((x) % NCOLUMS));
+    }
+
+    private int ROTL32(int a, int offset)
+    {
+        return (a << (offset & 31)) ^ (a >>> ((32 - (offset)) & 31));
+    }
+
+}
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/ec/CustomNamedCurves.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/ec/CustomNamedCurves.java
index aeb8590..d1c8033 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/ec/CustomNamedCurves.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/ec/CustomNamedCurves.java
@@ -87,10 +87,15 @@
      *
     static X9ECParametersHolder curve25519 = new X9ECParametersHolder()
     {
+        protected ECCurve createCurve()
+        {
+            return configureCurve(new Curve25519());
+        }
+
         protected X9ECParameters createParameters()
         {
             byte[] S = null;
-            ECCurve curve = configureCurve(new Curve25519());
+            ECCurve curve = getCurve();
 
             /*
              * NOTE: Curve25519 was specified in Montgomery form. Rewriting in Weierstrass form
@@ -113,10 +118,15 @@
      *
     static X9ECParametersHolder secp128r1 = new X9ECParametersHolder()
     {
+        protected ECCurve createCurve()
+        {
+            return configureCurve(new SecP128R1Curve());
+        }
+
         protected X9ECParameters createParameters()
         {
             byte[] S = Hex.decodeStrict("000E0D4D696E6768756151750CC03A4473D03679");
-            ECCurve curve = configureCurve(new SecP128R1Curve());
+            ECCurve curve = getCurve();
             X9ECPoint G = configureBasepoint(curve,
                 "04161FF7528B899B2D0C28607CA52C5B86CF5AC8395BAFEB13C02DA292DDED7A83");
             return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
@@ -128,9 +138,8 @@
      *
     static X9ECParametersHolder secp160k1 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
-            byte[] S = null;
             GLVTypeBParameters glv = new GLVTypeBParameters(
                 new BigInteger("9ba48cba5ebcb9b6bd33b92830b2a2e0e192f10a", 16),
                 new BigInteger("c39c6c3b3a36d7701b9c71a1f5804ae5d0003f4", 16),
@@ -144,7 +153,13 @@
                     new BigInteger("9162fbe73984472a0a9d0590", 16),
                     new BigInteger("96341f1138933bc2f503fd44", 16),
                     176));
-            ECCurve curve = configureCurveGLV(new SecP160K1Curve(), glv);
+            return configureCurveGLV(new SecP160K1Curve(), glv);
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = null;
+            ECCurve curve = getCurve();
             X9ECPoint G = configureBasepoint(curve,
                 "043B4C382CE37AA192A4019E763036F4F5DD4D7EBB938CF935318FDCED6BC28286531733C3F03C4FEE");
             return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
@@ -156,10 +171,15 @@
      *
     static X9ECParametersHolder secp160r1 = new X9ECParametersHolder()
     {
+        protected ECCurve createCurve()
+        {
+            return configureCurve(new SecP160R1Curve());
+        }
+
         protected X9ECParameters createParameters()
         {
             byte[] S = Hex.decodeStrict("1053CDE42C14D696E67687561517533BF3F83345");
-            ECCurve curve = configureCurve(new SecP160R1Curve());
+            ECCurve curve = getCurve();
             X9ECPoint G = configureBasepoint(curve,
                 "044A96B5688EF573284664698968C38BB913CBFC8223A628553168947D59DCC912042351377AC5FB32");
             return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
@@ -171,10 +191,15 @@
      *
     static X9ECParametersHolder secp160r2 = new X9ECParametersHolder()
     {
+        protected ECCurve createCurve()
+        {
+            return configureCurve(new SecP160R2Curve());
+        }
+
         protected X9ECParameters createParameters()
         {
             byte[] S = Hex.decodeStrict("B99B99B099B323E02709A4D696E6768756151751");
-            ECCurve curve = configureCurve(new SecP160R2Curve());
+            ECCurve curve = getCurve();
             X9ECPoint G = configureBasepoint(curve,
                 "0452DCB034293A117E1F4FF11B30F7199D3144CE6DFEAFFEF2E331F296E071FA0DF9982CFEA7D43F2E");
             return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
@@ -188,9 +213,8 @@
      */
     static X9ECParametersHolder secp192k1 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
-            byte[] S = null;
             GLVTypeBParameters glv = new GLVTypeBParameters(
                 new BigInteger("bb85691939b869c1d087f601554b96b80cb4f55b35f433c2", 16),
                 new BigInteger("3d84f26c12238d7b4f3d516613c1759033b1a5800175d0b1", 16),
@@ -204,7 +228,13 @@
                     new BigInteger("71169be7330b3038edb025f1d0f9", 16),
                     new BigInteger("b3fb3400dec5c4adceb8655d4c94", 16),
                     208));
-            ECCurve curve = configureCurveGLV(new SecP192K1Curve(), glv);
+            return configureCurveGLV(new SecP192K1Curve(), glv);
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = null;
+            ECCurve curve = getCurve();
             X9ECPoint G = configureBasepoint(curve,
                 "04DB4FF10EC057E9AE26B07D0280B7F4341DA5D1B1EAE06C7D9B2F2F6D9C5628A7844163D015BE86344082AA88D95E2F9D");
             return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
@@ -216,10 +246,15 @@
      */
     static X9ECParametersHolder secp192r1 = new X9ECParametersHolder()
     {
+        protected ECCurve createCurve()
+        {
+            return configureCurve(new SecP192R1Curve());
+        }
+
         protected X9ECParameters createParameters()
         {
             byte[] S = Hex.decodeStrict("3045AE6FC8422F64ED579528D38120EAE12196D5");
-            ECCurve curve = configureCurve(new SecP192R1Curve());
+            ECCurve curve = getCurve();
             X9ECPoint G = configureBasepoint(curve,
                 "04188DA80EB03090F67CBF20EB43A18800F4FF0AFD82FF101207192B95FFC8DA78631011ED6B24CDD573F977A11E794811");
             return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
@@ -231,9 +266,8 @@
      */
     static X9ECParametersHolder secp224k1 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
-            byte[] S = null;
             GLVTypeBParameters glv = new GLVTypeBParameters(
                 new BigInteger("fe0e87005b4e83761908c5131d552a850b3f58b749c37cf5b84d6768", 16),
                 new BigInteger("60dcd2104c4cbc0be6eeefc2bdd610739ec34e317f9b33046c9e4788", 16),
@@ -247,7 +281,13 @@
                     new BigInteger("6b8cf07d4ca75c88957d9d67059037a4", 16),
                     new BigInteger("b8adf1378a6eb73409fa6c9c637ba7f5", 16),
                     240));
-            ECCurve curve = configureCurveGLV(new SecP224K1Curve(), glv);
+            return configureCurveGLV(new SecP224K1Curve(), glv);
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = null;
+            ECCurve curve = getCurve();
             X9ECPoint G = configureBasepoint(curve,
                 "04A1455B334DF099DF30FC28A169A467E9E47075A90F7E650EB6B7A45C7E089FED7FBA344282CAFBD6F7E319F7C0B0BD59E2CA4BDB556D61A5");
             return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
@@ -259,10 +299,15 @@
      */
     static X9ECParametersHolder secp224r1 = new X9ECParametersHolder()
     {
+        protected ECCurve createCurve()
+        {
+            return configureCurve(new SecP224R1Curve());
+        }
+
         protected X9ECParameters createParameters()
         {
             byte[] S = Hex.decodeStrict("BD71344799D5C7FCDC45B59FA3B9AB8F6A948BC5");
-            ECCurve curve = configureCurve(new SecP224R1Curve());
+            ECCurve curve = getCurve();
             X9ECPoint G = configureBasepoint(curve,
                 "04B70E0CBD6BB4BF7F321390B94A03C1D356C21122343280D6115C1D21BD376388B5F723FB4C22DFE6CD4375A05A07476444D5819985007E34");
             return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
@@ -274,9 +319,8 @@
      */
     static X9ECParametersHolder secp256k1 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
-            byte[] S = null;
             GLVTypeBParameters glv = new GLVTypeBParameters(
                 new BigInteger("7ae96a2b657c07106e64479eac3434e99cf0497512f58995c1396c28719501ee", 16),
                 new BigInteger("5363ad4cc05c30e0a5261c028812645a122e22ea20816678df02967c1b23bd72", 16),
@@ -290,7 +334,13 @@
                     new BigInteger("3086d221a7d46bcde86c90e49284eb153dab", 16),
                     new BigInteger("e4437ed6010e88286f547fa90abfe4c42212", 16),
                     272));
-            ECCurve curve = configureCurveGLV(new SecP256K1Curve(), glv);
+            return configureCurveGLV(new SecP256K1Curve(), glv);
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = null;
+            ECCurve curve = getCurve();
             X9ECPoint G = configureBasepoint(curve,
                 "0479BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8");
             return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
@@ -302,10 +352,15 @@
      */
     static X9ECParametersHolder secp256r1 = new X9ECParametersHolder()
     {
+        protected ECCurve createCurve()
+        {
+            return configureCurve(new SecP256R1Curve());
+        }
+
         protected X9ECParameters createParameters()
         {
             byte[] S = Hex.decodeStrict("C49D360886E704936A6678E1139D26B7819F7E90");
-            ECCurve curve = configureCurve(new SecP256R1Curve());
+            ECCurve curve = getCurve();
             X9ECPoint G = configureBasepoint(curve,
                 "046B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C2964FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5");
             return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
@@ -317,10 +372,15 @@
      */
     static X9ECParametersHolder secp384r1 = new X9ECParametersHolder()
     {
+        protected ECCurve createCurve()
+        {
+            return configureCurve(new SecP384R1Curve());
+        }
+
         protected X9ECParameters createParameters()
         {
             byte[] S = Hex.decodeStrict("A335926AA319A27A1D00896A6773A4827ACDAC73");
-            ECCurve curve = configureCurve(new SecP384R1Curve());
+            ECCurve curve = getCurve();
             X9ECPoint G = configureBasepoint(curve, "04"
                 + "AA87CA22BE8B05378EB1C71EF320AD746E1D3B628BA79B9859F741E082542A385502F25DBF55296C3A545E3872760AB7"
                 + "3617DE4A96262C6F5D9E98BF9292DC29F8F41DBD289A147CE9DA3113B5F0B8C00A60B1CE1D7E819D7A431D7C90EA0E5F");
@@ -333,10 +393,15 @@
      */
     static X9ECParametersHolder secp521r1 = new X9ECParametersHolder()
     {
+        protected ECCurve createCurve()
+        {
+            return configureCurve(new SecP521R1Curve());
+        }
+
         protected X9ECParameters createParameters()
         {
             byte[] S = Hex.decodeStrict("D09E8800291CB85396CC6717393284AAA0DA64BA");
-            ECCurve curve = configureCurve(new SecP521R1Curve());
+            ECCurve curve = getCurve();
             X9ECPoint G = configureBasepoint(curve, "04"
                 + "00C6858E06B70404E9CD9E3ECB662395B4429C648139053FB521F828AF606B4D3DBAA14B5E77EFE75928FE1DC127A2FFA8DE3348B3C1856A429BF97E7E31C2E5BD66"
                 + "011839296A789A3BC0045C8A5FB42C7D1BD998F54449579B446817AFBD17273E662C97EE72995EF42640C550B9013FAD0761353C7086A272C24088BE94769FD16650");
@@ -351,10 +416,15 @@
      *
     static X9ECParametersHolder sect113r1 = new X9ECParametersHolder()
     {
+        protected ECCurve createCurve()
+        {
+            return configureCurve(new SecT113R1Curve());
+        }
+
         protected X9ECParameters createParameters()
         {
             byte[] S = Hex.decodeStrict("10E723AB14D696E6768756151756FEBF8FCB49A9");
-            ECCurve curve = configureCurve(new SecT113R1Curve());
+            ECCurve curve = getCurve();
             X9ECPoint G = configureBasepoint(curve,
                 "04009D73616F35F4AB1407D73562C10F00A52830277958EE84D1315ED31886");
             return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
@@ -366,10 +436,15 @@
      *
     static X9ECParametersHolder sect113r2 = new X9ECParametersHolder()
     {
+        protected ECCurve createCurve()
+        {
+            return configureCurve(new SecT113R2Curve());
+        }
+
         protected X9ECParameters createParameters()
         {
             byte[] S = Hex.decodeStrict("10C0FB15760860DEF1EEF4D696E676875615175D");
-            ECCurve curve = configureCurve(new SecT113R2Curve());
+            ECCurve curve = getCurve();
             X9ECPoint G = configureBasepoint(curve,
                 "0401A57A6A7B26CA5EF52FCDB816479700B3ADC94ED1FE674C06E695BABA1D");
             return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
@@ -381,10 +456,15 @@
      *
     static X9ECParametersHolder sect131r1 = new X9ECParametersHolder()
     {
+        protected ECCurve createCurve()
+        {
+            return configureCurve(new SecT131R1Curve());
+        }
+
         protected X9ECParameters createParameters()
         {
             byte[] S = Hex.decodeStrict("4D696E676875615175985BD3ADBADA21B43A97E2");
-            ECCurve curve = configureCurve(new SecT131R1Curve());
+            ECCurve curve = getCurve();
             X9ECPoint G = configureBasepoint(curve,
                 "040081BAF91FDF9833C40F9C181343638399078C6E7EA38C001F73C8134B1B4EF9E150");
             return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
@@ -396,10 +476,15 @@
      *
     static X9ECParametersHolder sect131r2 = new X9ECParametersHolder()
     {
+        protected ECCurve createCurve()
+        {
+            return configureCurve(new SecT131R2Curve());
+        }
+
         protected X9ECParameters createParameters()
         {
             byte[] S = Hex.decodeStrict("985BD3ADBAD4D696E676875615175A21B43A97E3");
-            ECCurve curve = configureCurve(new SecT131R2Curve());
+            ECCurve curve = getCurve();
             X9ECPoint G = configureBasepoint(curve,
                 "040356DCD8F2F95031AD652D23951BB366A80648F06D867940A5366D9E265DE9EB240F");
             return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
@@ -411,10 +496,15 @@
      *
     static X9ECParametersHolder sect163k1 = new X9ECParametersHolder()
     {
+        protected ECCurve createCurve()
+        {
+            return configureCurve(new SecT163K1Curve());
+        }
+
         protected X9ECParameters createParameters()
         {
             byte[] S = null;
-            ECCurve curve = configureCurve(new SecT163K1Curve());
+            ECCurve curve = getCurve();
             X9ECPoint G = configureBasepoint(curve,
                 "0402FE13C0537BBC11ACAA07D793DE4E6D5E5C94EEE80289070FB05D38FF58321F2E800536D538CCDAA3D9");
             return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
@@ -426,10 +516,15 @@
      *
     static X9ECParametersHolder sect163r1 = new X9ECParametersHolder()
     {
+        protected ECCurve createCurve()
+        {
+            return configureCurve(new SecT163R1Curve());
+        }
+
         protected X9ECParameters createParameters()
         {
             byte[] S = Hex.decodeStrict("24B7B137C8A14D696E6768756151756FD0DA2E5C");
-            ECCurve curve = configureCurve(new SecT163R1Curve());
+            ECCurve curve = getCurve();
             X9ECPoint G = configureBasepoint(curve,
                 "040369979697AB43897789566789567F787A7876A65400435EDB42EFAFB2989D51FEFCE3C80988F41FF883");
             return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
@@ -441,10 +536,15 @@
      *
     static X9ECParametersHolder sect163r2 = new X9ECParametersHolder()
     {
+        protected ECCurve createCurve()
+        {
+            return configureCurve(new SecT163R2Curve());
+        }
+
         protected X9ECParameters createParameters()
         {
             byte[] S = Hex.decodeStrict("85E25BFE5C86226CDB12016F7553F9D0E693A268");
-            ECCurve curve = configureCurve(new SecT163R2Curve());
+            ECCurve curve = getCurve();
             X9ECPoint G = configureBasepoint(curve,
                 "0403F0EBA16286A2D57EA0991168D4994637E8343E3600D51FBC6C71A0094FA2CDD545B11C5C0C797324F1");
             return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
@@ -456,10 +556,15 @@
      *
     static X9ECParametersHolder sect193r1 = new X9ECParametersHolder()
     {
+        protected ECCurve createCurve()
+        {
+            return configureCurve(new SecT193R1Curve());
+        }
+
         protected X9ECParameters createParameters()
         {
             byte[] S = Hex.decodeStrict("103FAEC74D696E676875615175777FC5B191EF30");
-            ECCurve curve = configureCurve(new SecT193R1Curve());
+            ECCurve curve = getCurve();
             X9ECPoint G = configureBasepoint(curve,
                 "0401F481BC5F0FF84A74AD6CDF6FDEF4BF6179625372D8C0C5E10025E399F2903712CCF3EA9E3A1AD17FB0B3201B6AF7CE1B05");
             return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
@@ -471,10 +576,15 @@
      *
     static X9ECParametersHolder sect193r2 = new X9ECParametersHolder()
     {
+        protected ECCurve createCurve()
+        {
+            return configureCurve(new SecT193R2Curve());
+        }
+
         protected X9ECParameters createParameters()
         {
             byte[] S = Hex.decodeStrict("10B7B4D696E676875615175137C8A16FD0DA2211");
-            ECCurve curve = configureCurve(new SecT193R2Curve());
+            ECCurve curve = getCurve();
             X9ECPoint G = configureBasepoint(curve,
                 "0400D9B67D192E0367C803F39E1A7E82CA14A651350AAE617E8F01CE94335607C304AC29E7DEFBD9CA01F596F927224CDECF6C");
             return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
@@ -486,10 +596,15 @@
      *
     static X9ECParametersHolder sect233k1 = new X9ECParametersHolder()
     {
+        protected ECCurve createCurve()
+        {
+            return configureCurve(new SecT233K1Curve());
+        }
+
         protected X9ECParameters createParameters()
         {
             byte[] S = null;
-            ECCurve curve = configureCurve(new SecT233K1Curve());
+            ECCurve curve = getCurve();
             X9ECPoint G = configureBasepoint(curve,
                 "04017232BA853A7E731AF129F22FF4149563A419C26BF50A4C9D6EEFAD612601DB537DECE819B7F70F555A67C427A8CD9BF18AEB9B56E0C11056FAE6A3");
             return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
@@ -501,10 +616,15 @@
      *
     static X9ECParametersHolder sect233r1 = new X9ECParametersHolder()
     {
+        protected ECCurve createCurve()
+        {
+            return configureCurve(new SecT233R1Curve());
+        }
+
         protected X9ECParameters createParameters()
         {
             byte[] S = Hex.decodeStrict("74D59FF07F6B413D0EA14B344B20A2DB049B50C3");
-            ECCurve curve = configureCurve(new SecT233R1Curve());
+            ECCurve curve = getCurve();
             X9ECPoint G = configureBasepoint(curve,
                 "0400FAC9DFCBAC8313BB2139F1BB755FEF65BC391F8B36F8F8EB7371FD558B01006A08A41903350678E58528BEBF8A0BEFF867A7CA36716F7E01F81052");
             return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
@@ -516,10 +636,15 @@
      *
     static X9ECParametersHolder sect239k1 = new X9ECParametersHolder()
     {
+        protected ECCurve createCurve()
+        {
+            return configureCurve(new SecT239K1Curve());
+        }
+
         protected X9ECParameters createParameters()
         {
             byte[] S = null;
-            ECCurve curve = configureCurve(new SecT239K1Curve());
+            ECCurve curve = getCurve();
             X9ECPoint G = configureBasepoint(curve,
                 "0429A0B6A887A983E9730988A68727A8B2D126C44CC2CC7B2A6555193035DC76310804F12E549BDB011C103089E73510ACB275FC312A5DC6B76553F0CA");
             return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
@@ -531,10 +656,15 @@
      *
     static X9ECParametersHolder sect283k1 = new X9ECParametersHolder()
     {
+        protected ECCurve createCurve()
+        {
+            return configureCurve(new SecT283K1Curve());
+        }
+
         protected X9ECParameters createParameters()
         {
             byte[] S = null;
-            ECCurve curve = configureCurve(new SecT283K1Curve());
+            ECCurve curve = getCurve();
             X9ECPoint G = configureBasepoint(curve, "04"
                 + "0503213F78CA44883F1A3B8162F188E553CD265F23C1567A16876913B0C2AC2458492836"
                 + "01CCDA380F1C9E318D90F95D07E5426FE87E45C0E8184698E45962364E34116177DD2259");
@@ -547,10 +677,15 @@
      *
     static X9ECParametersHolder sect283r1 = new X9ECParametersHolder()
     {
+        protected ECCurve createCurve()
+        {
+            return configureCurve(new SecT283R1Curve());
+        }
+
         protected X9ECParameters createParameters()
         {
             byte[] S = Hex.decodeStrict("77E2B07370EB0F832A6DD5B62DFC88CD06BB84BE");
-            ECCurve curve = configureCurve(new SecT283R1Curve());
+            ECCurve curve = getCurve();
             X9ECPoint G = configureBasepoint(curve, "04"
                 + "05F939258DB7DD90E1934F8C70B0DFEC2EED25B8557EAC9C80E2E198F8CDBECD86B12053"
                 + "03676854FE24141CB98FE6D4B20D02B4516FF702350EDDB0826779C813F0DF45BE8112F4");
@@ -563,10 +698,15 @@
      *
     static X9ECParametersHolder sect409k1 = new X9ECParametersHolder()
     {
+        protected ECCurve createCurve()
+        {
+            return configureCurve(new SecT409K1Curve());
+        }
+
         protected X9ECParameters createParameters()
         {
             byte[] S = null;
-            ECCurve curve = configureCurve(new SecT409K1Curve());
+            ECCurve curve = getCurve();
             X9ECPoint G = configureBasepoint(curve, "04"
                 + "0060F05F658F49C1AD3AB1890F7184210EFD0987E307C84C27ACCFB8F9F67CC2C460189EB5AAAA62EE222EB1B35540CFE9023746"
                 + "01E369050B7C4E42ACBA1DACBF04299C3460782F918EA427E6325165E9EA10E3DA5F6C42E9C55215AA9CA27A5863EC48D8E0286B");
@@ -579,10 +719,15 @@
      *
     static X9ECParametersHolder sect409r1 = new X9ECParametersHolder()
     {
+        protected ECCurve createCurve()
+        {
+            return configureCurve(new SecT409R1Curve());
+        }
+
         protected X9ECParameters createParameters()
         {
             byte[] S = Hex.decodeStrict("4099B5A457F9D69F79213D094C4BCD4D4262210B");
-            ECCurve curve = configureCurve(new SecT409R1Curve());
+            ECCurve curve = getCurve();
             X9ECPoint G = configureBasepoint(curve, "04"
                 + "015D4860D088DDB3496B0C6064756260441CDE4AF1771D4DB01FFE5B34E59703DC255A868A1180515603AEAB60794E54BB7996A7"
                 + "0061B1CFAB6BE5F32BBFA78324ED106A7636B9C5A7BD198D0158AA4F5488D08F38514F1FDF4B4F40D2181B3681C364BA0273C706");
@@ -595,10 +740,15 @@
      *
     static X9ECParametersHolder sect571k1 = new X9ECParametersHolder()
     {
+        protected ECCurve createCurve()
+        {
+            return configureCurve(new SecT571K1Curve());
+        }
+
         protected X9ECParameters createParameters()
         {
             byte[] S = null;
-            ECCurve curve = configureCurve(new SecT571K1Curve());
+            ECCurve curve = getCurve();
             X9ECPoint G = configureBasepoint(curve, "04"
                 + "026EB7A859923FBC82189631F8103FE4AC9CA2970012D5D46024804801841CA44370958493B205E647DA304DB4CEB08CBBD1BA39494776FB988B47174DCA88C7E2945283A01C8972"
                 + "0349DC807F4FBF374F4AEADE3BCA95314DD58CEC9F307A54FFC61EFC006D8A2C9D4979C0AC44AEA74FBEBBB9F772AEDCB620B01A7BA7AF1B320430C8591984F601CD4C143EF1C7A3");
@@ -611,10 +761,15 @@
      *
     static X9ECParametersHolder sect571r1 = new X9ECParametersHolder()
     {
+        protected ECCurve createCurve()
+        {
+            return configureCurve(new SecT571R1Curve());
+        }
+
         protected X9ECParameters createParameters()
         {
             byte[] S = Hex.decodeStrict("2AA058F73A0E33AB486B0F610410C53A7F132310");
-            ECCurve curve = configureCurve(new SecT571R1Curve());
+            ECCurve curve = getCurve();
             X9ECPoint G = configureBasepoint(curve, "04"
                 + "0303001D34B856296C16C0D40D3CD7750A93D1D2955FA80AA5F40FC8DB7B2ABDBDE53950F4C0D293CDD711A35B67FB1499AE60038614F1394ABFA3B4C850D927E1E7769C8EEC2D19"
                 + "037BF27342DA639B6DCCFFFEB73D69D78C6C27A6009CBBCA1980F8533921E8A684423E43BAB08A576291AF8F461BB2A8B3531D2F0485C19B16E2F1516E23DD3C1A4827AF1B8AC15B");
@@ -627,10 +782,15 @@
      *
     static X9ECParametersHolder sm2p256v1 = new X9ECParametersHolder()
     {
+        protected ECCurve createCurve()
+        {
+            return configureCurve(new SM2P256V1Curve());
+        }
+
         protected X9ECParameters createParameters()
         {
             byte[] S = null;
-            ECCurve curve = configureCurve(new SM2P256V1Curve());
+            ECCurve curve = getCurve();
             X9ECPoint G = configureBasepoint(curve,
                 "0432C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7BC3736A2F4F6779C59BDCEE36B692153D0A9877CC62A474002DF32E52139F0A0");
             return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
@@ -640,6 +800,7 @@
     // END Android-removed: Unsupported curves
 
 
+
     static final Hashtable nameToCurve = new Hashtable();
     static final Hashtable nameToOID = new Hashtable();
     static final Hashtable oidToCurve = new Hashtable();
@@ -746,10 +907,15 @@
 
     public static X9ECParameters getByName(String name)
     {
-        X9ECParametersHolder holder = (X9ECParametersHolder)nameToCurve.get(Strings.toLowerCase(name));
+        X9ECParametersHolder holder = getByNameLazy(name);
         return holder == null ? null : holder.getParameters();
     }
 
+    public static X9ECParametersHolder getByNameLazy(String name)
+    {
+        return (X9ECParametersHolder)nameToCurve.get(Strings.toLowerCase(name));
+    }
+
     /**
      * return the X9ECParameters object for the named curve represented by the passed in object
      * identifier. Null if the curve isn't present.
@@ -759,10 +925,15 @@
      */
     public static X9ECParameters getByOID(ASN1ObjectIdentifier oid)
     {
-        X9ECParametersHolder holder = (X9ECParametersHolder)oidToCurve.get(oid);
+        X9ECParametersHolder holder = getByOIDLazy(oid);
         return holder == null ? null : holder.getParameters();
     }
 
+    public static X9ECParametersHolder getByOIDLazy(ASN1ObjectIdentifier oid)
+    {
+        return (X9ECParametersHolder)oidToCurve.get(oid);
+    }
+
     /**
      * return the object identifier signified by the passed in name. Null if there is no object
      * identifier associated with name.
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/encodings/OAEPEncoding.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/encodings/OAEPEncoding.java
index c085844..43c7616 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/encodings/OAEPEncoding.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/encodings/OAEPEncoding.java
@@ -14,6 +14,7 @@
 // import org.bouncycastle.crypto.util.DigestFactory;
 import com.android.org.bouncycastle.crypto.digests.AndroidDigestFactory;
 import com.android.org.bouncycastle.util.Arrays;
+import com.android.org.bouncycastle.util.Pack;
 
 /**
  * Optimal Asymmetric Encryption Padding (OAEP) - see PKCS 1 V 2.
@@ -227,7 +228,9 @@
         // on encryption, we need to make sure our decrypted block comes back
         // the same size.
         //
-        boolean wrongData = (block.length < (2 * defHash.length) + 1);
+
+        // i.e. wrong when block.length < (2 * defHash.length) + 1
+        int wrongMask = (block.length - ((2 * defHash.length) + 1)) >> 31;
 
         if (data.length <= block.length)
         {
@@ -236,7 +239,7 @@
         else
         {
             System.arraycopy(data, 0, block, 0, block.length);
-            wrongData = true;
+            wrongMask |= 1;
         }
 
         //
@@ -264,39 +267,38 @@
         // check the hash of the encoding params.
         // long check to try to avoid this been a source of a timing attack.
         //
-        boolean defHashWrong = false;
-
         for (int i = 0; i != defHash.length; i++)
         {
-            if (defHash[i] != block[defHash.length + i])
-            {
-                defHashWrong = true;
-            }
+            wrongMask |= defHash[i] ^ block[defHash.length + i];
         }
 
         //
         // find the data block
         //
-        int start = block.length;
+        int start = -1;
 
         for (int index = 2 * defHash.length; index != block.length; index++)
         {
-            if (block[index] != 0 & start == block.length)
-            {
-                start = index;
-            }
+            int octet = block[index] & 0xFF;
+
+            // i.e. mask will be 0xFFFFFFFF if octet is non-zero and start is (still) negative, else 0.
+            int shouldSetMask = (-octet & start) >> 31;
+
+            start += index & shouldSetMask;
         }
 
-        boolean dataStartWrong = (start > (block.length - 1) | block[start] != 1);
+        wrongMask |= start >> 31;
+        ++start;
+        wrongMask |= block[start] ^ 1;
 
-        start++;
-
-        if (defHashWrong | wrongData | dataStartWrong)
+        if (wrongMask != 0)
         {
             Arrays.fill(block, (byte)0);
             throw new InvalidCipherTextException("data wrong");
         }
 
+        ++start;
+
         //
         // extract the data block
         //
@@ -309,19 +311,6 @@
     }
 
     /**
-     * int to octet string.
-     */
-    private void ItoOSP(
-        int     i,
-        byte[]  sp)
-    {
-        sp[0] = (byte)(i >>> 24);
-        sp[1] = (byte)(i >>> 16);
-        sp[2] = (byte)(i >>> 8);
-        sp[3] = (byte)(i >>> 0);
-    }
-
-    /**
      * mask generator function, as described in PKCS1v2.
      */
     private byte[] maskGeneratorFunction1(
@@ -339,7 +328,7 @@
 
         while (counter < (length / hashBuf.length))
         {
-            ItoOSP(counter, C);
+            Pack.intToBigEndian(counter, C, 0);
 
             mgf1Hash.update(Z, zOff, zLen);
             mgf1Hash.update(C, 0, C.length);
@@ -352,7 +341,7 @@
 
         if ((counter * hashBuf.length) < length)
         {
-            ItoOSP(counter, C);
+            Pack.intToBigEndian(counter, C, 0);
 
             mgf1Hash.update(Z, zOff, zLen);
             mgf1Hash.update(C, 0, C.length);
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/encodings/PKCS1Encoding.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/encodings/PKCS1Encoding.java
index 7f8b1ba..9627bd8 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/encodings/PKCS1Encoding.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/encodings/PKCS1Encoding.java
@@ -235,52 +235,86 @@
     }
 
     /**
-     * Checks if the argument is a correctly PKCS#1.5 encoded Plaintext
-     * for encryption.
-     *
-     * @param encoded The Plaintext.
-     * @param pLen    Expected length of the plaintext.
-     * @return Either 0, if the encoding is correct, or -1, if it is incorrect.
+     * Check the argument is a valid encoding with type 1. Returns the plaintext length if valid, or -1 if invalid.
      */
-    private static int checkPkcs1Encoding(byte[] encoded, int pLen)
+    private static int checkPkcs1Encoding1(byte[] buf)
     {
-        int correct = 0;
-        /*
-		 * Check if the first two bytes are 0 2
-		 */
-        correct |= (encoded[0] ^ 2);
+        int foundZeroMask = 0;
+        int lastPadPos = 0;
 
-		/*
-		 * Now the padding check, check for no 0 byte in the padding
-		 */
-        int plen = encoded.length - (
-            pLen /* Length of the PMS */
-                + 1 /* Final 0-byte before PMS */
-        );
+        // The first byte should be 0x01
+        int badPadSign = -((buf[0] & 0xFF) ^ 0x01);
 
-        for (int i = 1; i < plen; i++)
+        // There must be a zero terminator for the padding somewhere
+        for (int i = 1; i < buf.length; ++i)
         {
-            int tmp = encoded[i];
-            tmp |= tmp >> 1;
-            tmp |= tmp >> 2;
-            tmp |= tmp >> 4;
-            correct |= (tmp & 1) - 1;
+            int padByte = buf[i] & 0xFF;
+            int is0x00Mask = ((padByte ^ 0x00) - 1) >> 31;
+            int is0xFFMask = ((padByte ^ 0xFF) - 1) >> 31;
+            lastPadPos ^= i & ~foundZeroMask & is0x00Mask;
+            foundZeroMask |= is0x00Mask;
+            badPadSign |= ~(foundZeroMask | is0xFFMask);
         }
 
-		/*
-		 * Make sure the padding ends with a 0 byte.
-		 */
-        correct |= encoded[encoded.length - (pLen + 1)];
+        // The header should be at least 10 bytes
+        badPadSign |= lastPadPos - 9;
 
-		/*
-		 * Return 0 or 1, depending on the result.
-		 */
-        correct |= correct >> 1;
-        correct |= correct >> 2;
-        correct |= correct >> 4;
-        return ~((correct & 1) - 1);
+        int plaintextLength = buf.length - 1 - lastPadPos;
+        return plaintextLength | badPadSign >> 31;
     }
 
+    /**
+     * Check the argument is a valid encoding with type 2. Returns the plaintext length if valid, or -1 if invalid.
+     */
+    private static int checkPkcs1Encoding2(byte[] buf)
+    {
+        int foundZeroMask = 0;
+        int lastPadPos = 0;
+
+        // The first byte should be 0x02
+        int badPadSign = -((buf[0] & 0xFF) ^ 0x02);
+
+        // There must be a zero terminator for the padding somewhere
+        for (int i = 1; i < buf.length; ++i)
+        {
+            int padByte = buf[i] & 0xFF;
+            int is0x00Mask = ((padByte ^ 0x00) - 1) >> 31;
+            lastPadPos ^= i & ~foundZeroMask & is0x00Mask;
+            foundZeroMask |= is0x00Mask;
+        }
+
+        // The header should be at least 10 bytes
+        badPadSign |= lastPadPos - 9;
+
+        int plaintextLength = buf.length - 1 - lastPadPos;
+        return plaintextLength | badPadSign >> 31;
+    }
+
+    /**
+     * Check the argument is a valid encoding with type 2 of a plaintext with the given length. Returns 0 if
+     * valid, or -1 if invalid.
+     */
+    private static int checkPkcs1Encoding2(byte[] buf, int plaintextLength)
+    {
+        // The first byte should be 0x02
+        int badPadSign = -((buf[0] & 0xFF) ^ 0x02);
+
+        int lastPadPos = buf.length - 1 - plaintextLength;
+
+        // The header should be at least 10 bytes
+        badPadSign |= lastPadPos - 9;
+
+        // All pad bytes before the last one should be non-zero
+        for (int i = 1; i < lastPadPos; ++i)
+        {
+            badPadSign |= (buf[i] & 0xFF) - 1;
+        }
+
+        // Last pad byte should be zero
+        badPadSign |= -(buf[lastPadPos] & 0xFF);
+
+        return badPadSign >> 31;
+    }
 
     /**
      * Decode PKCS#1.5 encoding, and return a random value if the padding is not correct.
@@ -300,36 +334,43 @@
             throw new InvalidCipherTextException("sorry, this method is only for decryption, not for signing");
         }
 
-        byte[] block = engine.processBlock(in, inOff, inLen);
-        byte[] random;
-        if (this.fallback == null)
+        int plaintextLength = this.pLen;
+
+        byte[] random = fallback;
+        if (fallback == null)
         {
-            random = new byte[this.pLen];
+            random = new byte[plaintextLength];
             this.random.nextBytes(random);
         }
-        else
+
+        int badPadMask = 0;
+        int strictBlockSize = engine.getOutputBlockSize();
+        byte[] block = engine.processBlock(in, inOff, inLen);
+
+        byte[] data = block;
+        if (block.length != strictBlockSize)
         {
-            random = fallback;
+            if (useStrictLength || block.length < strictBlockSize)
+            {
+                data = blockBuffer;
+            }
         }
 
-        byte[] data = (useStrictLength & (block.length != engine.getOutputBlockSize())) ? blockBuffer : block;
+        badPadMask |= checkPkcs1Encoding2(data, plaintextLength);
 
-		/*
-		 * Check the padding.
-		 */
-        int correct = PKCS1Encoding.checkPkcs1Encoding(data, this.pLen);
-		
-		/*
-		 * Now, to a constant time constant memory copy of the decrypted value
-		 * or the random value, depending on the validity of the padding.
-		 */
-        byte[] result = new byte[this.pLen];
-        for (int i = 0; i < this.pLen; i++)
+        /*
+         * Now, to a constant time constant memory copy of the decrypted value
+         * or the random value, depending on the validity of the padding.
+         */
+        int dataOff = data.length - plaintextLength; 
+        byte[] result = new byte[plaintextLength];
+        for (int i = 0; i < plaintextLength; ++i)
         {
-            result[i] = (byte)((data[i + (data.length - pLen)] & (~correct)) | (random[i] & correct));
+            result[i] = (byte)((data[dataOff + i] & ~badPadMask) | (random[i] & badPadMask));
         }
 
-        Arrays.fill(data, (byte)0);
+        Arrays.fill(block, (byte)0);
+        Arrays.fill(blockBuffer, 0, Math.max(0, blockBuffer.length - block.length), (byte)0);
 
         return result;
     }
@@ -337,95 +378,50 @@
     /**
      * @throws InvalidCipherTextException if the decrypted block is not in PKCS1 format.
      */
-    private byte[] decodeBlock(
-        byte[] in,
-        int inOff,
-        int inLen)
+    private byte[] decodeBlock(byte[] in, int inOff, int inLen)
         throws InvalidCipherTextException
     {
         /*
          * If the length of the expected plaintext is known, we use a constant-time decryption.
          * If the decryption fails, we return a random value.
          */
-        if (this.pLen != -1)
+        if (forPrivateKey && this.pLen != -1)
         {
             return this.decodeBlockOrRandom(in, inOff, inLen);
         }
 
+        int strictBlockSize = engine.getOutputBlockSize();
         byte[] block = engine.processBlock(in, inOff, inLen);
-        boolean incorrectLength = (useStrictLength & (block.length != engine.getOutputBlockSize()));
 
-        byte[] data;
-        if (block.length < getOutputBlockSize())
+        boolean incorrectLength = useStrictLength & (block.length != strictBlockSize);
+
+        byte[] data = block;
+        if (block.length < strictBlockSize)
         {
             data = blockBuffer;
         }
-        else
+
+        int plaintextLength = forPrivateKey ? checkPkcs1Encoding2(data) : checkPkcs1Encoding1(data);
+
+        try
         {
-            data = block;
-        }
-
-        byte type = data[0];
-
-        boolean badType;
-        if (forPrivateKey)
-        {
-            badType = (type != 2);
-        }
-        else
-        {
-            badType = (type != 1);
-        }
-
-        //
-        // find and extract the message block.
-        //
-        int start = findStart(type, data);
-
-        start++;           // data should start at the next byte
-
-        if (badType | start < HEADER_LENGTH)
-        {
-            Arrays.fill(data, (byte)0);
-            throw new InvalidCipherTextException("block incorrect");
-        }
-
-        // if we get this far, it's likely to be a genuine encoding error
-        if (incorrectLength)
-        {
-            Arrays.fill(data, (byte)0);
-            throw new InvalidCipherTextException("block incorrect size");
-        }
-
-        byte[] result = new byte[data.length - start];
-
-        System.arraycopy(data, start, result, 0, result.length);
-
-        return result;
-    }
-
-    private int findStart(byte type, byte[] block)
-        throws InvalidCipherTextException
-    {
-        int start = -1;
-        boolean padErr = false;
-
-        for (int i = 1; i != block.length; i++)
-        {
-            byte pad = block[i];
-
-            if (pad == 0 & start < 0)
+            if (plaintextLength < 0)
             {
-                start = i;
+                throw new InvalidCipherTextException("block incorrect");
             }
-            padErr |= (type == 1 & start < 0 & pad != (byte)0xff);
-        }
+            if (incorrectLength)
+            {
+                throw new InvalidCipherTextException("block incorrect size");
+            }
 
-        if (padErr)
+            byte[] result = new byte[plaintextLength];
+            System.arraycopy(data, data.length - plaintextLength, result, 0, plaintextLength);
+            return result;
+        }
+        finally
         {
-            return -1;
+            Arrays.fill(block, (byte)0);
+            Arrays.fill(blockBuffer, 0, Math.max(0, blockBuffer.length - block.length), (byte)0);
         }
-
-        return start;
     }
 }
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/engines/AESEngine.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/engines/AESEngine.java
index 8c9e2dd..fcd0fe0 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/engines/AESEngine.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/engines/AESEngine.java
@@ -1,10 +1,13 @@
 /* GENERATED SOURCE. DO NOT MODIFY. */
 package com.android.org.bouncycastle.crypto.engines;
 
-import com.android.org.bouncycastle.crypto.BlockCipher;
 import com.android.org.bouncycastle.crypto.CipherParameters;
+import com.android.org.bouncycastle.crypto.CryptoServicesRegistrar;
 import com.android.org.bouncycastle.crypto.DataLengthException;
+import com.android.org.bouncycastle.crypto.DefaultMultiBlockCipher;
+import com.android.org.bouncycastle.crypto.MultiBlockCipher;
 import com.android.org.bouncycastle.crypto.OutputLengthException;
+import com.android.org.bouncycastle.crypto.constraints.DefaultServiceProperties;
 import com.android.org.bouncycastle.crypto.params.KeyParameter;
 import com.android.org.bouncycastle.util.Arrays;
 import com.android.org.bouncycastle.util.Pack;
@@ -15,7 +18,7 @@
  * For further details see: <a href="https://csrc.nist.gov/encryption/aes/">https://csrc.nist.gov/encryption/aes/</a>.
  *
  * This implementation is based on optimizations from Dr. Brian Gladman's paper and C code at
- * <a href="http://fp.gladman.plus.com/cryptography_technology/rijndael/">http://fp.gladman.plus.com/cryptography_technology/rijndael/</a>
+ * <a href="https://fp.gladman.plus.com/cryptography_technology/rijndael/">https://fp.gladman.plus.com/cryptography_technology/rijndael/</a>
  *
  * There are three levels of tradeoff of speed vs memory
  * Because java has no preprocessor, they are written as three separate classes from which to choose
@@ -34,7 +37,7 @@
  *
  */
 public class AESEngine
-    implements BlockCipher
+    extends DefaultMultiBlockCipher
 {
     // The S box
     private static final byte[] S = {
@@ -414,7 +417,6 @@
 
     private int         ROUNDS;
     private int[][]     WorkingKey = null;
-    private int         C0, C1, C2, C3;
     private boolean     forEncryption;
 
     private byte[]      s;
@@ -422,10 +424,22 @@
     private static final int BLOCK_SIZE = 16;
 
     /**
+      * Return an AESEngine.
+      *
+      * @return an AES ECB mode cipher.
+      */
+     public static MultiBlockCipher newInstance()
+     {
+         return new AESEngine();
+     }
+
+    /**
      * default constructor - 128 bit block size.
+     * @deprecated use AESEngine.newInstance()
      */
     public AESEngine()
     {
+        CryptoServicesRegistrar.checkConstraints(new DefaultServiceProperties(getAlgorithmName(), 256));
     }
 
     /**
@@ -452,6 +466,9 @@
             {
                 s = Arrays.clone(Si);
             }
+
+            CryptoServicesRegistrar.checkConstraints(new DefaultServiceProperties(getAlgorithmName(), bitsOfSecurity(), params, Utils.getPurpose(forEncryption)));
+
             return;
         }
 
@@ -468,38 +485,30 @@
         return BLOCK_SIZE;
     }
 
-    public int processBlock(
-        byte[] in,
-        int inOff,
-        byte[] out,
-        int outOff)
+    public int processBlock(byte[] in, int inOff, byte[] out, int outOff)
     {
         if (WorkingKey == null)
         {
             throw new IllegalStateException("AES engine not initialised");
         }
 
-        if ((inOff + (32 / 2)) > in.length)
+        if (inOff > (in.length - BLOCK_SIZE))
         {
             throw new DataLengthException("input buffer too short");
         }
 
-        if ((outOff + (32 / 2)) > out.length)
+        if (outOff > (out.length - BLOCK_SIZE))
         {
             throw new OutputLengthException("output buffer too short");
         }
 
         if (forEncryption)
         {
-            unpackBlock(in, inOff);
-            encryptBlock(WorkingKey);
-            packBlock(out, outOff);
+            encryptBlock(in, inOff, out, outOff, WorkingKey);
         }
         else
         {
-            unpackBlock(in, inOff);
-            decryptBlock(WorkingKey);
-            packBlock(out, outOff);
+            decryptBlock(in, inOff, out, outOff, WorkingKey);
         }
 
         return BLOCK_SIZE;
@@ -509,68 +518,18 @@
     {
     }
 
-    private void unpackBlock(
-        byte[]      bytes,
-        int         off)
+    private void encryptBlock(byte[] in, int inOff, byte[] out, int outOff, int[][] KW)
     {
-        int     index = off;
+        int C0 = Pack.littleEndianToInt(in, inOff +  0);
+        int C1 = Pack.littleEndianToInt(in, inOff +  4);
+        int C2 = Pack.littleEndianToInt(in, inOff +  8);
+        int C3 = Pack.littleEndianToInt(in, inOff + 12);
 
-        C0 = (bytes[index++] & 0xff);
-        C0 |= (bytes[index++] & 0xff) << 8;
-        C0 |= (bytes[index++] & 0xff) << 16;
-        C0 |= bytes[index++] << 24;
+        int t0 = C0 ^ KW[0][0];
+        int t1 = C1 ^ KW[0][1];
+        int t2 = C2 ^ KW[0][2];
 
-        C1 = (bytes[index++] & 0xff);
-        C1 |= (bytes[index++] & 0xff) << 8;
-        C1 |= (bytes[index++] & 0xff) << 16;
-        C1 |= bytes[index++] << 24;
-
-        C2 = (bytes[index++] & 0xff);
-        C2 |= (bytes[index++] & 0xff) << 8;
-        C2 |= (bytes[index++] & 0xff) << 16;
-        C2 |= bytes[index++] << 24;
-
-        C3 = (bytes[index++] & 0xff);
-        C3 |= (bytes[index++] & 0xff) << 8;
-        C3 |= (bytes[index++] & 0xff) << 16;
-        C3 |= bytes[index++] << 24;
-    }
-
-    private void packBlock(
-        byte[]      bytes,
-        int         off)
-    {
-        int     index = off;
-
-        bytes[index++] = (byte)C0;
-        bytes[index++] = (byte)(C0 >> 8);
-        bytes[index++] = (byte)(C0 >> 16);
-        bytes[index++] = (byte)(C0 >> 24);
-
-        bytes[index++] = (byte)C1;
-        bytes[index++] = (byte)(C1 >> 8);
-        bytes[index++] = (byte)(C1 >> 16);
-        bytes[index++] = (byte)(C1 >> 24);
-
-        bytes[index++] = (byte)C2;
-        bytes[index++] = (byte)(C2 >> 8);
-        bytes[index++] = (byte)(C2 >> 16);
-        bytes[index++] = (byte)(C2 >> 24);
-
-        bytes[index++] = (byte)C3;
-        bytes[index++] = (byte)(C3 >> 8);
-        bytes[index++] = (byte)(C3 >> 16);
-        bytes[index++] = (byte)(C3 >> 24);
-    }
-
-
-    private void encryptBlock(int[][] KW)
-    {
-        int t0 = this.C0 ^ KW[0][0];
-        int t1 = this.C1 ^ KW[0][1];
-        int t2 = this.C2 ^ KW[0][2];
-
-        int r = 1, r0, r1, r2, r3 = this.C3 ^ KW[0][3];
+        int r = 1, r0, r1, r2, r3 = C3 ^ KW[0][3];
         while (r < ROUNDS - 1)
         {
             r0 = T0[t0&255] ^ shift(T0[(t1>>8)&255], 24) ^ shift(T0[(t2>>16)&255], 16) ^ shift(T0[(r3>>24)&255], 8) ^ KW[r][0];
@@ -590,19 +549,29 @@
 
         // the final round's table is a simple function of S so we don't use a whole other four tables for it
 
-        this.C0 = (S[r0&255]&255) ^ ((S[(r1>>8)&255]&255)<<8) ^ ((s[(r2>>16)&255]&255)<<16) ^ (s[(r3>>24)&255]<<24) ^ KW[r][0];
-        this.C1 = (s[r1&255]&255) ^ ((S[(r2>>8)&255]&255)<<8) ^ ((S[(r3>>16)&255]&255)<<16) ^ (s[(r0>>24)&255]<<24) ^ KW[r][1];
-        this.C2 = (s[r2&255]&255) ^ ((S[(r3>>8)&255]&255)<<8) ^ ((S[(r0>>16)&255]&255)<<16) ^ (S[(r1>>24)&255]<<24) ^ KW[r][2];
-        this.C3 = (s[r3&255]&255) ^ ((s[(r0>>8)&255]&255)<<8) ^ ((s[(r1>>16)&255]&255)<<16) ^ (S[(r2>>24)&255]<<24) ^ KW[r][3];
+        C0 = (S[r0&255]&255) ^ ((S[(r1>>8)&255]&255)<<8) ^ ((s[(r2>>16)&255]&255)<<16) ^ (s[(r3>>24)&255]<<24) ^ KW[r][0];
+        C1 = (s[r1&255]&255) ^ ((S[(r2>>8)&255]&255)<<8) ^ ((S[(r3>>16)&255]&255)<<16) ^ (s[(r0>>24)&255]<<24) ^ KW[r][1];
+        C2 = (s[r2&255]&255) ^ ((S[(r3>>8)&255]&255)<<8) ^ ((S[(r0>>16)&255]&255)<<16) ^ (S[(r1>>24)&255]<<24) ^ KW[r][2];
+        C3 = (s[r3&255]&255) ^ ((s[(r0>>8)&255]&255)<<8) ^ ((s[(r1>>16)&255]&255)<<16) ^ (S[(r2>>24)&255]<<24) ^ KW[r][3];
+
+        Pack.intToLittleEndian(C0, out, outOff +  0);
+        Pack.intToLittleEndian(C1, out, outOff +  4);
+        Pack.intToLittleEndian(C2, out, outOff +  8);
+        Pack.intToLittleEndian(C3, out, outOff + 12);
     }
 
-    private void decryptBlock(int[][] KW)
+    private void decryptBlock(byte[] in, int inOff, byte[] out, int outOff, int[][] KW)
     {
-        int t0 = this.C0 ^ KW[ROUNDS][0];
-        int t1 = this.C1 ^ KW[ROUNDS][1];
-        int t2 = this.C2 ^ KW[ROUNDS][2];
+        int C0 = Pack.littleEndianToInt(in, inOff +  0);
+        int C1 = Pack.littleEndianToInt(in, inOff +  4);
+        int C2 = Pack.littleEndianToInt(in, inOff +  8);
+        int C3 = Pack.littleEndianToInt(in, inOff + 12);
 
-        int r = ROUNDS - 1, r0, r1, r2, r3 = this.C3 ^ KW[ROUNDS][3];
+        int t0 = C0 ^ KW[ROUNDS][0];
+        int t1 = C1 ^ KW[ROUNDS][1];
+        int t2 = C2 ^ KW[ROUNDS][2];
+
+        int r = ROUNDS - 1, r0, r1, r2, r3 = C3 ^ KW[ROUNDS][3];
         while (r > 1)
         {
             r0 = Tinv0[t0&255] ^ shift(Tinv0[(r3>>8)&255], 24) ^ shift(Tinv0[(t2>>16)&255], 16) ^ shift(Tinv0[(t1>>24)&255], 8) ^ KW[r][0];
@@ -622,9 +591,23 @@
         
         // the final round's table is a simple function of Si so we don't use a whole other four tables for it
 
-        this.C0 = (Si[r0&255]&255) ^ ((s[(r3>>8)&255]&255)<<8) ^ ((s[(r2>>16)&255]&255)<<16) ^ (Si[(r1>>24)&255]<<24) ^ KW[0][0];
-        this.C1 = (s[r1&255]&255) ^ ((s[(r0>>8)&255]&255)<<8) ^ ((Si[(r3>>16)&255]&255)<<16) ^ (s[(r2>>24)&255]<<24) ^ KW[0][1];
-        this.C2 = (s[r2&255]&255) ^ ((Si[(r1>>8)&255]&255)<<8) ^ ((Si[(r0>>16)&255]&255)<<16) ^ (s[(r3>>24)&255]<<24) ^ KW[0][2];
-        this.C3 = (Si[r3&255]&255) ^ ((s[(r2>>8)&255]&255)<<8) ^ ((s[(r1>>16)&255]&255)<<16) ^ (s[(r0>>24)&255]<<24) ^ KW[0][3];
+        C0 = (Si[r0&255]&255) ^ ((s[(r3>>8)&255]&255)<<8) ^ ((s[(r2>>16)&255]&255)<<16) ^ (Si[(r1>>24)&255]<<24) ^ KW[0][0];
+        C1 = (s[r1&255]&255) ^ ((s[(r0>>8)&255]&255)<<8) ^ ((Si[(r3>>16)&255]&255)<<16) ^ (s[(r2>>24)&255]<<24) ^ KW[0][1];
+        C2 = (s[r2&255]&255) ^ ((Si[(r1>>8)&255]&255)<<8) ^ ((Si[(r0>>16)&255]&255)<<16) ^ (s[(r3>>24)&255]<<24) ^ KW[0][2];
+        C3 = (Si[r3&255]&255) ^ ((s[(r2>>8)&255]&255)<<8) ^ ((s[(r1>>16)&255]&255)<<16) ^ (s[(r0>>24)&255]<<24) ^ KW[0][3];
+
+        Pack.intToLittleEndian(C0, out, outOff +  0);
+        Pack.intToLittleEndian(C1, out, outOff +  4);
+        Pack.intToLittleEndian(C2, out, outOff +  8);
+        Pack.intToLittleEndian(C3, out, outOff + 12);
+    }
+
+    private int bitsOfSecurity()
+    {
+        if (WorkingKey == null)
+        {
+            return 256;
+        }
+        return (WorkingKey.length - 7) << 5;
     }
 }
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/engines/AESFastEngine.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/engines/AESFastEngine.java
index 95ccb38..d222a67 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/engines/AESFastEngine.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/engines/AESFastEngine.java
@@ -3,8 +3,10 @@
 
 import com.android.org.bouncycastle.crypto.BlockCipher;
 import com.android.org.bouncycastle.crypto.CipherParameters;
+import com.android.org.bouncycastle.crypto.CryptoServicesRegistrar;
 import com.android.org.bouncycastle.crypto.DataLengthException;
 import com.android.org.bouncycastle.crypto.OutputLengthException;
+import com.android.org.bouncycastle.crypto.constraints.DefaultServiceProperties;
 import com.android.org.bouncycastle.crypto.params.KeyParameter;
 import com.android.org.bouncycastle.util.Pack;
 
@@ -14,7 +16,7 @@
  * For further details see: <a href="https://csrc.nist.gov/encryption/aes/">https://csrc.nist.gov/encryption/aes/</a>.
  *
  * This implementation is based on optimizations from Dr. Brian Gladman's paper and C code at
- * <a href="http://fp.gladman.plus.com/cryptography_technology/rijndael/">http://fp.gladman.plus.com/cryptography_technology/rijndael/</a>
+ * <a href="https://fp.gladman.plus.com/cryptography_technology/rijndael/">https://fp.gladman.plus.com/cryptography_technology/rijndael/</a>
  *
  * There are three levels of tradeoff of speed vs memory
  * Because java has no preprocessor, they are written as three separate classes from which to choose
@@ -743,7 +745,6 @@
 
     private int         ROUNDS;
     private int[][]     WorkingKey = null;
-    private int         C0, C1, C2, C3;
     private boolean     forEncryption;
 
     private static final int BLOCK_SIZE = 16;
@@ -753,6 +754,7 @@
      */
     public AESFastEngine()
     {
+        CryptoServicesRegistrar.checkConstraints(new DefaultServiceProperties(getAlgorithmName(), 256));
     }
 
     /**
@@ -771,6 +773,7 @@
         {
             WorkingKey = generateWorkingKey(((KeyParameter)params).getKey(), forEncryption);
             this.forEncryption = forEncryption;
+            CryptoServicesRegistrar.checkConstraints(new DefaultServiceProperties(getAlgorithmName(), bitsOfSecurity(), params, Utils.getPurpose(forEncryption)));
             return;
         }
 
@@ -787,40 +790,32 @@
         return BLOCK_SIZE;
     }
 
-    public int processBlock(
-        byte[] in,
-        int inOff,
-        byte[] out,
-        int outOff)
+    public int processBlock(byte[] in, int inOff, byte[] out, int outOff)
     {
         if (WorkingKey == null)
         {
             throw new IllegalStateException("AES engine not initialised");
         }
 
-        if ((inOff + (32 / 2)) > in.length)
+        if (inOff > (in.length - BLOCK_SIZE))
         {
             throw new DataLengthException("input buffer too short");
         }
 
-        if ((outOff + (32 / 2)) > out.length)
+        if (outOff > (out.length - BLOCK_SIZE))
         {
             throw new OutputLengthException("output buffer too short");
         }
 
-        unpackBlock(in, inOff);
-
         if (forEncryption)
         {
-            encryptBlock(WorkingKey);
+            encryptBlock(in, inOff, out, outOff, WorkingKey);
         }
         else
         {
-            decryptBlock(WorkingKey);
+            decryptBlock(in, inOff, out, outOff, WorkingKey);
         }
 
-        packBlock(out, outOff);
-
         return BLOCK_SIZE;
     }
 
@@ -828,27 +823,16 @@
     {
     }
 
-    private void unpackBlock(byte[] bytes, int off)
+    private void encryptBlock(byte[] in, int inOff, byte[] out, int outOff, int[][] KW)
     {
-        this.C0 = Pack.littleEndianToInt(bytes, off);
-        this.C1 = Pack.littleEndianToInt(bytes, off + 4);
-        this.C2 = Pack.littleEndianToInt(bytes, off + 8);
-        this.C3 = Pack.littleEndianToInt(bytes, off + 12);
-    }
+        int C0 = Pack.littleEndianToInt(in, inOff +  0);
+        int C1 = Pack.littleEndianToInt(in, inOff +  4);
+        int C2 = Pack.littleEndianToInt(in, inOff +  8);
+        int C3 = Pack.littleEndianToInt(in, inOff + 12);
 
-    private void packBlock(byte[] bytes, int off)
-    {
-        Pack.intToLittleEndian(this.C0, bytes, off);
-        Pack.intToLittleEndian(this.C1, bytes, off + 4);
-        Pack.intToLittleEndian(this.C2, bytes, off + 8);
-        Pack.intToLittleEndian(this.C3, bytes, off + 12);
-    }
-
-    private void encryptBlock(int[][] KW)
-    {
-        int t0 = this.C0 ^ KW[0][0];
-        int t1 = this.C1 ^ KW[0][1];
-        int t2 = this.C2 ^ KW[0][2];
+        int t0 = C0 ^ KW[0][0];
+        int t1 = C1 ^ KW[0][1];
+        int t2 = C2 ^ KW[0][2];
 
         /*
          * Fast engine has precomputed rotr(T0, 8/16/24) tables T1/T2/T3.
@@ -857,7 +841,7 @@
          * avoids additional array range checks on 3 more arrays (which on HotSpot are more
          * expensive than the offset additions).
          */
-        int r = 1, r0, r1, r2, r3 = this.C3 ^ KW[0][3];
+        int r = 1, r0, r1, r2, r3 = C3 ^ KW[0][3];
         int i0, i1, i2, i3;
 
         while (r < ROUNDS - 1)
@@ -915,28 +899,38 @@
 
         i0 = r0; i1 = r1 >>> 8; i2 = r2 >>> 16; i3 = r3 >>> 24;
         i0 = S[i0 & 255] & 255; i1 = S[i1 & 255] & 255; i2 = S[i2 & 255] & 255; i3 = S[i3 & 255] & 255;
-        this.C0 = i0 ^ i1 << 8 ^ i2 << 16 ^ i3 << 24 ^ KW[r][0];
+        C0 = i0 ^ i1 << 8 ^ i2 << 16 ^ i3 << 24 ^ KW[r][0];
 
         i0 = r1; i1 = r2 >>> 8; i2 = r3 >>> 16; i3 = r0 >>> 24;
         i0 = S[i0 & 255] & 255; i1 = S[i1 & 255] & 255; i2 = S[i2 & 255] & 255; i3 = S[i3 & 255] & 255;
-        this.C1 = i0 ^ i1 << 8 ^ i2 << 16 ^ i3 << 24 ^ KW[r][1];
+        C1 = i0 ^ i1 << 8 ^ i2 << 16 ^ i3 << 24 ^ KW[r][1];
 
         i0 = r2; i1 = r3 >>> 8; i2 = r0 >>> 16; i3 = r1 >>> 24;
         i0 = S[i0 & 255] & 255; i1 = S[i1 & 255] & 255; i2 = S[i2 & 255] & 255; i3 = S[i3 & 255] & 255;
-        this.C2 = i0 ^ i1 << 8 ^ i2 << 16 ^ i3 << 24 ^ KW[r][2];
+        C2 = i0 ^ i1 << 8 ^ i2 << 16 ^ i3 << 24 ^ KW[r][2];
 
         i0 = r3; i1 = r0 >>> 8; i2 = r1 >>> 16; i3 = r2 >>> 24;
         i0 = S[i0 & 255] & 255; i1 = S[i1 & 255] & 255; i2 = S[i2 & 255] & 255; i3 = S[i3 & 255] & 255;
-        this.C3 = i0 ^ i1 << 8 ^ i2 << 16 ^ i3 << 24 ^ KW[r][3];
+        C3 = i0 ^ i1 << 8 ^ i2 << 16 ^ i3 << 24 ^ KW[r][3];
+
+        Pack.intToLittleEndian(C0, out, outOff +  0);
+        Pack.intToLittleEndian(C1, out, outOff +  4);
+        Pack.intToLittleEndian(C2, out, outOff +  8);
+        Pack.intToLittleEndian(C3, out, outOff + 12);
     }
 
-    private void decryptBlock(int[][] KW)
+    private void decryptBlock(byte[] in, int inOff, byte[] out, int outOff, int[][] KW)
     {
-        int t0 = this.C0 ^ KW[ROUNDS][0];
-        int t1 = this.C1 ^ KW[ROUNDS][1];
-        int t2 = this.C2 ^ KW[ROUNDS][2];
+        int C0 = Pack.littleEndianToInt(in, inOff +  0);
+        int C1 = Pack.littleEndianToInt(in, inOff +  4);
+        int C2 = Pack.littleEndianToInt(in, inOff +  8);
+        int C3 = Pack.littleEndianToInt(in, inOff + 12);
 
-        int r = ROUNDS - 1, r0, r1, r2, r3 = this.C3 ^ KW[ROUNDS][3];
+        int t0 = C0 ^ KW[ROUNDS][0];
+        int t1 = C1 ^ KW[ROUNDS][1];
+        int t2 = C2 ^ KW[ROUNDS][2];
+
+        int r = ROUNDS - 1, r0, r1, r2, r3 = C3 ^ KW[ROUNDS][3];
         int i0, i1, i2, i3;
 
         while (r > 1)
@@ -994,18 +988,33 @@
 
         i0 = r0; i1 = r3 >>> 8; i2 = r2 >>> 16; i3 = r1 >>> 24;
         i0 = Si[i0 & 255] & 255; i1 = Si[i1 & 255] & 255; i2 = Si[i2 & 255] & 255; i3 = Si[i3 & 255] & 255;
-        this.C0 = i0 ^ i1 << 8 ^ i2 << 16 ^ i3 << 24 ^ KW[0][0];
+        C0 = i0 ^ i1 << 8 ^ i2 << 16 ^ i3 << 24 ^ KW[0][0];
 
         i0 = r1; i1 = r0 >>> 8; i2 = r3 >>> 16; i3 = r2 >>> 24;
         i0 = Si[i0 & 255] & 255; i1 = Si[i1 & 255] & 255; i2 = Si[i2 & 255] & 255; i3 = Si[i3 & 255] & 255;
-        this.C1 = i0 ^ i1 << 8 ^ i2 << 16 ^ i3 << 24 ^ KW[0][1];
+        C1 = i0 ^ i1 << 8 ^ i2 << 16 ^ i3 << 24 ^ KW[0][1];
 
         i0 = r2; i1 = r1 >>> 8; i2 = r0 >>> 16; i3 = r3 >>> 24;
         i0 = Si[i0 & 255] & 255; i1 = Si[i1 & 255] & 255; i2 = Si[i2 & 255] & 255; i3 = Si[i3 & 255] & 255;
-        this.C2 = i0 ^ i1 << 8 ^ i2 << 16 ^ i3 << 24 ^ KW[0][2];
+        C2 = i0 ^ i1 << 8 ^ i2 << 16 ^ i3 << 24 ^ KW[0][2];
 
         i0 = r3; i1 = r2 >>> 8; i2 = r1 >>> 16; i3 = r0 >>> 24;
         i0 = Si[i0 & 255] & 255; i1 = Si[i1 & 255] & 255; i2 = Si[i2 & 255] & 255; i3 = Si[i3 & 255] & 255;
-        this.C3 = i0 ^ i1 << 8 ^ i2 << 16 ^ i3 << 24 ^ KW[0][3];
+        C3 = i0 ^ i1 << 8 ^ i2 << 16 ^ i3 << 24 ^ KW[0][3];
+
+        Pack.intToLittleEndian(C0, out, outOff +  0);
+        Pack.intToLittleEndian(C1, out, outOff +  4);
+        Pack.intToLittleEndian(C2, out, outOff +  8);
+        Pack.intToLittleEndian(C3, out, outOff + 12);
+    }
+
+    // Service Definitions
+    private int bitsOfSecurity()
+    {
+        if (WorkingKey == null)
+        {
+            return 256;
+        }
+        return (WorkingKey.length - 7) << 5;
     }
 }
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/engines/AESWrapEngine.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/engines/AESWrapEngine.java
index 00690c1..e0caf1e 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/engines/AESWrapEngine.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/engines/AESWrapEngine.java
@@ -16,16 +16,16 @@
      */
     public AESWrapEngine()
     {
-        super(new AESEngine());
+        super(AESEngine.newInstance());
     }
 
     /**
-     * Create an AESWrapEngine where the underlying cipher is set to decrypt for wrapping, encrypt for unwrapping.
+     * Create an AESWrapEngine where the underlying cipher is (optionally) set to decrypt for wrapping, encrypt for unwrapping.
      *
      * @param useReverseDirection true if underlying cipher should be used in decryption mode, false otherwise.
      */
     public AESWrapEngine(boolean useReverseDirection)
     {
-        super(new AESEngine(), useReverseDirection);
+        super(AESEngine.newInstance(), useReverseDirection);
     }
 }
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/engines/AsconEngine.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/engines/AsconEngine.java
new file mode 100644
index 0000000..f088224
--- /dev/null
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/engines/AsconEngine.java
@@ -0,0 +1,743 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.org.bouncycastle.crypto.engines;
+
+import com.android.org.bouncycastle.crypto.CipherParameters;
+import com.android.org.bouncycastle.crypto.CryptoServicesRegistrar;
+import com.android.org.bouncycastle.crypto.DataLengthException;
+import com.android.org.bouncycastle.crypto.InvalidCipherTextException;
+import com.android.org.bouncycastle.crypto.OutputLengthException;
+import com.android.org.bouncycastle.crypto.constraints.DefaultServiceProperties;
+import com.android.org.bouncycastle.crypto.modes.AEADCipher;
+import com.android.org.bouncycastle.crypto.params.AEADParameters;
+import com.android.org.bouncycastle.crypto.params.KeyParameter;
+import com.android.org.bouncycastle.crypto.params.ParametersWithIV;
+import com.android.org.bouncycastle.util.Arrays;
+import com.android.org.bouncycastle.util.Longs;
+import com.android.org.bouncycastle.util.Pack;
+
+/**
+ * ASCON AEAD v1.2, https://ascon.iaik.tugraz.at/
+ * https://csrc.nist.gov/CSRC/media/Projects/lightweight-cryptography/documents/finalist-round/updated-spec-doc/ascon-spec-final.pdf
+ * <p>
+ * ASCON AEAD v1.2 with reference to C Reference Impl from: https://github.com/ascon/ascon-c
+ * </p>
+ * @hide This class is not part of the Android public SDK API
+ */
+public class AsconEngine
+    implements AEADCipher
+{
+    /**
+     * @hide This class is not part of the Android public SDK API
+     */
+    public enum AsconParameters
+    {
+        ascon80pq,
+        ascon128a,
+        ascon128
+    }
+
+    private enum State
+    {
+        Uninitialized,
+        EncInit,
+        EncAad,
+        EncData,
+        EncFinal,
+        DecInit,
+        DecAad,
+        DecData,
+        DecFinal,
+    }
+
+    private final AsconParameters asconParameters;
+    private State m_state = State.Uninitialized;
+    private byte[] mac;
+    private byte[] initialAssociatedText;
+    private final String algorithmName;
+    private final int CRYPTO_KEYBYTES;
+    private final int CRYPTO_ABYTES;
+    private final int ASCON_AEAD_RATE;
+    private final int nr;
+    private long K0;
+    private long K1;
+    private long K2;
+    private long N0;
+    private long N1;
+    private final long ASCON_IV;
+    private long x0;
+    private long x1;
+    private long x2;
+    private long x3;
+    private long x4;
+    private final int m_bufferSizeDecrypt;
+    private final byte[] m_buf;
+    private int m_bufPos = 0;
+
+    public AsconEngine(AsconParameters asconParameters)
+    {
+        this.asconParameters = asconParameters;
+        switch (asconParameters)
+        {
+        case ascon80pq:
+            CRYPTO_KEYBYTES = 20;
+            CRYPTO_ABYTES = 16;
+            ASCON_AEAD_RATE = 8;
+            ASCON_IV = 0xa0400c0600000000L;
+            algorithmName = "Ascon-80pq AEAD";
+            break;
+        case ascon128a:
+            CRYPTO_KEYBYTES = 16;
+            CRYPTO_ABYTES = 16;
+            ASCON_AEAD_RATE = 16;
+            ASCON_IV = 0x80800c0800000000L;
+            algorithmName = "Ascon-128a AEAD";
+            break;
+        case ascon128:
+            CRYPTO_KEYBYTES = 16;
+            CRYPTO_ABYTES = 16;
+            ASCON_AEAD_RATE = 8;
+            ASCON_IV = 0x80400c0600000000L;
+            algorithmName = "Ascon-128 AEAD";
+            break;
+        default:
+            throw new IllegalArgumentException("invalid parameter setting for ASCON AEAD");
+        }
+        nr = (ASCON_AEAD_RATE == 8) ? 6 : 8;
+        m_bufferSizeDecrypt = ASCON_AEAD_RATE + CRYPTO_ABYTES;
+        m_buf = new byte[m_bufferSizeDecrypt];
+    }
+
+    private long PAD(int i)
+    {
+        return 0x80L << (56 - (i << 3));
+    }
+
+    private void ROUND(long C)
+    {
+        long t0 = x0 ^ x1 ^ x2 ^ x3 ^ C ^ (x1 & (x0 ^ x2 ^ x4 ^ C));
+        long t1 = x0 ^ x2 ^ x3 ^ x4 ^ C ^ ((x1 ^ x2 ^ C) & (x1 ^ x3));
+        long t2 = x1 ^ x2 ^ x4 ^ C ^ (x3 & x4);
+        long t3 = x0 ^ x1 ^ x2 ^ C ^ ((~x0) & (x3 ^ x4));
+        long t4 = x1 ^ x3 ^ x4 ^ ((x0 ^ x4) & x1);
+        x0 = t0 ^ Longs.rotateRight(t0, 19) ^ Longs.rotateRight(t0, 28);
+        x1 = t1 ^ Longs.rotateRight(t1, 39) ^ Longs.rotateRight(t1, 61);
+        x2 = ~(t2 ^ Longs.rotateRight(t2, 1) ^ Longs.rotateRight(t2, 6));
+        x3 = t3 ^ Longs.rotateRight(t3, 10) ^ Longs.rotateRight(t3, 17);
+        x4 = t4 ^ Longs.rotateRight(t4, 7) ^ Longs.rotateRight(t4, 41);
+    }
+
+    private void P(int nr)
+    {
+        if (nr >= 8)
+        {
+            if (nr == 12)
+            {
+                ROUND(0xf0L);
+                ROUND(0xe1L);
+                ROUND(0xd2L);
+                ROUND(0xc3L);
+            }
+            ROUND(0xb4L);
+            ROUND(0xa5L);
+        }
+        ROUND(0x96L);
+        ROUND(0x87L);
+        ROUND(0x78L);
+        ROUND(0x69L);
+        ROUND(0x5aL);
+        ROUND(0x4bL);
+    }
+
+    private void ascon_aeadinit()
+    {
+        /* initialize */
+        x0 = ASCON_IV;
+        if (CRYPTO_KEYBYTES == 20)
+        {
+            x0 ^= K0;
+        }
+        x1 = K1;
+        x2 = K2;
+        x3 = N0;
+        x4 = N1;
+        P(12);
+        if (CRYPTO_KEYBYTES == 20)
+        {
+            x2 ^= K0;
+        }
+        x3 ^= K1;
+        x4 ^= K2;
+    }
+
+    private void checkAAD()
+    {
+        switch (m_state)
+        {
+        case DecInit:
+            m_state = State.DecAad;
+            break;
+        case EncInit:
+            m_state = State.EncAad;
+            break;
+        case DecAad:
+        case EncAad:
+            break;
+        case EncFinal:
+            throw new IllegalStateException(getAlgorithmName() + " cannot be reused for encryption");
+        default:
+            throw new IllegalStateException(getAlgorithmName() + " needs to be initialized");
+        }
+    }
+
+    private boolean checkData()
+    {
+        switch (m_state)
+        {
+        case DecInit:
+        case DecAad:
+            finishAAD(State.DecData);
+            return false;
+        case EncInit:
+        case EncAad:
+            finishAAD(State.EncData);
+            return true;
+        case DecData:
+            return false;
+        case EncData:
+            return true;
+        case EncFinal:
+            throw new IllegalStateException(getAlgorithmName() + " cannot be reused for encryption");
+        default:
+            throw new IllegalStateException(getAlgorithmName() + " needs to be initialized");
+        }
+    }
+
+    private void processBufferAAD(byte[] buffer, int inOff)
+    {
+        x0 ^= Pack.bigEndianToLong(buffer, inOff);
+        if (ASCON_AEAD_RATE == 16)
+        {
+            x1 ^= Pack.bigEndianToLong(buffer, 8 + inOff);
+        }
+        P(nr);
+    }
+
+    private void finishAAD(State nextState)
+    {
+        // State indicates whether we ever received AAD
+        switch (m_state)
+        {
+        case DecAad:
+        case EncAad:
+            m_buf[m_bufPos] = (byte)0x80;
+            if (m_bufPos >= 8) // ASCON_AEAD_RATE == 16 is implied
+            {
+                x0 ^= Pack.bigEndianToLong(m_buf, 0);
+                x1 ^= Pack.bigEndianToLong(m_buf, 8) & (-1L << (56 - ((m_bufPos - 8) << 3)));
+            }
+            else
+            {
+                x0 ^= Pack.bigEndianToLong(m_buf, 0) & (-1L << (56 - (m_bufPos << 3)));
+            }
+            P(nr);
+            break;
+        default:
+            break;
+        }
+        // domain separation
+        x4 ^= 1L;
+        m_bufPos = 0;
+        m_state = nextState;
+    }
+
+    private void processBufferDecrypt(byte[] buffer, int bufOff, byte[] output, int outOff)
+    {
+        if (outOff + ASCON_AEAD_RATE > output.length)
+        {
+            throw new OutputLengthException("output buffer too short");
+        }
+        long t0 = Pack.bigEndianToLong(buffer, bufOff);
+        Pack.longToBigEndian(x0 ^ t0, output, outOff);
+        x0 = t0;
+
+        if (ASCON_AEAD_RATE == 16)
+        {
+            long t1 = Pack.bigEndianToLong(buffer, bufOff + 8);
+            Pack.longToBigEndian(x1 ^ t1, output, outOff + 8);
+            x1 = t1;
+        }
+        P(nr);
+    }
+
+    private void processBufferEncrypt(byte[] buffer, int bufOff, byte[] output, int outOff)
+    {
+        if (outOff + ASCON_AEAD_RATE > output.length)
+        {
+            throw new OutputLengthException("output buffer too short");
+        }
+        x0 ^= Pack.bigEndianToLong(buffer, bufOff);
+        Pack.longToBigEndian(x0, output, outOff);
+
+        if (ASCON_AEAD_RATE == 16)
+        {
+            x1 ^= Pack.bigEndianToLong(buffer, bufOff + 8);
+            Pack.longToBigEndian(x1, output, outOff + 8);
+        }
+
+        P(nr);
+    }
+
+    private void processFinalDecrypt(byte[] input, int inOff, int inLen, byte[] output, int outOff)
+    {
+        if (inLen >= 8) // ASCON_AEAD_RATE == 16 is implied
+        {
+            long c0 = Pack.bigEndianToLong(input, inOff);
+            x0 ^= c0;
+            Pack.longToBigEndian(x0, output, outOff);
+            x0 = c0;
+            inOff += 8;
+            outOff += 8;
+            inLen -= 8;
+            x1 ^= PAD(inLen);
+            if (inLen != 0)
+            {
+                long c1 = Pack.littleEndianToLong_High(input, inOff, inLen);
+                x1 ^= c1;
+                Pack.longToLittleEndian_High(x1, output, outOff, inLen);
+                x1 &= -1L >>> (inLen << 3);
+                x1 ^= c1;
+            }
+        }
+        else
+        {
+            x0 ^= PAD(inLen);
+            if (inLen != 0)
+            {
+                long c0 = Pack.littleEndianToLong_High(input, inOff, inLen);
+                x0 ^= c0;
+                Pack.longToLittleEndian_High(x0, output, outOff, inLen);
+                x0 &= -1L >>> (inLen << 3);
+                x0 ^= c0;
+            }
+        }
+
+        finishData(State.DecFinal);
+    }
+
+    private void processFinalEncrypt(byte[] input, int inOff, int inLen, byte[] output, int outOff)
+    {
+        if (inLen >= 8) // ASCON_AEAD_RATE == 16 is implied
+        {
+            x0 ^= Pack.bigEndianToLong(input, inOff);
+            Pack.longToBigEndian(x0, output, outOff);
+            inOff += 8;
+            outOff += 8;
+            inLen -= 8;
+            x1 ^= PAD(inLen);
+            if (inLen != 0)
+            {
+                x1 ^= Pack.littleEndianToLong_High(input, inOff, inLen);
+                Pack.longToLittleEndian_High(x1, output, outOff, inLen);
+            }
+        }
+        else
+        {
+            x0 ^= PAD(inLen);
+            if (inLen != 0)
+            {
+                x0 ^= Pack.littleEndianToLong_High(input, inOff, inLen);
+                Pack.longToLittleEndian_High(x0, output, outOff, inLen);
+            }
+        }
+        finishData(State.EncFinal);
+    }
+
+    private void finishData(State nextState)
+    {
+        switch (asconParameters)
+        {
+        case ascon128:
+            x1 ^= K1;
+            x2 ^= K2;
+            break;
+        case ascon128a:
+            x2 ^= K1;
+            x3 ^= K2;
+            break;
+        case ascon80pq:
+            x1 ^= (K0 << 32 | K1 >> 32);
+            x2 ^= (K1 << 32 | K2 >> 32);
+            x3 ^= K2 << 32;
+            break;
+        default:
+            throw new IllegalStateException();
+        }
+        P(12);
+        x3 ^= K1;
+        x4 ^= K2;
+
+        m_state = nextState;
+    }
+
+    public void init(boolean forEncryption, CipherParameters params)
+        throws IllegalArgumentException
+    {
+        KeyParameter key;
+        byte[] npub;
+        if (params instanceof AEADParameters)
+        {
+            AEADParameters aeadParameters = (AEADParameters)params;
+            key = aeadParameters.getKey();
+            npub = aeadParameters.getNonce();
+            initialAssociatedText = aeadParameters.getAssociatedText();
+
+            int macSizeBits = aeadParameters.getMacSize();
+            if (macSizeBits != CRYPTO_ABYTES * 8)
+            {
+                throw new IllegalArgumentException("Invalid value for MAC size: " + macSizeBits);
+            }
+        }
+        else if (params instanceof ParametersWithIV)
+        {
+            ParametersWithIV withIV = (ParametersWithIV)params;
+            key = (KeyParameter)withIV.getParameters();
+            npub = withIV.getIV();
+            initialAssociatedText = null;
+        }
+        else
+        {
+            throw new IllegalArgumentException("invalid parameters passed to Ascon");
+        }
+
+        if (key == null)
+        {
+            throw new IllegalArgumentException("Ascon Init parameters must include a key");
+        }
+        if (npub == null || npub.length != CRYPTO_ABYTES)
+        {
+            throw new IllegalArgumentException(asconParameters + " requires exactly " + CRYPTO_ABYTES + " bytes of IV");
+        }
+
+        byte[] k = key.getKey();
+        if (k.length != CRYPTO_KEYBYTES)
+        {
+            throw new IllegalArgumentException(asconParameters + " key must be " + CRYPTO_KEYBYTES + " bytes long");
+        }
+
+        CryptoServicesRegistrar.checkConstraints(new DefaultServiceProperties(
+            this.getAlgorithmName(), 128, params, Utils.getPurpose(forEncryption)));
+        N0 = Pack.bigEndianToLong(npub, 0);
+        N1 = Pack.bigEndianToLong(npub, 8);
+        if (CRYPTO_KEYBYTES == 16)
+        {
+            K1 = Pack.bigEndianToLong(k, 0);
+            K2 = Pack.bigEndianToLong(k, 8);
+        }
+        else if (CRYPTO_KEYBYTES == 20)
+        {
+            K0 = Pack.bigEndianToInt(k, 0);
+            K1 = Pack.bigEndianToLong(k, 4);
+            K2 = Pack.bigEndianToLong(k, 12);
+        }
+        else
+        {
+            throw new IllegalStateException();
+        }
+
+        m_state = forEncryption ? State.EncInit : State.DecInit;
+
+        reset(true);
+    }
+
+    public String getAlgorithmName()
+    {
+        return algorithmName;
+    }
+
+    public String getAlgorithmVersion()
+    {
+        return "v1.2";
+    }
+
+    public void processAADByte(byte in)
+    {
+        checkAAD();
+        m_buf[m_bufPos] = in;
+        if (++m_bufPos == ASCON_AEAD_RATE)
+        {
+            processBufferAAD(m_buf, 0);
+        }
+    }
+
+    public void processAADBytes(byte[] inBytes, int inOff, int len)
+    {
+        if ((inOff + len) > inBytes.length)
+        {
+            throw new DataLengthException("input buffer too short");
+        }
+        // Don't enter AAD state until we actually get input
+        if (len <= 0)
+        {
+            return;
+        }
+        checkAAD();
+        if (m_bufPos > 0)
+        {
+            int available = ASCON_AEAD_RATE - m_bufPos;
+            if (len < available)
+            {
+                System.arraycopy(inBytes, inOff, m_buf, m_bufPos, len);
+                m_bufPos += len;
+                return;
+            }
+            System.arraycopy(inBytes, inOff, m_buf, m_bufPos, available);
+            inOff += available;
+            len -= available;
+            processBufferAAD(m_buf, 0);
+            //m_bufPos = 0;
+        }
+        while (len >= ASCON_AEAD_RATE)
+        {
+            processBufferAAD(inBytes, inOff);
+            inOff += ASCON_AEAD_RATE;
+            len -= ASCON_AEAD_RATE;
+        }
+        System.arraycopy(inBytes, inOff, m_buf, 0, len);
+        m_bufPos = len;
+    }
+
+    public int processByte(byte in, byte[] out, int outOff)
+        throws DataLengthException
+    {
+        return processBytes(new byte[]{in}, 0, 1, out, outOff);
+    }
+
+    public int processBytes(byte[] inBytes, int inOff, int len, byte[] outBytes, int outOff)
+        throws DataLengthException
+    {
+        if ((inOff + len) > inBytes.length)
+        {
+            throw new DataLengthException("input buffer too short");
+        }
+        boolean forEncryption = checkData();
+        int resultLength = 0;
+
+        if (forEncryption)
+        {
+            if (m_bufPos > 0)
+            {
+                int available = ASCON_AEAD_RATE - m_bufPos;
+                if (len < available)
+                {
+                    System.arraycopy(inBytes, inOff, m_buf, m_bufPos, len);
+                    m_bufPos += len;
+                    return 0;
+                }
+
+                System.arraycopy(inBytes, inOff, m_buf, m_bufPos, available);
+                inOff += available;
+                len -= available;
+
+                processBufferEncrypt(m_buf, 0, outBytes, outOff);
+                resultLength = ASCON_AEAD_RATE;
+                //m_bufPos = 0;
+            }
+
+            while (len >= ASCON_AEAD_RATE)
+            {
+                processBufferEncrypt(inBytes, inOff, outBytes, outOff + resultLength);
+                inOff += ASCON_AEAD_RATE;
+                len -= ASCON_AEAD_RATE;
+                resultLength += ASCON_AEAD_RATE;
+            }
+        }
+        else
+        {
+            int available = m_bufferSizeDecrypt - m_bufPos;
+            if (len < available)
+            {
+                System.arraycopy(inBytes, inOff, m_buf, m_bufPos, len);
+                m_bufPos += len;
+                return 0;
+            }
+
+            // NOTE: Need 'while' here because ASCON_AEAD_RATE < CRYPTO_ABYTES in some parameter sets
+            while (m_bufPos >= ASCON_AEAD_RATE)
+            {
+                processBufferDecrypt(m_buf, 0, outBytes, outOff + resultLength);
+                m_bufPos -= ASCON_AEAD_RATE;
+                System.arraycopy(m_buf, ASCON_AEAD_RATE, m_buf, 0, m_bufPos);
+                resultLength += ASCON_AEAD_RATE;
+
+                available += ASCON_AEAD_RATE;
+                if (len < available)
+                {
+                    System.arraycopy(inBytes, inOff, m_buf, m_bufPos, len);
+                    m_bufPos += len;
+                    return resultLength;
+                }
+            }
+
+            available = ASCON_AEAD_RATE - m_bufPos;
+            System.arraycopy(inBytes, inOff, m_buf, m_bufPos, available);
+            inOff += available;
+            len -= available;
+            processBufferDecrypt(m_buf, 0, outBytes, outOff + resultLength);
+            resultLength += ASCON_AEAD_RATE;
+            //m_bufPos = 0;
+
+            while (len >= m_bufferSizeDecrypt)
+            {
+                processBufferDecrypt(inBytes, inOff, outBytes, outOff + resultLength);
+                inOff += ASCON_AEAD_RATE;
+                len -= ASCON_AEAD_RATE;
+                resultLength += ASCON_AEAD_RATE;
+            }
+        }
+
+        System.arraycopy(inBytes, inOff, m_buf, 0, len);
+        m_bufPos = len;
+
+        return resultLength;
+    }
+
+    public int doFinal(byte[] outBytes, int outOff)
+        throws IllegalStateException, InvalidCipherTextException, DataLengthException
+    {
+        boolean forEncryption = checkData();
+        int resultLength;
+        if (forEncryption)
+        {
+            resultLength = m_bufPos + CRYPTO_ABYTES;
+            if (outOff + resultLength > outBytes.length)
+            {
+                throw new OutputLengthException("output buffer too short");
+            }
+            processFinalEncrypt(m_buf, 0, m_bufPos, outBytes, outOff);
+            mac = new byte[CRYPTO_ABYTES];
+            Pack.longToBigEndian(x3, mac, 0);
+            Pack.longToBigEndian(x4, mac, 8);
+            System.arraycopy(mac, 0, outBytes, outOff + m_bufPos, CRYPTO_ABYTES);
+            reset(false);
+        }
+        else
+        {
+            if (m_bufPos < CRYPTO_ABYTES)
+            {
+                throw new InvalidCipherTextException("data too short");
+            }
+            m_bufPos -= CRYPTO_ABYTES;
+            resultLength = m_bufPos;
+            if (outOff + resultLength > outBytes.length)
+            {
+                throw new OutputLengthException("output buffer too short");
+            }
+            processFinalDecrypt(m_buf, 0, m_bufPos, outBytes, outOff);
+            x3 ^= Pack.bigEndianToLong(m_buf, m_bufPos);
+            x4 ^= Pack.bigEndianToLong(m_buf, m_bufPos + 8);
+            if ((x3 | x4) != 0L)
+            {
+                throw new InvalidCipherTextException("mac check in " + getAlgorithmName() + " failed");
+            }
+            reset(true);
+        }
+        return resultLength;
+    }
+
+    public byte[] getMac()
+    {
+        return mac;
+    }
+
+    public int getUpdateOutputSize(int len)
+    {
+        int total = Math.max(0, len);
+        switch (m_state)
+        {
+        case DecInit:
+        case DecAad:
+            total = Math.max(0, total - CRYPTO_ABYTES);
+            break;
+        case DecData:
+        case DecFinal:
+            total = Math.max(0, total + m_bufPos - CRYPTO_ABYTES);
+            break;
+        case EncData:
+        case EncFinal:
+            total += m_bufPos;
+            break;
+        default:
+            break;
+        }
+        return total - total % ASCON_AEAD_RATE;
+    }
+
+    public int getOutputSize(int len)
+    {
+        int total = Math.max(0, len);
+
+        switch (m_state)
+        {
+        case DecInit:
+        case DecAad:
+            return Math.max(0, total - CRYPTO_ABYTES);
+        case DecData:
+        case DecFinal:
+            return Math.max(0, total + m_bufPos - CRYPTO_ABYTES);
+        case EncData:
+        case EncFinal:
+            return total + m_bufPos + CRYPTO_ABYTES;
+        default:
+            return total + CRYPTO_ABYTES;
+        }
+    }
+
+    public void reset()
+    {
+        reset(true);
+    }
+
+    private void reset(boolean clearMac)
+    {
+        if (clearMac)
+        {
+            mac = null;
+        }
+        Arrays.clear(m_buf);
+        m_bufPos = 0;
+
+        switch (m_state)
+        {
+        case DecInit:
+        case EncInit:
+            break;
+        case DecAad:
+        case DecData:
+        case DecFinal:
+            m_state = State.DecInit;
+            break;
+        case EncAad:
+        case EncData:
+        case EncFinal:
+            m_state = State.EncFinal;
+            return;
+        default:
+            throw new IllegalStateException(getAlgorithmName() + " needs to be initialized");
+        }
+        ascon_aeadinit();
+        if (initialAssociatedText != null)
+        {
+            processAADBytes(initialAssociatedText, 0, initialAssociatedText.length);
+        }
+    }
+
+    public int getKeyBytesSize()
+    {
+        return CRYPTO_KEYBYTES;
+    }
+
+    public int getIVBytesSize()
+    {
+        return CRYPTO_ABYTES;
+    }
+}
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/engines/BlowfishEngine.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/engines/BlowfishEngine.java
index 2979a61..ab6f8d6 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/engines/BlowfishEngine.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/engines/BlowfishEngine.java
@@ -3,8 +3,11 @@
 
 import com.android.org.bouncycastle.crypto.BlockCipher;
 import com.android.org.bouncycastle.crypto.CipherParameters;
+import com.android.org.bouncycastle.crypto.CryptoServicePurpose;
+import com.android.org.bouncycastle.crypto.CryptoServicesRegistrar;
 import com.android.org.bouncycastle.crypto.DataLengthException;
 import com.android.org.bouncycastle.crypto.OutputLengthException;
+import com.android.org.bouncycastle.crypto.constraints.DefaultServiceProperties;
 import com.android.org.bouncycastle.crypto.params.KeyParameter;
 
 /**
@@ -317,6 +320,7 @@
         S2 = new int[SBOX_SK];
         S3 = new int[SBOX_SK];
         P = new int[P_SZ];
+        CryptoServicesRegistrar.checkConstraints(new DefaultServiceProperties(getAlgorithmName(), bitsOfSecurity()));
     }
 
     /**
@@ -337,6 +341,7 @@
             this.workingKey = ((KeyParameter)params).getKey();
             setKey(this.workingKey);
 
+            CryptoServicesRegistrar.checkConstraints(new DefaultServiceProperties(getAlgorithmName(), bitsOfSecurity(), params, getPurpose()));
             return;
         }
 
@@ -433,6 +438,11 @@
 
     private void setKey(byte[] key)
     {
+        if (key.length < 4 || key.length > 56)
+        {
+            throw new IllegalArgumentException("key length must be in range 32 to 448 bits");
+        }
+
         /*
          * - comments are from _Applied Crypto_, Schneier, p338
          * please be careful comparing the two, AC numbers the
@@ -577,4 +587,22 @@
         b[offset + 1] = (byte)(in >> 16);
         b[offset]     = (byte)(in >> 24);
     }
+
+    private int bitsOfSecurity()
+    {
+        if (workingKey == null)
+        {
+            return 256;
+        }
+        return (workingKey.length > 32) ? 256 : workingKey.length * 8;
+    }
+
+    private CryptoServicePurpose getPurpose()
+    {
+        if (workingKey == null)
+        {
+            return CryptoServicePurpose.ANY;
+        }
+        return encrypting ? CryptoServicePurpose.ENCRYPTION : CryptoServicePurpose.DECRYPTION;
+    }
 }
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/engines/DESBase.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/engines/DESBase.java
new file mode 100644
index 0000000..8ec449c
--- /dev/null
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/engines/DESBase.java
@@ -0,0 +1,408 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.org.bouncycastle.crypto.engines;
+
+import com.android.org.bouncycastle.util.Pack;
+
+/**
+ * a class that provides a basic DES engine.
+ */
+class DESBase
+{
+    protected static final int  BLOCK_SIZE = 8;
+
+    /**
+     * standard constructor.
+     */
+    public DESBase()
+    {
+    }
+
+    /**
+     * what follows is mainly taken from "Applied Cryptography", by
+     * Bruce Schneier, however it also bears great resemblance to Richard
+     * Outerbridge's D3DES...
+     */
+
+//    private static final short[]    Df_Key =
+//        {
+//            0x01,0x23,0x45,0x67,0x89,0xab,0xcd,0xef,
+//            0xfe,0xdc,0xba,0x98,0x76,0x54,0x32,0x10,
+//            0x89,0xab,0xcd,0xef,0x01,0x23,0x45,0x67
+//        };
+
+    private static final short[]    bytebit =
+        {
+            0200, 0100, 040, 020, 010, 04, 02, 01
+        };
+
+    private static final int[]    bigbyte =
+        {
+            0x800000, 0x400000, 0x200000, 0x100000,
+            0x80000,  0x40000,  0x20000,  0x10000,
+            0x8000,      0x4000,   0x2000,   0x1000,
+            0x800,    0x400,    0x200,    0x100,
+            0x80,      0x40,        0x20,     0x10,
+            0x8,      0x4,      0x2,      0x1
+        };
+
+    /*
+     * Use the key schedule specified in the Standard (ANSI X3.92-1981).
+     */
+
+    private static final byte[]    pc1 =
+        {
+            56, 48, 40, 32, 24, 16,  8,   0, 57, 49, 41, 33, 25, 17,
+             9,  1, 58, 50, 42, 34, 26,  18, 10,  2, 59, 51, 43, 35,
+            62, 54, 46, 38, 30, 22, 14,   6, 61, 53, 45, 37, 29, 21,
+            13,  5, 60, 52, 44, 36, 28,  20, 12,  4, 27, 19, 11,  3
+        };
+
+    private static final byte[] totrot =
+        {
+            1, 2, 4, 6, 8, 10, 12, 14,
+            15, 17, 19, 21, 23, 25, 27, 28
+        };
+
+    private static final byte[] pc2 =
+        {
+            13, 16, 10, 23,  0,  4,  2, 27, 14,  5, 20,  9,
+            22, 18, 11,  3, 25,  7, 15,  6, 26, 19, 12,  1,
+            40, 51, 30, 36, 46, 54, 29, 39, 50, 44, 32, 47,
+            43, 48, 38, 55, 33, 52, 45, 41, 49, 35, 28, 31
+        };
+
+    private static final int[] SP1 = {
+        0x01010400, 0x00000000, 0x00010000, 0x01010404,
+        0x01010004, 0x00010404, 0x00000004, 0x00010000,
+        0x00000400, 0x01010400, 0x01010404, 0x00000400,
+        0x01000404, 0x01010004, 0x01000000, 0x00000004,
+        0x00000404, 0x01000400, 0x01000400, 0x00010400,
+        0x00010400, 0x01010000, 0x01010000, 0x01000404,
+        0x00010004, 0x01000004, 0x01000004, 0x00010004,
+        0x00000000, 0x00000404, 0x00010404, 0x01000000,
+        0x00010000, 0x01010404, 0x00000004, 0x01010000,
+        0x01010400, 0x01000000, 0x01000000, 0x00000400,
+        0x01010004, 0x00010000, 0x00010400, 0x01000004,
+        0x00000400, 0x00000004, 0x01000404, 0x00010404,
+        0x01010404, 0x00010004, 0x01010000, 0x01000404,
+        0x01000004, 0x00000404, 0x00010404, 0x01010400,
+        0x00000404, 0x01000400, 0x01000400, 0x00000000,
+        0x00010004, 0x00010400, 0x00000000, 0x01010004
+    };
+
+    private static final int[] SP2 = {
+        0x80108020, 0x80008000, 0x00008000, 0x00108020,
+        0x00100000, 0x00000020, 0x80100020, 0x80008020,
+        0x80000020, 0x80108020, 0x80108000, 0x80000000,
+        0x80008000, 0x00100000, 0x00000020, 0x80100020,
+        0x00108000, 0x00100020, 0x80008020, 0x00000000,
+        0x80000000, 0x00008000, 0x00108020, 0x80100000,
+        0x00100020, 0x80000020, 0x00000000, 0x00108000,
+        0x00008020, 0x80108000, 0x80100000, 0x00008020,
+        0x00000000, 0x00108020, 0x80100020, 0x00100000,
+        0x80008020, 0x80100000, 0x80108000, 0x00008000,
+        0x80100000, 0x80008000, 0x00000020, 0x80108020,
+        0x00108020, 0x00000020, 0x00008000, 0x80000000,
+        0x00008020, 0x80108000, 0x00100000, 0x80000020,
+        0x00100020, 0x80008020, 0x80000020, 0x00100020,
+        0x00108000, 0x00000000, 0x80008000, 0x00008020,
+        0x80000000, 0x80100020, 0x80108020, 0x00108000
+    };
+
+    private static final int[] SP3 = {
+        0x00000208, 0x08020200, 0x00000000, 0x08020008,
+        0x08000200, 0x00000000, 0x00020208, 0x08000200,
+        0x00020008, 0x08000008, 0x08000008, 0x00020000,
+        0x08020208, 0x00020008, 0x08020000, 0x00000208,
+        0x08000000, 0x00000008, 0x08020200, 0x00000200,
+        0x00020200, 0x08020000, 0x08020008, 0x00020208,
+        0x08000208, 0x00020200, 0x00020000, 0x08000208,
+        0x00000008, 0x08020208, 0x00000200, 0x08000000,
+        0x08020200, 0x08000000, 0x00020008, 0x00000208,
+        0x00020000, 0x08020200, 0x08000200, 0x00000000,
+        0x00000200, 0x00020008, 0x08020208, 0x08000200,
+        0x08000008, 0x00000200, 0x00000000, 0x08020008,
+        0x08000208, 0x00020000, 0x08000000, 0x08020208,
+        0x00000008, 0x00020208, 0x00020200, 0x08000008,
+        0x08020000, 0x08000208, 0x00000208, 0x08020000,
+        0x00020208, 0x00000008, 0x08020008, 0x00020200
+    };
+
+    private static final int[] SP4 = {
+        0x00802001, 0x00002081, 0x00002081, 0x00000080,
+        0x00802080, 0x00800081, 0x00800001, 0x00002001,
+        0x00000000, 0x00802000, 0x00802000, 0x00802081,
+        0x00000081, 0x00000000, 0x00800080, 0x00800001,
+        0x00000001, 0x00002000, 0x00800000, 0x00802001,
+        0x00000080, 0x00800000, 0x00002001, 0x00002080,
+        0x00800081, 0x00000001, 0x00002080, 0x00800080,
+        0x00002000, 0x00802080, 0x00802081, 0x00000081,
+        0x00800080, 0x00800001, 0x00802000, 0x00802081,
+        0x00000081, 0x00000000, 0x00000000, 0x00802000,
+        0x00002080, 0x00800080, 0x00800081, 0x00000001,
+        0x00802001, 0x00002081, 0x00002081, 0x00000080,
+        0x00802081, 0x00000081, 0x00000001, 0x00002000,
+        0x00800001, 0x00002001, 0x00802080, 0x00800081,
+        0x00002001, 0x00002080, 0x00800000, 0x00802001,
+        0x00000080, 0x00800000, 0x00002000, 0x00802080
+    };
+
+    private static final int[] SP5 = {
+        0x00000100, 0x02080100, 0x02080000, 0x42000100,
+        0x00080000, 0x00000100, 0x40000000, 0x02080000,
+        0x40080100, 0x00080000, 0x02000100, 0x40080100,
+        0x42000100, 0x42080000, 0x00080100, 0x40000000,
+        0x02000000, 0x40080000, 0x40080000, 0x00000000,
+        0x40000100, 0x42080100, 0x42080100, 0x02000100,
+        0x42080000, 0x40000100, 0x00000000, 0x42000000,
+        0x02080100, 0x02000000, 0x42000000, 0x00080100,
+        0x00080000, 0x42000100, 0x00000100, 0x02000000,
+        0x40000000, 0x02080000, 0x42000100, 0x40080100,
+        0x02000100, 0x40000000, 0x42080000, 0x02080100,
+        0x40080100, 0x00000100, 0x02000000, 0x42080000,
+        0x42080100, 0x00080100, 0x42000000, 0x42080100,
+        0x02080000, 0x00000000, 0x40080000, 0x42000000,
+        0x00080100, 0x02000100, 0x40000100, 0x00080000,
+        0x00000000, 0x40080000, 0x02080100, 0x40000100
+    };
+
+    private static final int[] SP6 = {
+        0x20000010, 0x20400000, 0x00004000, 0x20404010,
+        0x20400000, 0x00000010, 0x20404010, 0x00400000,
+        0x20004000, 0x00404010, 0x00400000, 0x20000010,
+        0x00400010, 0x20004000, 0x20000000, 0x00004010,
+        0x00000000, 0x00400010, 0x20004010, 0x00004000,
+        0x00404000, 0x20004010, 0x00000010, 0x20400010,
+        0x20400010, 0x00000000, 0x00404010, 0x20404000,
+        0x00004010, 0x00404000, 0x20404000, 0x20000000,
+        0x20004000, 0x00000010, 0x20400010, 0x00404000,
+        0x20404010, 0x00400000, 0x00004010, 0x20000010,
+        0x00400000, 0x20004000, 0x20000000, 0x00004010,
+        0x20000010, 0x20404010, 0x00404000, 0x20400000,
+        0x00404010, 0x20404000, 0x00000000, 0x20400010,
+        0x00000010, 0x00004000, 0x20400000, 0x00404010,
+        0x00004000, 0x00400010, 0x20004010, 0x00000000,
+        0x20404000, 0x20000000, 0x00400010, 0x20004010
+    };
+
+    private static final int[] SP7 = {
+        0x00200000, 0x04200002, 0x04000802, 0x00000000,
+        0x00000800, 0x04000802, 0x00200802, 0x04200800,
+        0x04200802, 0x00200000, 0x00000000, 0x04000002,
+        0x00000002, 0x04000000, 0x04200002, 0x00000802,
+        0x04000800, 0x00200802, 0x00200002, 0x04000800,
+        0x04000002, 0x04200000, 0x04200800, 0x00200002,
+        0x04200000, 0x00000800, 0x00000802, 0x04200802,
+        0x00200800, 0x00000002, 0x04000000, 0x00200800,
+        0x04000000, 0x00200800, 0x00200000, 0x04000802,
+        0x04000802, 0x04200002, 0x04200002, 0x00000002,
+        0x00200002, 0x04000000, 0x04000800, 0x00200000,
+        0x04200800, 0x00000802, 0x00200802, 0x04200800,
+        0x00000802, 0x04000002, 0x04200802, 0x04200000,
+        0x00200800, 0x00000000, 0x00000002, 0x04200802,
+        0x00000000, 0x00200802, 0x04200000, 0x00000800,
+        0x04000002, 0x04000800, 0x00000800, 0x00200002
+    };
+
+    private static final int[] SP8 = {
+        0x10001040, 0x00001000, 0x00040000, 0x10041040,
+        0x10000000, 0x10001040, 0x00000040, 0x10000000,
+        0x00040040, 0x10040000, 0x10041040, 0x00041000,
+        0x10041000, 0x00041040, 0x00001000, 0x00000040,
+        0x10040000, 0x10000040, 0x10001000, 0x00001040,
+        0x00041000, 0x00040040, 0x10040040, 0x10041000,
+        0x00001040, 0x00000000, 0x00000000, 0x10040040,
+        0x10000040, 0x10001000, 0x00041040, 0x00040000,
+        0x00041040, 0x00040000, 0x10041000, 0x00001000,
+        0x00000040, 0x10040040, 0x00001000, 0x00041040,
+        0x10001000, 0x00000040, 0x10000040, 0x10040000,
+        0x10040040, 0x10000000, 0x00040000, 0x10001040,
+        0x00000000, 0x10041040, 0x00040040, 0x10000040,
+        0x10040000, 0x10001000, 0x10001040, 0x00000000,
+        0x10041040, 0x00041000, 0x00041000, 0x00001040,
+        0x00001040, 0x00040040, 0x10000000, 0x10041000
+    };
+
+    /**
+     * generate an integer based working key based on our secret key
+     * and what we processing we are planning to do.
+     *
+     * Acknowledgements for this routine go to James Gillogly &amp; Phil Karn.
+     *         (whoever, and wherever they are!).
+     */
+    protected int[] generateWorkingKey(
+        boolean encrypting,
+        byte[]  key)
+    {
+        int[]       newKey = new int[32];
+        boolean[]   pc1m = new boolean[56],
+                    pcr = new boolean[56];
+
+        for (int j = 0; j < 56; j++)
+        {
+            int    l = pc1[j];
+
+            pc1m[j] = ((key[l >>> 3] & bytebit[l & 07]) != 0);
+        }
+
+        for (int i = 0; i < 16; i++)
+        {
+            int    l, m, n;
+
+            if (encrypting)
+            {
+                m = i << 1;
+            }
+            else
+            {
+                m = (15 - i) << 1;
+            }
+
+            n = m + 1;
+            newKey[m] = newKey[n] = 0;
+
+            for (int j = 0; j < 28; j++)
+            {
+                l = j + totrot[i];
+                if (l < 28)
+                {
+                    pcr[j] = pc1m[l];
+                }
+                else
+                {
+                    pcr[j] = pc1m[l - 28];
+                }
+            }
+
+            for (int j = 28; j < 56; j++)
+            {
+                l = j + totrot[i];
+                if (l < 56)
+                {
+                    pcr[j] = pc1m[l];
+                }
+                else
+                {
+                    pcr[j] = pc1m[l - 28];
+                }
+            }
+
+            for (int j = 0; j < 24; j++)
+            {
+                if (pcr[pc2[j]])
+                {
+                    newKey[m] |= bigbyte[j];
+                }
+
+                if (pcr[pc2[j + 24]])
+                {
+                    newKey[n] |= bigbyte[j];
+                }
+            }
+        }
+
+        //
+        // store the processed key
+        //
+        for (int i = 0; i != 32; i += 2)
+        {
+            int    i1, i2;
+
+            i1 = newKey[i];
+            i2 = newKey[i + 1];
+
+            newKey[i] = ((i1 & 0x00fc0000) << 6) | ((i1 & 0x00000fc0) << 10)
+                                   | ((i2 & 0x00fc0000) >>> 10) | ((i2 & 0x00000fc0) >>> 6);
+
+            newKey[i + 1] = ((i1 & 0x0003f000) << 12) | ((i1 & 0x0000003f) << 16)
+                                   | ((i2 & 0x0003f000) >>> 4) | (i2 & 0x0000003f);
+        }
+
+        return newKey;
+    }
+
+    /**
+     * the DES engine.
+     */
+    protected void desFunc(
+        int[]   wKey,
+        byte[]  in,
+        int     inOff,
+        byte[]  out,
+        int     outOff)
+    {
+        int     work, right, left;
+
+        left = Pack.bigEndianToInt(in, inOff);
+        right = Pack.bigEndianToInt(in, inOff + 4);
+
+        work = ((left >>> 4) ^ right) & 0x0f0f0f0f;
+        right ^= work;
+        left ^= (work << 4);
+        work = ((left >>> 16) ^ right) & 0x0000ffff;
+        right ^= work;
+        left ^= (work << 16);
+        work = ((right >>> 2) ^ left) & 0x33333333;
+        left ^= work;
+        right ^= (work << 2);
+        work = ((right >>> 8) ^ left) & 0x00ff00ff;
+        left ^= work;
+        right ^= (work << 8);
+        right = (right << 1) | (right >>> 31);
+        work = (left ^ right) & 0xaaaaaaaa;
+        left ^= work;
+        right ^= work;
+        left = (left << 1) | (left >>> 31);
+
+        for (int round = 0; round < 8; round++)
+        {
+            int     fval;
+
+            work  = (right << 28) | (right >>> 4);
+            work ^= wKey[round * 4 + 0];
+            fval  = SP7[ work      & 0x3f];
+            fval |= SP5[(work >>>  8) & 0x3f];
+            fval |= SP3[(work >>> 16) & 0x3f];
+            fval |= SP1[(work >>> 24) & 0x3f];
+            work  = right ^ wKey[round * 4 + 1];
+            fval |= SP8[ work      & 0x3f];
+            fval |= SP6[(work >>>  8) & 0x3f];
+            fval |= SP4[(work >>> 16) & 0x3f];
+            fval |= SP2[(work >>> 24) & 0x3f];
+            left ^= fval;
+            work  = (left << 28) | (left >>> 4);
+            work ^= wKey[round * 4 + 2];
+            fval  = SP7[ work      & 0x3f];
+            fval |= SP5[(work >>>  8) & 0x3f];
+            fval |= SP3[(work >>> 16) & 0x3f];
+            fval |= SP1[(work >>> 24) & 0x3f];
+            work  = left ^ wKey[round * 4 + 3];
+            fval |= SP8[ work      & 0x3f];
+            fval |= SP6[(work >>>  8) & 0x3f];
+            fval |= SP4[(work >>> 16) & 0x3f];
+            fval |= SP2[(work >>> 24) & 0x3f];
+            right ^= fval;
+        }
+
+        right = (right << 31) | (right >>> 1);
+        work = (left ^ right) & 0xaaaaaaaa;
+        left ^= work;
+        right ^= work;
+        left = (left << 31) | (left >>> 1);
+        work = ((left >>> 8) ^ right) & 0x00ff00ff;
+        right ^= work;
+        left ^= (work << 8);
+        work = ((left >>> 2) ^ right) & 0x33333333;
+        right ^= work;
+        left ^= (work << 2);
+        work = ((right >>> 16) ^ left) & 0x0000ffff;
+        left ^= work;
+        right ^= (work << 16);
+        work = ((right >>> 4) ^ left) & 0x0f0f0f0f;
+        left ^= work;
+        right ^= (work << 4);
+
+        Pack.intToBigEndian(right, out, outOff);
+        Pack.intToBigEndian(left, out, outOff + 4);
+    }
+}
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/engines/DESEngine.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/engines/DESEngine.java
index 7d11710..5581827 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/engines/DESEngine.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/engines/DESEngine.java
@@ -3,8 +3,10 @@
 
 import com.android.org.bouncycastle.crypto.BlockCipher;
 import com.android.org.bouncycastle.crypto.CipherParameters;
+import com.android.org.bouncycastle.crypto.CryptoServicesRegistrar;
 import com.android.org.bouncycastle.crypto.DataLengthException;
 import com.android.org.bouncycastle.crypto.OutputLengthException;
+import com.android.org.bouncycastle.crypto.constraints.DefaultServiceProperties;
 import com.android.org.bouncycastle.crypto.params.KeyParameter;
 import com.android.org.bouncycastle.util.Pack;
 
@@ -13,10 +15,12 @@
  * @hide This class is not part of the Android public SDK API
  */
 public class DESEngine
+    extends DESBase
     implements BlockCipher
 {
     protected static final int  BLOCK_SIZE = 8;
 
+    private boolean             forEncryption;
     private int[]               workingKey = null;
 
     /**
@@ -24,6 +28,7 @@
      */
     public DESEngine()
     {
+        CryptoServicesRegistrar.checkConstraints(new DefaultServiceProperties(getAlgorithmName(), 56));
     }
 
     /**
@@ -40,14 +45,17 @@
     {
         if (params instanceof KeyParameter)
         {
-            if (((KeyParameter)params).getKey().length > 8)
+            if (((KeyParameter)params).getKeyLength() > 8)
             {
                 throw new IllegalArgumentException("DES key too long - should be 8 bytes");
             }
-            
+
+            forEncryption = encrypting;
             workingKey = generateWorkingKey(encrypting,
                                   ((KeyParameter)params).getKey());
 
+            CryptoServicesRegistrar.checkConstraints(new DefaultServiceProperties(getAlgorithmName(), 56, params, Utils.getPurpose(forEncryption)));
+
             return;
         }
 
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/engines/DESedeEngine.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/engines/DESedeEngine.java
index 885d0f8..3392fd1 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/engines/DESedeEngine.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/engines/DESedeEngine.java
@@ -1,9 +1,12 @@
 /* GENERATED SOURCE. DO NOT MODIFY. */
 package com.android.org.bouncycastle.crypto.engines;
 
+import com.android.org.bouncycastle.crypto.BlockCipher;
 import com.android.org.bouncycastle.crypto.CipherParameters;
+import com.android.org.bouncycastle.crypto.CryptoServicesRegistrar;
 import com.android.org.bouncycastle.crypto.DataLengthException;
 import com.android.org.bouncycastle.crypto.OutputLengthException;
+import com.android.org.bouncycastle.crypto.constraints.DefaultServiceProperties;
 import com.android.org.bouncycastle.crypto.params.KeyParameter;
 
 /**
@@ -11,34 +14,36 @@
  * @hide This class is not part of the Android public SDK API
  */
 public class DESedeEngine
-    extends DESEngine
+    extends DESBase
+    implements BlockCipher
 {
-    protected static final int  BLOCK_SIZE = 8;
+    protected static final int BLOCK_SIZE = 8;
 
-    private int[]               workingKey1 = null;
-    private int[]               workingKey2 = null;
-    private int[]               workingKey3 = null;
+    private int[] workingKey1 = null;
+    private int[] workingKey2 = null;
+    private int[] workingKey3 = null;
 
-    private boolean             forEncryption;
+    private boolean forEncryption;
 
     /**
      * standard constructor.
      */
     public DESedeEngine()
     {
+        CryptoServicesRegistrar.checkConstraints(new DefaultServiceProperties(getAlgorithmName(), bitsOfSecurity()));
     }
 
     /**
      * initialise a DESede cipher.
      *
      * @param encrypting whether or not we are for encryption.
-     * @param params the parameters required to set up the cipher.
-     * @exception IllegalArgumentException if the params argument is
-     * inappropriate.
+     * @param params     the parameters required to set up the cipher.
+     * @throws IllegalArgumentException if the params argument is
+     *                                  inappropriate.
      */
     public void init(
-        boolean           encrypting,
-        CipherParameters  params)
+        boolean encrypting,
+        CipherParameters params)
     {
         if (!(params instanceof KeyParameter))
         {
@@ -72,6 +77,8 @@
         {
             workingKey3 = workingKey1;
         }
+
+        CryptoServicesRegistrar.checkConstraints(new DefaultServiceProperties(getAlgorithmName(), bitsOfSecurity(), params, Utils.getPurpose(forEncryption)));
     }
 
     public String getAlgorithmName()
@@ -126,4 +133,14 @@
     public void reset()
     {
     }
+
+    // Service Definitions
+    private int bitsOfSecurity()
+    {
+        if (workingKey1 != null && workingKey1 == workingKey3)
+        {
+            return 80;
+        }
+        return 112;
+    }
 }
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/engines/DESedeWrapEngine.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/engines/DESedeWrapEngine.java
index d1c05ce..dde38f9 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/engines/DESedeWrapEngine.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/engines/DESedeWrapEngine.java
@@ -184,8 +184,8 @@
       System.arraycopy(this.iv, 0, TEMP2, 0, this.iv.length);
       System.arraycopy(TEMP1, 0, TEMP2, this.iv.length, TEMP1.length);
 
-      // Reverse the order of the octets in TEMP2 and call the result TEMP3.
-      byte[] TEMP3 = reverse(TEMP2);
+      // Reverse the order of the octets in TEMP2.
+      Arrays.reverseInPlace(TEMP2);
 
       // Encrypt TEMP3 in CBC mode using the KEK and an initialization vector
       // of 0x 4a dd a2 2c 79 e8 21 05. The resulting cipher text is the desired
@@ -194,12 +194,12 @@
 
       this.engine.init(true, param2);
 
-      for (int currentBytePos = 0; currentBytePos != TEMP3.length; currentBytePos += blockSize) 
+      for (int currentBytePos = 0; currentBytePos != TEMP2.length; currentBytePos += blockSize) 
       {
-         engine.processBlock(TEMP3, currentBytePos, TEMP3, currentBytePos);
+         engine.processBlock(TEMP2, currentBytePos, TEMP2, currentBytePos);
       }
 
-      return TEMP3;
+      return TEMP2;
    }
 
    /**
@@ -252,15 +252,15 @@
 
       this.engine.init(false, param2);
 
-      byte TEMP3[] = new byte[inLen];
+      byte TEMP2[] = new byte[inLen];
 
       for (int currentBytePos = 0; currentBytePos != inLen; currentBytePos += blockSize) 
       {
-         engine.processBlock(in, inOff + currentBytePos, TEMP3, currentBytePos);
+         engine.processBlock(in, inOff + currentBytePos, TEMP2, currentBytePos);
       }
 
-      // Reverse the order of the octets in TEMP3 and call the result TEMP2.
-      byte[] TEMP2 = reverse(TEMP3);
+      // Reverse the order of the octets in TEMP2.
+      Arrays.reverseInPlace(TEMP2);
 
       // Decompose TEMP2 into IV, the first 8 octets, and TEMP1, the remaining octets.
       this.iv = new byte[8];
@@ -343,14 +343,4 @@
     {
         return Arrays.constantTimeAreEqual(calculateCMSKeyChecksum(key), checksum);
     }
-
-    private static byte[] reverse(byte[] bs)
-    {
-        byte[] result = new byte[bs.length];
-        for (int i = 0; i < bs.length; i++) 
-        {
-           result[i] = bs[bs.length - (i + 1)];
-        }
-        return result;
-    }
 }
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/engines/RC2Engine.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/engines/RC2Engine.java
index 05c3d1e..6bf05fa 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/engines/RC2Engine.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/engines/RC2Engine.java
@@ -1,10 +1,8 @@
 /* GENERATED SOURCE. DO NOT MODIFY. */
 package com.android.org.bouncycastle.crypto.engines;
 
-import com.android.org.bouncycastle.crypto.BlockCipher;
-import com.android.org.bouncycastle.crypto.CipherParameters;
-import com.android.org.bouncycastle.crypto.DataLengthException;
-import com.android.org.bouncycastle.crypto.OutputLengthException;
+import com.android.org.bouncycastle.crypto.*;
+import com.android.org.bouncycastle.crypto.constraints.DefaultServiceProperties;
 import com.android.org.bouncycastle.crypto.params.KeyParameter;
 import com.android.org.bouncycastle.crypto.params.RC2Parameters;
 
@@ -124,17 +122,18 @@
         CipherParameters  params)
     {
         this.encrypting = encrypting;
-
+        byte[] key;
         if (params instanceof RC2Parameters)
         {
             RC2Parameters   param = (RC2Parameters)params;
 
             workingKey = generateWorkingKey(param.getKey(),
                                             param.getEffectiveKeyBits());
+            key = param.getKey();
         }
         else if (params instanceof KeyParameter)
         {
-            byte[]    key = ((KeyParameter)params).getKey();
+            key = ((KeyParameter)params).getKey();
 
             workingKey = generateWorkingKey(key, key.length * 8);
         }
@@ -143,6 +142,7 @@
             throw new IllegalArgumentException("invalid parameter passed to RC2 init - " + params.getClass().getName());
         }
 
+        CryptoServicesRegistrar.checkConstraints(new DefaultServiceProperties(getAlgorithmName(), key.length * 8, params, Utils.getPurpose(encrypting)));
     }
 
     public void reset()
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/engines/RC4Engine.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/engines/RC4Engine.java
index 2a8b108..989810c 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/engines/RC4Engine.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/engines/RC4Engine.java
@@ -2,15 +2,18 @@
 package com.android.org.bouncycastle.crypto.engines;
 
 import com.android.org.bouncycastle.crypto.CipherParameters;
+import com.android.org.bouncycastle.crypto.CryptoServicesRegistrar;
 import com.android.org.bouncycastle.crypto.DataLengthException;
 import com.android.org.bouncycastle.crypto.OutputLengthException;
 import com.android.org.bouncycastle.crypto.StreamCipher;
+import com.android.org.bouncycastle.crypto.constraints.DefaultServiceProperties;
 import com.android.org.bouncycastle.crypto.params.KeyParameter;
 
 /**
  * @hide This class is not part of the Android public SDK API
  */
-public class RC4Engine implements StreamCipher
+public class RC4Engine
+    implements StreamCipher
 {
     private final static int STATE_LENGTH = 256;
 
@@ -23,6 +26,12 @@
     private int         x = 0;
     private int         y = 0;
     private byte[]      workingKey = null;
+    private boolean     forEncryption;
+
+    public RC4Engine()
+    {
+        CryptoServicesRegistrar.checkConstraints(new DefaultServiceProperties(getAlgorithmName(), 20));
+    }
 
     /**
      * initialise a RC4 cipher.
@@ -34,8 +43,7 @@
      */
     public void init(
         boolean             forEncryption, 
-        CipherParameters     params
-   )
+        CipherParameters     params)
     {
         if (params instanceof KeyParameter)
         {
@@ -44,9 +52,12 @@
              * symmetrical, so the 'forEncryption' is 
              * irrelevant.
              */
-            workingKey = ((KeyParameter)params).getKey();
+            this.workingKey = ((KeyParameter)params).getKey();
+            this.forEncryption = forEncryption;
             setKey(workingKey);
 
+            CryptoServicesRegistrar.checkConstraints(new DefaultServiceProperties(getAlgorithmName(), 20, params, Utils.getPurpose(forEncryption)));
+
             return;
         }
 
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/engines/RFC3394WrapEngine.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/engines/RFC3394WrapEngine.java
index f1f9684..986aef4 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/engines/RFC3394WrapEngine.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/engines/RFC3394WrapEngine.java
@@ -22,14 +22,15 @@
 public class RFC3394WrapEngine
     implements Wrapper
 {
-    private BlockCipher     engine;
-    private boolean         wrapCipherMode;
-    private KeyParameter    param;
-    private boolean         forWrapping;
+    private static final byte[] DEFAULT_IV = { (byte)0xa6, (byte)0xa6, (byte)0xa6, (byte)0xa6, (byte)0xa6, (byte)0xa6,
+        (byte)0xa6, (byte)0xa6 };
 
-    private byte[]          iv = {
-                              (byte)0xa6, (byte)0xa6, (byte)0xa6, (byte)0xa6,
-                              (byte)0xa6, (byte)0xa6, (byte)0xa6, (byte)0xa6 };
+    private final BlockCipher engine;
+    private final boolean wrapCipherMode;
+    private final byte[] iv = new byte[8];
+
+    private KeyParameter param = null;
+    private boolean forWrapping = true;
 
     /**
      * Create a RFC 3394 WrapEngine specifying the encrypt for wrapping, decrypt for unwrapping.
@@ -50,7 +51,7 @@
     public RFC3394WrapEngine(BlockCipher engine, boolean useReverseDirection)
     {
         this.engine = engine;
-        this.wrapCipherMode = (useReverseDirection) ? false : true;
+        this.wrapCipherMode = !useReverseDirection;
     }
 
     public void init(
@@ -67,15 +68,24 @@
         if (param instanceof KeyParameter)
         {
             this.param = (KeyParameter)param;
+            System.arraycopy(DEFAULT_IV, 0, iv, 0, 8);
         }
         else if (param instanceof ParametersWithIV)
         {
-            this.iv = ((ParametersWithIV)param).getIV();
-            this.param = (KeyParameter)((ParametersWithIV) param).getParameters();
-            if (this.iv.length != 8)
+            ParametersWithIV withIV = (ParametersWithIV)param;
+
+            byte[] iv = withIV.getIV();
+            if (iv.length != 8)
             {
                throw new IllegalArgumentException("IV not equal to 8");
             }
+
+            this.param = (KeyParameter)withIV.getParameters();
+            System.arraycopy(iv, 0, this.iv, 0, 8);
+        }
+        else
+        {
+            // TODO Throw an exception for bad parameters?
         }
     }
 
@@ -93,6 +103,10 @@
         {
             throw new IllegalStateException("not set for wrapping");
         }
+        if (inLen < 8)
+        {
+            throw new DataLengthException("wrap data must be at least 8 bytes");
+        }
 
         int     n = inLen / 8;
 
@@ -101,34 +115,41 @@
             throw new DataLengthException("wrap data must be a multiple of 8 bytes");
         }
 
-        byte[]  block = new byte[inLen + iv.length];
-        byte[]  buf = new byte[8 + iv.length];
+        engine.init(wrapCipherMode, param);
 
+        byte[] block = new byte[inLen + iv.length];
         System.arraycopy(iv, 0, block, 0, iv.length);
         System.arraycopy(in, inOff, block, iv.length, inLen);
 
-        engine.init(wrapCipherMode, param);
-
-        for (int j = 0; j != 6; j++)
+        if (n == 1)
         {
-            for (int i = 1; i <= n; i++)
+            engine.processBlock(block, 0, block, 0);
+        }
+        else
+        {
+            byte[] buf = new byte[8 + iv.length];
+
+            for (int j = 0; j != 6; j++)
             {
-                System.arraycopy(block, 0, buf, 0, iv.length);
-                System.arraycopy(block, 8 * i, buf, iv.length, 8);
-                engine.processBlock(buf, 0, buf, 0);
-
-                int t = n * j + i;
-                for (int k = 1; t != 0; k++)
+                for (int i = 1; i <= n; i++)
                 {
-                    byte    v = (byte)t;
+                    System.arraycopy(block, 0, buf, 0, iv.length);
+                    System.arraycopy(block, 8 * i, buf, iv.length, 8);
+                    engine.processBlock(buf, 0, buf, 0);
 
-                    buf[iv.length - k] ^= v;
+                    int t = n * j + i;
+                    for (int k = 1; t != 0; k++)
+                    {
+                        byte    v = (byte)t;
 
-                    t >>>= 8;
+                        buf[iv.length - k] ^= v;
+
+                        t >>>= 8;
+                    }
+
+                    System.arraycopy(buf, 0, block, 0, 8);
+                    System.arraycopy(buf, 8, block, 8 * i, 8);
                 }
-
-                System.arraycopy(buf, 0, block, 0, 8);
-                System.arraycopy(buf, 8, block, 8 * i, 8);
             }
         }
 
@@ -145,6 +166,10 @@
         {
             throw new IllegalStateException("not set for unwrapping");
         }
+        if (inLen < 16)
+        {
+            throw new InvalidCipherTextException("unwrap data too short");
+        }
 
         int     n = inLen / 8;
 
@@ -153,43 +178,89 @@
             throw new InvalidCipherTextException("unwrap data must be a multiple of 8 bytes");
         }
 
-        byte[]  block = new byte[inLen - iv.length];
-        byte[]  a = new byte[iv.length];
-        byte[]  buf = new byte[8 + iv.length];
-
-        System.arraycopy(in, inOff, a, 0, iv.length);
-        System.arraycopy(in, inOff + iv.length, block, 0, inLen - iv.length);
-
         engine.init(!wrapCipherMode, param);
 
+        byte[] block = new byte[inLen - iv.length];
+        byte[] a = new byte[iv.length];
+        byte[] buf = new byte[8 + iv.length];
+
         n = n - 1;
 
-        for (int j = 5; j >= 0; j--)
+        if (n == 1)
         {
-            for (int i = n; i >= 1; i--)
+            engine.processBlock(in, inOff, buf, 0);
+            System.arraycopy(buf, 0, a, 0, iv.length);
+            System.arraycopy(buf, iv.length, block, 0, 8);
+        }
+        else
+        {
+            System.arraycopy(in, inOff, a, 0, iv.length);
+            System.arraycopy(in, inOff + iv.length, block, 0, inLen - iv.length);
+
+            for (int j = 5; j >= 0; j--)
             {
-                System.arraycopy(a, 0, buf, 0, iv.length);
-                System.arraycopy(block, 8 * (i - 1), buf, iv.length, 8);
-
-                int t = n * j + i;
-                for (int k = 1; t != 0; k++)
+                for (int i = n; i >= 1; i--)
                 {
-                    byte    v = (byte)t;
-
-                    buf[iv.length - k] ^= v;
-
-                    t >>>= 8;
+                    System.arraycopy(a, 0, buf, 0, iv.length);
+                    System.arraycopy(block, 8 * (i - 1), buf, iv.length, 8);
+    
+                    int t = n * j + i;
+                    for (int k = 1; t != 0; k++)
+                    {
+                        byte    v = (byte)t;
+    
+                        buf[iv.length - k] ^= v;
+    
+                        t >>>= 8;
+                    }
+    
+                    engine.processBlock(buf, 0, buf, 0);
+                    System.arraycopy(buf, 0, a, 0, 8);
+                    System.arraycopy(buf, 8, block, 8 * (i - 1), 8);
                 }
-
-                engine.processBlock(buf, 0, buf, 0);
-                System.arraycopy(buf, 0, a, 0, 8);
-                System.arraycopy(buf, 8, block, 8 * (i - 1), 8);
             }
         }
 
-        if (!Arrays.constantTimeAreEqual(a, iv))
+        if (n != 1)
         {
-            throw new InvalidCipherTextException("checksum failed");
+            if (!Arrays.constantTimeAreEqual(a, iv))
+            {
+                throw new InvalidCipherTextException("checksum failed");
+            }
+        }
+        else
+        {
+            // TODO: old (incorrect) backwards compatible unwrap - will be removed.
+            if (!Arrays.constantTimeAreEqual(a, iv))
+            {
+                System.arraycopy(in, inOff, a, 0, iv.length);
+                System.arraycopy(in, inOff + iv.length, block, 0, inLen - iv.length);
+
+                for (int j = 5; j >= 0; j--)
+                {
+                    System.arraycopy(a, 0, buf, 0, iv.length);
+                    System.arraycopy(block, 0, buf, iv.length, 8);
+
+                    int t = n * j + 1;
+                    for (int k = 1; t != 0; k++)
+                    {
+                        byte v = (byte)t;
+
+                        buf[iv.length - k] ^= v;
+
+                        t >>>= 8;
+                    }
+
+                    engine.processBlock(buf, 0, buf, 0);
+                    System.arraycopy(buf, 0, a, 0, 8);
+                    System.arraycopy(buf, 8, block, 0, 8);
+                }
+                
+                if (!Arrays.constantTimeAreEqual(a, iv))
+                {
+                    throw new InvalidCipherTextException("checksum failed");
+                }
+            }
         }
 
         return block;
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/engines/RSABlindedEngine.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/engines/RSABlindedEngine.java
index 2ff1b4f..4db17e9 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/engines/RSABlindedEngine.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/engines/RSABlindedEngine.java
@@ -112,39 +112,31 @@
         }
 
         BigInteger input = core.convertInput(in, inOff, inLen);
+        BigInteger result = processInput(input);
+        return core.convertOutput(result);
+    }
 
-        BigInteger result;
+    private BigInteger processInput(BigInteger input)
+    {
         if (key instanceof RSAPrivateCrtKeyParameters)
         {
-            RSAPrivateCrtKeyParameters k = (RSAPrivateCrtKeyParameters)key;
+            RSAPrivateCrtKeyParameters crtKey = (RSAPrivateCrtKeyParameters)key;
 
-            BigInteger e = k.getPublicExponent();
+            BigInteger e = crtKey.getPublicExponent();
             if (e != null)   // can't do blinding without a public exponent
             {
-                BigInteger m = k.getModulus();
+                BigInteger m = crtKey.getModulus();
+
                 BigInteger r = BigIntegers.createRandomInRange(ONE, m.subtract(ONE), random);
+                BigInteger blind = r.modPow(e, m);
+                BigInteger unblind = BigIntegers.modOddInverse(m, r);
 
-                BigInteger blindedInput = r.modPow(e, m).multiply(input).mod(m);
+                BigInteger blindedInput = blind.multiply(input).mod(m);
                 BigInteger blindedResult = core.processBlock(blindedInput);
-
-                BigInteger rInv = BigIntegers.modOddInverse(m, r);
-                result = blindedResult.multiply(rInv).mod(m);
-                // defence against Arjen Lenstra’s CRT attack
-                if (!input.equals(result.modPow(e, m)))
-                {
-                    throw new IllegalStateException("RSA engine faulty decryption/signing detected");
-                }
-            }
-            else
-            {
-                result = core.processBlock(input);
+                return unblind.multiply(blindedResult).mod(m);
             }
         }
-        else
-        {
-            result = core.processBlock(input);
-        }
 
-        return core.convertOutput(result);
+        return core.processBlock(input);
     }
 }
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/engines/RSACoreEngine.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/engines/RSACoreEngine.java
index 66ae3b7..7820551 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/engines/RSACoreEngine.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/engines/RSACoreEngine.java
@@ -4,7 +4,11 @@
 import java.math.BigInteger;
 
 import com.android.org.bouncycastle.crypto.CipherParameters;
+import com.android.org.bouncycastle.crypto.CryptoServicePurpose;
+import com.android.org.bouncycastle.crypto.CryptoServicesRegistrar;
 import com.android.org.bouncycastle.crypto.DataLengthException;
+import com.android.org.bouncycastle.crypto.constraints.ConstraintUtils;
+import com.android.org.bouncycastle.crypto.constraints.DefaultServiceProperties;
 import com.android.org.bouncycastle.crypto.params.ParametersWithRandom;
 import com.android.org.bouncycastle.crypto.params.RSAKeyParameters;
 import com.android.org.bouncycastle.crypto.params.RSAPrivateCrtKeyParameters;
@@ -16,21 +20,21 @@
 class RSACoreEngine
 {
     private RSAKeyParameters key;
-    private boolean          forEncryption;
+    private boolean forEncryption;
 
     /**
      * initialise the RSA engine.
      *
      * @param forEncryption true if we are encrypting, false otherwise.
-     * @param param the necessary RSA key parameters.
+     * @param param         the necessary RSA key parameters.
      */
     public void init(
-        boolean          forEncryption,
+        boolean forEncryption,
         CipherParameters param)
     {
         if (param instanceof ParametersWithRandom)
         {
-            ParametersWithRandom    rParam = (ParametersWithRandom)param;
+            ParametersWithRandom rParam = (ParametersWithRandom)param;
 
             key = (RSAKeyParameters)rParam.getParameters();
         }
@@ -40,6 +44,8 @@
         }
 
         this.forEncryption = forEncryption;
+
+        CryptoServicesRegistrar.checkConstraints(new DefaultServiceProperties("RSA", ConstraintUtils.bitsOfSecurityFor(key.getModulus()), key, getPurpose(key.isPrivate(), forEncryption)));
     }
 
     /**
@@ -51,7 +57,7 @@
      */
     public int getInputBlockSize()
     {
-        int     bitSize = key.getModulus().bitLength();
+        int bitSize = key.getModulus().bitLength();
 
         if (forEncryption)
         {
@@ -72,7 +78,7 @@
      */
     public int getOutputBlockSize()
     {
-        int     bitSize = key.getModulus().bitLength();
+        int bitSize = key.getModulus().bitLength();
 
         if (forEncryption)
         {
@@ -85,9 +91,9 @@
     }
 
     public BigInteger convertInput(
-        byte[]  in,
-        int     inOff,
-        int     inLen)
+        byte[] in,
+        int inOff,
+        int inLen)
     {
         if (inLen > (getInputBlockSize() + 1))
         {
@@ -98,7 +104,7 @@
             throw new DataLengthException("input too large for RSA cipher.");
         }
 
-        byte[]  block;
+        byte[] block;
 
         if (inOff != 0 || inLen != in.length)
         {
@@ -123,13 +129,13 @@
     public byte[] convertOutput(
         BigInteger result)
     {
-        byte[]      output = result.toByteArray();
+        byte[] output = result.toByteArray();
 
         if (forEncryption)
         {
             if (output[0] == 0 && output.length > getOutputBlockSize())        // have ended up with an extra zero byte, copy down.
             {
-                byte[]  tmp = new byte[output.length - 1];
+                byte[] tmp = new byte[output.length - 1];
 
                 System.arraycopy(output, 1, tmp, 0, tmp.length);
 
@@ -138,7 +144,7 @@
 
             if (output.length < getOutputBlockSize())     // have ended up with less bytes than normal, lengthen
             {
-                byte[]  tmp = new byte[getOutputBlockSize()];
+                byte[] tmp = new byte[getOutputBlockSize()];
 
                 System.arraycopy(output, 0, tmp, tmp.length - output.length, output.length);
 
@@ -149,7 +155,7 @@
         }
         else
         {
-            byte[]  rv;
+            byte[] rv;
             if (output[0] == 0)        // have ended up with an extra zero byte, copy down.
             {
                 rv = new byte[output.length - 1];
@@ -180,35 +186,64 @@
             //
             RSAPrivateCrtKeyParameters crtKey = (RSAPrivateCrtKeyParameters)key;
 
-            BigInteger p = crtKey.getP();
-            BigInteger q = crtKey.getQ();
-            BigInteger dP = crtKey.getDP();
-            BigInteger dQ = crtKey.getDQ();
-            BigInteger qInv = crtKey.getQInv();
+            BigInteger e = crtKey.getPublicExponent();
+            if (e != null)   // can't apply fault-attack countermeasure without public exponent
+            {
+                BigInteger p = crtKey.getP();
+                BigInteger q = crtKey.getQ();
+                BigInteger dP = crtKey.getDP();
+                BigInteger dQ = crtKey.getDQ();
+                BigInteger qInv = crtKey.getQInv();
 
-            BigInteger mP, mQ, h, m;
+                BigInteger mP, mQ, h, m;
 
-            // mP = ((input mod p) ^ dP)) mod p
-            mP = (input.remainder(p)).modPow(dP, p);
+                // mP = ((input mod p) ^ dP)) mod p
+                mP = (input.remainder(p)).modPow(dP, p);
 
-            // mQ = ((input mod q) ^ dQ)) mod q
-            mQ = (input.remainder(q)).modPow(dQ, q);
+                // mQ = ((input mod q) ^ dQ)) mod q
+                mQ = (input.remainder(q)).modPow(dQ, q);
 
-            // h = qInv * (mP - mQ) mod p
-            h = mP.subtract(mQ);
-            h = h.multiply(qInv);
-            h = h.mod(p);               // mod (in Java) returns the positive residual
+                // h = qInv * (mP - mQ) mod p
+                h = mP.subtract(mQ);
+                h = h.multiply(qInv);
+                h = h.mod(p);               // mod (in Java) returns the positive residual
 
-            // m = h * q + mQ
-            m = h.multiply(q);
-            m = m.add(mQ);
+                // m = h * q + mQ
+                m = h.multiply(q).add(mQ);
 
-            return m;
+                // defence against Arjen Lenstra’s CRT attack
+                BigInteger check = m.modPow(e, crtKey.getModulus()); 
+                if (!check.equals(input))
+                {
+                    throw new IllegalStateException("RSA engine faulty decryption/signing detected");
+                }
+
+                return m;
+            }
         }
-        else
+
+        return input.modPow(key.getExponent(), key.getModulus());
+    }
+
+    private CryptoServicePurpose getPurpose(boolean isPrivate, boolean forEncryption)
+    {
+        boolean isSigning = isPrivate && forEncryption;
+        boolean isEncryption = !isPrivate && forEncryption;
+        boolean isVerifying = !isPrivate && !forEncryption;
+
+        if (isSigning)
         {
-            return input.modPow(
-                        key.getExponent(), key.getModulus());
+            return CryptoServicePurpose.SIGNING;
         }
+        if (isEncryption)
+        {
+            return CryptoServicePurpose.ENCRYPTION;
+        }
+        if (isVerifying)
+        {
+            return CryptoServicePurpose.VERIFYING;
+        }
+
+        return CryptoServicePurpose.DECRYPTION;
     }
 }
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/engines/TwofishEngine.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/engines/TwofishEngine.java
index ef52518..32186fd 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/engines/TwofishEngine.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/engines/TwofishEngine.java
@@ -3,9 +3,13 @@
 
 import com.android.org.bouncycastle.crypto.BlockCipher;
 import com.android.org.bouncycastle.crypto.CipherParameters;
+import com.android.org.bouncycastle.crypto.CryptoServicesRegistrar;
 import com.android.org.bouncycastle.crypto.DataLengthException;
 import com.android.org.bouncycastle.crypto.OutputLengthException;
+import com.android.org.bouncycastle.crypto.constraints.DefaultServiceProperties;
 import com.android.org.bouncycastle.crypto.params.KeyParameter;
+import com.android.org.bouncycastle.util.Integers;
+import com.android.org.bouncycastle.util.Pack;
 
 /**
  * A class that provides Twofish encryption operations.
@@ -226,6 +230,8 @@
 
     public TwofishEngine()
     {
+        CryptoServicesRegistrar.checkConstraints(new DefaultServiceProperties(getAlgorithmName(), 256));
+
         // calculate the MDS matrix
         int[] m1 = new int[2];
         int[] mX = new int[2];
@@ -274,7 +280,21 @@
         {
             this.encrypting = encrypting;
             this.workingKey = ((KeyParameter)params).getKey();
-            this.k64Cnt = (this.workingKey.length / 8); // pre-padded ?
+
+            int keyBits = this.workingKey.length * 8;
+            switch (keyBits)
+            {
+            case 128:
+            case 192:
+            case 256:
+                break;
+            default:
+                throw new IllegalArgumentException("Key length not 128/192/256 bits.");
+            }
+
+            CryptoServicesRegistrar.checkConstraints(new DefaultServiceProperties(getAlgorithmName(), keyBits, params, Utils.getPurpose(encrypting)));
+
+            this.k64Cnt = this.workingKey.length / 8;
             setKey(this.workingKey);
 
             return;
@@ -346,28 +366,16 @@
         int[] sBoxKeys = new int[MAX_KEY_BITS/64]; // 4 
         gSubKeys = new int[TOTAL_SUBKEYS];
 
-        if (k64Cnt < 1) 
-        {
-            throw new IllegalArgumentException("Key size less than 64 bits");
-        }
-        
-        if (k64Cnt > 4)
-        {
-            throw new IllegalArgumentException("Key size larger than 256 bits");
-        }
-
         /*
-         * k64Cnt is the number of 8 byte blocks (64 chunks)
-         * that are in the input key.  The input key is a
-         * maximum of 32 bytes (256 bits), so the range
-         * for k64Cnt is 1..4
+         * k64Cnt is the number of 8 byte blocks (64 chunks) that are in the input key.
+         * The input key is 16, 24 or 32 bytes, so the range for k64Cnt is 2..4
          */
         for (int i=0; i<k64Cnt ; i++)
         {
             int p = i* 8;
 
-            k32e[i] = BytesTo32Bits(key, p);
-            k32o[i] = BytesTo32Bits(key, p+4);
+            k32e[i] = Pack.littleEndianToInt(key, p);
+            k32o[i] = Pack.littleEndianToInt(key, p + 4);
 
             sBoxKeys[k64Cnt-1-i] = RS_MDS_Encode(k32e[i], k32o[i]);
         }
@@ -378,7 +386,7 @@
             q = i*SK_STEP;
             A = F32(q,         k32e);
             B = F32(q+SK_BUMP, k32o);
-            B = B << 8 | B >>> 24;
+            B = Integers.rotateLeft(B, 8);
             A += B;
             gSubKeys[i*2] = A;
             A += B;
@@ -450,10 +458,10 @@
         byte[] dst,
         int dstIndex)
     {
-        int x0 = BytesTo32Bits(src, srcIndex) ^ gSubKeys[INPUT_WHITEN];
-        int x1 = BytesTo32Bits(src, srcIndex + 4) ^ gSubKeys[INPUT_WHITEN + 1];
-        int x2 = BytesTo32Bits(src, srcIndex + 8) ^ gSubKeys[INPUT_WHITEN + 2];
-        int x3 = BytesTo32Bits(src, srcIndex + 12) ^ gSubKeys[INPUT_WHITEN + 3];
+        int x0 = Pack.littleEndianToInt(src, srcIndex) ^ gSubKeys[INPUT_WHITEN];
+        int x1 = Pack.littleEndianToInt(src, srcIndex + 4) ^ gSubKeys[INPUT_WHITEN + 1];
+        int x2 = Pack.littleEndianToInt(src, srcIndex + 8) ^ gSubKeys[INPUT_WHITEN + 2];
+        int x3 = Pack.littleEndianToInt(src, srcIndex + 12) ^ gSubKeys[INPUT_WHITEN + 3];
 
         int k = ROUND_SUBKEYS;
         int t0, t1;
@@ -462,20 +470,20 @@
             t0 = Fe32_0(x0);
             t1 = Fe32_3(x1);
             x2 ^= t0 + t1 + gSubKeys[k++];
-            x2 = x2 >>>1 | x2 << 31;
-            x3 = (x3 << 1 | x3 >>> 31) ^ (t0 + 2*t1 + gSubKeys[k++]);
+            x2 = Integers.rotateRight(x2, 1);
+            x3 = Integers.rotateLeft(x3, 1) ^ (t0 + 2*t1 + gSubKeys[k++]);
 
             t0 = Fe32_0(x2);
             t1 = Fe32_3(x3);
             x0 ^= t0 + t1 + gSubKeys[k++];
-            x0 = x0 >>>1 | x0 << 31;
-            x1 = (x1 << 1 | x1 >>> 31) ^ (t0 + 2*t1 + gSubKeys[k++]);
+            x0 = Integers.rotateRight(x0, 1);
+            x1 = Integers.rotateLeft(x1, 1) ^ (t0 + 2*t1 + gSubKeys[k++]);
         }
 
-        Bits32ToBytes(x2 ^ gSubKeys[OUTPUT_WHITEN], dst, dstIndex);
-        Bits32ToBytes(x3 ^ gSubKeys[OUTPUT_WHITEN + 1], dst, dstIndex + 4);
-        Bits32ToBytes(x0 ^ gSubKeys[OUTPUT_WHITEN + 2], dst, dstIndex + 8);
-        Bits32ToBytes(x1 ^ gSubKeys[OUTPUT_WHITEN + 3], dst, dstIndex + 12);
+        Pack.intToLittleEndian(x2 ^ gSubKeys[OUTPUT_WHITEN], dst, dstIndex);
+        Pack.intToLittleEndian(x3 ^ gSubKeys[OUTPUT_WHITEN + 1], dst, dstIndex + 4);
+        Pack.intToLittleEndian(x0 ^ gSubKeys[OUTPUT_WHITEN + 2], dst, dstIndex + 8);
+        Pack.intToLittleEndian(x1 ^ gSubKeys[OUTPUT_WHITEN + 3], dst, dstIndex + 12);
     }
 
     /**
@@ -489,10 +497,10 @@
         byte[] dst,
         int dstIndex)
     {
-        int x2 = BytesTo32Bits(src, srcIndex) ^ gSubKeys[OUTPUT_WHITEN];
-        int x3 = BytesTo32Bits(src, srcIndex+4) ^ gSubKeys[OUTPUT_WHITEN + 1];
-        int x0 = BytesTo32Bits(src, srcIndex+8) ^ gSubKeys[OUTPUT_WHITEN + 2];
-        int x1 = BytesTo32Bits(src, srcIndex+12) ^ gSubKeys[OUTPUT_WHITEN + 3];
+        int x2 = Pack.littleEndianToInt(src, srcIndex) ^ gSubKeys[OUTPUT_WHITEN];
+        int x3 = Pack.littleEndianToInt(src, srcIndex + 4) ^ gSubKeys[OUTPUT_WHITEN + 1];
+        int x0 = Pack.littleEndianToInt(src, srcIndex + 8) ^ gSubKeys[OUTPUT_WHITEN + 2];
+        int x1 = Pack.littleEndianToInt(src, srcIndex + 12) ^ gSubKeys[OUTPUT_WHITEN + 3];
 
         int k = ROUND_SUBKEYS + 2 * ROUNDS -1 ;
         int t0, t1;
@@ -501,20 +509,20 @@
             t0 = Fe32_0(x2);
             t1 = Fe32_3(x3);
             x1 ^= t0 + 2*t1 + gSubKeys[k--];
-            x0 = (x0 << 1 | x0 >>> 31) ^ (t0 + t1 + gSubKeys[k--]);
-            x1 = x1 >>>1 | x1 << 31;
+            x0 = Integers.rotateLeft(x0, 1) ^ (t0 + t1 + gSubKeys[k--]);
+            x1 = Integers.rotateRight(x1, 1);
 
             t0 = Fe32_0(x0);
             t1 = Fe32_3(x1);
             x3 ^= t0 + 2*t1 + gSubKeys[k--];
-            x2 = (x2 << 1 | x2 >>> 31) ^ (t0 + t1 + gSubKeys[k--]);
-            x3 = x3 >>>1 | x3 << 31;
+            x2 = Integers.rotateLeft(x2, 1) ^ (t0 + t1 + gSubKeys[k--]);
+            x3 = Integers.rotateRight(x3, 1);
         }
 
-        Bits32ToBytes(x0 ^ gSubKeys[INPUT_WHITEN], dst, dstIndex);
-        Bits32ToBytes(x1 ^ gSubKeys[INPUT_WHITEN + 1], dst, dstIndex + 4);
-        Bits32ToBytes(x2 ^ gSubKeys[INPUT_WHITEN + 2], dst, dstIndex + 8);
-        Bits32ToBytes(x3 ^ gSubKeys[INPUT_WHITEN + 3], dst, dstIndex + 12);
+        Pack.intToLittleEndian(x0 ^ gSubKeys[INPUT_WHITEN], dst, dstIndex);
+        Pack.intToLittleEndian(x1 ^ gSubKeys[INPUT_WHITEN + 1], dst, dstIndex + 4);
+        Pack.intToLittleEndian(x2 ^ gSubKeys[INPUT_WHITEN + 2], dst, dstIndex + 8);
+        Pack.intToLittleEndian(x3 ^ gSubKeys[INPUT_WHITEN + 3], dst, dstIndex + 12);
     }
 
     /* 
@@ -663,20 +671,4 @@
                gSBox[ 0x200 + 2*((x >>> 8) & 0xff) ] ^
                gSBox[ 0x201 + 2*((x >>> 16) & 0xff) ];
     }
-    
-    private int BytesTo32Bits(byte[] b, int p)
-    {
-        return ((b[p] & 0xff)) | 
-             ((b[p+1] & 0xff) << 8) |
-             ((b[p+2] & 0xff) << 16) |
-             ((b[p+3] & 0xff) << 24);
-    }
-
-    private void Bits32ToBytes(int in,  byte[] b, int offset)
-    {
-        b[offset] = (byte)in;
-        b[offset + 1] = (byte)(in >> 8);
-        b[offset + 2] = (byte)(in >> 16);
-        b[offset + 3] = (byte)(in >> 24);
-    }
 }
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/engines/Utils.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/engines/Utils.java
new file mode 100644
index 0000000..4c563f1
--- /dev/null
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/engines/Utils.java
@@ -0,0 +1,12 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.org.bouncycastle.crypto.engines;
+
+import com.android.org.bouncycastle.crypto.CryptoServicePurpose;
+
+class Utils
+{
+    static CryptoServicePurpose getPurpose(boolean forEncryption)
+    {
+        return forEncryption ? CryptoServicePurpose.ENCRYPTION : CryptoServicePurpose.DECRYPTION;
+    }
+}
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/engines/Zuc128CoreEngine.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/engines/Zuc128CoreEngine.java
new file mode 100644
index 0000000..b408995
--- /dev/null
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/engines/Zuc128CoreEngine.java
@@ -0,0 +1,577 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.org.bouncycastle.crypto.engines;
+
+import com.android.org.bouncycastle.crypto.CipherParameters;
+import com.android.org.bouncycastle.crypto.CryptoServicePurpose;
+import com.android.org.bouncycastle.crypto.CryptoServicesRegistrar;
+import com.android.org.bouncycastle.crypto.DataLengthException;
+import com.android.org.bouncycastle.crypto.OutputLengthException;
+import com.android.org.bouncycastle.crypto.StreamCipher;
+import com.android.org.bouncycastle.crypto.constraints.DefaultServiceProperties;
+import com.android.org.bouncycastle.crypto.params.KeyParameter;
+import com.android.org.bouncycastle.crypto.params.ParametersWithIV;
+import com.android.org.bouncycastle.util.Memoable;
+
+/**
+ * Zuc128Engine implementation.
+ * Based on https://www.gsma.com/aboutus/wp-content/uploads/2014/12/eea3eia3zucv16.pdf
+ * @hide This class is not part of the Android public SDK API
+ */
+public class Zuc128CoreEngine
+    implements StreamCipher, Memoable
+{
+    /* the s-boxes */
+    private static final byte[] S0 = new byte[]{
+        (byte)0x3e, (byte)0x72, (byte)0x5b, (byte)0x47, (byte)0xca, (byte)0xe0, (byte)0x00, (byte)0x33, (byte)0x04, (byte)0xd1, (byte)0x54, (byte)0x98, (byte)0x09, (byte)0xb9, (byte)0x6d, (byte)0xcb,
+        (byte)0x7b, (byte)0x1b, (byte)0xf9, (byte)0x32, (byte)0xaf, (byte)0x9d, (byte)0x6a, (byte)0xa5, (byte)0xb8, (byte)0x2d, (byte)0xfc, (byte)0x1d, (byte)0x08, (byte)0x53, (byte)0x03, (byte)0x90,
+        (byte)0x4d, (byte)0x4e, (byte)0x84, (byte)0x99, (byte)0xe4, (byte)0xce, (byte)0xd9, (byte)0x91, (byte)0xdd, (byte)0xb6, (byte)0x85, (byte)0x48, (byte)0x8b, (byte)0x29, (byte)0x6e, (byte)0xac,
+        (byte)0xcd, (byte)0xc1, (byte)0xf8, (byte)0x1e, (byte)0x73, (byte)0x43, (byte)0x69, (byte)0xc6, (byte)0xb5, (byte)0xbd, (byte)0xfd, (byte)0x39, (byte)0x63, (byte)0x20, (byte)0xd4, (byte)0x38,
+        (byte)0x76, (byte)0x7d, (byte)0xb2, (byte)0xa7, (byte)0xcf, (byte)0xed, (byte)0x57, (byte)0xc5, (byte)0xf3, (byte)0x2c, (byte)0xbb, (byte)0x14, (byte)0x21, (byte)0x06, (byte)0x55, (byte)0x9b,
+        (byte)0xe3, (byte)0xef, (byte)0x5e, (byte)0x31, (byte)0x4f, (byte)0x7f, (byte)0x5a, (byte)0xa4, (byte)0x0d, (byte)0x82, (byte)0x51, (byte)0x49, (byte)0x5f, (byte)0xba, (byte)0x58, (byte)0x1c,
+        (byte)0x4a, (byte)0x16, (byte)0xd5, (byte)0x17, (byte)0xa8, (byte)0x92, (byte)0x24, (byte)0x1f, (byte)0x8c, (byte)0xff, (byte)0xd8, (byte)0xae, (byte)0x2e, (byte)0x01, (byte)0xd3, (byte)0xad,
+        (byte)0x3b, (byte)0x4b, (byte)0xda, (byte)0x46, (byte)0xeb, (byte)0xc9, (byte)0xde, (byte)0x9a, (byte)0x8f, (byte)0x87, (byte)0xd7, (byte)0x3a, (byte)0x80, (byte)0x6f, (byte)0x2f, (byte)0xc8,
+        (byte)0xb1, (byte)0xb4, (byte)0x37, (byte)0xf7, (byte)0x0a, (byte)0x22, (byte)0x13, (byte)0x28, (byte)0x7c, (byte)0xcc, (byte)0x3c, (byte)0x89, (byte)0xc7, (byte)0xc3, (byte)0x96, (byte)0x56,
+        (byte)0x07, (byte)0xbf, (byte)0x7e, (byte)0xf0, (byte)0x0b, (byte)0x2b, (byte)0x97, (byte)0x52, (byte)0x35, (byte)0x41, (byte)0x79, (byte)0x61, (byte)0xa6, (byte)0x4c, (byte)0x10, (byte)0xfe,
+        (byte)0xbc, (byte)0x26, (byte)0x95, (byte)0x88, (byte)0x8a, (byte)0xb0, (byte)0xa3, (byte)0xfb, (byte)0xc0, (byte)0x18, (byte)0x94, (byte)0xf2, (byte)0xe1, (byte)0xe5, (byte)0xe9, (byte)0x5d,
+        (byte)0xd0, (byte)0xdc, (byte)0x11, (byte)0x66, (byte)0x64, (byte)0x5c, (byte)0xec, (byte)0x59, (byte)0x42, (byte)0x75, (byte)0x12, (byte)0xf5, (byte)0x74, (byte)0x9c, (byte)0xaa, (byte)0x23,
+        (byte)0x0e, (byte)0x86, (byte)0xab, (byte)0xbe, (byte)0x2a, (byte)0x02, (byte)0xe7, (byte)0x67, (byte)0xe6, (byte)0x44, (byte)0xa2, (byte)0x6c, (byte)0xc2, (byte)0x93, (byte)0x9f, (byte)0xf1,
+        (byte)0xf6, (byte)0xfa, (byte)0x36, (byte)0xd2, (byte)0x50, (byte)0x68, (byte)0x9e, (byte)0x62, (byte)0x71, (byte)0x15, (byte)0x3d, (byte)0xd6, (byte)0x40, (byte)0xc4, (byte)0xe2, (byte)0x0f,
+        (byte)0x8e, (byte)0x83, (byte)0x77, (byte)0x6b, (byte)0x25, (byte)0x05, (byte)0x3f, (byte)0x0c, (byte)0x30, (byte)0xea, (byte)0x70, (byte)0xb7, (byte)0xa1, (byte)0xe8, (byte)0xa9, (byte)0x65,
+        (byte)0x8d, (byte)0x27, (byte)0x1a, (byte)0xdb, (byte)0x81, (byte)0xb3, (byte)0xa0, (byte)0xf4, (byte)0x45, (byte)0x7a, (byte)0x19, (byte)0xdf, (byte)0xee, (byte)0x78, (byte)0x34, (byte)0x60
+    };
+
+    private static final byte[] S1 = new byte[]{
+        (byte)0x55, (byte)0xc2, (byte)0x63, (byte)0x71, (byte)0x3b, (byte)0xc8, (byte)0x47, (byte)0x86, (byte)0x9f, (byte)0x3c, (byte)0xda, (byte)0x5b, (byte)0x29, (byte)0xaa, (byte)0xfd, (byte)0x77,
+        (byte)0x8c, (byte)0xc5, (byte)0x94, (byte)0x0c, (byte)0xa6, (byte)0x1a, (byte)0x13, (byte)0x00, (byte)0xe3, (byte)0xa8, (byte)0x16, (byte)0x72, (byte)0x40, (byte)0xf9, (byte)0xf8, (byte)0x42,
+        (byte)0x44, (byte)0x26, (byte)0x68, (byte)0x96, (byte)0x81, (byte)0xd9, (byte)0x45, (byte)0x3e, (byte)0x10, (byte)0x76, (byte)0xc6, (byte)0xa7, (byte)0x8b, (byte)0x39, (byte)0x43, (byte)0xe1,
+        (byte)0x3a, (byte)0xb5, (byte)0x56, (byte)0x2a, (byte)0xc0, (byte)0x6d, (byte)0xb3, (byte)0x05, (byte)0x22, (byte)0x66, (byte)0xbf, (byte)0xdc, (byte)0x0b, (byte)0xfa, (byte)0x62, (byte)0x48,
+        (byte)0xdd, (byte)0x20, (byte)0x11, (byte)0x06, (byte)0x36, (byte)0xc9, (byte)0xc1, (byte)0xcf, (byte)0xf6, (byte)0x27, (byte)0x52, (byte)0xbb, (byte)0x69, (byte)0xf5, (byte)0xd4, (byte)0x87,
+        (byte)0x7f, (byte)0x84, (byte)0x4c, (byte)0xd2, (byte)0x9c, (byte)0x57, (byte)0xa4, (byte)0xbc, (byte)0x4f, (byte)0x9a, (byte)0xdf, (byte)0xfe, (byte)0xd6, (byte)0x8d, (byte)0x7a, (byte)0xeb,
+        (byte)0x2b, (byte)0x53, (byte)0xd8, (byte)0x5c, (byte)0xa1, (byte)0x14, (byte)0x17, (byte)0xfb, (byte)0x23, (byte)0xd5, (byte)0x7d, (byte)0x30, (byte)0x67, (byte)0x73, (byte)0x08, (byte)0x09,
+        (byte)0xee, (byte)0xb7, (byte)0x70, (byte)0x3f, (byte)0x61, (byte)0xb2, (byte)0x19, (byte)0x8e, (byte)0x4e, (byte)0xe5, (byte)0x4b, (byte)0x93, (byte)0x8f, (byte)0x5d, (byte)0xdb, (byte)0xa9,
+        (byte)0xad, (byte)0xf1, (byte)0xae, (byte)0x2e, (byte)0xcb, (byte)0x0d, (byte)0xfc, (byte)0xf4, (byte)0x2d, (byte)0x46, (byte)0x6e, (byte)0x1d, (byte)0x97, (byte)0xe8, (byte)0xd1, (byte)0xe9,
+        (byte)0x4d, (byte)0x37, (byte)0xa5, (byte)0x75, (byte)0x5e, (byte)0x83, (byte)0x9e, (byte)0xab, (byte)0x82, (byte)0x9d, (byte)0xb9, (byte)0x1c, (byte)0xe0, (byte)0xcd, (byte)0x49, (byte)0x89,
+        (byte)0x01, (byte)0xb6, (byte)0xbd, (byte)0x58, (byte)0x24, (byte)0xa2, (byte)0x5f, (byte)0x38, (byte)0x78, (byte)0x99, (byte)0x15, (byte)0x90, (byte)0x50, (byte)0xb8, (byte)0x95, (byte)0xe4,
+        (byte)0xd0, (byte)0x91, (byte)0xc7, (byte)0xce, (byte)0xed, (byte)0x0f, (byte)0xb4, (byte)0x6f, (byte)0xa0, (byte)0xcc, (byte)0xf0, (byte)0x02, (byte)0x4a, (byte)0x79, (byte)0xc3, (byte)0xde,
+        (byte)0xa3, (byte)0xef, (byte)0xea, (byte)0x51, (byte)0xe6, (byte)0x6b, (byte)0x18, (byte)0xec, (byte)0x1b, (byte)0x2c, (byte)0x80, (byte)0xf7, (byte)0x74, (byte)0xe7, (byte)0xff, (byte)0x21,
+        (byte)0x5a, (byte)0x6a, (byte)0x54, (byte)0x1e, (byte)0x41, (byte)0x31, (byte)0x92, (byte)0x35, (byte)0xc4, (byte)0x33, (byte)0x07, (byte)0x0a, (byte)0xba, (byte)0x7e, (byte)0x0e, (byte)0x34,
+        (byte)0x88, (byte)0xb1, (byte)0x98, (byte)0x7c, (byte)0xf3, (byte)0x3d, (byte)0x60, (byte)0x6c, (byte)0x7b, (byte)0xca, (byte)0xd3, (byte)0x1f, (byte)0x32, (byte)0x65, (byte)0x04, (byte)0x28,
+        (byte)0x64, (byte)0xbe, (byte)0x85, (byte)0x9b, (byte)0x2f, (byte)0x59, (byte)0x8a, (byte)0xd7, (byte)0xb0, (byte)0x25, (byte)0xac, (byte)0xaf, (byte)0x12, (byte)0x03, (byte)0xe2, (byte)0xf2
+    };
+
+    /* the constants D */
+    private static final short[] EK_d = new short[]{
+        0x44D7, 0x26BC, 0x626B, 0x135E, 0x5789, 0x35E2, 0x7135, 0x09AF,
+        0x4D78, 0x2F13, 0x6BC4, 0x1AF1, 0x5E26, 0x3C4D, 0x789A, 0x47AC
+    };
+
+    /**
+     * State.
+     */
+    private final int[] LFSR = new int[16];
+    private final int[] F = new int[2];
+    private final int[] BRC = new int[4];
+
+    /**
+     * index of next byte in keyStream.
+     */
+    private int theIndex;
+
+    /**
+     * Advanced stream.
+     */
+    private final byte[] keyStream = new byte[4];   // Integer.BYTES
+
+    /**
+     * The iterations.
+     */
+    private int theIterations;
+
+    /**
+     * Reset state.
+     */
+    private Zuc128CoreEngine theResetState;
+
+    /**
+     * Constructor.
+     */
+    protected Zuc128CoreEngine()
+    {
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param pSource the source engine
+     */
+    protected Zuc128CoreEngine(final Zuc128CoreEngine pSource)
+    {
+        reset(pSource);
+    }
+
+    /**
+     * initialise a Snow3G cipher.
+     *
+     * @param forEncryption whether or not we are for encryption.
+     * @param params        the parameters required to set up the cipher.
+     * @throws IllegalArgumentException if the params argument is inappropriate.
+     */
+    public void init(final boolean forEncryption,
+                     final CipherParameters params)
+    {
+        /*
+         * encryption and decryption is completely symmetrical.
+         */
+
+        /* Determine parameters */
+        CipherParameters myParams = params;
+        byte[] newKey = null;
+        byte[] newIV = null;
+        if ((myParams instanceof ParametersWithIV))
+        {
+            final ParametersWithIV ivParams = (ParametersWithIV)myParams;
+            newIV = ivParams.getIV();
+            myParams = ivParams.getParameters();
+        }
+        if (myParams instanceof KeyParameter)
+        {
+            final KeyParameter keyParam = (KeyParameter)myParams;
+            newKey = keyParam.getKey();
+        }
+
+        /* Initialise engine and mark as initialised */
+        theIndex = 0;
+        theIterations = 0;
+        setKeyAndIV(newKey, newIV);
+
+        CryptoServicesRegistrar.checkConstraints(new DefaultServiceProperties(getAlgorithmName(), newKey.length * 8,
+            params, forEncryption ? CryptoServicePurpose.ENCRYPTION : CryptoServicePurpose.DECRYPTION));
+
+        /* Save reset state */
+        theResetState = (Zuc128CoreEngine)copy();
+    }
+
+    /**
+     * Obtain Max iterations.
+     *
+     * @return the maximum iterations
+     */
+    protected int getMaxIterations()
+    {
+        return 2047;
+    }
+
+    /**
+     * Obtain Algorithm Name.
+     *
+     * @return the name
+     */
+    public String getAlgorithmName()
+    {
+        return "Zuc-128";
+    }
+
+    /**
+     * Process bytes.
+     *
+     * @param in     the input buffer
+     * @param inOff  the starting offset in the input buffer
+     * @param len    the length of data in the input buffer
+     * @param out    the output buffer
+     * @param outOff the starting offset in the output buffer
+     * @return the number of bytes returned in the output buffer
+     */
+    public int processBytes(final byte[] in,
+                            final int inOff,
+                            final int len,
+                            final byte[] out,
+                            final int outOff)
+    {
+        /* Check for errors */
+        if (theResetState == null)
+        {
+            throw new IllegalStateException(getAlgorithmName() + " not initialised");
+        }
+        if ((inOff + len) > in.length)
+        {
+            throw new DataLengthException("input buffer too short");
+        }
+        if ((outOff + len) > out.length)
+        {
+            throw new OutputLengthException("output buffer too short");
+        }
+
+        /* Loop through the input bytes */
+        for (int i = 0; i < len; i++)
+        {
+            out[i + outOff] = returnByte(in[i + inOff]);
+        }
+        return len;
+    }
+
+    /**
+     * Reset the engine.
+     */
+    public void reset()
+    {
+        if (theResetState != null)
+        {
+            reset(theResetState);
+        }
+    }
+
+    /**
+     * Process single byte.
+     *
+     * @param in the input byte
+     * @return the output byte
+     */
+    public byte returnByte(final byte in)
+    {
+        /* Make the keyStream if required */
+        if (theIndex == 0)
+        {
+            makeKeyStream();
+        }
+
+        /* Map the next byte and adjust index */
+        final byte out = (byte)(keyStream[theIndex] ^ in);
+        theIndex = (theIndex + 1) % 4; // Integer.BYTES
+
+        /* Return the mapped character */
+        return out;
+    }
+
+    /**
+     * Encode a 32-bit value into a buffer (little-endian).
+     *
+     * @param val the value to encode
+     * @param buf the output buffer
+     * @param off the output offset
+     */
+    public static void encode32be(int val, byte[] buf, int off)
+    {
+        buf[off] = (byte)(val >> 24);
+        buf[off + 1] = (byte)(val >> 16);
+        buf[off + 2] = (byte)(val >> 8);
+        buf[off + 3] = (byte)val;
+    }
+
+    /* ����������������������- */
+
+    /**
+     * Modular add c = a + b mod (2^31 � 1).
+     *
+     * @param a value A
+     * @param b value B
+     * @return the result
+     */
+    private int AddM(final int a, final int b)
+    {
+        final int c = a + b;
+        return (c & 0x7FFFFFFF) + (c >>> 31);
+    }
+
+    /**
+     * Multiply by power of two.
+     *
+     * @param x input value
+     * @param k the power of two
+     * @return the result
+     */
+    private static int MulByPow2(final int x, final int k)
+    {
+        return ((((x) << k) | ((x) >>> (31 - k))) & 0x7FFFFFFF);
+    }
+
+    /**
+     * LFSR with initialisation mode.
+     *
+     * @param u
+     */
+    private void LFSRWithInitialisationMode(final int u)
+    {
+        int f = LFSR[0];
+        int v = MulByPow2(LFSR[0], 8);
+        f = AddM(f, v);
+        v = MulByPow2(LFSR[4], 20);
+        f = AddM(f, v);
+        v = MulByPow2(LFSR[10], 21);
+        f = AddM(f, v);
+        v = MulByPow2(LFSR[13], 17);
+        f = AddM(f, v);
+        v = MulByPow2(LFSR[15], 15);
+        f = AddM(f, v);
+        f = AddM(f, u);
+
+        /* update the state */
+        LFSR[0] = LFSR[1];
+        LFSR[1] = LFSR[2];
+        LFSR[2] = LFSR[3];
+        LFSR[3] = LFSR[4];
+        LFSR[4] = LFSR[5];
+        LFSR[5] = LFSR[6];
+        LFSR[6] = LFSR[7];
+        LFSR[7] = LFSR[8];
+        LFSR[8] = LFSR[9];
+        LFSR[9] = LFSR[10];
+        LFSR[10] = LFSR[11];
+        LFSR[11] = LFSR[12];
+        LFSR[12] = LFSR[13];
+        LFSR[13] = LFSR[14];
+        LFSR[14] = LFSR[15];
+        LFSR[15] = f;
+    }
+
+    /**
+     * LFSR with work mode.
+     */
+    private void LFSRWithWorkMode()
+    {
+        int f, v;
+        f = LFSR[0];
+        v = MulByPow2(LFSR[0], 8);
+        f = AddM(f, v);
+        v = MulByPow2(LFSR[4], 20);
+        f = AddM(f, v);
+        v = MulByPow2(LFSR[10], 21);
+        f = AddM(f, v);
+        v = MulByPow2(LFSR[13], 17);
+        f = AddM(f, v);
+        v = MulByPow2(LFSR[15], 15);
+        f = AddM(f, v);
+
+        /* update the state */
+        LFSR[0] = LFSR[1];
+        LFSR[1] = LFSR[2];
+        LFSR[2] = LFSR[3];
+        LFSR[3] = LFSR[4];
+        LFSR[4] = LFSR[5];
+        LFSR[5] = LFSR[6];
+        LFSR[6] = LFSR[7];
+        LFSR[7] = LFSR[8];
+        LFSR[8] = LFSR[9];
+        LFSR[9] = LFSR[10];
+        LFSR[10] = LFSR[11];
+        LFSR[11] = LFSR[12];
+        LFSR[12] = LFSR[13];
+        LFSR[13] = LFSR[14];
+        LFSR[14] = LFSR[15];
+        LFSR[15] = f;
+    }
+
+    /**
+     * BitReorganization.
+     */
+    private void BitReorganization()
+    {
+        BRC[0] = ((LFSR[15] & 0x7FFF8000) << 1) | (LFSR[14] & 0xFFFF);
+        BRC[1] = ((LFSR[11] & 0xFFFF) << 16) | (LFSR[9] >>> 15);
+        BRC[2] = ((LFSR[7] & 0xFFFF) << 16) | (LFSR[5] >>> 15);
+        BRC[3] = ((LFSR[2] & 0xFFFF) << 16) | (LFSR[0] >>> 15);
+    }
+
+    /**
+     * Rotate integer.
+     *
+     * @param a the integer
+     * @param k the shift
+     * @return the result
+     */
+    static int ROT(int a, int k)
+    {
+        return (((a) << k) | ((a) >>> (32 - k)));
+    }
+
+    /**
+     * L1.
+     *
+     * @param X the input integer.
+     * @return the result
+     */
+    private static int L1(final int X)
+    {
+        return (X ^ ROT(X, 2) ^ ROT(X, 10) ^ ROT(X, 18) ^ ROT(X, 24));
+    }
+
+    /**
+     * L2.
+     *
+     * @param X the input integer.
+     * @return the result
+     */
+    private static int L2(final int X)
+    {
+        return (X ^ ROT(X, 8) ^ ROT(X, 14) ^ ROT(X, 22) ^ ROT(X, 30));
+    }
+
+    /**
+     * Build a 32-bit integer from constituent parts.
+     *
+     * @param a part A
+     * @param b part B
+     * @param c part C
+     * @param d part D
+     * @return the built integer
+     */
+    private static int MAKEU32(final byte a,
+                               final byte b,
+                               final byte c,
+                               final byte d)
+    {
+        return (((a & 0xFF) << 24) | ((b & 0xFF) << 16) | ((c & 0xFF) << 8) | ((d & 0xFF)));
+    }
+
+    /**
+     * F.
+     */
+    int F()
+    {
+        int W, W1, W2, u, v;
+        W = (BRC[0] ^ F[0]) + F[1];
+        W1 = F[0] + BRC[1];
+        W2 = F[1] ^ BRC[2];
+        u = L1((W1 << 16) | (W2 >>> 16));
+        v = L2((W2 << 16) | (W1 >>> 16));
+        F[0] = MAKEU32(S0[u >>> 24], S1[(u >>> 16) & 0xFF],
+            S0[(u >>> 8) & 0xFF], S1[u & 0xFF]);
+        F[1] = MAKEU32(S0[v >>> 24], S1[(v >>> 16) & 0xFF],
+            S0[(v >>> 8) & 0xFF], S1[v & 0xFF]);
+        return W;
+    }
+
+    /**
+     * Build a 31-bit integer from constituent parts.
+     *
+     * @param a part A
+     * @param b part B
+     * @param c part C
+     * @return the built integer
+     */
+    private static int MAKEU31(final byte a,
+                               final short b,
+                               final byte c)
+    {
+        return (((a & 0xFF) << 23) | ((b & 0xFFFF) << 8) | (c & 0xFF));
+    }
+
+    /**
+     * Process key and IV into LFSR.
+     *
+     * @param pLFSR the LFSR
+     * @param k     the key
+     * @param iv    the iv
+     */
+    protected void setKeyAndIV(final int[] pLFSR,
+                               final byte[] k,
+                               final byte[] iv)
+    {
+        /* Check lengths */
+        if (k == null || k.length != 16)
+        {
+            throw new IllegalArgumentException("A key of 16 bytes is needed");
+        }
+        if (iv == null || iv.length != 16)
+        {
+            throw new IllegalArgumentException("An IV of 16 bytes is needed");
+        }
+
+        /* expand key */
+        LFSR[0] = MAKEU31(k[0], EK_d[0], iv[0]);
+        LFSR[1] = MAKEU31(k[1], EK_d[1], iv[1]);
+        LFSR[2] = MAKEU31(k[2], EK_d[2], iv[2]);
+        LFSR[3] = MAKEU31(k[3], EK_d[3], iv[3]);
+        LFSR[4] = MAKEU31(k[4], EK_d[4], iv[4]);
+        LFSR[5] = MAKEU31(k[5], EK_d[5], iv[5]);
+        LFSR[6] = MAKEU31(k[6], EK_d[6], iv[6]);
+        LFSR[7] = MAKEU31(k[7], EK_d[7], iv[7]);
+        LFSR[8] = MAKEU31(k[8], EK_d[8], iv[8]);
+        LFSR[9] = MAKEU31(k[9], EK_d[9], iv[9]);
+        LFSR[10] = MAKEU31(k[10], EK_d[10], iv[10]);
+        LFSR[11] = MAKEU31(k[11], EK_d[11], iv[11]);
+        LFSR[12] = MAKEU31(k[12], EK_d[12], iv[12]);
+        LFSR[13] = MAKEU31(k[13], EK_d[13], iv[13]);
+        LFSR[14] = MAKEU31(k[14], EK_d[14], iv[14]);
+        LFSR[15] = MAKEU31(k[15], EK_d[15], iv[15]);
+    }
+
+    /**
+     * Process key and IV.
+     *
+     * @param k  the key
+     * @param iv the IV
+     */
+    private void setKeyAndIV(final byte[] k,
+                             final byte[] iv)
+    {
+        /* Initialise LFSR */
+        setKeyAndIV(LFSR, k, iv);
+
+        /* set F_R1 and F_R2 to zero */
+        F[0] = 0;
+        F[1] = 0;
+        int nCount = 32;
+        while (nCount > 0)
+        {
+            BitReorganization();
+            final int w = F();
+            LFSRWithInitialisationMode(w >>> 1);
+            nCount--;
+        }
+        BitReorganization();
+        F(); /* discard the output of F */
+        LFSRWithWorkMode();
+    }
+
+    /**
+     * Create the next byte keyStream.
+     */
+    private void makeKeyStream()
+    {
+        encode32be(makeKeyStreamWord(), keyStream, 0);
+    }
+
+    /**
+     * Create the next keyStream word.
+     *
+     * @return the next word
+     */
+    protected int makeKeyStreamWord()
+    {
+        if (theIterations++ >= getMaxIterations())
+        {
+            throw new IllegalStateException("Too much data processed by singleKey/IV");
+        }
+        BitReorganization();
+        final int result = F() ^ BRC[3];
+        LFSRWithWorkMode();
+        return result;
+    }
+
+    /**
+     * Create a copy of the engine.
+     *
+     * @return the copy
+     */
+    public Memoable copy()
+    {
+        return new Zuc128CoreEngine(this);
+    }
+
+    /**
+     * Reset from saved engine state.
+     *
+     * @param pState the state to restore
+     */
+    public void reset(final Memoable pState)
+    {
+        final Zuc128CoreEngine e = (Zuc128CoreEngine)pState;
+        System.arraycopy(e.LFSR, 0, LFSR, 0, LFSR.length);
+        System.arraycopy(e.F, 0, F, 0, F.length);
+        System.arraycopy(e.BRC, 0, BRC, 0, BRC.length);
+        System.arraycopy(e.keyStream, 0, keyStream, 0, keyStream.length);
+        theIndex = e.theIndex;
+        theIterations = e.theIterations;
+        theResetState = e;
+    }
+}
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/engines/Zuc256CoreEngine.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/engines/Zuc256CoreEngine.java
new file mode 100644
index 0000000..6c21db9
--- /dev/null
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/engines/Zuc256CoreEngine.java
@@ -0,0 +1,183 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.org.bouncycastle.crypto.engines;
+
+import com.android.org.bouncycastle.util.Memoable;
+
+/**
+ * Zuc256 implementation.
+ * Based on https://www.is.cas.cn/ztzl2016/zouchongzhi/201801/W020180126529970733243.pdf
+ * @hide This class is not part of the Android public SDK API
+ */
+public class Zuc256CoreEngine
+    extends Zuc128CoreEngine
+{
+    /* the constants D */
+    private static final byte[] EK_d = new byte[]{
+        0x22, 0x2f, 0x24, 0x2a, 0x6d, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x52, 0x10, 0x30
+//        0b0100010, 0b0101111, 0b0100100, 0b0101010, 0b1101101, 0b1000000, 0b1000000, 0b1000000,
+//        0b1000000, 0b1000000, 0b1000000, 0b1000000, 0b1000000, 0b1010010, 0b0010000, 0b0110000
+    };
+
+    /* the constants D for 32 bit Mac*/
+    private static final byte[] EK_d32 = new byte[]{
+         0x22, 0x2f, 0x25, 0x2a, 0x6d, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x52, 0x10, 0x30
+//        0b0100010, 0b0101111, 0b0100101, 0b0101010, 0b1101101, 0b1000000, 0b1000000, 0b1000000,
+//        0b1000000, 0b1000000, 0b1000000, 0b1000000, 0b1000000, 0b1010010, 0b0010000, 0b0110000
+    };
+
+    /* the constants D for 64 bit Mac */
+    private static final byte[] EK_d64 = new byte[]{
+        0x23, 0x2f, 0x24, 0x2a, 0x6d, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x52, 0x10, 0x30
+//        0b0100011, 0b0101111, 0b0100100, 0b0101010, 0b1101101, 0b1000000, 0b1000000, 0b1000000,
+//        0b1000000, 0b1000000, 0b1000000, 0b1000000, 0b1000000, 0b1010010, 0b0010000, 0b0110000
+    };
+
+    /* the constants D for 128 bit Mac */
+    private static final byte[] EK_d128 = new byte[]{
+        0x23, 0x2f, 0x25, 0x2a, 0x6d, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x52, 0x10, 0x30
+//        0b0100011, 0b0101111, 0b0100101, 0b0101010, 0b1101101, 0b1000000, 0b1000000, 0b1000000,
+//        0b1000000, 0b1000000, 0b1000000, 0b1000000, 0b1000000, 0b1010010, 0b0010000, 0b0110000
+    };
+
+    /**
+     * The selected D constants.
+     */
+    private byte[] theD;
+
+    /**
+     * Constructor for streamCipher.
+     */
+    protected Zuc256CoreEngine()
+    {
+        theD = EK_d;
+    }
+
+    /**
+     * Constructor for Mac.
+     *
+     * @param pLength the Mac length
+     */
+    protected Zuc256CoreEngine(final int pLength)
+    {
+        switch (pLength)
+        {
+        case 32:
+            theD = EK_d32;
+            break;
+        case 64:
+            theD = EK_d64;
+            break;
+        case 128:
+            theD = EK_d128;
+            break;
+        default:
+            throw new IllegalArgumentException("Unsupported length: " + pLength);
+        }
+    }
+
+    /**
+     * Constructor for Memoable.
+     *
+     * @param pSource the source engine
+     */
+    protected Zuc256CoreEngine(final Zuc256CoreEngine pSource)
+    {
+        super(pSource);
+    }
+
+    /**
+     * Obtain Max iterations.
+     *
+     * @return the maximum iterations
+     */
+    protected int getMaxIterations()
+    {
+        return 625;
+    }
+
+    /**
+     * Obtain Algorithm Name.
+     *
+     * @return the name
+     */
+    public String getAlgorithmName()
+    {
+        return "Zuc-256";
+    }
+
+    /**
+     * Build a 31-bit integer from constituent parts.
+     *
+     * @param a part A
+     * @param b part B
+     * @param c part C
+     * @param d part D
+     * @return the built integer
+     */
+    private static int MAKEU31(byte a, byte b, byte c, byte d)
+    {
+        return (((a & 0xFF) << 23) | ((b & 0xFF) << 16) | ((c & 0xFF) << 8) | (d & 0xFF));
+    }
+
+    /**
+     * Process key and IV into LFSR.
+     *
+     * @param pLFSR the LFSR
+     * @param k     the key
+     * @param iv    the iv
+     */
+    protected void setKeyAndIV(final int[] pLFSR,
+                               final byte[] k,
+                               final byte[] iv)
+    {
+        /* Check lengths */
+        if (k == null || k.length != 32)
+        {
+            throw new IllegalArgumentException("A key of 32 bytes is needed");
+        }
+        if (iv == null || iv.length != 25)
+        {
+            throw new IllegalArgumentException("An IV of 25 bytes is needed");
+        }
+
+        /* expand key and IV */
+        pLFSR[0] = MAKEU31(k[0], theD[0], k[21], k[16]);
+        pLFSR[1] = MAKEU31(k[1], theD[1], k[22], k[17]);
+        pLFSR[2] = MAKEU31(k[2], theD[2], k[23], k[18]);
+        pLFSR[3] = MAKEU31(k[3], theD[3], k[24], k[19]);
+        pLFSR[4] = MAKEU31(k[4], theD[4], k[25], k[20]);
+        pLFSR[5] = MAKEU31(iv[0], (byte)(theD[5] | (iv[17] & 0x3F)), k[5], k[26]);
+        pLFSR[6] = MAKEU31(iv[1], (byte)(theD[6] | (iv[18] & 0x3F)), k[6], k[27]);
+        pLFSR[7] = MAKEU31(iv[10], (byte)(theD[7] | (iv[19] & 0x3F)), k[7], iv[2]);
+        pLFSR[8] = MAKEU31(k[8], (byte)(theD[8] | (iv[20] & 0x3F)), iv[3], iv[11]);
+        pLFSR[9] = MAKEU31(k[9], (byte)(theD[9] | (iv[21] & 0x3F)), iv[12], iv[4]);
+        pLFSR[10] = MAKEU31(iv[5], (byte)(theD[10] | (iv[22] & 0x3F)), k[10], k[28]);
+        pLFSR[11] = MAKEU31(k[11], (byte)(theD[11] | (iv[23] & 0x3F)), iv[6], iv[13]);
+        pLFSR[12] = MAKEU31(k[12], (byte)(theD[12] | (iv[24] & 0x3F)), iv[7], iv[14]);
+        pLFSR[13] = MAKEU31(k[13], theD[13], iv[15], iv[8]);
+        pLFSR[14] = MAKEU31(k[14], (byte)(theD[14] | ((k[31] >>> 4) & 0xF)), iv[16], iv[9]);
+        pLFSR[15] = MAKEU31(k[15], (byte)(theD[15] | (k[31] & 0xF)), k[30], k[29]);
+    }
+
+    /**
+     * Create a copy of the engine.
+     *
+     * @return the copy
+     */
+    public Memoable copy()
+    {
+        return new Zuc256CoreEngine(this);
+    }
+
+    /**
+     * Reset from saved engine state.
+     *
+     * @param pState the state to restore
+     */
+    public void reset(final Memoable pState)
+    {
+        final Zuc256CoreEngine e = (Zuc256CoreEngine)pState;
+        super.reset(pState);
+        theD = e.theD;
+    }
+}
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/engines/Zuc256Engine.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/engines/Zuc256Engine.java
new file mode 100644
index 0000000..0b70c60
--- /dev/null
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/engines/Zuc256Engine.java
@@ -0,0 +1,51 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.org.bouncycastle.crypto.engines;
+
+import com.android.org.bouncycastle.util.Memoable;
+
+/**
+ * Zuc256 implementation.
+ * Based on https://www.is.cas.cn/ztzl2016/zouchongzhi/201801/W020180126529970733243.pdf
+ * @hide This class is not part of the Android public SDK API
+ */
+public final class Zuc256Engine
+    extends Zuc256CoreEngine
+{
+    /**
+     * Constructor for streamCipher.
+     */
+    public Zuc256Engine()
+    {
+        super();
+    }
+
+    /**
+     * Constructor for Mac.
+     *
+     * @param pLength the Mac length
+     */
+    public Zuc256Engine(final int pLength)
+    {
+        super(pLength);
+    }
+
+    /**
+     * Constructor for Memoable.
+     *
+     * @param pSource the source engine
+     */
+    private Zuc256Engine(final Zuc256Engine pSource)
+    {
+        super(pSource);
+    }
+
+    /**
+     * Create a copy of the engine.
+     *
+     * @return the copy
+     */
+    public Memoable copy()
+    {
+        return new Zuc256Engine(this);
+    }
+}
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/generators/DESKeyGenerator.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/generators/DESKeyGenerator.java
index def7dd0..3f593c3 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/generators/DESKeyGenerator.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/generators/DESKeyGenerator.java
@@ -2,7 +2,10 @@
 package com.android.org.bouncycastle.crypto.generators;
 
 import com.android.org.bouncycastle.crypto.CipherKeyGenerator;
+import com.android.org.bouncycastle.crypto.CryptoServicePurpose;
+import com.android.org.bouncycastle.crypto.CryptoServicesRegistrar;
 import com.android.org.bouncycastle.crypto.KeyGenerationParameters;
+import com.android.org.bouncycastle.crypto.constraints.DefaultServiceProperties;
 import com.android.org.bouncycastle.crypto.params.DESParameters;
 
 /**
@@ -33,6 +36,8 @@
                     + (DESParameters.DES_KEY_LENGTH * 8)
                     + " bits long.");
         }
+
+        CryptoServicesRegistrar.checkConstraints(new DefaultServiceProperties("DESKeyGen", 56, null, CryptoServicePurpose.KEYGEN));
     }
 
     public byte[] generateKey()
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/generators/DESedeKeyGenerator.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/generators/DESedeKeyGenerator.java
index a797969..e380848 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/generators/DESedeKeyGenerator.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/generators/DESedeKeyGenerator.java
@@ -1,7 +1,10 @@
 /* GENERATED SOURCE. DO NOT MODIFY. */
 package com.android.org.bouncycastle.crypto.generators;
 
+import com.android.org.bouncycastle.crypto.CryptoServicePurpose;
+import com.android.org.bouncycastle.crypto.CryptoServicesRegistrar;
 import com.android.org.bouncycastle.crypto.KeyGenerationParameters;
+import com.android.org.bouncycastle.crypto.constraints.DefaultServiceProperties;
 import com.android.org.bouncycastle.crypto.params.DESedeParameters;
 
 /**
@@ -43,6 +46,8 @@
                 + (2 * 8 * DESedeParameters.DES_KEY_LENGTH)
                 + " bits long.");
         }
+
+        CryptoServicesRegistrar.checkConstraints(new DefaultServiceProperties("DESedeKeyGen", 112, null, CryptoServicePurpose.KEYGEN));
     }
 
     public byte[] generateKey()
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/generators/DHBasicKeyPairGenerator.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/generators/DHBasicKeyPairGenerator.java
index 05d32d7..f47d753 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/generators/DHBasicKeyPairGenerator.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/generators/DHBasicKeyPairGenerator.java
@@ -1,16 +1,20 @@
 /* GENERATED SOURCE. DO NOT MODIFY. */
 package com.android.org.bouncycastle.crypto.generators;
 
+import java.math.BigInteger;
+
 import com.android.org.bouncycastle.crypto.AsymmetricCipherKeyPair;
 import com.android.org.bouncycastle.crypto.AsymmetricCipherKeyPairGenerator;
+import com.android.org.bouncycastle.crypto.CryptoServicePurpose;
+import com.android.org.bouncycastle.crypto.CryptoServicesRegistrar;
 import com.android.org.bouncycastle.crypto.KeyGenerationParameters;
+import com.android.org.bouncycastle.crypto.constraints.ConstraintUtils;
+import com.android.org.bouncycastle.crypto.constraints.DefaultServiceProperties;
 import com.android.org.bouncycastle.crypto.params.DHKeyGenerationParameters;
 import com.android.org.bouncycastle.crypto.params.DHParameters;
 import com.android.org.bouncycastle.crypto.params.DHPrivateKeyParameters;
 import com.android.org.bouncycastle.crypto.params.DHPublicKeyParameters;
 
-import java.math.BigInteger;
-
 /**
  * a basic Diffie-Hellman key pair generator.
  *
@@ -27,6 +31,8 @@
         KeyGenerationParameters param)
     {
         this.param = (DHKeyGenerationParameters)param;
+
+        CryptoServicesRegistrar.checkConstraints(new DefaultServiceProperties("DHBasicKeyGen", ConstraintUtils.bitsOfSecurityFor(this.param.getParameters().getP()), this.param.getParameters(), CryptoServicePurpose.KEYGEN));
     }
 
     public AsymmetricCipherKeyPair generateKeyPair()
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/generators/DSAKeyPairGenerator.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/generators/DSAKeyPairGenerator.java
index ec8945d..b749f39 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/generators/DSAKeyPairGenerator.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/generators/DSAKeyPairGenerator.java
@@ -6,7 +6,11 @@
 
 import com.android.org.bouncycastle.crypto.AsymmetricCipherKeyPair;
 import com.android.org.bouncycastle.crypto.AsymmetricCipherKeyPairGenerator;
+import com.android.org.bouncycastle.crypto.CryptoServicePurpose;
+import com.android.org.bouncycastle.crypto.CryptoServicesRegistrar;
 import com.android.org.bouncycastle.crypto.KeyGenerationParameters;
+import com.android.org.bouncycastle.crypto.constraints.ConstraintUtils;
+import com.android.org.bouncycastle.crypto.constraints.DefaultServiceProperties;
 import com.android.org.bouncycastle.crypto.params.DSAKeyGenerationParameters;
 import com.android.org.bouncycastle.crypto.params.DSAParameters;
 import com.android.org.bouncycastle.crypto.params.DSAPrivateKeyParameters;
@@ -32,6 +36,8 @@
         KeyGenerationParameters param)
     {
         this.param = (DSAKeyGenerationParameters)param;
+
+        CryptoServicesRegistrar.checkConstraints(new DefaultServiceProperties("DSAKeyGen", ConstraintUtils.bitsOfSecurityFor(this.param.getParameters().getP()), this.param.getParameters(), CryptoServicePurpose.KEYGEN));
     }
 
     public AsymmetricCipherKeyPair generateKeyPair()
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/generators/ECKeyPairGenerator.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/generators/ECKeyPairGenerator.java
index 6779b85..ac1daaa 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/generators/ECKeyPairGenerator.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/generators/ECKeyPairGenerator.java
@@ -6,7 +6,11 @@
 
 import com.android.org.bouncycastle.crypto.AsymmetricCipherKeyPair;
 import com.android.org.bouncycastle.crypto.AsymmetricCipherKeyPairGenerator;
+import com.android.org.bouncycastle.crypto.CryptoServicePurpose;
+import com.android.org.bouncycastle.crypto.CryptoServicesRegistrar;
 import com.android.org.bouncycastle.crypto.KeyGenerationParameters;
+import com.android.org.bouncycastle.crypto.constraints.ConstraintUtils;
+import com.android.org.bouncycastle.crypto.constraints.DefaultServiceProperties;
 import com.android.org.bouncycastle.crypto.params.ECDomainParameters;
 import com.android.org.bouncycastle.crypto.params.ECKeyGenerationParameters;
 import com.android.org.bouncycastle.crypto.params.ECPrivateKeyParameters;
@@ -24,9 +28,20 @@
 public class ECKeyPairGenerator
     implements AsymmetricCipherKeyPairGenerator, ECConstants
 {
+    private final String name;
     ECDomainParameters  params;
     SecureRandom        random;
 
+    public ECKeyPairGenerator()
+    {
+        this("ECKeyGen");
+    }
+
+    protected ECKeyPairGenerator(String name)
+    {
+        this.name = name;
+    }
+
     public void init(
         KeyGenerationParameters param)
     {
@@ -34,6 +49,8 @@
 
         this.random = ecP.getRandom();
         this.params = ecP.getDomainParameters();
+
+        CryptoServicesRegistrar.checkConstraints(new DefaultServiceProperties(name, ConstraintUtils.bitsOfSecurityFor(this.params.getCurve()), ecP.getDomainParameters(), CryptoServicePurpose.KEYGEN));
     }
 
     /**
@@ -51,7 +68,7 @@
         {
             d = BigIntegers.createRandomBigInteger(nBitLength, random);
 
-            if (d.compareTo(ONE) < 0  || (d.compareTo(n) >= 0))
+            if (isOutOfRangeD(d, n))
             {
                 continue;
             }
@@ -71,6 +88,11 @@
             new ECPrivateKeyParameters(d, params));
     }
 
+    protected boolean isOutOfRangeD(BigInteger d, BigInteger n)
+    {
+        return d.compareTo(ONE) < 0 || (d.compareTo(n) >= 0);
+    }
+
     protected ECMultiplier createBasePointMultiplier()
     {
         return new FixedPointCombMultiplier();
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/generators/OpenSSLPBEParametersGenerator.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/generators/OpenSSLPBEParametersGenerator.java
index 116aa7d..bfbc415 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/generators/OpenSSLPBEParametersGenerator.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/generators/OpenSSLPBEParametersGenerator.java
@@ -13,8 +13,9 @@
 /**
  * Generator for PBE derived keys and ivs as usd by OpenSSL.
  * <p>
- * The scheme is a simple extension of PKCS 5 V2.0 Scheme 1 using MD5 with an
- * iteration count of 1.
+ * Originally this scheme was a simple extension of PKCS 5 V2.0 Scheme 1 using MD5 with an
+ * iteration count of 1. The default digest was changed to SHA-256 with OpenSSL 1.1.0. This
+ * implementation still defaults to MD5, but the digest can now be set.
  * <p>
  * @hide This class is not part of the Android public SDK API
  */
@@ -26,7 +27,7 @@
     private Digest  digest = AndroidDigestFactory.getMD5();
 
     /**
-     * Construct a OpenSSL Parameters generator. 
+     * Construct a OpenSSL Parameters generator.
      */
     public OpenSSLPBEParametersGenerator()
     {
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/generators/RSAKeyPairGenerator.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/generators/RSAKeyPairGenerator.java
index fa7aea2..2a41500 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/generators/RSAKeyPairGenerator.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/generators/RSAKeyPairGenerator.java
@@ -5,7 +5,11 @@
 
 import com.android.org.bouncycastle.crypto.AsymmetricCipherKeyPair;
 import com.android.org.bouncycastle.crypto.AsymmetricCipherKeyPairGenerator;
+import com.android.org.bouncycastle.crypto.CryptoServicePurpose;
+import com.android.org.bouncycastle.crypto.CryptoServicesRegistrar;
 import com.android.org.bouncycastle.crypto.KeyGenerationParameters;
+import com.android.org.bouncycastle.crypto.constraints.ConstraintUtils;
+import com.android.org.bouncycastle.crypto.constraints.DefaultServiceProperties;
 import com.android.org.bouncycastle.crypto.params.RSAKeyGenerationParameters;
 import com.android.org.bouncycastle.crypto.params.RSAKeyParameters;
 import com.android.org.bouncycastle.crypto.params.RSAPrivateCrtKeyParameters;
@@ -27,6 +31,8 @@
     public void init(KeyGenerationParameters param)
     {
         this.param = (RSAKeyGenerationParameters)param;
+
+        CryptoServicesRegistrar.checkConstraints(new DefaultServiceProperties("RSAKeyGen", ConstraintUtils.bitsOfSecurityForFF(param.getStrength()), null, CryptoServicePurpose.KEYGEN));
     }
 
     public AsymmetricCipherKeyPair generateKeyPair()
@@ -93,12 +99,12 @@
                     continue;
                 }
 
-	            /*
+                /*
                  * Require a minimum weight of the NAF representation, since low-weight composites may
-	             * be weak against a version of the number-field-sieve for factoring.
-	             *
-	             * See "The number field sieve for integers of low weight", Oliver Schirokauer.
-	             */
+                 * be weak against a version of the number-field-sieve for factoring.
+                 *
+                 * See "The number field sieve for integers of low weight", Oliver Schirokauer.
+                 */
                 if (WNafUtil.getNafWeight(n) < minWeight)
                 {
                     p = chooseRandomPrime(pbitlength, e, squaredBound);
@@ -144,8 +150,8 @@
             qInv = BigIntegers.modOddInverse(p, q);
 
             result = new AsymmetricCipherKeyPair(
-                new RSAKeyParameters(false, n, e),
-                new RSAPrivateCrtKeyParameters(n, e, d, p, q, dP, dQ, qInv));
+                new RSAKeyParameters(false, n, e, true),
+                new RSAPrivateCrtKeyParameters(n, e, d, p, q, dP, dQ, qInv, true));
         }
 
         return result;
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/generators/SM2KeyPairGenerator.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/generators/SM2KeyPairGenerator.java
new file mode 100644
index 0000000..d38240f
--- /dev/null
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/generators/SM2KeyPairGenerator.java
@@ -0,0 +1,40 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.org.bouncycastle.crypto.generators;
+
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
+import com.android.org.bouncycastle.crypto.AsymmetricCipherKeyPair;
+import com.android.org.bouncycastle.crypto.AsymmetricCipherKeyPairGenerator;
+import com.android.org.bouncycastle.crypto.CryptoServicePurpose;
+import com.android.org.bouncycastle.crypto.CryptoServicesRegistrar;
+import com.android.org.bouncycastle.crypto.KeyGenerationParameters;
+import com.android.org.bouncycastle.crypto.constraints.ConstraintUtils;
+import com.android.org.bouncycastle.crypto.constraints.DefaultServiceProperties;
+import com.android.org.bouncycastle.crypto.params.ECDomainParameters;
+import com.android.org.bouncycastle.crypto.params.ECKeyGenerationParameters;
+import com.android.org.bouncycastle.crypto.params.ECPrivateKeyParameters;
+import com.android.org.bouncycastle.crypto.params.ECPublicKeyParameters;
+import com.android.org.bouncycastle.math.ec.ECConstants;
+import com.android.org.bouncycastle.math.ec.ECMultiplier;
+import com.android.org.bouncycastle.math.ec.ECPoint;
+import com.android.org.bouncycastle.math.ec.FixedPointCombMultiplier;
+import com.android.org.bouncycastle.math.ec.WNafUtil;
+import com.android.org.bouncycastle.util.BigIntegers;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+public class SM2KeyPairGenerator
+    extends ECKeyPairGenerator
+{
+    public SM2KeyPairGenerator()
+    {
+        super("SM2KeyGen");
+    }
+
+    protected boolean isOutOfRangeD(BigInteger d, BigInteger n)
+    {
+        return d.compareTo(ONE) < 0 || (d.compareTo(n.subtract(BigIntegers.ONE)) >= 0);
+    }
+}
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/macs/CBCBlockCipherMac.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/macs/CBCBlockCipherMac.java
index 8569ea0..0e95b03 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/macs/CBCBlockCipherMac.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/macs/CBCBlockCipherMac.java
@@ -94,7 +94,7 @@
             throw new IllegalArgumentException("MAC size must be multiple of 8");
         }
 
-        this.cipher = new CBCBlockCipher(cipher);
+        this.cipher = CBCBlockCipher.newInstance(cipher);
         this.padding = padding;
         this.macSize = macSizeInBits / 8;
 
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/macs/HMac.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/macs/HMac.java
index a77a0e8..00a23c3 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/macs/HMac.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/macs/HMac.java
@@ -218,15 +218,15 @@
      */
     public void reset()
     {
-        /*
-         * reset the underlying digest.
-         */
-        digest.reset();
-
-        /*
-         * reinitialize the digest.
-         */
-        digest.update(inputPad, 0, inputPad.length);
+        if (ipadState != null)
+        {
+            ((Memoable)digest).reset(ipadState);
+        }
+        else
+        {
+            digest.reset();
+            digest.update(inputPad, 0, inputPad.length);
+        }
     }
 
     private static void xorPad(byte[] pad, int len, byte n)
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/macs/Zuc128Mac.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/macs/Zuc128Mac.java
new file mode 100644
index 0000000..4a886c7
--- /dev/null
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/macs/Zuc128Mac.java
@@ -0,0 +1,247 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.org.bouncycastle.crypto.macs;
+
+import com.android.org.bouncycastle.crypto.CipherParameters;
+import com.android.org.bouncycastle.crypto.Mac;
+import com.android.org.bouncycastle.crypto.engines.Zuc128CoreEngine;
+
+/**
+ * Zuc128 Mac implementation.
+ * Based on https://www.qtc.jp/3GPP/Specs/eea3eia3specificationv16.pdf
+ * @hide This class is not part of the Android public SDK API
+ */
+public final class Zuc128Mac
+    implements Mac
+{
+    /**
+     * The Maximum Bit Mask.
+     */
+    private static final int TOPBIT = 0x80;
+
+    /**
+     * The Zuc128 Engine.
+     */
+    private final InternalZuc128Engine theEngine;
+
+    /**
+     * The calculated Mac in words.
+     */
+    private int theMac;
+
+    /**
+     * The active keyStream.
+     */
+    private final int[] theKeyStream;
+
+    /**
+     * The initialised state.
+     */
+    private Zuc128CoreEngine theState;
+
+    /**
+     * The current word index.
+     */
+    private int theWordIndex;
+
+    /**
+     * The current byte index.
+     */
+    private int theByteIndex;
+
+    /**
+     * Constructor.
+     */
+    public Zuc128Mac()
+    {
+        theEngine = new InternalZuc128Engine();
+        theKeyStream = new int[2];
+    }
+
+    /**
+     * Obtain Algorithm Name.
+     *
+     * @return the name
+     */
+    public String getAlgorithmName()
+    {
+        return "Zuc128Mac";
+    }
+
+    /**
+     * Obtain Mac Size.
+     *
+     * @return the size in Bytes
+     */
+    public int getMacSize()
+    {
+        return 4; // Integer.Bytes
+    }
+
+    /**
+     * Initialise the Mac.
+     *
+     * @param pParams the parameters
+     */
+    public void init(final CipherParameters pParams)
+    {
+        /* Initialise the engine */
+        theEngine.init(true, pParams);
+        theState = (Zuc128CoreEngine)theEngine.copy();
+        initKeyStream();
+    }
+
+    /**
+     * Initialise the keyStream.
+     */
+    private void initKeyStream()
+    {
+        /* Initialise the Mac */
+        theMac = 0;
+
+        /* Initialise the KeyStream */
+        for (int i = 0; i < theKeyStream.length - 1; i++)
+        {
+            theKeyStream[i] = theEngine.createKeyStreamWord();
+        }
+        theWordIndex = theKeyStream.length - 1;
+        theByteIndex = 3; //Integer.BYTES - 1;
+    }
+
+    /**
+     * Update the mac with a single byte.
+     *
+     * @param in the byte to update with
+     */
+    public void update(final byte in)
+    {
+        /* shift for next byte */
+        shift4NextByte();
+
+        /* Loop through the bits */
+        final int bitBase = theByteIndex * 8; //Byte.SIZE;
+        for (int bitMask = TOPBIT, bitNo = 0; bitMask > 0; bitMask >>= 1, bitNo++)
+        {
+            /* If the bit is set */
+            if ((in & bitMask) != 0)
+            {
+                /* update theMac */
+                updateMac(bitBase + bitNo);
+            }
+        }
+    }
+
+    /**
+     * Shift for next byte.
+     */
+    private void shift4NextByte()
+    {
+        /* Adjust the byte index */
+        theByteIndex = (theByteIndex + 1) % 4; //Integer.BYTES;
+
+        /* Adjust keyStream if required */
+        if (theByteIndex == 0)
+        {
+            theKeyStream[theWordIndex] = theEngine.createKeyStreamWord();
+            theWordIndex = (theWordIndex + 1) % theKeyStream.length;
+        }
+    }
+
+    /**
+     * Update the Mac.
+     *
+     * @param bitNo the bit number
+     */
+    private void updateMac(final int bitNo)
+    {
+        /* Update the Mac */
+        theMac ^= getKeyStreamWord(bitNo);
+    }
+
+    /**
+     * Obtain the keyStreamWord.
+     *
+     * @param bitNo the bitNumber
+     * @return the word
+     */
+    private int getKeyStreamWord(final int bitNo)
+    {
+        /* Access the first word and return it if this is bit 0 */
+        final int myFirst = theKeyStream[theWordIndex];
+        if (bitNo == 0)
+        {
+            return myFirst;
+        }
+
+        /* Access the second word */
+        final int mySecond = theKeyStream[(theWordIndex + 1) % theKeyStream.length];
+        return (myFirst << bitNo) | (mySecond >>> (32 - bitNo)); // Integer.SIZE - bitNo
+    }
+
+    /**
+     * Update the mac.
+     *
+     * @param in    the input buffer
+     * @param inOff the starting offset in the input buffer
+     * @param len   the length of data to process
+     */
+    public void update(final byte[] in, final int inOff, final int len)
+    {
+        for (int byteNo = 0; byteNo < len; byteNo++)
+        {
+            update(in[inOff + byteNo]);
+        }
+    }
+
+    /**
+     * Obtain the final word.
+     *
+     * @return the final word
+     */
+    private int getFinalWord()
+    {
+        if (theByteIndex != 0)
+        {
+            return theEngine.createKeyStreamWord();
+        }
+        theWordIndex = (theWordIndex + 1) % theKeyStream.length;
+        return theKeyStream[theWordIndex];
+    }
+
+    /**
+     * Finalize the mac.
+     *
+     * @param out    the output buffer
+     * @param outOff the starting offset in the input buffer
+     * @return the size of the mac
+     */
+    public int doFinal(final byte[] out, final int outOff)
+    {
+        /* Finish the Mac and output it */
+        shift4NextByte();
+        theMac ^= getKeyStreamWord(theByteIndex * 8); //Byte.SIZE
+        theMac ^= getFinalWord();
+        Zuc128CoreEngine.encode32be(theMac, out, outOff);
+
+        /* Reset the Mac */
+        reset();
+        return getMacSize();
+    }
+
+    public void reset()
+    {
+        if (theState != null)
+        {
+            theEngine.reset(theState);
+        }
+        initKeyStream();
+    }
+
+    private static class InternalZuc128Engine
+        extends Zuc128CoreEngine
+    {
+        int createKeyStreamWord()
+        {
+            return super.makeKeyStreamWord();
+        }
+    }
+}
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/macs/Zuc256Mac.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/macs/Zuc256Mac.java
new file mode 100644
index 0000000..c162db9
--- /dev/null
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/macs/Zuc256Mac.java
@@ -0,0 +1,276 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.org.bouncycastle.crypto.macs;
+
+import com.android.org.bouncycastle.crypto.CipherParameters;
+import com.android.org.bouncycastle.crypto.Mac;
+import com.android.org.bouncycastle.crypto.engines.Zuc256CoreEngine;
+
+/**
+ * Zuc256 Mac implementation.
+ * Based on https://www.is.cas.cn/ztzl2016/zouchongzhi/201801/W020180126529970733243.pdf
+ * @hide This class is not part of the Android public SDK API
+ */
+public final class Zuc256Mac
+    implements Mac
+{
+    /**
+     * The Maximum Bit Mask.
+     */
+    private static final int TOPBIT = 0x80;
+
+    /**
+     * The Zuc256 Engine.
+     */
+    private final InternalZuc256Engine theEngine;
+
+    /**
+     * The mac length.
+     */
+    private final int theMacLength;
+
+    /**
+     * The calculated Mac in words.
+     */
+    private final int[] theMac;
+
+    /**
+     * The active keyStream.
+     */
+    private final int[] theKeyStream;
+
+    /**
+     * The initialised state.
+     */
+    private Zuc256CoreEngine theState;
+
+    /**
+     * The current word index.
+     */
+    private int theWordIndex;
+
+    /**
+     * The current byte index.
+     */
+    private int theByteIndex;
+
+    /**
+     * Constructor.
+     *
+     * @param pLength the bit length of the Mac
+     */
+    public Zuc256Mac(final int pLength)
+    {
+        theEngine = new InternalZuc256Engine(pLength);
+        theMacLength = pLength;
+        final int numWords = pLength / 32; // Integer.SIZE
+        theMac = new int[numWords];
+        theKeyStream = new int[numWords + 1];
+    }
+
+    /**
+     * Obtain Algorithm Name.
+     *
+     * @return the name
+     */
+    public String getAlgorithmName()
+    {
+        return "Zuc256Mac-" + theMacLength;
+    }
+
+    /**
+     * Obtain Mac Size.
+     *
+     * @return the size in Bytes
+     */
+    public int getMacSize()
+    {
+        return theMacLength / 8; //Byte.SIZE
+    }
+
+    /**
+     * Initialise the Mac.
+     *
+     * @param pParams the parameters
+     */
+    public void init(final CipherParameters pParams)
+    {
+        /* Initialise the engine */
+        theEngine.init(true, pParams);
+        theState = (Zuc256CoreEngine)theEngine.copy();
+        initKeyStream();
+    }
+
+    /**
+     * Initialise the keyStream.
+     */
+    private void initKeyStream()
+    {
+        /* Initialise the Mac */
+        for (int i = 0; i < theMac.length; i++)
+        {
+            theMac[i] = theEngine.createKeyStreamWord();
+        }
+
+        /* Initialise the KeyStream */
+        for (int i = 0; i < theKeyStream.length - 1; i++)
+        {
+            theKeyStream[i] = theEngine.createKeyStreamWord();
+        }
+        theWordIndex = theKeyStream.length - 1;
+        theByteIndex = 4 - 1;    // Integer.SIZE
+    }
+
+    /**
+     * Update the mac with a single byte.
+     *
+     * @param in the byte to update with
+     */
+    public void update(final byte in)
+    {
+        /* shift for next byte */
+        shift4NextByte();
+
+        /* Loop through the bits */
+        final int bitBase = theByteIndex * 8; //Byte.SIZE;
+        for (int bitMask = TOPBIT, bitNo = 0; bitMask > 0; bitMask >>= 1, bitNo++)
+        {
+            /* If the bit is set */
+            if ((in & bitMask) != 0)
+            {
+                /* update theMac */
+                updateMac(bitBase + bitNo);
+            }
+        }
+    }
+
+    /**
+     * Shift for next byte.
+     */
+    private void shift4NextByte()
+    {
+        /* Adjust the byte index */
+        theByteIndex = (theByteIndex + 1) % 4; //Integer.BYTES
+
+        /* Adjust keyStream if required */
+        if (theByteIndex == 0)
+        {
+            theKeyStream[theWordIndex] = theEngine.createKeyStreamWord();
+            theWordIndex = (theWordIndex + 1) % theKeyStream.length;
+        }
+    }
+
+    /**
+     * Shift for final update.
+     */
+    private void shift4Final()
+    {
+        /* Adjust the byte index */
+        theByteIndex = (theByteIndex + 1) % 4; //Integer.BYTES
+
+        /* No need to read another word to the keyStream */
+        if (theByteIndex == 0)
+        {
+            theWordIndex = (theWordIndex + 1) % theKeyStream.length;
+        }
+    }
+
+    /**
+     * Update the Mac.
+     *
+     * @param bitNo the bit number
+     */
+    private void updateMac(final int bitNo)
+    {
+        /* Loop through the Mac */
+        for (int wordNo = 0; wordNo < theMac.length; wordNo++)
+        {
+            theMac[wordNo] ^= getKeyStreamWord(wordNo, bitNo);
+        }
+    }
+
+    /**
+     * Obtain the keyStreamWord.
+     *
+     * @param wordNo the wordNumber
+     * @param bitNo  the bitNumber
+     * @return the word
+     */
+    private int getKeyStreamWord(final int wordNo, final int bitNo)
+    {
+        /* Access the first word and return it if this is bit 0 */
+        final int myFirst = theKeyStream[(theWordIndex + wordNo) % theKeyStream.length];
+        if (bitNo == 0)
+        {
+            return myFirst;
+        }
+
+        /* Access the second word */
+        final int mySecond = theKeyStream[(theWordIndex + wordNo + 1) % theKeyStream.length];
+        return (myFirst << bitNo) | (mySecond >>> (32 - bitNo)); //Integer.SIZE - bitNo
+    }
+
+    /**
+     * Update the mac.
+     *
+     * @param in    the input buffer
+     * @param inOff the starting offset in the input buffer
+     * @param len   the length of data to process
+     */
+    public void update(final byte[] in, final int inOff, final int len)
+    {
+        for (int byteNo = 0; byteNo < len; byteNo++)
+        {
+            update(in[inOff + byteNo]);
+        }
+    }
+
+    /**
+     * Finalize the mac.
+     *
+     * @param out    the output buffer
+     * @param outOff the starting offset in the output buffer
+     * @return the size of the mac
+     */
+    public int doFinal(final byte[] out, final int outOff)
+    {
+        /* shift for final update */
+        shift4Final();
+
+        /* Finish the Mac and output it */
+        updateMac(theByteIndex * 8); //Byte.SIZE)
+        for (int i = 0; i < theMac.length; i++)
+        {
+            Zuc256CoreEngine.encode32be(theMac[i], out, outOff + i * 4); //Integer.BYTES)
+        }
+
+        /* Reset the Mac */
+        reset();
+        return getMacSize();
+    }
+
+    /**
+     * Reset the Mac.
+     */
+    public void reset()
+    {
+        if (theState != null)
+        {
+            theEngine.reset(theState);
+        }
+        initKeyStream();
+    }
+
+    private static class InternalZuc256Engine
+        extends Zuc256CoreEngine
+    {
+        public InternalZuc256Engine(int pLength)
+        {
+            super(pLength);
+        }
+
+        int createKeyStreamWord()
+        {
+            return super.makeKeyStreamWord();
+        }
+    }
+}
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/modes/CBCBlockCipher.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/modes/CBCBlockCipher.java
index 765d4c0..1282c26 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/modes/CBCBlockCipher.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/modes/CBCBlockCipher.java
@@ -4,6 +4,7 @@
 import com.android.org.bouncycastle.crypto.BlockCipher;
 import com.android.org.bouncycastle.crypto.CipherParameters;
 import com.android.org.bouncycastle.crypto.DataLengthException;
+import com.android.org.bouncycastle.crypto.DefaultMultiBlockCipher;
 import com.android.org.bouncycastle.crypto.params.ParametersWithIV;
 import com.android.org.bouncycastle.util.Arrays;
 
@@ -12,7 +13,8 @@
  * @hide This class is not part of the Android public SDK API
  */
 public class CBCBlockCipher
-    implements BlockCipher
+    extends DefaultMultiBlockCipher
+    implements CBCModeCipher
 {
     private byte[]          IV;
     private byte[]          cbcV;
@@ -23,9 +25,20 @@
     private boolean         encrypting;
 
     /**
+     * Return a new CBC mode cipher based on the passed in base cipher
+     *
+     * @param cipher the base cipher for the CBC mode.
+     */
+    public static CBCModeCipher newInstance(BlockCipher cipher)
+    {
+        return new CBCBlockCipher(cipher);
+    }
+
+    /**
      * Basic constructor.
      *
      * @param cipher the block cipher to be used as the basis of chaining.
+     * @deprecated use the CBCBlockCipher.newInstance() static method.
      */
     public CBCBlockCipher(
         BlockCipher cipher)
@@ -79,31 +92,23 @@
 
             System.arraycopy(iv, 0, IV, 0, iv.length);
 
-            reset();
-
-            // if null it's an IV changed only.
-            if (ivParam.getParameters() != null)
-            {
-                cipher.init(encrypting, ivParam.getParameters());
-            }
-            else if (oldEncrypting != encrypting)
-            {
-                throw new IllegalArgumentException("cannot change encrypting state without providing key.");
-            }
+            params = ivParam.getParameters();
         }
         else
         {
-            reset();
+            Arrays.fill(IV, (byte)0);
+        }
 
-            // if it's null, key is to be reused.
-            if (params != null)
-            {
-                cipher.init(encrypting, params);
-            }
-            else if (oldEncrypting != encrypting)
-            {
-                throw new IllegalArgumentException("cannot change encrypting state without providing key.");
-            }
+        reset();
+
+        // if null it's an IV changed only (key is to be reused).
+        if (params != null)
+        {
+            cipher.init(encrypting, params);
+        }
+        else if (oldEncrypting != encrypting)
+        {
+            throw new IllegalArgumentException("cannot change encrypting state without providing key.");
         }
     }
 
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/modes/CBCModeCipher.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/modes/CBCModeCipher.java
new file mode 100644
index 0000000..2bac162
--- /dev/null
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/modes/CBCModeCipher.java
@@ -0,0 +1,19 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.org.bouncycastle.crypto.modes;
+
+import com.android.org.bouncycastle.crypto.BlockCipher;
+import com.android.org.bouncycastle.crypto.MultiBlockCipher;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+public interface CBCModeCipher
+    extends MultiBlockCipher
+{
+    /**
+     * return the underlying block cipher that we are wrapping.
+     *
+     * @return the underlying block cipher that we are wrapping.
+     */
+    BlockCipher getUnderlyingCipher();
+}
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/modes/CCMBlockCipher.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/modes/CCMBlockCipher.java
index 7bd69d2..1be6a3c 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/modes/CCMBlockCipher.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/modes/CCMBlockCipher.java
@@ -22,7 +22,7 @@
  * @hide This class is not part of the Android public SDK API
  */
 public class CCMBlockCipher
-    implements AEADBlockCipher
+    implements CCMModeCipher
 {
     private BlockCipher           cipher;
     private int                   blockSize;
@@ -36,9 +36,20 @@
     private ExposedByteArrayOutputStream data = new ExposedByteArrayOutputStream();
 
     /**
+     * Return a new CCM mode cipher based on the passed in base cipher
+     *
+     * @param cipher the base cipher for the CCM mode.
+     */
+    public static CCMModeCipher newInstance(BlockCipher cipher)
+    {
+        return new CCMBlockCipher(cipher);
+    }
+
+    /**
      * Basic constructor.
      *
      * @param c the block cipher to be used.
+     * @deprecated use the CCMBlockCipher.newInstance() static method.
      */
     public CCMBlockCipher(BlockCipher c)
     {
@@ -262,7 +273,7 @@
         iv[0] = (byte)((q - 1) & 0x7);
         System.arraycopy(nonce, 0, iv, 1, nonce.length);
 
-        BlockCipher ctrCipher = new SICBlockCipher(cipher);
+        BlockCipher ctrCipher = SICBlockCipher.newInstance(cipher);
         ctrCipher.init(forEncryption, new ParametersWithIV(keyParam, iv));
 
         int outputLen;
@@ -456,7 +467,7 @@
         return getAssociatedTextLength() > 0;
     }
 
-    private class ExposedByteArrayOutputStream
+    private static class ExposedByteArrayOutputStream
         extends ByteArrayOutputStream
     {
         public ExposedByteArrayOutputStream()
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/modes/CCMModeCipher.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/modes/CCMModeCipher.java
new file mode 100644
index 0000000..fdcf5f1
--- /dev/null
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/modes/CCMModeCipher.java
@@ -0,0 +1,10 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.org.bouncycastle.crypto.modes;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+public interface CCMModeCipher
+    extends AEADBlockCipher
+{
+}
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/modes/CFBBlockCipher.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/modes/CFBBlockCipher.java
index 341ef89..ca84869 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/modes/CFBBlockCipher.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/modes/CFBBlockCipher.java
@@ -14,6 +14,7 @@
  */
 public class CFBBlockCipher
     extends StreamBlockCipher
+    implements CFBModeCipher
 {
     private byte[]          IV;
     private byte[]          cfbV;
@@ -26,11 +27,23 @@
     private int             byteCount;
 
     /**
+     * Return a new CFB mode cipher based on the passed in base cipher
+     *
+     * @param cipher the base cipher for the CFB mode.
+     * @param blockSize the block size (in bits) used for the CFB mode.
+     */
+    public static CFBModeCipher newInstance(BlockCipher cipher, int blockSize)
+    {
+        return new CFBBlockCipher(cipher, blockSize);
+    }
+
+    /**
      * Basic constructor.
      *
      * @param cipher the block cipher to be used as the basis of the
      * feedback mode.
      * @param bitBlockSize the block size in bits (note: a multiple of 8)
+     * @deprecated use the equivalent CFBBlockCipher.newInstance() static method.
      */
     public CFBBlockCipher(
         BlockCipher cipher,
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/modes/CFBModeCipher.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/modes/CFBModeCipher.java
new file mode 100644
index 0000000..31bf193
--- /dev/null
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/modes/CFBModeCipher.java
@@ -0,0 +1,13 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.org.bouncycastle.crypto.modes;
+
+import com.android.org.bouncycastle.crypto.MultiBlockCipher;
+import com.android.org.bouncycastle.crypto.StreamCipher;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+public interface CFBModeCipher
+    extends MultiBlockCipher, StreamCipher
+{
+}
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/modes/CTRModeCipher.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/modes/CTRModeCipher.java
new file mode 100644
index 0000000..5281554
--- /dev/null
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/modes/CTRModeCipher.java
@@ -0,0 +1,20 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.org.bouncycastle.crypto.modes;
+
+import com.android.org.bouncycastle.crypto.BlockCipher;
+import com.android.org.bouncycastle.crypto.MultiBlockCipher;
+import com.android.org.bouncycastle.crypto.SkippingStreamCipher;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+public interface CTRModeCipher
+    extends MultiBlockCipher, SkippingStreamCipher
+{
+    /**
+     * return the underlying block cipher that we are wrapping.
+     *
+     * @return the underlying block cipher that we are wrapping.
+     */
+    BlockCipher getUnderlyingCipher();
+}
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/modes/CTSBlockCipher.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/modes/CTSBlockCipher.java
index 8233865..87dd1e0 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/modes/CTSBlockCipher.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/modes/CTSBlockCipher.java
@@ -2,8 +2,8 @@
 package com.android.org.bouncycastle.crypto.modes;
 
 import com.android.org.bouncycastle.crypto.BlockCipher;
-import com.android.org.bouncycastle.crypto.BufferedBlockCipher;
 import com.android.org.bouncycastle.crypto.DataLengthException;
+import com.android.org.bouncycastle.crypto.DefaultBufferedBlockCipher;
 import com.android.org.bouncycastle.crypto.InvalidCipherTextException;
 import com.android.org.bouncycastle.crypto.OutputLengthException;
 import com.android.org.bouncycastle.crypto.StreamBlockCipher;
@@ -14,7 +14,7 @@
  * @hide This class is not part of the Android public SDK API
  */
 public class CTSBlockCipher
-    extends BufferedBlockCipher
+    extends DefaultBufferedBlockCipher
 {
     private int     blockSize;
 
@@ -223,9 +223,9 @@
                     buf[i] ^= block[i - blockSize];
                 }
 
-                if (cipher instanceof CBCBlockCipher)
+                if (cipher instanceof CBCModeCipher)
                 {
-                    BlockCipher c = ((CBCBlockCipher)cipher).getUnderlyingCipher();
+                    BlockCipher c = ((CBCModeCipher)cipher).getUnderlyingCipher();
 
                     c.processBlock(buf, blockSize, out, outOff);
                 }
@@ -252,9 +252,9 @@
 
             if (bufOff > blockSize)
             {
-                if (cipher instanceof CBCBlockCipher)
+                if (cipher instanceof CBCModeCipher)
                 {
-                    BlockCipher c = ((CBCBlockCipher)cipher).getUnderlyingCipher();
+                    BlockCipher c = ((CBCModeCipher)cipher).getUnderlyingCipher();
 
                     c.processBlock(buf, 0, block, 0);
                 }
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/modes/GCMBlockCipher.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/modes/GCMBlockCipher.java
index 398d0f5..a549455 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/modes/GCMBlockCipher.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/modes/GCMBlockCipher.java
@@ -23,7 +23,7 @@
  * @hide This class is not part of the Android public SDK API
  */
 public class GCMBlockCipher
-    implements AEADBlockCipher
+    implements GCMModeCipher
 {
     private static final int BLOCK_SIZE = 16;
     // BEGIN Android-added: Max input size limitation from NIST.
@@ -60,11 +60,45 @@
     private long        atLength;
     private long        atLengthPre;
 
+    /**
+     * Return a new GCM mode cipher based on the passed in base cipher
+     *
+     * @param cipher the base cipher for the GCM mode.
+     */
+    public static GCMModeCipher newInstance(BlockCipher cipher)
+    {
+        return new GCMBlockCipher(cipher);
+    }
+
+    /**
+     * Return a new GCM mode cipher based on the passed in base cipher and multiplier.
+     *
+     * @param cipher the base cipher for the GCM mode.
+     * @param m the GCM multiplier to use.
+     */
+    public static GCMModeCipher newInstance(BlockCipher cipher, GCMMultiplier m)
+    {
+        return new GCMBlockCipher(cipher, m);
+    }
+
+    /**
+     * Base constructor - GCM mode over base cipher c.
+     *
+     * @param c the base cipher.
+     * @deprecated use the GCMBlockCipher.newInstance() static method.
+     */
     public GCMBlockCipher(BlockCipher c)
     {
         this(c, null);
     }
 
+    /**
+     * Base constructor - GCM mode over base cipher c over base multiplier m.
+     *
+     * @param c the base cipher.
+     * @param m the GCM multiplier to use.
+     * @deprecated use the CBCBlockCipher.newInstance() static method.
+     */
     public GCMBlockCipher(BlockCipher c, GCMMultiplier m)
     {
         if (c.getBlockSize() != BLOCK_SIZE)
@@ -289,17 +323,35 @@
         }
         // END Android-added: Max input size limitation from NIST.
 
-        for (int i = 0; i < len; ++i)
+        if (atBlockPos > 0)
         {
-            atBlock[atBlockPos] = in[inOff + i];
-            if (++atBlockPos == BLOCK_SIZE)
+            int available = BLOCK_SIZE - atBlockPos;
+            if (len < available)
             {
-                // Hash each block as it fills
-                gHASHBlock(S_at, atBlock);
-                atBlockPos = 0;
-                atLength += BLOCK_SIZE;
+                System.arraycopy(in, inOff, atBlock, atBlockPos, len);
+                atBlockPos += len;
+                return;
             }
+
+            System.arraycopy(in, inOff, atBlock, atBlockPos, available);
+            gHASHBlock(S_at, atBlock);
+            atLength += BLOCK_SIZE;
+            inOff += available;
+            len -= available;
+            //atBlockPos = 0;
         }
+
+        int inLimit = inOff + len - BLOCK_SIZE;
+
+        while (inOff <= inLimit)
+        {
+            gHASHBlock(S_at, in, inOff);
+            atLength += BLOCK_SIZE;
+            inOff += BLOCK_SIZE;
+        }
+
+        atBlockPos = BLOCK_SIZE + inLimit - inOff;
+        System.arraycopy(in, inOff, atBlock, 0, atBlockPos);
     }
 
     private void initCipher()
@@ -336,13 +388,14 @@
         bufBlock[bufOff] = in;
         if (++bufOff == bufBlock.length)
         {
-            processBlock(bufBlock, 0, out, outOff);
             if (forEncryption)
             {
+                encryptBlock(bufBlock, 0, out, outOff);
                 bufOff = 0;
             }
             else
             {
+                decryptBlock(bufBlock, 0, out, outOff);
                 System.arraycopy(bufBlock, BLOCK_SIZE, bufBlock, 0, macSize);
                 bufOff = macSize;
             }
@@ -370,49 +423,79 @@
 
         if (forEncryption)
         {
-            if (bufOff != 0)
+            if (bufOff > 0)
             {
-                while (len > 0)
+                int available = BLOCK_SIZE - bufOff;
+                if (len < available)
                 {
-                    --len;
-                    bufBlock[bufOff] = in[inOff++];
-                    if (++bufOff == BLOCK_SIZE)
-                    {
-                        processBlock(bufBlock, 0, out, outOff);
-                        bufOff = 0;
-                        resultLen += BLOCK_SIZE;
-                        break;
-                    }
+                    System.arraycopy(in, inOff, bufBlock, bufOff, len);
+                    bufOff += len;
+                    return 0;
                 }
+
+                System.arraycopy(in, inOff, bufBlock, bufOff, available);
+                encryptBlock(bufBlock, 0, out, outOff);
+                inOff += available;
+                len -= available;
+                resultLen = BLOCK_SIZE;
+                //bufOff = 0;
             }
 
-            while (len >= BLOCK_SIZE)
+            int inLimit = inOff + len - BLOCK_SIZE;
+
+            while (inOff <= inLimit)
             {
-                processBlock(in, inOff, out, outOff + resultLen);
+                encryptBlock(in, inOff, out, outOff + resultLen);
                 inOff += BLOCK_SIZE;
-                len -= BLOCK_SIZE;
                 resultLen += BLOCK_SIZE;
             }
 
-            if (len > 0)
-            {
-                System.arraycopy(in, inOff, bufBlock, 0, len);
-                bufOff = len;
-            }
+            bufOff = BLOCK_SIZE + inLimit - inOff;
+            System.arraycopy(in, inOff, bufBlock, 0, bufOff);
         }
         else
         {
-            for (int i = 0; i < len; ++i)
+            int available = bufBlock.length - bufOff;
+            if (len < available)
             {
-                bufBlock[bufOff] = in[inOff + i];
-                if (++bufOff == bufBlock.length)
+                System.arraycopy(in, inOff, bufBlock, bufOff, len);
+                bufOff += len;
+                return 0;
+            }
+
+            if (bufOff >= BLOCK_SIZE)
+            {
+                decryptBlock(bufBlock, 0, out, outOff);
+                System.arraycopy(bufBlock, BLOCK_SIZE, bufBlock, 0, bufOff -= BLOCK_SIZE);
+                resultLen = BLOCK_SIZE;
+
+                available += BLOCK_SIZE;
+                if (len < available)
                 {
-                    processBlock(bufBlock, 0, out, outOff + resultLen);
-                    System.arraycopy(bufBlock, BLOCK_SIZE, bufBlock, 0, macSize);
-                    bufOff = macSize;
-                    resultLen += BLOCK_SIZE;
+                    System.arraycopy(in, inOff, bufBlock, bufOff, len);
+                    bufOff += len;
+                    return resultLen;
                 }
             }
+
+            int inLimit = inOff + len - bufBlock.length;
+
+            available = BLOCK_SIZE - bufOff;
+            System.arraycopy(in, inOff, bufBlock, bufOff, available);
+            decryptBlock(bufBlock, 0, out, outOff + resultLen);
+            inOff += available;
+            resultLen += BLOCK_SIZE;
+            //bufOff = 0;
+
+            while (inOff <= inLimit)
+            {
+                decryptBlock(in, inOff, out, outOff + resultLen);
+                inOff += BLOCK_SIZE;
+                resultLen += BLOCK_SIZE;
+            }
+
+            bufOff = bufBlock.length + inLimit - inOff;
+            System.arraycopy(in, inOff, bufBlock, 0, bufOff);
         }
 
         return resultLen;
@@ -585,7 +668,7 @@
         }
     }
 
-    private void processBlock(byte[] buf, int bufOff, byte[] out, int outOff)
+    private void decryptBlock(byte[] buf, int bufOff, byte[] out, int outOff)
     {
         if ((out.length - outOff) < BLOCK_SIZE)
         {
@@ -599,18 +682,30 @@
         byte[] ctrBlock = new byte[BLOCK_SIZE];
         getNextCTRBlock(ctrBlock);
 
-        if (forEncryption)
+        gHASHBlock(S, buf, bufOff);
+        GCMUtil.xor(ctrBlock, 0, buf, bufOff, out, outOff);
+
+        totalLength += BLOCK_SIZE;
+    }
+
+    private void encryptBlock(byte[] buf, int bufOff, byte[] out, int outOff)
+    {
+        if ((out.length - outOff) < BLOCK_SIZE)
         {
-            GCMUtil.xor(ctrBlock, buf, bufOff);
-            gHASHBlock(S, ctrBlock);
-            System.arraycopy(ctrBlock, 0, out, outOff, BLOCK_SIZE);
+            throw new OutputLengthException("Output buffer too short");
         }
-        else
+        if (totalLength == 0)
         {
-            gHASHBlock(S, buf, bufOff);
-            GCMUtil.xor(ctrBlock, 0, buf, bufOff, out, outOff);
+            initCipher();
         }
 
+        byte[] ctrBlock = new byte[BLOCK_SIZE];
+
+        getNextCTRBlock(ctrBlock);
+        GCMUtil.xor(ctrBlock, buf, bufOff);
+        gHASHBlock(S, ctrBlock);
+        System.arraycopy(ctrBlock, 0, out, outOff, BLOCK_SIZE);
+
         totalLength += BLOCK_SIZE;
     }
 
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/modes/GCMModeCipher.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/modes/GCMModeCipher.java
new file mode 100644
index 0000000..3ac9332
--- /dev/null
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/modes/GCMModeCipher.java
@@ -0,0 +1,10 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.org.bouncycastle.crypto.modes;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+public interface GCMModeCipher
+    extends AEADBlockCipher
+{
+}
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/modes/GCMSIVBlockCipher.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/modes/GCMSIVBlockCipher.java
new file mode 100644
index 0000000..3a5fa4d
--- /dev/null
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/modes/GCMSIVBlockCipher.java
@@ -0,0 +1,973 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.org.bouncycastle.crypto.modes;
+
+import java.io.ByteArrayOutputStream;
+
+import com.android.org.bouncycastle.crypto.BlockCipher;
+import com.android.org.bouncycastle.crypto.CipherParameters;
+import com.android.org.bouncycastle.crypto.DataLengthException;
+import com.android.org.bouncycastle.crypto.InvalidCipherTextException;
+import com.android.org.bouncycastle.crypto.OutputLengthException;
+import com.android.org.bouncycastle.crypto.engines.AESEngine;
+import com.android.org.bouncycastle.crypto.modes.gcm.GCMMultiplier;
+import com.android.org.bouncycastle.crypto.modes.gcm.Tables4kGCMMultiplier;
+import com.android.org.bouncycastle.crypto.params.AEADParameters;
+import com.android.org.bouncycastle.crypto.params.KeyParameter;
+import com.android.org.bouncycastle.crypto.params.ParametersWithIV;
+import com.android.org.bouncycastle.util.Arrays;
+import com.android.org.bouncycastle.util.Bytes;
+import com.android.org.bouncycastle.util.Integers;
+import com.android.org.bouncycastle.util.Longs;
+import com.android.org.bouncycastle.util.Pack;
+
+/**
+ * GCM-SIV Mode.
+ * <p>It should be noted that the specified limit of 2<sup>36</sup> bytes is not supported. This is because all bytes are
+ * cached in a <b>ByteArrayOutputStream</b> object (which has a limit of a little less than 2<sup>31</sup> bytes),
+ * and are output on the <b>doFinal</b>() call (which can only process a maximum of 2<sup>31</sup> bytes).</p>
+ * <p>The practical limit of 2<sup>31</sup> - 24 bytes is policed, and attempts to breach the limit will be rejected</p>
+ * <p>In order to properly support the higher limit, an extended form of <b>ByteArrayOutputStream</b> would be needed
+ * which would use multiple arrays to store the data. In addition, a new <b>doOutput</b> method would be required (similar
+ * to that in <b>XOF</b> digests), which would allow the data to be output over multiple calls. Alternatively an extended
+ * form of <b>ByteArrayInputStream</b> could be used to deliver the data.</p>
+ * @hide This class is not part of the Android public SDK API
+ */
+public class GCMSIVBlockCipher
+         implements AEADBlockCipher
+{
+     /**
+      * The buffer length.
+      */
+     private static final int BUFLEN = 16;
+
+     /**
+      * The halfBuffer length.
+      */
+     private static final int HALFBUFLEN = BUFLEN >> 1;
+
+     /**
+      * The nonce length.
+      */
+     private static final int NONCELEN = 12;
+
+     /**
+      * The maximum data length (AEAD/PlainText). Due to implementation constraints this is restricted to the maximum
+      * array length (https://programming.guide/java/array-maximum-length.html) minus the BUFLEN to allow for the MAC
+      */
+     private static final int MAX_DATALEN = Integer.MAX_VALUE - 8 - BUFLEN;
+
+     /**
+      * The top bit mask.
+      */
+     private static final byte MASK = (byte) 0x80;
+
+     /**
+      * The addition constant.
+      */
+     private static final byte ADD = (byte) 0xE1;
+
+     /**
+      * The initialisation flag.
+      */
+     private static final int INIT = 1;
+
+     /**
+      * The aeadComplete flag.
+      */
+     private static final int AEAD_COMPLETE = 2;
+
+     /**
+      * The cipher.
+      */
+     private final BlockCipher theCipher;
+
+     /**
+      * The multiplier.
+      */
+     private final GCMMultiplier theMultiplier;
+
+     /**
+      * The gHash buffer.
+      */
+     private final byte[] theGHash = new byte[BUFLEN];
+
+     /**
+      * The reverse buffer.
+      */
+     private final byte[] theReverse = new byte[BUFLEN];
+
+     /**
+      * The aeadHasher.
+      */
+     private final GCMSIVHasher theAEADHasher;
+
+     /**
+      * The dataHasher.
+      */
+     private final GCMSIVHasher theDataHasher;
+
+     /**
+      * The plainDataStream.
+      */
+     private GCMSIVCache thePlain;
+
+     /**
+      * The encryptedDataStream (decryption only).
+      */
+     private GCMSIVCache theEncData;
+
+     /**
+      * Are we encrypting?
+      */
+     private boolean forEncryption;
+
+     /**
+      * The initialAEAD.
+      */
+     private byte[] theInitialAEAD;
+
+     /**
+      * The nonce.
+      */
+     private byte[] theNonce;
+
+     /**
+      * The flags.
+      */
+     private int theFlags;
+
+     // defined fixed
+     private byte[]      macBlock = new byte[16];
+
+     /**
+      * Constructor.
+      */
+     public GCMSIVBlockCipher()
+     {
+         this(AESEngine.newInstance());
+     }
+
+     /**
+      * Constructor.
+      * @param pCipher the underlying cipher
+      */
+     public GCMSIVBlockCipher(final BlockCipher pCipher)
+     {
+         this(pCipher, new Tables4kGCMMultiplier());
+     }
+
+     /**
+      * Constructor.
+      * @param pCipher the underlying cipher
+      * @param pMultiplier the multiplier
+      */
+     public GCMSIVBlockCipher(final BlockCipher pCipher,
+                              final GCMMultiplier pMultiplier)
+     {
+         /* Ensure that the cipher is the correct size */
+         if (pCipher.getBlockSize() != BUFLEN)
+         {
+             throw new IllegalArgumentException("Cipher required with a block size of " + BUFLEN + ".");
+         }
+
+         /* Store parameters */
+         theCipher = pCipher;
+         theMultiplier = pMultiplier;
+
+         /* Create the hashers */
+         theAEADHasher = new GCMSIVHasher();
+         theDataHasher = new GCMSIVHasher();
+     }
+
+     public BlockCipher getUnderlyingCipher()
+     {
+         return theCipher;
+     }
+
+     public void init(final boolean pEncrypt,
+                      final CipherParameters cipherParameters) throws IllegalArgumentException
+     {
+         /* Set defaults */
+         byte[] myInitialAEAD = null;
+         byte[] myNonce = null;
+         KeyParameter myKey = null;
+
+         /* Access parameters */
+         if (cipherParameters instanceof AEADParameters)
+         {
+             final AEADParameters myAEAD = (AEADParameters) cipherParameters;
+             myInitialAEAD = myAEAD.getAssociatedText();
+             myNonce = myAEAD.getNonce();
+             myKey = myAEAD.getKey();
+         }
+         else if (cipherParameters instanceof ParametersWithIV)
+         {
+             final ParametersWithIV myParms = (ParametersWithIV) cipherParameters;
+             myNonce = myParms.getIV();
+             myKey = (KeyParameter) myParms.getParameters();
+         }
+         else
+         {
+             throw new IllegalArgumentException("invalid parameters passed to GCM-SIV");
+         }
+
+         /* Check nonceSize */
+         if (myNonce == null || myNonce.length != NONCELEN)
+         {
+             throw new IllegalArgumentException("Invalid nonce");
+         }
+
+         /* Check keysize */
+         if (myKey == null
+             || (myKey.getKeyLength() != BUFLEN
+                 && myKey.getKeyLength() != (BUFLEN << 1)))
+         {
+             throw new IllegalArgumentException("Invalid key");
+         }
+
+         /* Reset details */
+         forEncryption = pEncrypt;
+         theInitialAEAD = myInitialAEAD;
+         theNonce = myNonce;
+
+         /* Initialise the keys */
+         deriveKeys(myKey);
+         resetStreams();
+     }
+
+     public String getAlgorithmName()
+     {
+         return theCipher.getAlgorithmName() + "-GCM-SIV";
+     }
+
+     /**
+      * check AEAD status.
+      * @param pLen the aeadLength
+      */
+     private void checkAEADStatus(final int pLen)
+     {
+         /* Check we are initialised */
+         if ((theFlags & INIT) == 0)
+         {
+             throw new IllegalStateException("Cipher is not initialised");
+         }
+
+         /* Check AAD is allowed */
+         if ((theFlags & AEAD_COMPLETE) != 0)
+         {
+             throw new IllegalStateException("AEAD data cannot be processed after ordinary data");
+         }
+
+         /* Make sure that we haven't breached AEAD data limit */
+         if (theAEADHasher.getBytesProcessed() + Long.MIN_VALUE
+              > (MAX_DATALEN - pLen) + Long.MIN_VALUE)
+         {
+             throw new IllegalStateException("AEAD byte count exceeded");
+         }
+     }
+
+     /**
+      * check status.
+      * @param pLen the dataLength
+      */
+     private void checkStatus(final int pLen)
+     {
+         /* Check we are initialised */
+         if ((theFlags & INIT) == 0)
+         {
+             throw new IllegalStateException("Cipher is not initialised");
+         }
+
+         /* Complete the AEAD section if this is the first data */
+         if ((theFlags & AEAD_COMPLETE) == 0)
+         {
+             theAEADHasher.completeHash();
+             theFlags |= AEAD_COMPLETE;
+         }
+
+         /* Make sure that we haven't breached data limit */
+         long dataLimit = MAX_DATALEN;
+         long currBytes = thePlain.size();
+         if (!forEncryption)
+         {
+             dataLimit += BUFLEN;
+             currBytes = theEncData.size();
+         }
+         if (currBytes + Long.MIN_VALUE
+               > (dataLimit - pLen) + Long.MIN_VALUE)
+         {
+             throw new IllegalStateException("byte count exceeded");
+         }
+     }
+
+     public void processAADByte(final byte pByte)
+     {
+         /* Check that we can supply AEAD */
+         checkAEADStatus(1);
+
+         /* Process the aead */
+         theAEADHasher.updateHash(pByte);
+     }
+
+     public void processAADBytes(final byte[] pData,
+                                 final int pOffset,
+                                 final int pLen)
+     {
+         /* Check that we can supply AEAD */
+         checkAEADStatus(pLen);
+
+         /* Check input buffer */
+         checkBuffer(pData, pOffset, pLen, false);
+
+         /* Process the aead */
+         theAEADHasher.updateHash(pData, pOffset, pLen);
+     }
+
+     public int processByte(final byte pByte,
+                            final byte[] pOutput,
+                            final int pOutOffset) throws DataLengthException
+     {
+         /* Check that we have initialised */
+         checkStatus(1);
+
+         /* Store the data */
+         if (forEncryption)
+         {
+             thePlain.write(pByte);
+             theDataHasher.updateHash(pByte);
+         }
+         else
+         {
+             theEncData.write(pByte);
+         }
+
+         /* No data returned */
+         return 0;
+     }
+
+     public int processBytes(final byte[] pData,
+                             final int pOffset,
+                             final int pLen,
+                             final byte[] pOutput,
+                             final int pOutOffset) throws DataLengthException
+     {
+         /* Check that we have initialised */
+         checkStatus(pLen);
+
+         /* Check input buffer */
+         checkBuffer(pData, pOffset, pLen, false);
+
+         /* Store the data */
+         if (forEncryption)
+         {
+             thePlain.write(pData, pOffset, pLen);
+             theDataHasher.updateHash(pData, pOffset, pLen);
+         }
+         else
+         {
+             theEncData.write(pData, pOffset, pLen);
+         }
+
+         /* No data returned */
+         return 0;
+     }
+
+     public int doFinal(final byte[] pOutput,
+                        final int pOffset) throws IllegalStateException, InvalidCipherTextException
+     {
+         /* Check that we have initialised */
+         checkStatus(0);
+
+         /* Check output buffer */
+         checkBuffer(pOutput, pOffset, getOutputSize(0), true);
+
+         /* If we are encrypting */
+         if (forEncryption)
+         {
+             /* Derive the tag */
+             final byte[] myTag = calculateTag();
+
+             /* encrypt the plain text */
+             final int myDataLen = BUFLEN + encryptPlain(myTag, pOutput, pOffset);
+
+             /* Add the tag to the output */
+             System.arraycopy(myTag, 0, pOutput, pOffset + thePlain.size(), BUFLEN);
+
+             System.arraycopy(myTag, 0, macBlock, 0, macBlock.length);
+
+             /* Reset the streams */
+             resetStreams();
+             return myDataLen;
+
+             /* else we are decrypting */
+         }
+         else
+         {
+             /* decrypt to plain text */
+             decryptPlain();
+
+             /* Release plain text */
+             final int myDataLen = thePlain.size();
+             final byte[] mySrc = thePlain.getBuffer();
+             System.arraycopy(mySrc, 0, pOutput, pOffset, myDataLen);
+
+             /* Reset the streams */
+             resetStreams();
+             return myDataLen;
+         }
+     }
+
+     public byte[] getMac()
+     {
+         return Arrays.clone(macBlock);
+     }
+
+     public int getUpdateOutputSize(final int pLen)
+     {
+         return 0;
+     }
+
+     public int getOutputSize(final int pLen)
+     {
+         if (forEncryption)
+         {
+             return pLen + thePlain.size() + BUFLEN;
+         }
+         final int myCurr = pLen + theEncData.size();
+         return myCurr > BUFLEN ? myCurr - BUFLEN : 0;
+     }
+
+     public void reset()
+     {
+         resetStreams();
+     }
+
+     /**
+      * Reset Streams.
+      */
+     private void resetStreams()
+     {
+         /* Clear the plainText buffer */
+         if (thePlain != null)
+         {
+             thePlain.clearBuffer();
+         }
+
+         /* Reset hashers */
+         theAEADHasher.reset();
+         theDataHasher.reset();
+
+         /* Recreate streams (to release memory) */
+         thePlain = new GCMSIVCache();
+         theEncData = forEncryption ? null : new GCMSIVCache();
+
+         /* Initialise AEAD if required */
+         theFlags &= ~AEAD_COMPLETE;
+         Arrays.fill(theGHash, (byte) 0);
+         if (theInitialAEAD != null)
+         {
+             theAEADHasher.updateHash(theInitialAEAD, 0, theInitialAEAD.length);
+         }
+      }
+
+     /**
+      * Obtain buffer length (allowing for null).
+      * @param pBuffer the buffere
+      * @return the length
+      */
+     private static int bufLength(final byte[] pBuffer)
+     {
+         return pBuffer == null ? 0 : pBuffer.length;
+     }
+
+     /**
+      * Check buffer.
+      * @param pBuffer the buffer
+      * @param pOffset the offset
+      * @param pLen the length
+      * @param pOutput is this an output buffer?
+      */
+     private static void checkBuffer(final byte[] pBuffer,
+                                     final int pOffset,
+                                     final int pLen,
+                                     final boolean pOutput)
+     {
+         /* Access lengths */
+         final int myBufLen = bufLength(pBuffer);
+         final int myLast = pOffset + pLen;
+
+         /* Check for negative values and buffer overflow */
+         final boolean badLen = pLen < 0 || pOffset < 0 || myLast < 0;
+         if (badLen || myLast > myBufLen)
+         {
+             throw pOutput
+                     ? new OutputLengthException("Output buffer too short.")
+                     : new DataLengthException("Input buffer too short.");
+         }
+     }
+
+     /**
+      * encrypt data stream.
+      * @param pCounter the counter
+      * @param pTarget the target buffer
+      * @param pOffset the target offset
+      * @return the length of data encrypted
+      */
+     private int encryptPlain(final byte[] pCounter,
+                              final byte[] pTarget,
+                              final int pOffset)
+     {
+         /* Access buffer and length */
+         final byte[] mySrc = thePlain.getBuffer();
+         final byte[] myCounter = Arrays.clone(pCounter);
+         myCounter[BUFLEN - 1] |= MASK;
+         final byte[] myMask = new byte[BUFLEN];
+         int myRemaining = thePlain.size();
+         int myOff = 0;
+
+         /* While we have data to process */
+         while (myRemaining > 0)
+         {
+             /* Generate the next mask */
+             theCipher.processBlock(myCounter, 0, myMask, 0);
+
+             /* Xor data into mask */
+             final int myLen = Math.min(BUFLEN, myRemaining);
+             xorBlock(myMask, mySrc, myOff, myLen);
+
+             /* Copy encrypted data to output */
+             System.arraycopy(myMask, 0, pTarget, pOffset + myOff, myLen);
+
+             /* Adjust counters */
+             myRemaining -= myLen;
+             myOff += myLen;
+             incrementCounter(myCounter);
+         }
+
+         /* Return the amount of data processed */
+         return thePlain.size();
+     }
+
+     /**
+      * decrypt data stream.
+      * @throws InvalidCipherTextException on data too short or mac check failed
+      */
+     private void decryptPlain() throws InvalidCipherTextException
+     {
+         /* Access buffer and length */
+         final byte[] mySrc = theEncData.getBuffer();
+         int myRemaining = theEncData.size() - BUFLEN;
+
+         /* Check for insufficient data */
+         if (myRemaining < 0)
+         {
+             throw new InvalidCipherTextException("Data too short");
+         }
+
+         /* Access counter */
+         final byte[] myExpected = Arrays.copyOfRange(mySrc, myRemaining, myRemaining + BUFLEN);
+         final byte[] myCounter = Arrays.clone(myExpected);
+         myCounter[BUFLEN - 1] |= MASK;
+         final byte[] myMask = new byte[BUFLEN];
+         int myOff = 0;
+
+         /* While we have data to process */
+         while (myRemaining > 0)
+         {
+             /* Generate the next mask */
+             theCipher.processBlock(myCounter, 0, myMask, 0);
+
+             /* Xor data into mask */
+             final int myLen = Math.min(BUFLEN, myRemaining);
+             xorBlock(myMask, mySrc, myOff, myLen);
+
+             /* Write data to plain dataStream */
+             thePlain.write(myMask, 0, myLen);
+             theDataHasher.updateHash(myMask, 0, myLen);
+
+             /* Adjust counters */
+             myRemaining -= myLen;
+             myOff += myLen;
+             incrementCounter(myCounter);
+         }
+
+         /* Derive and check the tag */
+         final byte[] myTag = calculateTag();
+         if (!Arrays.constantTimeAreEqual(myTag, myExpected))
+         {
+             reset();
+             throw new InvalidCipherTextException("mac check failed");
+         }
+
+         System.arraycopy(myTag, 0, macBlock, 0, macBlock.length);
+     }
+
+     /**
+      * calculate tag.
+      * @return the calculated tag
+      */
+     private byte[] calculateTag()
+     {
+         /* Complete the hash */
+         theDataHasher.completeHash();
+         final byte[] myPolyVal = completePolyVal();
+
+         /* calculate polyVal */
+         final byte[] myResult = new byte[BUFLEN];
+
+         /* Fold in the nonce */
+         for (int i = 0; i < NONCELEN; i++)
+         {
+             myPolyVal[i] ^= theNonce[i];
+         }
+
+         /* Clear top bit */
+         myPolyVal[BUFLEN - 1] &= (MASK - 1);
+
+         /* Calculate tag and return it */
+         theCipher.processBlock(myPolyVal, 0, myResult, 0);
+         return myResult;
+     }
+
+     /**
+      * complete polyVAL.
+      * @return the calculated value
+      */
+     private byte[] completePolyVal()
+     {
+         /* Build the polyVal result */
+         final byte[] myResult = new byte[BUFLEN];
+         gHashLengths();
+         fillReverse(theGHash, 0, BUFLEN, myResult);
+         return myResult;
+     }
+
+     /**
+      * process lengths.
+      */
+     private void gHashLengths()
+     {
+         /* Create reversed bigEndian buffer to keep it simple */
+         final byte[] myIn = new byte[BUFLEN];
+         Pack.longToBigEndian(Bytes.SIZE * theDataHasher.getBytesProcessed(), myIn, 0);
+         Pack.longToBigEndian(Bytes.SIZE * theAEADHasher.getBytesProcessed(), myIn, Longs.BYTES);
+
+         /* hash value */
+         gHASH(myIn);
+     }
+
+     /**
+      * perform the next GHASH step.
+      * @param pNext the next value
+      */
+     private void gHASH(final byte[] pNext)
+     {
+         xorBlock(theGHash, pNext);
+         theMultiplier.multiplyH(theGHash);
+     }
+
+     /**
+      * Byte reverse a buffer.
+      * @param pInput the input buffer
+      * @param pOffset the offset
+      * @param pLength the length of data (<= BUFLEN)
+      * @param pOutput the output buffer
+      */
+     private static void fillReverse(final byte[] pInput,
+                                     final int pOffset,
+                                     final int pLength,
+                                     final byte[] pOutput)
+     {
+         /* Loop through the buffer */
+         for (int i = 0, j = BUFLEN - 1; i < pLength; i++, j--)
+         {
+             /* Copy byte */
+             pOutput[j] = pInput[pOffset + i];
+         }
+     }
+
+     /**
+      * xor a full block buffer.
+      * @param pLeft the left operand and result
+      * @param pRight the right operand
+      */
+     private static void xorBlock(final byte[] pLeft,
+                                  final byte[] pRight)
+     {
+         /* Loop through the bytes */
+         for (int i = 0; i < BUFLEN; i++)
+         {
+             pLeft[i] ^= pRight[i];
+         }
+     }
+
+     /**
+      * xor a partial block buffer.
+      * @param pLeft the left operand and result
+      * @param pRight the right operand
+      * @param pOffset the offset in the right operand
+      * @param pLength the length of data in the right operand
+      */
+     private static void xorBlock(final byte[] pLeft,
+                                  final byte[] pRight,
+                                  final int pOffset,
+                                  final int pLength)
+                                  {
+         /* Loop through the bytes */
+         for (int i = 0; i < pLength; i++)
+         {
+             pLeft[i] ^= pRight[i + pOffset];
+         }
+     }
+
+     /**
+      * increment the counter.
+      * @param pCounter the counter to increment
+      */
+     private static void incrementCounter(final byte[] pCounter)
+     {
+         /* Loop through the bytes incrementing counter */
+         for (int i = 0; i < Integers.BYTES; i++)
+         {
+             if (++pCounter[i] != 0)
+             {
+                 break;
+             }
+         }
+     }
+
+     /**
+      * multiply by X.
+      * @param pValue the value to adjust
+      */
+     private static void mulX(final byte[] pValue)
+     {
+         /* Loop through the bytes */
+         byte myMask = (byte) 0;
+         for (int i = 0; i < BUFLEN; i++)
+         {
+             final byte myValue = pValue[i];
+             pValue[i] = (byte) (((myValue >> 1) & ~MASK) | myMask);
+             myMask = (myValue & 1) == 0 ? 0 : MASK;
+         }
+
+         /* Xor in addition if last bit was set */
+         if (myMask != 0)
+         {
+             pValue[0] ^= ADD;
+         }
+     }
+
+     /**
+      * Derive Keys.
+      * @param pKey the keyGeneration key
+      */
+     private void deriveKeys(final KeyParameter pKey)
+     {
+         /* Create the buffers */
+         final byte[] myIn = new byte[BUFLEN];
+         final byte[] myOut = new byte[BUFLEN];
+         final byte[] myResult = new byte[BUFLEN];
+         final byte[] myEncKey = new byte[pKey.getKeyLength()];
+
+         /* Prepare for encryption */
+         System.arraycopy(theNonce, 0, myIn, BUFLEN - NONCELEN, NONCELEN);
+         theCipher.init(true, pKey);
+
+         /* Derive authentication key */
+         int myOff = 0;
+         theCipher.processBlock(myIn, 0, myOut, 0);
+         System.arraycopy(myOut, 0, myResult, myOff, HALFBUFLEN);
+         myIn[0]++;
+         myOff += HALFBUFLEN;
+         theCipher.processBlock(myIn, 0, myOut, 0);
+         System.arraycopy(myOut, 0, myResult, myOff, HALFBUFLEN);
+
+         /* Derive encryption key */
+         myIn[0]++;
+         myOff = 0;
+         theCipher.processBlock(myIn, 0, myOut, 0);
+         System.arraycopy(myOut, 0, myEncKey, myOff, HALFBUFLEN);
+         myIn[0]++;
+         myOff += HALFBUFLEN;
+         theCipher.processBlock(myIn, 0, myOut, 0);
+         System.arraycopy(myOut, 0, myEncKey, myOff, HALFBUFLEN);
+
+         /* If we have a 32byte key */
+         if (myEncKey.length == BUFLEN << 1)
+         {
+             /* Derive remainder of encryption key */
+             myIn[0]++;
+             myOff += HALFBUFLEN;
+             theCipher.processBlock(myIn, 0, myOut, 0);
+             System.arraycopy(myOut, 0, myEncKey, myOff, HALFBUFLEN);
+             myIn[0]++;
+             myOff += HALFBUFLEN;
+             theCipher.processBlock(myIn, 0, myOut, 0);
+             System.arraycopy(myOut, 0, myEncKey, myOff, HALFBUFLEN);
+         }
+
+         /* Initialise the Cipher */
+         theCipher.init(true, new KeyParameter(myEncKey));
+
+         /* Initialise the multiplier */
+         fillReverse(myResult, 0, BUFLEN, myOut);
+         mulX(myOut);
+         theMultiplier.init(myOut);
+         theFlags |= INIT;
+     }
+
+     /**
+      * GCMSIVCache.
+      */
+     private static class GCMSIVCache
+             extends ByteArrayOutputStream
+     {
+         /**
+          * Constructor.
+          */
+         GCMSIVCache()
+         {
+         }
+
+         /**
+          * Obtain the buffer.
+          * @return the buffer
+          */
+         byte[] getBuffer()
+         {
+             return this.buf;
+         }
+
+         /**
+          * Clear the buffer.
+          */
+         void clearBuffer()
+         {
+             Arrays.fill(getBuffer(), (byte) 0);
+         }
+     }
+
+     /**
+      * Hash Control.
+      */
+     private class GCMSIVHasher
+     {
+         /**
+          * Cache.
+          */
+         private final byte[] theBuffer = new byte[BUFLEN];
+
+         /**
+          * Single byte cache.
+          */
+         private final byte[] theByte = new byte[1];
+
+         /**
+          * Count of active bytes in cache.
+          */
+         private int numActive;
+
+         /**
+          * Count of hashed bytes.
+          */
+         private long numHashed;
+
+         /**
+          * Obtain the count of bytes hashed.
+          * @return the count
+          */
+         long getBytesProcessed()
+         {
+             return numHashed;
+         }
+
+         /**
+          * Reset the hasher.
+          */
+         void reset()
+         {
+             numActive = 0;
+             numHashed = 0;
+         }
+
+         /**
+          * update hash.
+          * @param pByte the byte
+          */
+         void updateHash(final byte pByte)
+         {
+             theByte[0] = pByte;
+             updateHash(theByte, 0, 1);
+         }
+
+         /**
+          * update hash.
+          * @param pBuffer the buffer
+          * @param pOffset the offset within the buffer
+          * @param pLen the length of data
+          */
+         void updateHash(final byte[] pBuffer,
+                         final int pOffset,
+                         final int pLen)
+         {
+             /* If we should process the cache */
+             final int mySpace = BUFLEN - numActive;
+             int numProcessed = 0;
+             int myRemaining = pLen;
+             if (numActive > 0
+                     && pLen >= mySpace)
+             {
+                 /* Copy data into the cache and hash it */
+                 System.arraycopy(pBuffer, pOffset, theBuffer, numActive, mySpace);
+                 fillReverse(theBuffer, 0, BUFLEN, theReverse);
+                 gHASH(theReverse);
+
+                 /* Adjust counters */
+                 numProcessed += mySpace;
+                 myRemaining -= mySpace;
+                 numActive = 0;
+             }
+
+             /* While we have full blocks */
+             while (myRemaining >= BUFLEN)
+             {
+                 /* Access the next data */
+                 fillReverse(pBuffer, pOffset + numProcessed, BUFLEN, theReverse);
+                 gHASH(theReverse);
+
+                 /* Adjust counters */
+                 numProcessed += BUFLEN;
+                 myRemaining -= BUFLEN;
+             }
+
+             /* If we have remaining data */
+             if (myRemaining > 0)
+             {
+                 /* Copy data into the cache */
+                 System.arraycopy(pBuffer, pOffset + numProcessed, theBuffer, numActive, myRemaining);
+                 numActive += myRemaining;
+             }
+
+             /* Adjust the number of bytes processed */
+             numHashed += pLen;
+         }
+
+         /**
+          * complete hash.
+          */
+         void completeHash()
+         {
+             /* If we have remaining data */
+             if (numActive > 0)
+             {
+                 /* Access the next data */
+                 Arrays.fill(theReverse, (byte) 0);
+                 fillReverse(theBuffer, 0, numActive, theReverse);
+
+                 /* hash value */
+                 gHASH(theReverse);
+             }
+         }
+     }
+ }
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/modes/SICBlockCipher.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/modes/SICBlockCipher.java
index 73bdc58..1745112 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/modes/SICBlockCipher.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/modes/SICBlockCipher.java
@@ -4,7 +4,7 @@
 import com.android.org.bouncycastle.crypto.BlockCipher;
 import com.android.org.bouncycastle.crypto.CipherParameters;
 import com.android.org.bouncycastle.crypto.DataLengthException;
-import com.android.org.bouncycastle.crypto.SkippingStreamCipher;
+import com.android.org.bouncycastle.crypto.OutputLengthException;
 import com.android.org.bouncycastle.crypto.StreamBlockCipher;
 import com.android.org.bouncycastle.crypto.params.ParametersWithIV;
 import com.android.org.bouncycastle.util.Arrays;
@@ -17,7 +17,7 @@
  */
 public class SICBlockCipher
     extends StreamBlockCipher
-    implements SkippingStreamCipher
+    implements CTRModeCipher
 {
     private final BlockCipher     cipher;
     private final int             blockSize;
@@ -28,9 +28,20 @@
     private int             byteCount;
 
     /**
+     * Return a new SIC/CTR mode cipher based on the passed in base cipher
+     *
+     * @param cipher the base cipher for the SIC/CTR mode.
+     */
+    public static CTRModeCipher newInstance(BlockCipher cipher)
+    {
+        return new SICBlockCipher(cipher);
+    }
+
+    /**
      * Basic constructor.
      *
      * @param c the block cipher to be used.
+     * @deprecated use newInstance() method.
      */
     public SICBlockCipher(BlockCipher c)
     {
@@ -93,16 +104,75 @@
     public int processBlock(byte[] in, int inOff, byte[] out, int outOff)
           throws DataLengthException, IllegalStateException
     {
-        processBytes(in, inOff, blockSize, out, outOff);
+        if (byteCount != 0)
+        {
+            processBytes(in, inOff, blockSize, out, outOff);
+            return blockSize;
+        }
 
+        if (inOff + blockSize > in.length)
+        {
+            throw new DataLengthException("input buffer too small");
+        }
+        if (outOff + blockSize > out.length)
+        {
+            throw new OutputLengthException("output buffer too short");
+        }
+
+        cipher.processBlock(counter, 0, counterOut, 0);
+        for (int i = 0; i < blockSize; ++i)
+        {
+            out[outOff + i] = (byte)(in[inOff + i] ^ counterOut[i]);
+        }
+        incrementCounter();
         return blockSize;
     }
 
+    public int processBytes(byte[] in, int inOff, int len, byte[] out, int outOff)
+        throws DataLengthException
+    {
+        if (inOff + len > in.length)
+        {
+            throw new DataLengthException("input buffer too small");
+        }
+        if (outOff + len > out.length)
+        {
+            throw new OutputLengthException("output buffer too short");
+        }
+
+        for (int i = 0; i < len; ++i)
+        {
+            byte next;
+
+            if (byteCount == 0)
+            {
+                checkLastIncrement();
+
+                cipher.processBlock(counter, 0, counterOut, 0);
+                next = (byte)(in[inOff + i] ^ counterOut[byteCount++]);
+            }
+            else
+            {
+                next = (byte)(in[inOff + i] ^ counterOut[byteCount++]);
+                if (byteCount == counter.length)
+                {
+                    byteCount = 0;
+                    incrementCounter();
+                }
+            }
+            out[outOff + i] = next;
+        }
+
+        return len;
+    }
+
     protected byte calculateByte(byte in)
           throws DataLengthException, IllegalStateException
     {
         if (byteCount == 0)
         {
+            checkLastIncrement();
+
             cipher.processBlock(counter, 0, counterOut, 0);
 
             return (byte)(counterOut[byteCount++] ^ in);
@@ -113,10 +183,7 @@
         if (byteCount == counter.length)
         {
             byteCount = 0;
-
-            incrementCounterAt(0);
-
-            checkCounter();
+            incrementCounter();
         }
 
         return rv;
@@ -127,7 +194,7 @@
         // if the IV is the same as the blocksize we assume the user knows what they are doing
         if (IV.length < blockSize)
         {
-            for (int i = 0; i != IV.length; i++)
+            for (int i = IV.length - 1; i >= 0; i--)
             {
                 if (counter[i] != IV[i])
                 {
@@ -137,6 +204,30 @@
         }
     }
 
+    private void checkLastIncrement()
+    {
+        // if the IV is the same as the blocksize we assume the user knows what they are doing
+        if (IV.length < blockSize)
+        {
+            if (counter[IV.length - 1] != IV[IV.length - 1])
+            {
+                throw new IllegalStateException("Counter in CTR/SIC mode out of range.");
+            }
+        }
+    }
+
+    private void incrementCounter()
+    {
+        int i = counter.length;
+        while (--i >= 0)
+        {
+            if (++counter[i] != 0)
+            {
+                break;
+            }
+        }
+    }
+
     private void incrementCounterAt(int pos)
     {
         int i = counter.length - pos;
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/modes/gcm/BasicGCMExponentiator.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/modes/gcm/BasicGCMExponentiator.java
index dd1ed48..ff33927 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/modes/gcm/BasicGCMExponentiator.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/modes/gcm/BasicGCMExponentiator.java
@@ -1,8 +1,6 @@
 /* GENERATED SOURCE. DO NOT MODIFY. */
 package com.android.org.bouncycastle.crypto.modes.gcm;
 
-import com.android.org.bouncycastle.util.Arrays;
-
 /**
  * @hide This class is not part of the Android public SDK API
  */
@@ -23,7 +21,9 @@
 
         if (pow > 0)
         {
-            long[] powX = Arrays.clone(x);
+            long[] powX = new long[GCMUtil.SIZE_LONGS];
+            GCMUtil.copy(x, powX);
+
             do
             {
                 if ((pow & 1L) != 0)
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/modes/gcm/GCMUtil.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/modes/gcm/GCMUtil.java
index e56c31e..c436d26 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/modes/gcm/GCMUtil.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/modes/gcm/GCMUtil.java
@@ -10,76 +10,119 @@
  */
 public abstract class GCMUtil
 {
+    public static final int SIZE_BYTES = 16;
+    public static final int SIZE_INTS = 4;
+    public static final int SIZE_LONGS = 2;
+
     private static final int E1 = 0xe1000000;
     private static final long E1L = (E1 & 0xFFFFFFFFL) << 32;
 
     public static byte[] oneAsBytes()
     {
-        byte[] tmp = new byte[16];
+        byte[] tmp = new byte[SIZE_BYTES];
         tmp[0] = (byte)0x80;
         return tmp;
     }
 
     public static int[] oneAsInts()
     {
-        int[] tmp = new int[4];
+        int[] tmp = new int[SIZE_INTS];
         tmp[0] = 1 << 31;
         return tmp;
     }
 
     public static long[] oneAsLongs()
     {
-        long[] tmp = new long[2];
+        long[] tmp = new long[SIZE_LONGS];
         tmp[0] = 1L << 63;
         return tmp;
     }
 
+    public static byte areEqual(byte[] x, byte[] y)
+    {
+        int d = 0;
+        for (int i = 0; i < SIZE_BYTES; ++i)
+        {
+            d |= x[i] ^ y[i];
+        }
+        d = (d >>> 1) | (d & 1);
+        return (byte)((d - 1) >> 31);
+    }
+
+    public static int areEqual(int[] x, int[] y)
+    {
+        int d = 0;
+        d |= x[0] ^ y[0];
+        d |= x[1] ^ y[1];
+        d |= x[2] ^ y[2];
+        d |= x[3] ^ y[3];
+        d = (d >>> 1) | (d & 1);
+        return (d - 1) >> 31;
+    }
+
+    public static long areEqual(long[] x, long[] y)
+    {
+        long d = 0L;
+        d |= x[0] ^ y[0];
+        d |= x[1] ^ y[1];
+        d = (d >>> 1) | (d & 1L);
+        return (d - 1L) >> 63;
+    }
+
     public static byte[] asBytes(int[] x)
     {
-        byte[] z = new byte[16];
-        Pack.intToBigEndian(x, z, 0);
+        byte[] z = new byte[SIZE_BYTES];
+        Pack.intToBigEndian(x, 0, SIZE_INTS, z, 0);
         return z;
     }
 
     public static void asBytes(int[] x, byte[] z)
     {
-        Pack.intToBigEndian(x, z, 0);
+        Pack.intToBigEndian(x, 0, SIZE_INTS, z, 0);
     }
 
     public static byte[] asBytes(long[] x)
     {
-        byte[] z = new byte[16];
-        Pack.longToBigEndian(x, z, 0);
+        byte[] z = new byte[SIZE_BYTES];
+        Pack.longToBigEndian(x, 0, SIZE_LONGS, z, 0);
         return z;
     }
 
     public static void asBytes(long[] x, byte[] z)
     {
-        Pack.longToBigEndian(x, z, 0);
+        Pack.longToBigEndian(x, 0, SIZE_LONGS, z, 0);
     }
 
     public static int[] asInts(byte[] x)
     {
-        int[] z = new int[4];
-        Pack.bigEndianToInt(x, 0, z);
+        int[] z = new int[SIZE_INTS];
+        Pack.bigEndianToInt(x, 0, z, 0, SIZE_INTS);
         return z;
     }
 
     public static void asInts(byte[] x, int[] z)
     {
-        Pack.bigEndianToInt(x, 0, z);
+        Pack.bigEndianToInt(x, 0, z, 0, SIZE_INTS);
     }
 
     public static long[] asLongs(byte[] x)
     {
-        long[] z = new long[2];
-        Pack.bigEndianToLong(x, 0, z);
+        long[] z = new long[SIZE_LONGS];
+        Pack.bigEndianToLong(x, 0, z, 0, SIZE_LONGS);
         return z;
     }
 
     public static void asLongs(byte[] x, long[] z)
     {
-        Pack.bigEndianToLong(x, 0, z);
+        Pack.bigEndianToLong(x, 0, z, 0, SIZE_LONGS);
+    }
+
+    public static void copy(byte[] x, byte[] z)
+    {
+        for (int i = 0; i < SIZE_BYTES; ++i)
+        {
+            z[i] = x[i];
+        }
     }
 
     public static void copy(int[] x, int[] z)
@@ -107,10 +150,48 @@
 
     public static void multiply(byte[] x, byte[] y)
     {
-        long[] t1 = GCMUtil.asLongs(x);
-        long[] t2 = GCMUtil.asLongs(y);
-        GCMUtil.multiply(t1, t2);
-        GCMUtil.asBytes(t1, x);
+        long[] t1 = asLongs(x);
+        long[] t2 = asLongs(y);
+        multiply(t1, t2);
+        asBytes(t1, x);
+    }
+
+    static void multiply(byte[] x, long[] y)
+    {
+        /*
+         * "Three-way recursion" as described in "Batch binary Edwards", Daniel J. Bernstein.
+         *
+         * Without access to the high part of a 64x64 product x * y, we use a bit reversal to calculate it:
+         *     rev(x) * rev(y) == rev((x * y) << 1) 
+         */
+
+        long x0 = Pack.bigEndianToLong(x, 0);
+        long x1 = Pack.bigEndianToLong(x, 8);
+        long y0 = y[0], y1 = y[1];
+        long x0r = Longs.reverse(x0), x1r = Longs.reverse(x1);
+        long y0r = Longs.reverse(y0), y1r = Longs.reverse(y1);
+
+        long h0  = Longs.reverse(implMul64(x0r, y0r));
+        long h1  = implMul64(x0, y0) << 1;
+        long h2  = Longs.reverse(implMul64(x1r, y1r));
+        long h3  = implMul64(x1, y1) << 1;
+        long h4  = Longs.reverse(implMul64(x0r ^ x1r, y0r ^ y1r));
+        long h5  = implMul64(x0 ^ x1, y0 ^ y1) << 1;
+
+        long z0  = h0;
+        long z1  = h1 ^ h0 ^ h2 ^ h4;
+        long z2  = h2 ^ h1 ^ h3 ^ h5;
+        long z3  = h3;
+
+        z1 ^= z3 ^ (z3 >>>  1) ^ (z3 >>>  2) ^ (z3 >>>  7);
+//      z2 ^=      (z3 <<  63) ^ (z3 <<  62) ^ (z3 <<  57);
+        z2 ^=                    (z3 <<  62) ^ (z3 <<  57);
+
+        z0 ^= z2 ^ (z2 >>>  1) ^ (z2 >>>  2) ^ (z2 >>>  7);
+        z1 ^=      (z2 <<  63) ^ (z2 <<  62) ^ (z2 <<  57);
+
+        Pack.longToBigEndian(z0, x, 0);
+        Pack.longToBigEndian(z1, x, 8);
     }
 
     public static void multiply(int[] x, int[] y)
@@ -118,7 +199,7 @@
         int y0 = y[0], y1 = y[1], y2 = y[2], y3 = y[3];
         int z0 = 0, z1 = 0, z2 = 0, z3 = 0;
 
-        for (int i = 0; i < 4; ++i)
+        for (int i = 0; i < SIZE_INTS; ++i)
         {
             int bits = x[i];
             for (int j = 0; j < 32; ++j)
@@ -301,16 +382,24 @@
         y[1] = (x1 >>> 8) | (x0 << 56);
     }
 
+    public static void multiplyP16(long[] x)
+    {
+        long x0 = x[0], x1 = x[1];
+        long c = x1 << 48;
+        x[0] = (x0 >>> 16) ^ c ^ (c >>> 1) ^ (c >>> 2) ^ (c >>> 7);
+        x[1] = (x1 >>> 16) | (x0 << 48);
+    }
+
     public static long[] pAsLongs()
     {
-        long[] tmp = new long[2];
+        long[] tmp = new long[SIZE_LONGS];
         tmp[0] = 1L << 62;
         return tmp;
     }
 
     public static void square(long[] x, long[] z)
     {
-        long[] t  = new long[4];
+        long[] t  = new long[SIZE_LONGS * 2];
         Interleave.expand64To128Rev(x[0], t, 0);
         Interleave.expand64To128Rev(x[1], t, 2);
 
@@ -336,7 +425,7 @@
             x[i] ^= y[i]; ++i;
             x[i] ^= y[i]; ++i;
         }
-        while (i < 16);
+        while (i < SIZE_BYTES);
     }
 
     public static void xor(byte[] x, byte[] y, int yOff)
@@ -349,7 +438,7 @@
             x[i] ^= y[yOff + i]; ++i;
             x[i] ^= y[yOff + i]; ++i;
         }
-        while (i < 16);
+        while (i < SIZE_BYTES);
     }
 
     public static void xor(byte[] x, int xOff, byte[] y, int yOff, byte[] z, int zOff)
@@ -362,7 +451,7 @@
             z[zOff + i] = (byte)(x[xOff + i] ^ y[yOff + i]); ++i;
             z[zOff + i] = (byte)(x[xOff + i] ^ y[yOff + i]); ++i;
         }
-        while (i < 16);
+        while (i < SIZE_BYTES);
     }
 
     public static void xor(byte[] x, byte[] y, int yOff, int yLen)
@@ -391,7 +480,7 @@
             z[i] = (byte)(x[i] ^ y[i]); ++i;
             z[i] = (byte)(x[i] ^ y[i]); ++i;
         }
-        while (i < 16);
+        while (i < SIZE_BYTES);
     }
 
     public static void xor(int[] x, int[] y)
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/modes/gcm/Tables1kGCMExponentiator.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/modes/gcm/Tables1kGCMExponentiator.java
index 1adb197..087e33e 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/modes/gcm/Tables1kGCMExponentiator.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/modes/gcm/Tables1kGCMExponentiator.java
@@ -1,9 +1,8 @@
 /* GENERATED SOURCE. DO NOT MODIFY. */
 package com.android.org.bouncycastle.crypto.modes.gcm;
 
-import java.util.Vector;
-
-import com.android.org.bouncycastle.util.Arrays;
+import java.util.ArrayList;
+import java.util.List;
 
 /**
  * @hide This class is not part of the Android public SDK API
@@ -13,18 +12,18 @@
 {
     // A lookup table of the power-of-two powers of 'x'
     // - lookupPowX2[i] = x^(2^i)
-    private Vector lookupPowX2;
+    private List lookupPowX2;
 
     public void init(byte[] x)
     {
         long[] y = GCMUtil.asLongs(x);
-        if (lookupPowX2 != null && Arrays.areEqual(y, (long[])lookupPowX2.elementAt(0)))
+        if (lookupPowX2 != null && 0L != GCMUtil.areEqual(y, (long[])lookupPowX2.get(0)))
         {
             return;
         }
 
-        lookupPowX2 = new Vector(8);
-        lookupPowX2.addElement(y);
+        lookupPowX2 = new ArrayList(8);
+        lookupPowX2.add(y);
     }
 
     public void exponentiateX(long pow, byte[] output)
@@ -35,8 +34,7 @@
         {
             if ((pow & 1L) != 0)
             {
-                ensureAvailable(bit);
-                GCMUtil.multiply(y, (long[])lookupPowX2.elementAt(bit));
+                GCMUtil.multiply(y, getPowX2(bit));
             }
             ++bit;
             pow >>>= 1;
@@ -45,19 +43,22 @@
         GCMUtil.asBytes(y, output);
     }
 
-    private void ensureAvailable(int bit)
+    private long[] getPowX2(int bit)
     {
-        int count = lookupPowX2.size();
-        if (count <= bit)
+        int last = lookupPowX2.size() - 1;
+        if (last < bit)
         {
-            long[] tmp = (long[])lookupPowX2.elementAt(count - 1);
+            long[] prev = (long[])lookupPowX2.get(last);
             do
             {
-                tmp = Arrays.clone(tmp);
-                GCMUtil.square(tmp, tmp);
-                lookupPowX2.addElement(tmp);
+                long[] next = new long[GCMUtil.SIZE_LONGS];
+                GCMUtil.square(prev, next);
+                lookupPowX2.add(next);
+                prev = next;
             }
-            while (++count <= bit);
+            while (++last < bit);
         }
+
+        return (long[])lookupPowX2.get(bit);
     }
 }
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/modes/gcm/Tables4kGCMMultiplier.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/modes/gcm/Tables4kGCMMultiplier.java
index 0580d01..251d043 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/modes/gcm/Tables4kGCMMultiplier.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/modes/gcm/Tables4kGCMMultiplier.java
@@ -1,7 +1,6 @@
 /* GENERATED SOURCE. DO NOT MODIFY. */
 package com.android.org.bouncycastle.crypto.modes.gcm;
 
-import com.android.org.bouncycastle.util.Arrays;
 import com.android.org.bouncycastle.util.Pack;
 
 /**
@@ -19,12 +18,13 @@
         {
             T = new long[256][2];
         }
-        else if (Arrays.areEqual(this.H, H))
+        else if (0 != GCMUtil.areEqual(this.H, H))
         {
             return;
         }
 
-        this.H = Arrays.clone(H);
+        this.H = new byte[GCMUtil.SIZE_BYTES];
+        GCMUtil.copy(H, this.H);
 
         // T[0] = 0
 
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/modes/gcm/Tables8kGCMMultiplier.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/modes/gcm/Tables8kGCMMultiplier.java
index 8b2dd22..2c4a546 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/modes/gcm/Tables8kGCMMultiplier.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/modes/gcm/Tables8kGCMMultiplier.java
@@ -1,7 +1,6 @@
 /* GENERATED SOURCE. DO NOT MODIFY. */
 package com.android.org.bouncycastle.crypto.modes.gcm;
 
-import com.android.org.bouncycastle.util.Arrays;
 import com.android.org.bouncycastle.util.Pack;
 
 /**
@@ -17,16 +16,17 @@
     {
         if (T == null)
         {
-            T = new long[32][16][2];
+            T = new long[2][256][2];
         }
-        else if (Arrays.areEqual(this.H, H))
+        else if (0 != GCMUtil.areEqual(this.H, H))
         {
             return;
         }
 
-        this.H = Arrays.clone(H);
+        this.H = new byte[GCMUtil.SIZE_BYTES];
+        GCMUtil.copy(H, this.H);
 
-        for (int i = 0; i < 32; ++i)
+        for (int i = 0; i < 2; ++i)
         {
             long[][] t = T[i];
 
@@ -34,17 +34,17 @@
 
             if (i == 0)
             {
-                // t[1] = H.p^3
+                // t[1] = H.p^7
                 GCMUtil.asLongs(this.H, t[1]);
-                GCMUtil.multiplyP3(t[1], t[1]);
+                GCMUtil.multiplyP7(t[1], t[1]);
             }
             else
             {
-                // t[1] = T[i-1][1].p^4
-                GCMUtil.multiplyP4(T[i - 1][1], t[1]);
+                // t[1] = T[i-1][1].p^8
+                GCMUtil.multiplyP8(T[i - 1][1], t[1]);
             }
 
-            for (int n = 2; n < 16; n += 2)
+            for (int n = 2; n < 256; n += 2)
             {
                 // t[2.n] = t[n].p^-1
                 GCMUtil.divideP(t[n >> 1], t[n]);
@@ -53,28 +53,33 @@
                 GCMUtil.xor(t[n], t[1], t[n + 1]);
             }
         }
-
     }
 
     public void multiplyH(byte[] x)
     {
+        long[][] T0 = T[0], T1 = T[1];
+
 //        long[] z = new long[2];
-//        for (int i = 15; i >= 0; --i)
+//        for (int i = 14; i >= 0; i -= 2)
 //        {
-//            GCMUtil.xor(z, T[i + i + 1][(x[i] & 0x0F)]);
-//            GCMUtil.xor(z, T[i + i    ][(x[i] & 0xF0) >>> 4]);
+//            GCMUtil.multiplyP16(z);
+//            GCMUtil.xor(z, T0[x[i] & 0xFF]);
+//            GCMUtil.xor(z, T1[x[i + 1] & 0xFF]);
 //        }
 //        Pack.longToBigEndian(z, x, 0);
 
-        long z0 = 0, z1 = 0;
+        long[] u = T0[x[14] & 0xFF];
+        long[] v = T1[x[15] & 0xFF];
+        long z0 = u[0] ^ v[0], z1 = u[1] ^ v[1];
 
-        for (int i = 15; i >= 0; --i)
+        for (int i = 12; i >= 0; i -= 2)
         {
-            long[] u = T[i + i + 1][(x[i] & 0x0F)];
-            long[] v = T[i + i    ][(x[i] & 0xF0) >>> 4];
+            u = T0[x[i] & 0xFF];
+            v = T1[x[i + 1] & 0xFF];
 
-            z0 ^= u[0] ^ v[0];
-            z1 ^= u[1] ^ v[1];
+            long c = z1 << 48;
+            z1 = u[1] ^ v[1] ^ ((z1 >>> 16) | (z0 << 48));
+            z0 = u[0] ^ v[0] ^ (z0 >>> 16) ^ c ^ (c >>> 1) ^ (c >>> 2) ^ (c >>> 7);
         }
 
         Pack.longToBigEndian(z0, x, 0);
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/paddings/ISO10126d2Padding.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/paddings/ISO10126d2Padding.java
index c7a7b63..91cc024 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/paddings/ISO10126d2Padding.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/paddings/ISO10126d2Padding.java
@@ -63,9 +63,11 @@
     public int padCount(byte[] in)
         throws InvalidCipherTextException
     {
-        int count = in[in.length - 1] & 0xff;
+        int count = in[in.length - 1] & 0xFF;
+        int position = in.length - count;
 
-        if (count > in.length)
+        int failed = (position | (count - 1)) >> 31;
+        if (failed != 0)
         {
             throw new InvalidCipherTextException("pad block corrupted");
         }
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/paddings/ISO7816d4Padding.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/paddings/ISO7816d4Padding.java
index d2eb0e3..d566aaf 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/paddings/ISO7816d4Padding.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/paddings/ISO7816d4Padding.java
@@ -62,18 +62,20 @@
     public int padCount(byte[] in)
         throws InvalidCipherTextException
     {
-        int count = in.length - 1;
-
-        while (count > 0 && in[count] == 0)
+        int position = -1, still00Mask = -1;
+        int i = in.length;
+        while (--i >= 0)
         {
-            count--;
+            int next = in[i] & 0xFF;
+            int match00Mask = ((next ^ 0x00) - 1) >> 31;
+            int match80Mask = ((next ^ 0x80) - 1) >> 31;
+            position ^= (i ^ position) & (still00Mask & match80Mask);
+            still00Mask &= match00Mask;
         }
-
-        if (in[count] != (byte)0x80)
+        if (position < 0)
         {
             throw new InvalidCipherTextException("pad block corrupted");
         }
-        
-        return in.length - count;
+        return in.length - position;
     }
 }
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/paddings/PKCS7Padding.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/paddings/PKCS7Padding.java
index f1712b6..ffa4e62 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/paddings/PKCS7Padding.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/paddings/PKCS7Padding.java
@@ -58,18 +58,16 @@
     public int padCount(byte[] in)
         throws InvalidCipherTextException
     {
-        int count = in[in.length - 1] & 0xff;
-        byte countAsbyte = (byte)count;
+        byte countAsByte = in[in.length - 1];
+        int count = countAsByte & 0xFF;
+        int position = in.length - count;
 
-        // constant time version
-        boolean failed = (count > in.length | count == 0);
-
-        for (int i = 0; i < in.length; i++)
+        int failed = (position | (count - 1)) >> 31;
+        for (int i = 0; i < in.length; ++i)
         {
-            failed |= (in.length - i <= count) & (in[i] != countAsbyte);
+            failed |= (in[i] ^ countAsByte) & ~((i - position) >> 31);
         }
-
-        if (failed)
+        if (failed != 0)
         {
             throw new InvalidCipherTextException("pad block corrupted");
         }
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/paddings/PaddedBufferedBlockCipher.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/paddings/PaddedBufferedBlockCipher.java
index 54e0b85..7baeb09 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/paddings/PaddedBufferedBlockCipher.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/paddings/PaddedBufferedBlockCipher.java
@@ -2,9 +2,9 @@
 package com.android.org.bouncycastle.crypto.paddings;
 
 import com.android.org.bouncycastle.crypto.BlockCipher;
-import com.android.org.bouncycastle.crypto.BufferedBlockCipher;
 import com.android.org.bouncycastle.crypto.CipherParameters;
 import com.android.org.bouncycastle.crypto.DataLengthException;
+import com.android.org.bouncycastle.crypto.DefaultBufferedBlockCipher;
 import com.android.org.bouncycastle.crypto.InvalidCipherTextException;
 import com.android.org.bouncycastle.crypto.OutputLengthException;
 import com.android.org.bouncycastle.crypto.params.ParametersWithRandom;
@@ -18,7 +18,7 @@
  * @hide This class is not part of the Android public SDK API
  */
 public class PaddedBufferedBlockCipher
-    extends BufferedBlockCipher
+    extends DefaultBufferedBlockCipher
 {
     BlockCipherPadding  padding;
 
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/paddings/TBCPadding.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/paddings/TBCPadding.java
index 333eef0..b4ee693 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/paddings/TBCPadding.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/paddings/TBCPadding.java
@@ -78,14 +78,15 @@
     public int padCount(byte[] in)
         throws InvalidCipherTextException
     {
-        byte code = in[in.length - 1];
-
-        int index = in.length - 1;
-        while (index > 0 && in[index - 1] == code)
+        int i = in.length;
+        int code = in[--i] & 0xFF, count = 1, countingMask = -1;
+        while (--i >= 0)
         {
-            index--;
+            int next = in[i] & 0xFF;
+            int matchMask = ((next ^ code) - 1) >> 31;
+            countingMask &= matchMask;
+            count -= countingMask;
         }
-
-        return in.length - index;
+        return count;
     }
 }
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/paddings/X923Padding.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/paddings/X923Padding.java
index c93135f..4129bad 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/paddings/X923Padding.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/paddings/X923Padding.java
@@ -70,9 +70,11 @@
     public int padCount(byte[] in)
         throws InvalidCipherTextException
     {
-        int count = in[in.length - 1] & 0xff;
+        int count = in[in.length - 1] & 0xFF;
+        int position = in.length - count;
 
-        if (count > in.length)
+        int failed = (position | (count - 1)) >> 31;
+        if (failed != 0)
         {
             throw new InvalidCipherTextException("pad block corrupted");
         }
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/paddings/ZeroBytePadding.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/paddings/ZeroBytePadding.java
index 783b2a9..a068c0b 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/paddings/ZeroBytePadding.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/paddings/ZeroBytePadding.java
@@ -58,18 +58,15 @@
     public int padCount(byte[] in)
         throws InvalidCipherTextException
     {
-        int count = in.length;
-
-        while (count > 0)
+        int count = 0, still00Mask = -1;
+        int i = in.length;
+        while (--i >= 0)
         {
-            if (in[count - 1] != 0)
-            {
-                break;
-            }
-
-            count--;
+            int next = in[i] & 0xFF;
+            int match00Mask = ((next ^ 0x00) - 1) >> 31;
+            still00Mask &= match00Mask;
+            count -= still00Mask;
         }
-
-        return in.length - count;
+        return count;
     }
 }
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/params/Blake3Parameters.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/params/Blake3Parameters.java
new file mode 100644
index 0000000..a30e8e8
--- /dev/null
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/params/Blake3Parameters.java
@@ -0,0 +1,86 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.org.bouncycastle.crypto.params;
+
+import com.android.org.bouncycastle.crypto.CipherParameters;
+import com.android.org.bouncycastle.util.Arrays;
+
+/**
+ * Blake3 Parameters.
+ * @hide This class is not part of the Android public SDK API
+ */
+public class Blake3Parameters
+        implements CipherParameters
+{
+    /**
+     * The key length.
+     */
+    private static final int KEYLEN = 32;
+
+    /**
+     * The key.
+     */
+    private byte[] theKey;
+
+    /**
+     * The context.
+     */
+    private byte[] theContext;
+
+    /**
+     * Create a key parameter.
+     * @param pContext the context
+     * @return the parameter
+     */
+    public static Blake3Parameters context(final byte[] pContext)
+    {
+        if (pContext == null)
+        {
+            throw new IllegalArgumentException("Invalid context");
+        }
+        final Blake3Parameters myParams = new Blake3Parameters();
+        myParams.theContext = Arrays.clone(pContext);
+        return myParams;
+    }
+
+    /**
+     * Create a key parameter.
+     * @param pKey the key
+     * @return the parameter
+     */
+    public static Blake3Parameters key(final byte[] pKey)
+    {
+        if (pKey == null || pKey.length != KEYLEN)
+        {
+            throw new IllegalArgumentException("Invalid keyLength");
+        }
+        final Blake3Parameters myParams = new Blake3Parameters();
+        myParams.theKey = Arrays.clone(pKey);
+        return myParams;
+    }
+
+    /**
+     * Obtain the key.
+     * @return the key
+     */
+    public byte[] getKey()
+    {
+        return Arrays.clone(theKey);
+    }
+
+    /**
+     * Clear the key bytes.
+      */
+    public void clearKey()
+    {
+        Arrays.fill(theKey, (byte) 0);
+    }
+
+    /**
+     * Obtain the salt.
+     * @return the salt
+     */
+    public byte[] getContext()
+    {
+        return Arrays.clone(theContext);
+    }
+}
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/params/FPEParameters.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/params/FPEParameters.java
new file mode 100644
index 0000000..5ec0f90
--- /dev/null
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/params/FPEParameters.java
@@ -0,0 +1,61 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.org.bouncycastle.crypto.params;
+
+import com.android.org.bouncycastle.crypto.CipherParameters;
+import com.android.org.bouncycastle.crypto.util.RadixConverter;
+import com.android.org.bouncycastle.util.Arrays;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+public final class FPEParameters
+    implements CipherParameters
+{
+    private final KeyParameter key;
+    private final RadixConverter radixConverter;
+    private final byte[] tweak;
+    private final boolean useInverse;
+
+    public FPEParameters(KeyParameter key, int radix, byte[] tweak)
+    {
+        this(key, radix, tweak, false);
+    }
+
+    public FPEParameters(KeyParameter key, int radix, byte[] tweak, boolean useInverse)
+    {
+        this(key, new RadixConverter(radix), tweak, useInverse);
+    }
+
+    public FPEParameters(KeyParameter key, RadixConverter radixConverter, byte[] tweak, boolean useInverse)
+    {
+        this.key = key;
+        this.radixConverter = radixConverter;
+        this.tweak = Arrays.clone(tweak);
+        this.useInverse = useInverse;
+    }
+
+    public KeyParameter getKey()
+    {
+        return key;
+    }
+
+    public int getRadix()
+    {
+        return radixConverter.getRadix();
+    }
+
+    public RadixConverter getRadixConverter()
+    {
+        return radixConverter;
+    }
+
+    public byte[] getTweak()
+    {
+        return Arrays.clone(tweak);
+    }
+
+    public boolean isUsingInverseFunction()
+    {
+        return useInverse;
+    }
+}
\ No newline at end of file
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/params/KeyParameter.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/params/KeyParameter.java
index ad8fa0c..2657e4b 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/params/KeyParameter.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/params/KeyParameter.java
@@ -2,6 +2,7 @@
 package com.android.org.bouncycastle.crypto.params;
 
 import com.android.org.bouncycastle.crypto.CipherParameters;
+import com.android.org.bouncycastle.util.Arrays;
 
 /**
  * @hide This class is not part of the Android public SDK API
@@ -22,13 +23,38 @@
         int     keyOff,
         int     keyLen)
     {
-        this.key = new byte[keyLen];
+        this(keyLen);
 
         System.arraycopy(key, keyOff, this.key, 0, keyLen);
     }
 
+    private KeyParameter(int length)
+    {
+        this.key = new byte[length];
+    }
+
+    public void copyTo(byte[] buf, int off, int len)
+    {
+        if (key.length != len)
+            throw new IllegalArgumentException("len");
+
+        System.arraycopy(key, 0, buf, off, len);
+    }
+
     public byte[] getKey()
     {
         return key;
     }
+
+    public int getKeyLength()
+    {
+        return key.length;
+    }
+
+    public KeyParameter reverse()
+    {
+        KeyParameter reversed = new KeyParameter(key.length);
+        Arrays.reverse(this.key, reversed.key);
+        return reversed;
+    }
 }
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/params/RSAKeyParameters.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/params/RSAKeyParameters.java
index ac7c2ee..7839449 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/params/RSAKeyParameters.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/params/RSAKeyParameters.java
@@ -3,6 +3,9 @@
 
 import java.math.BigInteger;
 
+import com.android.org.bouncycastle.crypto.CryptoServicesRegistrar;
+import com.android.org.bouncycastle.math.Primes;
+import com.android.org.bouncycastle.util.BigIntegers;
 import com.android.org.bouncycastle.util.Properties;
 
 /**
@@ -11,6 +14,8 @@
 public class RSAKeyParameters
     extends AsymmetricKeyParameter
 {
+    private static final BigIntegers.Cache validated = new BigIntegers.Cache();
+
     // Hexadecimal value of the product of the 131 smallest odd primes from 3 to 743
     private static final BigInteger SMALL_PRIMES_PRODUCT = new BigInteger(
               "8138e8a0fcf3a4e84a771d40fd305d7f4aa59306d7251de54d98af8fe95729a1f"
@@ -29,6 +34,15 @@
         BigInteger  modulus,
         BigInteger  exponent)
     {
+        this(isPrivate, modulus, exponent, false);
+    }   
+
+    public RSAKeyParameters(
+        boolean     isPrivate,
+        BigInteger  modulus,
+        BigInteger  exponent,
+        boolean     isInternal)
+    {
         super(isPrivate);
 
         if (!isPrivate)
@@ -38,13 +52,20 @@
                 throw new IllegalArgumentException("RSA publicExponent is even");
             }
         }
-
-        this.modulus = validate(modulus);
+  
+        this.modulus = validated.contains(modulus) ? modulus : validate(modulus, isInternal);
         this.exponent = exponent;
-    }   
+    }
 
-    private BigInteger validate(BigInteger modulus)
+    private BigInteger validate(BigInteger modulus, boolean isInternal)
     {
+        if (isInternal)
+        {
+            validated.add(modulus);
+
+            return modulus;
+        }
+
         if ((modulus.intValue() & 1) == 0)
         {
             throw new IllegalArgumentException("RSA modulus is even");
@@ -57,16 +78,45 @@
             return modulus;
         }
 
+        int maxBitLength = Properties.asInteger("com.android.org.bouncycastle.rsa.max_size", 15360);
+
+        int modBitLength = modulus.bitLength();
+        if (maxBitLength < modBitLength)
+        {
+            throw new IllegalArgumentException("modulus value out of range");
+        }
+
         if (!modulus.gcd(SMALL_PRIMES_PRODUCT).equals(ONE))
         {
             throw new IllegalArgumentException("RSA modulus has a small prime factor");
         }
 
-        // TODO: add additional primePower/Composite test - expensive!!
+        int bits = modulus.bitLength() / 2;
+        int iterations = Properties.asInteger("com.android.org.bouncycastle.rsa.max_mr_tests", getMRIterations(bits));
 
+        if (iterations > 0)
+        {
+            Primes.MROutput mr = Primes.enhancedMRProbablePrimeTest(modulus, CryptoServicesRegistrar.getSecureRandom(), iterations);
+            if (!mr.isProvablyComposite())
+            {
+                throw new IllegalArgumentException("RSA modulus is not composite");
+            }
+        }
+
+        validated.add(modulus);
+        
         return modulus;
     }
 
+    private static int getMRIterations(int bits)
+    {
+        int iterations = bits >= 1536 ? 3
+            : bits >= 1024 ? 4
+            : bits >= 512 ? 7
+            : 50;
+        return iterations;
+    }
+
     public BigInteger getModulus()
     {
         return modulus;
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/params/RSAPrivateCrtKeyParameters.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/params/RSAPrivateCrtKeyParameters.java
index 81ecfa6..32aa2be 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/params/RSAPrivateCrtKeyParameters.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/params/RSAPrivateCrtKeyParameters.java
@@ -20,6 +20,19 @@
      * 
      */
     public RSAPrivateCrtKeyParameters(
+         BigInteger  modulus,
+         BigInteger  publicExponent,
+         BigInteger  privateExponent,
+         BigInteger  p,
+         BigInteger  q,
+         BigInteger  dP,
+         BigInteger  dQ,
+         BigInteger  qInv)
+     {
+         this(modulus, publicExponent, privateExponent, p, q, dP, dQ, qInv, false);
+     }
+
+    public RSAPrivateCrtKeyParameters(
         BigInteger  modulus,
         BigInteger  publicExponent,
         BigInteger  privateExponent,
@@ -27,9 +40,10 @@
         BigInteger  q,
         BigInteger  dP,
         BigInteger  dQ,
-        BigInteger  qInv)
+        BigInteger  qInv,
+        boolean     isInternal)
     {
-        super(true, modulus, privateExponent);
+        super(true, modulus, privateExponent, isInternal);
 
         this.e = publicExponent;
         this.p = p;
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/signers/DSASigner.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/signers/DSASigner.java
index 1d3c992..9a977e6 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/signers/DSASigner.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/signers/DSASigner.java
@@ -70,6 +70,8 @@
             this.key = (DSAPublicKeyParameters)param;
         }
 
+        CryptoServicesRegistrar.checkConstraints(Utils.getDefaultProperties("DSA", key, forSigning));
+
         this.random = initSecureRandom(forSigning && !kCalculator.isDeterministic(), providedRandom);
     }
 
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/signers/ECDSASigner.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/signers/ECDSASigner.java
index e81ab37..c5e0d80 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/signers/ECDSASigner.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/signers/ECDSASigner.java
@@ -76,6 +76,8 @@
             this.key = (ECPublicKeyParameters)param;
         }
 
+        CryptoServicesRegistrar.checkConstraints(Utils.getDefaultProperties("ECDSA", key, forSigning));
+
         this.random = initSecureRandom(forSigning && !kCalculator.isDeterministic(), providedRandom);
     }
 
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/signers/Utils.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/signers/Utils.java
new file mode 100644
index 0000000..79c9a96
--- /dev/null
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/signers/Utils.java
@@ -0,0 +1,41 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.org.bouncycastle.crypto.signers;
+
+import com.android.org.bouncycastle.crypto.CipherParameters;
+import com.android.org.bouncycastle.crypto.CryptoServiceProperties;
+import com.android.org.bouncycastle.crypto.CryptoServicePurpose;
+import com.android.org.bouncycastle.crypto.constraints.ConstraintUtils;
+import com.android.org.bouncycastle.crypto.constraints.DefaultServiceProperties;
+import com.android.org.bouncycastle.crypto.params.DSAKeyParameters;
+import com.android.org.bouncycastle.crypto.params.ECKeyParameters;
+// Android-removed: unsupported algorithm
+// import org.bouncycastle.crypto.params.GOST3410KeyParameters;
+
+class Utils
+{
+    static CryptoServiceProperties getDefaultProperties(String algorithm, DSAKeyParameters k, boolean forSigning)
+    {
+        return new DefaultServiceProperties(algorithm, ConstraintUtils.bitsOfSecurityFor(k.getParameters().getP()), k, getPurpose(forSigning));
+    }
+
+    // Android-removed: unsupported algorithm
+    // static CryptoServiceProperties getDefaultProperties(String algorithm, GOST3410KeyParameters k, boolean forSigning)
+    // {
+    //     return new DefaultServiceProperties(algorithm, ConstraintUtils.bitsOfSecurityFor(k.getParameters().getP()), k, getPurpose(forSigning));
+    // }
+
+    static CryptoServiceProperties getDefaultProperties(String algorithm, ECKeyParameters k, boolean forSigning)
+    {
+        return new DefaultServiceProperties(algorithm, ConstraintUtils.bitsOfSecurityFor(k.getParameters().getCurve()), k, getPurpose(forSigning));
+    }
+
+    static CryptoServiceProperties getDefaultProperties(String algorithm, int bitsOfSecurity, CipherParameters k, boolean forSigning)
+    {
+        return new DefaultServiceProperties(algorithm, bitsOfSecurity, k, getPurpose(forSigning));
+    }
+
+    static CryptoServicePurpose getPurpose(boolean forSigning)
+    {
+        return forSigning ? CryptoServicePurpose.SIGNING : CryptoServicePurpose.VERIFYING;
+    }
+}
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/util/BasicAlphabetMapper.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/util/BasicAlphabetMapper.java
new file mode 100644
index 0000000..1ef1ef7
--- /dev/null
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/util/BasicAlphabetMapper.java
@@ -0,0 +1,107 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.org.bouncycastle.crypto.util;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import com.android.org.bouncycastle.crypto.AlphabetMapper;
+
+/**
+ * A basic alphabet mapper that just creates a mapper based on the
+ * passed in array of characters.
+ * @hide This class is not part of the Android public SDK API
+ */
+public class BasicAlphabetMapper
+    implements AlphabetMapper
+{
+    private Map<Character, Integer> indexMap = new HashMap<Character, Integer>();
+    private Map<Integer, Character> charMap = new HashMap<Integer, Character>();
+
+    /**
+     * Base constructor.
+     *
+     * @param alphabet a String of characters making up the alphabet.
+     */
+    public BasicAlphabetMapper(String alphabet)
+    {
+        this(alphabet.toCharArray());
+    }
+
+    /**
+     * Base constructor.
+     *
+     * @param alphabet an array of characters making up the alphabet.
+     */
+    public BasicAlphabetMapper(char[] alphabet)
+    {
+        for (int i = 0; i != alphabet.length; i++)
+        {
+            if (indexMap.containsKey(alphabet[i]))
+            {
+                throw new IllegalArgumentException("duplicate key detected in alphabet: " + alphabet[i]);
+            }
+            indexMap.put(alphabet[i], i);
+            charMap.put(i, alphabet[i]);
+        }
+    }
+
+    public int getRadix()
+    {
+        return indexMap.size();
+    }
+
+    public byte[] convertToIndexes(char[] input)
+    {
+        byte[] out;
+
+        if (indexMap.size() <= 256)
+        {
+            out = new byte[input.length];
+            for (int i = 0; i != input.length; i++)
+            {
+                out[i] = indexMap.get(input[i]).byteValue();
+            }
+        }
+        else
+        {
+            out = new byte[input.length * 2];
+            for (int i = 0; i != input.length; i++)
+            {
+                int idx = indexMap.get(input[i]);
+                out[i * 2] = (byte)((idx >> 8) & 0xff);
+                out[i * 2  + 1] = (byte)(idx & 0xff);
+            }
+        }
+
+        return out;
+    }
+
+    public char[] convertToChars(byte[] input)
+    {
+        char[] out;
+
+        if (charMap.size() <= 256)
+        {
+            out = new char[input.length];
+            for (int i = 0; i != input.length; i++)
+            {
+                out[i] = charMap.get(input[i] & 0xff);
+            }
+        }
+        else
+        {
+            if ((input.length & 0x1) != 0)
+            {
+                throw new IllegalArgumentException("two byte radix and input string odd length");
+            }
+            
+            out = new char[input.length / 2];
+            for (int i = 0; i != input.length; i += 2)
+            {
+                out[i / 2] = charMap.get(((input[i] << 8) & 0xff00) | (input[i + 1] & 0xff));
+            }
+        }
+
+        return out;
+    }
+}
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/util/PrivateKeyFactory.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/util/PrivateKeyFactory.java
index 4c3ea49..9cddb2b 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/util/PrivateKeyFactory.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/util/PrivateKeyFactory.java
@@ -70,6 +70,14 @@
     public static AsymmetricKeyParameter createKey(byte[] privateKeyInfoData)
         throws IOException
     {
+        if (privateKeyInfoData == null)
+        {
+            throw new IllegalArgumentException("privateKeyInfoData array null");
+        }
+        if (privateKeyInfoData.length == 0)
+        {
+            throw new IllegalArgumentException("privateKeyInfoData array empty");
+        }
         return createKey(PrivateKeyInfo.getInstance(ASN1Primitive.fromByteArray(privateKeyInfoData)));
     }
 
@@ -97,6 +105,11 @@
     public static AsymmetricKeyParameter createKey(PrivateKeyInfo keyInfo)
         throws IOException
     {
+        if (keyInfo == null)
+        {
+            throw new IllegalArgumentException("keyInfo argument null");
+        }
+
         AlgorithmIdentifier algId = keyInfo.getPrivateKeyAlgorithm();
         ASN1ObjectIdentifier algOID = algId.getAlgorithm();
 
@@ -139,12 +152,12 @@
         else if (algOID.equals(X9ObjectIdentifiers.id_dsa))
         {
             ASN1Integer derX = (ASN1Integer)keyInfo.parsePrivateKey();
-            ASN1Encodable de = algId.getParameters();
+            ASN1Encodable algParameters = algId.getParameters();
 
             DSAParameters parameters = null;
-            if (de != null)
+            if (algParameters != null)
             {
-                DSAParameter params = DSAParameter.getInstance(de.toASN1Primitive());
+                DSAParameter params = DSAParameter.getInstance(algParameters.toASN1Primitive());
                 parameters = new DSAParameters(params.getP(), params.getQ(), params.getG());
             }
 
@@ -184,32 +197,44 @@
         /*
         else if (algOID.equals(EdECObjectIdentifiers.id_X25519))
         {
-            return new X25519PrivateKeyParameters(getRawKey(keyInfo, X25519PrivateKeyParameters.KEY_SIZE), 0);
+            // Java 11 bug: exact length of X25519/X448 secret used in Java 11
+            if (X25519PrivateKeyParameters.KEY_SIZE == keyInfo.getPrivateKeyLength())
+            {
+                return new X25519PrivateKeyParameters(keyInfo.getPrivateKey().getOctets());
+            }
+
+            return new X25519PrivateKeyParameters(getRawKey(keyInfo));
         }
         else if (algOID.equals(EdECObjectIdentifiers.id_X448))
         {
-            return new X448PrivateKeyParameters(getRawKey(keyInfo, X448PrivateKeyParameters.KEY_SIZE), 0);
+            // Java 11 bug: exact length of X25519/X448 secret used in Java 11
+            if (X448PrivateKeyParameters.KEY_SIZE == keyInfo.getPrivateKeyLength())
+            {
+                return new X448PrivateKeyParameters(keyInfo.getPrivateKey().getOctets());
+            }
+
+            return new X448PrivateKeyParameters(getRawKey(keyInfo));
         }
         else if (algOID.equals(EdECObjectIdentifiers.id_Ed25519))
         {
-            return new Ed25519PrivateKeyParameters(getRawKey(keyInfo, Ed25519PrivateKeyParameters.KEY_SIZE), 0);
+            return new Ed25519PrivateKeyParameters(getRawKey(keyInfo));
         }
         else if (algOID.equals(EdECObjectIdentifiers.id_Ed448))
         {
-            return new Ed448PrivateKeyParameters(getRawKey(keyInfo, Ed448PrivateKeyParameters.KEY_SIZE), 0);
+            return new Ed448PrivateKeyParameters(getRawKey(keyInfo));
         }
         else if (
             algOID.equals(CryptoProObjectIdentifiers.gostR3410_2001) ||
                 algOID.equals(RosstandartObjectIdentifiers.id_tc26_gost_3410_12_512) ||
                 algOID.equals(RosstandartObjectIdentifiers.id_tc26_gost_3410_12_256))
         {
-            GOST3410PublicKeyAlgParameters gostParams = GOST3410PublicKeyAlgParameters.getInstance(keyInfo.getPrivateKeyAlgorithm().getParameters());
+            ASN1Encodable algParameters = algId.getParameters();
+            GOST3410PublicKeyAlgParameters gostParams = GOST3410PublicKeyAlgParameters.getInstance(algParameters);
             ECGOST3410Parameters ecSpec = null;
             BigInteger d = null;
-            ASN1Primitive p = keyInfo.getPrivateKeyAlgorithm().getParameters().toASN1Primitive();
+            ASN1Primitive p = algParameters.toASN1Primitive();
             if (p instanceof ASN1Sequence && (ASN1Sequence.getInstance(p).size() == 2 || ASN1Sequence.getInstance(p).size() == 3))
             {
-
                 X9ECParameters ecP = ECGOST3410NamedCurves.getByOIDX9(gostParams.getPublicKeyParamSet());
 
                 ecSpec = new ECGOST3410Parameters(
@@ -218,11 +243,12 @@
                     gostParams.getPublicKeyParamSet(),
                     gostParams.getDigestParamSet(),
                     gostParams.getEncryptionParamSet());
-                ASN1OctetString privEnc = keyInfo.getPrivateKey();
 
-                if (privEnc.getOctets().length == 32 || privEnc.getOctets().length == 64)
+                int privateKeyLength = keyInfo.getPrivateKeyLength();
+
+                if (privateKeyLength == 32 || privateKeyLength == 64)
                 {
-                    d = new BigInteger(1, Arrays.reverse(privEnc.getOctets()));
+                    d = new BigInteger(1, Arrays.reverse(keyInfo.getPrivateKey().getOctets()));
                 }
                 else
                 {
@@ -240,7 +266,7 @@
             }
             else
             {
-                X962Parameters params = X962Parameters.getInstance(keyInfo.getPrivateKeyAlgorithm().getParameters());
+                X962Parameters params = X962Parameters.getInstance(algId.getParameters());
 
                 if (params.isNamedCurve())
                 {
@@ -296,14 +322,8 @@
         }
     }
 
-    private static byte[] getRawKey(PrivateKeyInfo keyInfo, int expectedSize)
-        throws IOException
+    private static byte[] getRawKey(PrivateKeyInfo keyInfo) throws IOException
     {
-        byte[] result = ASN1OctetString.getInstance(keyInfo.parsePrivateKey()).getOctets();
-        if (expectedSize != result.length)
-        {
-            throw new RuntimeException("private key encoding has incorrect length");
-        }
-        return result;
+        return ASN1OctetString.getInstance(keyInfo.parsePrivateKey()).getOctets();
     }
 }
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/util/PublicKeyFactory.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/util/PublicKeyFactory.java
index 39e0967..55639f7 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/util/PublicKeyFactory.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/util/PublicKeyFactory.java
@@ -7,13 +7,13 @@
 import java.util.HashMap;
 import java.util.Map;
 
+import com.android.org.bouncycastle.asn1.ASN1BitString;
 import com.android.org.bouncycastle.asn1.ASN1Encodable;
 import com.android.org.bouncycastle.asn1.ASN1InputStream;
 import com.android.org.bouncycastle.asn1.ASN1Integer;
 import com.android.org.bouncycastle.asn1.ASN1ObjectIdentifier;
 import com.android.org.bouncycastle.asn1.ASN1OctetString;
 import com.android.org.bouncycastle.asn1.ASN1Primitive;
-import com.android.org.bouncycastle.asn1.DERBitString;
 import com.android.org.bouncycastle.asn1.DEROctetString;
 // Android-removed: Unsupported algorithms
 // import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers;
@@ -115,6 +115,14 @@
     public static AsymmetricKeyParameter createKey(byte[] keyInfoData)
         throws IOException
     {
+        if (keyInfoData == null)
+        {
+            throw new IllegalArgumentException("keyInfoData array null");
+        }
+        if (keyInfoData.length == 0)
+        {
+            throw new IllegalArgumentException("keyInfoData array empty");
+        }
         return createKey(SubjectPublicKeyInfo.getInstance(ASN1Primitive.fromByteArray(keyInfoData)));
     }
 
@@ -141,6 +149,10 @@
     public static AsymmetricKeyParameter createKey(SubjectPublicKeyInfo keyInfo)
         throws IOException
     {
+        if (keyInfo == null)
+        {
+            throw new IllegalArgumentException("keyInfo argument null");
+        }
         return createKey(keyInfo, null);
     }
 
@@ -155,6 +167,11 @@
     public static AsymmetricKeyParameter createKey(SubjectPublicKeyInfo keyInfo, Object defaultParams)
         throws IOException
     {
+        if (keyInfo == null)
+        {
+            throw new IllegalArgumentException("keyInfo argument null");
+        }
+
         AlgorithmIdentifier algID = keyInfo.getAlgorithm();
 
         SubjectPublicKeyInfoConverter converter = (SubjectPublicKeyInfoConverter)converters.get(algID.getAlgorithm());
@@ -306,7 +323,7 @@
                 dParams = new ECDomainParameters(x9);
             }
 
-            DERBitString bits = keyInfo.getPublicKeyData();
+            ASN1BitString bits = keyInfo.getPublicKeyData();
             byte[] data = bits.getBytes();
             ASN1OctetString key = new DEROctetString(data);
 
@@ -483,7 +500,7 @@
                 }
                 BigInteger b = new BigInteger(1, b_bytes);
                 DSTU4145BinaryField field = binary.getField();
-                ECCurve curve = new ECCurve.F2m(field.getM(), field.getK1(), field.getK2(), field.getK3(), binary.getA(), b);
+                ECCurve curve = new ECCurve.F2m(field.getM(), field.getK1(), field.getK2(), field.getK3(), binary.getA(), b, null, null);
                 byte[] g_bytes = binary.getG();
                 if (algOid.equals(UAObjectIdentifiers.dstu4145le))
                 {
@@ -516,7 +533,7 @@
     {
         AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Object defaultParams)
         {
-            return new X25519PublicKeyParameters(getRawKey(keyInfo, defaultParams, X25519PublicKeyParameters.KEY_SIZE), 0);
+            return new X25519PublicKeyParameters(getRawKey(keyInfo, defaultParams));
         }
     }
 
@@ -525,7 +542,7 @@
     {
         AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Object defaultParams)
         {
-            return new X448PublicKeyParameters(getRawKey(keyInfo, defaultParams, X448PublicKeyParameters.KEY_SIZE), 0);
+            return new X448PublicKeyParameters(getRawKey(keyInfo, defaultParams));
         }
     }
 
@@ -534,7 +551,7 @@
     {
         AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Object defaultParams)
         {
-            return new Ed25519PublicKeyParameters(getRawKey(keyInfo, defaultParams, Ed25519PublicKeyParameters.KEY_SIZE), 0);
+            return new Ed25519PublicKeyParameters(getRawKey(keyInfo, defaultParams));
         }
     }
 
@@ -543,24 +560,19 @@
     {
         AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Object defaultParams)
         {
-            return new Ed448PublicKeyParameters(getRawKey(keyInfo, defaultParams, Ed448PublicKeyParameters.KEY_SIZE), 0);
+            return new Ed448PublicKeyParameters(getRawKey(keyInfo, defaultParams));
         }
     }
     */
     // END Android-removed: Unsupported algorithms
 
-    private static byte[] getRawKey(SubjectPublicKeyInfo keyInfo, Object defaultParams, int expectedSize)
+    private static byte[] getRawKey(SubjectPublicKeyInfo keyInfo, Object defaultParams)
     {
         /*
          * TODO[RFC 8422]
          * - Require defaultParams == null?
          * - Require keyInfo.getAlgorithm().getParameters() == null?
          */
-        byte[] result = keyInfo.getPublicKeyData().getOctets();
-        if (expectedSize != result.length)
-        {
-            throw new RuntimeException("public key encoding has incorrect length");
-        }
-        return result;
+        return keyInfo.getPublicKeyData().getOctets();
     }
 }
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/util/RadixConverter.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/util/RadixConverter.java
new file mode 100644
index 0000000..81ba33d
--- /dev/null
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/util/RadixConverter.java
@@ -0,0 +1,190 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.org.bouncycastle.crypto.util;
+
+import java.math.BigInteger;
+
+import com.android.org.bouncycastle.util.BigIntegers;
+
+/**
+ * Utility class to convert decimal numbers (BigInteger) into a number in the base provided and the other way round.
+ * <p>For an application of this see the FPE parameter classes.</p>
+ * @hide This class is not part of the Android public SDK API
+ */
+public class RadixConverter
+{
+
+    /*
+    The conversions in this class are more complex than the standard ways of converting between basis because we want to improve the performance by limiting the
+    operations on BigInteger which are not very efficient.
+    The general idea is to perform math operations on primitive long as much as we can and just work with BigInteger when necessary.
+    Converting between basis uses the fact that a number in base 'B' have a unique representation in polynomial form.
+
+    num = ... + r8B^8 + r7B^7 + r6B^6 + r5B^5 + r4B^4 + r3B^3 + r2B^2 + r1B + r0
+
+    We can compute how many digits in base 'B' can fit in a long. For example, for a radix R=2^16 the number of digits 'n' that we can fit into a long is the
+    max 'n' that still satisfies: R^n < Long.MAX_VALUE (i.e. 2^63 -1). In this case 'n' is 3. To convert 'num' from its decimal representation to base 'B'
+    representation we can write down 'num' in a polynomial form of B^3:
+
+    num = (((...)B^3 + r8B^2 + r7B + r6)B^3 + r5B^2 + r4B + r3)B^3 + (r2B^2 + r1B + r0)
+
+    B^3 would be our intermediate base. We can convert numbers in base B^3 while operating on primitive long.
+    To convert a decimal num to its representation in base B we can first build its B^3 representation and then figure out the digits in base B from the single
+    digit in base B^3. num % B^3 gives us a single digit in base B^3 which corresponds to a group of 3 digits in base B.
+
+    An equivalent way of writing the polynomial form of num would be:
+
+    num = (...)B^9 + (r8B^8 + r7B^7 + r6B^6)B^6 + (r5B^5 + r4B^4 + r3)B^3 + (r2B^2 + r1B + r0)
+
+    In this form it becomes clear that to obtain 'num' from a sequence of digits, one can group the digits in group of 3 and compute the corresponding decimal
+    number for the group in base B. We can then multiply the decimal numbers by the corresponding power of B^3 and sum up the result to obtain the decimal
+    representation of num in base B,
+     */
+    private static final double LOG_LONG_MAX_VALUE = Math.log(Long.MAX_VALUE);
+    private static final int DEFAULT_POWERS_TO_CACHE = 10;
+    // the max number of digits in base 'radix' that fits in a long
+    private final int digitsGroupLength;
+    // the total number of digits combination in a group. radix ^ digitsGroupLength
+    private final BigInteger digitsGroupSpaceSize;
+    private final int radix;
+    private final BigInteger[] digitsGroupSpacePowers;
+
+    /**
+     * @param radix                the radix to use for base conversions
+     * @param numberOfCachedPowers number of intermediate base powers to precompute and cache.
+     */
+    public RadixConverter(int radix, int numberOfCachedPowers)
+    {
+        this.radix = radix;
+        // solves radix^n < Long.MAX_VALUE to find n (maxDigitsFitsInLong)
+        this.digitsGroupLength = (int)Math.floor(LOG_LONG_MAX_VALUE / Math.log(radix));
+        this.digitsGroupSpaceSize = BigInteger.valueOf(radix).pow(digitsGroupLength);
+        this.digitsGroupSpacePowers = precomputeDigitsGroupPowers(numberOfCachedPowers, digitsGroupSpaceSize);
+    }
+
+    /**
+     * @param radix the radix to use for base conversions.
+     */
+    public RadixConverter(int radix)
+    {
+        this(radix, DEFAULT_POWERS_TO_CACHE);
+    }
+
+    public int getRadix()
+    {
+        return radix;
+    }
+
+    public void toEncoding(BigInteger number, int messageLength, short[] out)
+    {
+        if (number.signum() < 0)
+        {
+            throw new IllegalArgumentException();
+        }
+        // convert number into its representation in base 'radix'.
+        // writes leading '0' if the messageLength is greater than the number of digits required to encode in base 'radix'
+        int digitIndex = messageLength - 1;
+        do
+        {
+            if (number.equals(BigInteger.ZERO))
+            {
+                out[digitIndex--] = 0;
+                continue;
+            }
+            BigInteger[] quotientAndRemainder = number.divideAndRemainder(digitsGroupSpaceSize);
+            number = quotientAndRemainder[0];
+            digitIndex = toEncoding(quotientAndRemainder[1].longValue(), digitIndex, out);
+        }
+        while (digitIndex >= 0);
+        if (number.signum() != 0)
+        {
+            throw new IllegalArgumentException();
+        }
+    }
+
+    private int toEncoding(long number, int digitIndex, short[] out)
+    {
+        for (int i = 0; i < digitsGroupLength && digitIndex >= 0; i++)
+        {
+            if (number == 0)
+            {
+                out[digitIndex--] = 0;
+                continue;
+            }
+            out[digitIndex--] = (short)(number % radix);
+            number = number / radix;
+        }
+        if (number != 0)
+        {
+            throw new IllegalStateException("Failed to convert decimal number");
+        }
+        return digitIndex;
+    }
+
+    public BigInteger fromEncoding(short[] digits)
+    {
+        // from a sequence of digits in base 'radix' to a decimal number
+        // iterate through groups of digits right to left
+        // digitsGroupLength = 2;  digits: [22, 45, 11, 31, 24]
+        // groups are, in order of iteration: [31, 24], [45, 11], [22]
+        BigInteger currentGroupCardinality = BigIntegers.ONE;
+        BigInteger res = null;
+        int indexGroup = 0;
+        int numberOfDigits = digits.length;
+        for (int groupStartDigitIndex = numberOfDigits - digitsGroupLength;
+             groupStartDigitIndex > -digitsGroupLength;
+             groupStartDigitIndex = groupStartDigitIndex - digitsGroupLength)
+        {
+            int actualDigitsInGroup = digitsGroupLength;
+            if (groupStartDigitIndex < 0)
+            {
+                // last group might contain fewer digits so adjust offsets
+                actualDigitsInGroup = digitsGroupLength + groupStartDigitIndex;
+                groupStartDigitIndex = 0;
+            }
+            int groupEndDigitIndex = Math.min(groupStartDigitIndex + actualDigitsInGroup, numberOfDigits);
+            long groupInBaseRadix = fromEncoding(groupStartDigitIndex, groupEndDigitIndex, digits);
+            BigInteger bigInteger = BigInteger.valueOf(groupInBaseRadix);
+            if (indexGroup == 0)
+            {
+                res = bigInteger;
+            }
+            else
+            {
+                currentGroupCardinality =
+                    indexGroup <= digitsGroupSpacePowers.length
+                        ? digitsGroupSpacePowers[indexGroup - 1]
+                        : currentGroupCardinality.multiply(digitsGroupSpaceSize);
+                res = res.add(bigInteger.multiply(currentGroupCardinality));
+            }
+            indexGroup++;
+        }
+        return res;
+    }
+
+    public int getDigitsGroupLength()
+    {
+        return digitsGroupLength;
+    }
+
+    private long fromEncoding(int groupStartDigitIndex, int groupEndDigitIndex, short[] digits)
+    {
+        long decimalNumber = 0;
+        for (int digitIndex = groupStartDigitIndex; digitIndex < groupEndDigitIndex; digitIndex++)
+        {
+            decimalNumber = (decimalNumber * radix) + (digits[digitIndex] & 0xFFFF);
+        }
+        return decimalNumber;
+    }
+
+    private BigInteger[] precomputeDigitsGroupPowers(int numberOfCachedPowers, BigInteger digitsGroupSpaceSize)
+    {
+        BigInteger[] cachedPowers = new BigInteger[numberOfCachedPowers];
+        BigInteger currentPower = digitsGroupSpaceSize;
+        for (int i = 0; i < numberOfCachedPowers; i++)
+        {
+            cachedPowers[i] = currentPower;
+            currentPower = currentPower.multiply(digitsGroupSpaceSize);
+        }
+        return cachedPowers;
+    }
+}
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/util/SSHNamedCurves.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/util/SSHNamedCurves.java
index 7c65234..a5ae9f7 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/util/SSHNamedCurves.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/crypto/util/SSHNamedCurves.java
@@ -73,8 +73,8 @@
             while (e.hasMoreElements())
             {
                 String name = (String)e.nextElement();
-                X9ECParameters parameters = CustomNamedCurves.getByName(name);
-                put(parameters.getCurve(), name);
+                ECCurve curve = CustomNamedCurves.getByNameLazy(name).getCurve();
+                put(curve, name);
             }
 
         }
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/cms/GCMParameters.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/internal/asn1/cms/GCMParameters.java
similarity index 89%
rename from repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/cms/GCMParameters.java
rename to repackaged/bcprov/src/main/java/com/android/org/bouncycastle/internal/asn1/cms/GCMParameters.java
index 17e3ba0..1443718 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/cms/GCMParameters.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/internal/asn1/cms/GCMParameters.java
@@ -1,5 +1,5 @@
 /* GENERATED SOURCE. DO NOT MODIFY. */
-package com.android.org.bouncycastle.asn1.cms;
+package com.android.org.bouncycastle.internal.asn1.cms;
 
 import com.android.org.bouncycastle.asn1.ASN1EncodableVector;
 import com.android.org.bouncycastle.asn1.ASN1Integer;
@@ -33,8 +33,8 @@
      * Accepted inputs:
      * <ul>
      * <li> null &rarr; null
-     * <li> {@link com.android.org.bouncycastle.asn1.cms.GCMParameters} object
-     * <li> {@link com.android.org.bouncycastle.asn1.ASN1Sequence#getInstance(Object) ASN1Sequence} input formats with GCMParameters structure inside
+     * <li> {@link GCMParameters} object
+     * <li> {@link ASN1Sequence#getInstance(Object) ASN1Sequence} input formats with GCMParameters structure inside
      * </ul>
      *
      * @param obj the object we want converted.
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/isismtt/ISISMTTObjectIdentifiers.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/internal/asn1/isismtt/ISISMTTObjectIdentifiers.java
similarity index 94%
rename from repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/isismtt/ISISMTTObjectIdentifiers.java
rename to repackaged/bcprov/src/main/java/com/android/org/bouncycastle/internal/asn1/isismtt/ISISMTTObjectIdentifiers.java
index e2f0901..511fb95 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/asn1/isismtt/ISISMTTObjectIdentifiers.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/internal/asn1/isismtt/ISISMTTObjectIdentifiers.java
@@ -1,5 +1,5 @@
 /* GENERATED SOURCE. DO NOT MODIFY. */
-package com.android.org.bouncycastle.asn1.isismtt;
+package com.android.org.bouncycastle.internal.asn1.isismtt;
 
 import com.android.org.bouncycastle.asn1.ASN1ObjectIdentifier;
 
@@ -105,8 +105,6 @@
      * </pre>
      * <p>
      * OID: 1.3.36.8.3.8
-     * 
-     * @see com.android.org.bouncycastle.asn1.isismtt.x509.Restriction
      */
     static final ASN1ObjectIdentifier id_isismtt_at_restriction = id_isismtt_at.branch("8");
 
@@ -131,8 +129,6 @@
      * returned in this extension.
      * <p>
      * OID: 1.3.36.8.3.10
-     * 
-     * @see com.android.org.bouncycastle.asn1.isismtt.ocsp.RequestedCertificate
      */
     static final ASN1ObjectIdentifier id_isismtt_at_requestedCertificate = id_isismtt_at.branch("10");
 
@@ -161,8 +157,7 @@
      * Hash of a certificate in OCSP.
      * <p>
      * OID: 1.3.36.8.3.13
-     * 
-     * @see com.android.org.bouncycastle.asn1.isismtt.ocsp.CertHash
+     *
      */
     static final ASN1ObjectIdentifier id_isismtt_at_certHash = id_isismtt_at.branch("13");
 
@@ -188,8 +183,7 @@
      * </pre>
      * <p>
      * OID: 1.3.36.8.3.15
-     * 
-     * @see com.android.org.bouncycastle.asn1.isismtt.x509.AdditionalInformationSyntax
+     *
      */
     static final ASN1ObjectIdentifier id_isismtt_at_additionalInformation = id_isismtt_at.branch("15");
 
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/its/asn1/EndEntityType.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/its/asn1/EndEntityType.java
deleted file mode 100644
index b95294a..0000000
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/its/asn1/EndEntityType.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/* GENERATED SOURCE. DO NOT MODIFY. */
-package com.android.org.bouncycastle.its.asn1;
-
-import com.android.org.bouncycastle.asn1.ASN1BitString;
-import com.android.org.bouncycastle.asn1.ASN1Object;
-import com.android.org.bouncycastle.asn1.ASN1Primitive;
-import com.android.org.bouncycastle.asn1.DERBitString;
-
-/**
- * <pre>
- *     EndEntityType ::= BIT STRING { app(0), enrol(1) } (SIZE (8)) (ALL EXCEPT ())
- * </pre>
- * @hide This class is not part of the Android public SDK API
- */
-public class EndEntityType
-    extends ASN1Object
-{
-    public static final int        app = (1 << 7);
-    public static final int        enrol = (1 << 6);
-
-    private final ASN1BitString type;
-
-    public EndEntityType(int type)
-    {
-        if (type != app && type != enrol)
-        {
-            throw new IllegalArgumentException("value out of range");
-        }
-
-        this.type = new DERBitString(type);
-    }
-
-    private EndEntityType(DERBitString str)
-    {
-        this.type = str;
-    }
-
-    public static EndEntityType getInstance(Object src)
-    {
-        if (src instanceof EndEntityType)
-        {
-            return (EndEntityType)src;
-        }
-        else if (src != null)
-        {
-            return new EndEntityType(DERBitString.getInstance(src));
-        }
-
-        return null;
-    }
-
-    public ASN1Primitive toASN1Primitive()
-    {
-        return type;
-    }
-}
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/its/asn1/PsidGroupPermissions.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/its/asn1/PsidGroupPermissions.java
deleted file mode 100644
index 1ad2194..0000000
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/its/asn1/PsidGroupPermissions.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/* GENERATED SOURCE. DO NOT MODIFY. */
-package com.android.org.bouncycastle.its.asn1;
-
-import java.math.BigInteger;
-
-import com.android.org.bouncycastle.asn1.ASN1Integer;
-import com.android.org.bouncycastle.asn1.ASN1Object;
-import com.android.org.bouncycastle.asn1.ASN1Primitive;
-import com.android.org.bouncycastle.asn1.ASN1Sequence;
-
-/**
- * <pre>
- *     PsidGroupPermissions ::= SEQUENCE {
- *         subjectPermissions SubjectPermissions,
- *         minChainLength INTEGER DEFAULT 1,
- *         chainLengthRange INTEGER DEFAULT 0,
- *         eeType EndEntityType DEFAULT (app)
- *     }
- * </pre>
- * @hide This class is not part of the Android public SDK API
- */
-public class PsidGroupPermissions
-    extends ASN1Object
-{
-    private final SubjectPermissions subjectPermissions;
-    private final BigInteger minChainLength;
-    private final BigInteger chainLengthRange;
-    private final Object eeType;
-
-    private PsidGroupPermissions(ASN1Sequence seq)
-    {
-        if (seq.size() != 2)
-        {
-            throw new IllegalArgumentException("sequence not length 2");
-        }
-
-        this.subjectPermissions = SubjectPermissions.getInstance(seq.getObjectAt(0));
-        this.minChainLength = ASN1Integer.getInstance(seq.getObjectAt(1)).getValue();
-        this.chainLengthRange = ASN1Integer.getInstance(seq.getObjectAt(2)).getValue();
-        this.eeType = EndEntityType.getInstance(seq.getObjectAt(3));
-    }
-
-    public static PsidGroupPermissions getInstance(Object src)
-    {
-        if (src instanceof PsidGroupPermissions)
-        {
-            return (PsidGroupPermissions)src;
-        }
-        else if (src != null)
-        {
-            return new PsidGroupPermissions(ASN1Sequence.getInstance(src));
-        }
-
-        return null;
-    }
-
-    public ASN1Primitive toASN1Primitive()
-    {
-        return null;
-    }
-}
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/BCLoadStoreParameter.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/BCLoadStoreParameter.java
new file mode 100644
index 0000000..88c7e9b
--- /dev/null
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/BCLoadStoreParameter.java
@@ -0,0 +1,75 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.org.bouncycastle.jcajce;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.security.KeyStore;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+public class BCLoadStoreParameter
+    implements KeyStore.LoadStoreParameter
+{
+    private final InputStream in;
+    private final OutputStream out;
+    private final KeyStore.ProtectionParameter protectionParameter;
+
+    /**
+     * Base constructor for
+     *
+     * @param out
+     * @param password
+     */
+    public BCLoadStoreParameter(OutputStream out, char[] password)
+    {
+        this(out, new KeyStore.PasswordProtection(password));
+    }
+
+    public BCLoadStoreParameter(InputStream in, char[] password)
+    {
+        this(in, new KeyStore.PasswordProtection(password));
+    }
+
+    public BCLoadStoreParameter(InputStream in, KeyStore.ProtectionParameter protectionParameter)
+    {
+        this(in, null, protectionParameter);
+    }
+
+    public BCLoadStoreParameter(OutputStream out, KeyStore.ProtectionParameter protectionParameter)
+    {
+        this(null, out, protectionParameter);
+    }
+
+    BCLoadStoreParameter(InputStream in, OutputStream out, KeyStore.ProtectionParameter protectionParameter)
+    {
+        this.in = in;
+        this.out = out;
+        this.protectionParameter = protectionParameter;
+    }
+
+    public KeyStore.ProtectionParameter getProtectionParameter()
+    {
+        return protectionParameter;
+    }
+
+    public OutputStream getOutputStream()
+    {
+        if (out == null)
+        {
+            throw new UnsupportedOperationException("parameter not configured for storage - no OutputStream");
+        }
+
+        return out;
+    }
+
+    public InputStream getInputStream()
+    {
+        if (out != null)
+        {
+            throw new UnsupportedOperationException("parameter configured for storage OutputStream present");
+        }
+
+        return in;
+    }
+}
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/CompositePrivateKey.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/CompositePrivateKey.java
index f5edf6d..d65f44e 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/CompositePrivateKey.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/CompositePrivateKey.java
@@ -75,7 +75,7 @@
         try
         {
             return new PrivateKeyInfo(
-                new AlgorithmIdentifier(MiscObjectIdentifiers.id_alg_composite), new DERSequence(v)).getEncoded(ASN1Encoding.DER);
+                new AlgorithmIdentifier(MiscObjectIdentifiers.id_composite_key), new DERSequence(v)).getEncoded(ASN1Encoding.DER);
         }
         catch (IOException e)
         {
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/CompositePublicKey.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/CompositePublicKey.java
index d7161e8..aa57747 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/CompositePublicKey.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/CompositePublicKey.java
@@ -75,7 +75,7 @@
         try
         {
             return new SubjectPublicKeyInfo(
-                new AlgorithmIdentifier(MiscObjectIdentifiers.id_alg_composite), new DERSequence(v)).getEncoded(ASN1Encoding.DER);
+                new AlgorithmIdentifier(MiscObjectIdentifiers.id_composite_key), new DERSequence(v)).getEncoded(ASN1Encoding.DER);
         }
         catch (IOException e)
         {
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/ExternalPublicKey.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/ExternalPublicKey.java
new file mode 100644
index 0000000..dd894b3
--- /dev/null
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/ExternalPublicKey.java
@@ -0,0 +1,105 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.org.bouncycastle.jcajce;
+
+import java.io.IOException;
+import java.security.MessageDigest;
+import java.security.PublicKey;
+
+import com.android.org.bouncycastle.asn1.ASN1Encoding;
+import com.android.org.bouncycastle.asn1.bc.BCObjectIdentifiers;
+import com.android.org.bouncycastle.asn1.bc.ExternalValue;
+import com.android.org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import com.android.org.bouncycastle.asn1.x509.GeneralName;
+import com.android.org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import com.android.org.bouncycastle.jcajce.util.MessageDigestUtils;
+import com.android.org.bouncycastle.util.Arrays;
+
+/**
+ * Wrapper class which returns an "ExternalValue" for the public key encoding. In this case
+ * the key encoding is a hash and the actual key needs to be looked up somewhere else. Useful
+ * for where the public keys are really large but it's required to keep certificates small.
+ * @hide This class is not part of the Android public SDK API
+ */
+public class ExternalPublicKey
+    implements PublicKey
+{
+    private final GeneralName location;
+    private final AlgorithmIdentifier digestAlg;
+    private final byte[] digest;
+
+    /**
+     * Base constructor with fundamental contents.
+     *
+     * @param location location URI for the actual public key.
+     * @param digestAlg hashing algorithm used to hash the actual public key encoding.
+     * @param digest digest of the actual public key.
+     */
+    public ExternalPublicKey(GeneralName location, AlgorithmIdentifier digestAlg, byte[] digest)
+    {
+        this.location = location;
+        this.digestAlg = digestAlg;
+        this.digest = Arrays.clone(digest);
+    }
+
+    /**
+     * Helper constructor with JCA contents.
+     *
+     * @param key the public key we are externalising.
+     * @param location location URI for the actual public key.
+     * @param digest digest to use for hashing the key.
+     */
+    // Android-removed: unsupported
+    // public ExternalPublicKey(PublicKey key, GeneralName location, MessageDigest digest)
+    // {
+    //     this(location, MessageDigestUtils.getDigestAlgID(digest.getAlgorithm()), digest.digest(key.getEncoded()));
+    // }
+
+    /**
+     * Base constructor with ASN.1 structure.
+     *
+     * @param extKey structure with location, hashing algorithm and hash for the public key.
+     */
+    public ExternalPublicKey(ExternalValue extKey)
+    {
+        this(extKey.getLocation(), extKey.getHashAlg(), extKey.getHashValue());
+    }
+
+    /**
+     * Return "ExternalKey"
+     *
+     * @return  "ExternalKey"
+     */
+    public String getAlgorithm()
+    {
+        return "ExternalKey";
+    }
+
+    /**
+     * Return "X.509" (DER encoded SubjectPublicKeyInfo)
+     *
+     * @return  "X.509"
+     */
+    public String getFormat()
+    {
+        return "X.509";
+    }
+
+    /**
+     * Return a SubjectPublicKeyInfo structure containing an ExternalValue encoding for the key.
+     *
+     * @return a DER encoding of SubjectPublicKeyInfo containing an ExternalValue structure.
+     */
+    public byte[] getEncoded()
+    {
+        try
+        {
+            return new SubjectPublicKeyInfo(
+                new AlgorithmIdentifier(BCObjectIdentifiers.external_value),
+                    new ExternalValue(location, digestAlg, digest)).getEncoded(ASN1Encoding.DER);
+        }
+        catch (IOException e)
+        {
+            throw new IllegalStateException("unable to encode composite key: " + e.getMessage());
+        }
+    }
+}
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/SecretKeyWithEncapsulation.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/SecretKeyWithEncapsulation.java
new file mode 100644
index 0000000..4abb0fa
--- /dev/null
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/SecretKeyWithEncapsulation.java
@@ -0,0 +1,79 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.org.bouncycastle.jcajce;
+
+import javax.crypto.SecretKey;
+
+import com.android.org.bouncycastle.util.Arrays;
+
+/**
+ * Carrier class for a KEM/KTS secret key plus its encapsulation.
+ * @hide This class is not part of the Android public SDK API
+ */
+public final class SecretKeyWithEncapsulation
+    implements SecretKey
+{
+    private final SecretKey secretKey;
+    private final byte[] encapsulation;
+
+    /**
+     * Basic constructor.
+     *
+     * @param secretKey the secret key that was arrived at.
+     * @param encapsulation the encapsulation the key data was carried in.
+     */
+    public SecretKeyWithEncapsulation(SecretKey secretKey, byte[] encapsulation)
+    {
+        this.secretKey = secretKey;
+        this.encapsulation = Arrays.clone(encapsulation);
+    }
+
+    /**
+     * Return the algorithm for the agreed secret key.
+     *
+     * @return the secret key value.
+     */
+    public String getAlgorithm()
+    {
+        return secretKey.getAlgorithm();
+    }
+
+    /**
+     * Return the format for the agreed secret key.
+     *
+     * @return the secret key format.
+     */
+    public String getFormat()
+    {
+        return secretKey.getFormat();
+    }
+
+    /**
+     * Return the encoding of the agreed secret key.
+     *
+     * @return the secret key encoding.
+     */
+    public byte[] getEncoded()
+    {
+        return secretKey.getEncoded();
+    }
+
+    /**
+     * Return the encapsulation that carried the key material used in creating the agreed secret key.
+     *
+     * @return the encrypted encapsulation of the agreed secret key.
+     */
+    public byte[] getEncapsulation()
+    {
+        return Arrays.clone(encapsulation);
+    }
+
+    public boolean equals(Object o)
+    {
+        return secretKey.equals(o);
+    }
+
+    public int hashCode()
+    {
+        return secretKey.hashCode();
+    }
+}
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/io/SignatureUpdatingOutputStream.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/io/SignatureUpdatingOutputStream.java
index c96bcf2..db02afa 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/io/SignatureUpdatingOutputStream.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/io/SignatureUpdatingOutputStream.java
@@ -6,6 +6,8 @@
 import java.security.Signature;
 import java.security.SignatureException;
 
+import com.android.org.bouncycastle.util.Exceptions;
+
 class SignatureUpdatingOutputStream
     extends OutputStream
 {
@@ -25,7 +27,7 @@
         }
         catch (SignatureException e)
         {
-            throw new IOException(e.getMessage());
+            throw Exceptions.ioException(e.getMessage(), e);
         }
     }
 
@@ -38,7 +40,7 @@
         }
         catch (SignatureException e)
         {
-            throw new IOException(e.getMessage());
+            throw Exceptions.ioException(e.getMessage(), e);
         }
     }
 
@@ -51,7 +53,7 @@
         }
         catch (SignatureException e)
         {
-            throw new IOException(e.getMessage());
+            throw Exceptions.ioException(e.getMessage(), e);
         }
     }
 }
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/asymmetric/COMPOSITE.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/asymmetric/COMPOSITE.java
new file mode 100644
index 0000000..58e8b06
--- /dev/null
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/asymmetric/COMPOSITE.java
@@ -0,0 +1,149 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.org.bouncycastle.jcajce.provider.asymmetric;
+
+import java.io.IOException;
+import java.security.InvalidKeyException;
+import java.security.Key;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.util.HashMap;
+import java.util.Map;
+
+import com.android.org.bouncycastle.asn1.ASN1Sequence;
+import com.android.org.bouncycastle.asn1.misc.MiscObjectIdentifiers;
+import com.android.org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
+import com.android.org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import com.android.org.bouncycastle.jcajce.CompositePrivateKey;
+import com.android.org.bouncycastle.jcajce.CompositePublicKey;
+import com.android.org.bouncycastle.jcajce.provider.asymmetric.util.BaseKeyFactorySpi;
+import com.android.org.bouncycastle.jcajce.provider.config.ConfigurableProvider;
+import com.android.org.bouncycastle.jcajce.provider.util.AsymmetricAlgorithmProvider;
+import com.android.org.bouncycastle.jcajce.provider.util.AsymmetricKeyInfoConverter;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+public class COMPOSITE
+{
+    private static final String PREFIX = "com.android.org.bouncycastle.jcajce.provider.asymmetric.COMPOSITE";
+
+    private static final Map<String, String> compositeAttributes = new HashMap<String, String>();
+
+    static
+    {
+        compositeAttributes.put("SupportedKeyClasses", "com.android.org.bouncycastle.jcajce.CompositePublicKey|com.android.org.bouncycastle.jcajce.CompositePrivateKey");
+        compositeAttributes.put("SupportedKeyFormats", "PKCS#8|X.509");
+    }
+
+    private static AsymmetricKeyInfoConverter baseConverter;
+
+    /**
+     * @hide This class is not part of the Android public SDK API
+     */
+    public static class KeyFactory
+        extends BaseKeyFactorySpi
+    {
+        protected Key engineTranslateKey(Key key)
+            throws InvalidKeyException
+        {
+            try
+            {
+                if (key instanceof PrivateKey)
+                {
+                    return generatePrivate(PrivateKeyInfo.getInstance(key.getEncoded()));
+                }
+                else if (key instanceof PublicKey)
+                {
+                    return generatePublic(SubjectPublicKeyInfo.getInstance(key.getEncoded()));
+                }
+            }
+            catch (IOException e)
+            {
+                throw new InvalidKeyException("key could not be parsed: " + e.getMessage());
+            }
+
+            throw new InvalidKeyException("key not recognized");
+        }
+
+        public PrivateKey generatePrivate(PrivateKeyInfo keyInfo)
+            throws IOException
+        {
+            return baseConverter.generatePrivate(keyInfo);
+        }
+
+        public PublicKey generatePublic(SubjectPublicKeyInfo keyInfo)
+            throws IOException
+        {
+            return baseConverter.generatePublic(keyInfo);
+        }
+    }
+
+    private static class CompositeKeyInfoConverter
+        implements AsymmetricKeyInfoConverter
+    {
+        private final ConfigurableProvider provider;
+
+        public CompositeKeyInfoConverter(ConfigurableProvider provider)
+        {
+            this.provider = provider;
+        }
+
+        public PrivateKey generatePrivate(PrivateKeyInfo keyInfo)
+            throws IOException
+        {
+            ASN1Sequence keySeq = ASN1Sequence.getInstance(keyInfo.parsePrivateKey());
+            PrivateKey[] privKeys = new PrivateKey[keySeq.size()];
+
+            for (int i = 0; i != keySeq.size(); i++)
+            {
+                PrivateKeyInfo privInfo = PrivateKeyInfo.getInstance(keySeq.getObjectAt(i));
+
+                privKeys[i] = provider.getKeyInfoConverter(
+                    privInfo.getPrivateKeyAlgorithm().getAlgorithm()).generatePrivate(privInfo);
+            }
+
+            return new CompositePrivateKey(privKeys);
+        }
+
+        public PublicKey generatePublic(SubjectPublicKeyInfo keyInfo)
+            throws IOException
+        {
+            ASN1Sequence keySeq = ASN1Sequence.getInstance(keyInfo.getPublicKeyData().getBytes());
+            PublicKey[] pubKeys = new PublicKey[keySeq.size()];
+
+            for (int i = 0; i != keySeq.size(); i++)
+            {
+                SubjectPublicKeyInfo pubInfo = SubjectPublicKeyInfo.getInstance(keySeq.getObjectAt(i));
+
+                pubKeys[i] = provider.getKeyInfoConverter((pubInfo.getAlgorithm().getAlgorithm())).generatePublic(pubInfo);
+            }
+
+            return new CompositePublicKey(pubKeys);
+        }
+    }
+
+    /**
+     * @hide This class is not part of the Android public SDK API
+     */
+    public static class Mappings
+        extends AsymmetricAlgorithmProvider
+    {
+        public Mappings()
+        {
+        }
+
+        public void configure(ConfigurableProvider provider)
+        {
+            provider.addAlgorithm("KeyFactory.COMPOSITE", PREFIX + "$KeyFactory");
+            provider.addAlgorithm("KeyFactory." + MiscObjectIdentifiers.id_alg_composite, PREFIX + "$KeyFactory");
+            provider.addAlgorithm("KeyFactory.OID." + MiscObjectIdentifiers.id_alg_composite, PREFIX + "$KeyFactory");
+            provider.addAlgorithm("KeyFactory." + MiscObjectIdentifiers.id_composite_key, PREFIX + "$KeyFactory");
+            provider.addAlgorithm("KeyFactory.OID." + MiscObjectIdentifiers.id_composite_key, PREFIX + "$KeyFactory");
+
+            baseConverter = new CompositeKeyInfoConverter(provider);
+
+            provider.addKeyInfoConverter(MiscObjectIdentifiers.id_alg_composite, baseConverter);
+            provider.addKeyInfoConverter(MiscObjectIdentifiers.id_composite_key, baseConverter);
+        }
+    }
+}
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/asymmetric/DSA.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/asymmetric/DSA.java
index da4e38d..646004a 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/asymmetric/DSA.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/asymmetric/DSA.java
@@ -24,7 +24,7 @@
         public Mappings()
         {
         }
-        
+
         public void configure(ConfigurableProvider provider)
         {
             provider.addAlgorithm("AlgorithmParameters.DSA", PREFIX + "AlgorithmParametersSpi");
@@ -91,6 +91,8 @@
             provider.addAlgorithm("Alg.Alias.Signature.DSAWithSHA1", "SHA1withDSA");
             // END Android-changed: Change primary ID from DSA to SHA1withDSA
 
+            // addSignatureAlgorithm(provider, "RIPEMD160", "DSA", PREFIX + "DSASigner$dsaRMD160");
+
             AsymmetricKeyInfoConverter keyFact = new KeyFactorySpi();
 
             for (int i = 0; i != DSAUtil.dsaOids.length; i++)
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/asymmetric/EC.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/asymmetric/EC.java
index 2dfdd58..3a12bd9 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/asymmetric/EC.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/asymmetric/EC.java
@@ -12,6 +12,9 @@
 // import org.bouncycastle.asn1.teletrust.TeleTrusTObjectIdentifiers;
 // END Android-removed: Unsupported algorithms
 import com.android.org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
+// import org.bouncycastle.internal.asn1.bsi.BSIObjectIdentifiers;
+// import org.bouncycastle.internal.asn1.cms.CMSObjectIdentifiers;
+// import org.bouncycastle.internal.asn1.eac.EACObjectIdentifiers;
 import com.android.org.bouncycastle.jcajce.provider.asymmetric.ec.KeyFactorySpi;
 import com.android.org.bouncycastle.jcajce.provider.config.ConfigurableProvider;
 import com.android.org.bouncycastle.jcajce.provider.util.AsymmetricAlgorithmProvider;
@@ -50,77 +53,73 @@
             /*
             provider.addAlgorithm("AlgorithmParameters.EC", PREFIX + "AlgorithmParametersSpi");
 
-            provider.addAttributes("KeyAgreement.ECDH", generalEcAttributes);
-            provider.addAlgorithm("KeyAgreement.ECDH", PREFIX + "KeyAgreementSpi$DH");
-            provider.addAttributes("KeyAgreement.ECDHC", generalEcAttributes);
-            provider.addAlgorithm("KeyAgreement.ECDHC", PREFIX + "KeyAgreementSpi$DHC");
-            provider.addAttributes("KeyAgreement.ECCDH", generalEcAttributes);
-            provider.addAlgorithm("KeyAgreement.ECCDH", PREFIX + "KeyAgreementSpi$DHC");
+            provider.addAlgorithm("KeyAgreement.ECDH", PREFIX + "KeyAgreementSpi$DH", generalEcAttributes);
+            provider.addAlgorithm("KeyAgreement.ECDHC", PREFIX + "KeyAgreementSpi$DHC", generalEcAttributes);
+            provider.addAlgorithm("KeyAgreement.ECCDH", PREFIX + "KeyAgreementSpi$DHC", generalEcAttributes);
+            
+            provider.addAlgorithm("KeyAgreement.ECCDHU", PREFIX + "KeyAgreementSpi$DHUC", generalEcAttributes);
 
-            provider.addAttributes("KeyAgreement.ECCDHU", generalEcAttributes);
-            provider.addAlgorithm("KeyAgreement.ECCDHU", PREFIX + "KeyAgreementSpi$DHUC");
+            provider.addAlgorithm("KeyAgreement.ECDHWITHSHA1KDF", PREFIX + "KeyAgreementSpi$DHwithSHA1KDFAndSharedInfo", generalEcAttributes);
+            provider.addAlgorithm("KeyAgreement.ECCDHWITHSHA1KDF", PREFIX + "KeyAgreementSpi$CDHwithSHA1KDFAndSharedInfo", generalEcAttributes);
 
-            provider.addAlgorithm("KeyAgreement.ECDHWITHSHA1KDF", PREFIX + "KeyAgreementSpi$DHwithSHA1KDFAndSharedInfo");
-            provider.addAlgorithm("KeyAgreement.ECCDHWITHSHA1KDF", PREFIX + "KeyAgreementSpi$CDHwithSHA1KDFAndSharedInfo");
+            provider.addAlgorithm("KeyAgreement.ECDHWITHSHA224KDF", PREFIX + "KeyAgreementSpi$DHwithSHA224KDFAndSharedInfo", generalEcAttributes);
+            provider.addAlgorithm("KeyAgreement.ECCDHWITHSHA224KDF", PREFIX + "KeyAgreementSpi$CDHwithSHA224KDFAndSharedInfo", generalEcAttributes);
 
-            provider.addAlgorithm("KeyAgreement.ECDHWITHSHA224KDF", PREFIX + "KeyAgreementSpi$DHwithSHA224KDFAndSharedInfo");
-            provider.addAlgorithm("KeyAgreement.ECCDHWITHSHA224KDF", PREFIX + "KeyAgreementSpi$CDHwithSHA224KDFAndSharedInfo");
+            provider.addAlgorithm("KeyAgreement.ECDHWITHSHA256KDF", PREFIX + "KeyAgreementSpi$DHwithSHA256KDFAndSharedInfo", generalEcAttributes);
+            provider.addAlgorithm("KeyAgreement.ECCDHWITHSHA256KDF", PREFIX + "KeyAgreementSpi$CDHwithSHA256KDFAndSharedInfo", generalEcAttributes);
 
-            provider.addAlgorithm("KeyAgreement.ECDHWITHSHA256KDF", PREFIX + "KeyAgreementSpi$DHwithSHA256KDFAndSharedInfo");
-            provider.addAlgorithm("KeyAgreement.ECCDHWITHSHA256KDF", PREFIX + "KeyAgreementSpi$CDHwithSHA256KDFAndSharedInfo");
+            provider.addAlgorithm("KeyAgreement.ECDHWITHSHA384KDF", PREFIX + "KeyAgreementSpi$DHwithSHA384KDFAndSharedInfo", generalEcAttributes);
+            provider.addAlgorithm("KeyAgreement.ECCDHWITHSHA384KDF", PREFIX + "KeyAgreementSpi$CDHwithSHA384KDFAndSharedInfo", generalEcAttributes);
 
-            provider.addAlgorithm("KeyAgreement.ECDHWITHSHA384KDF", PREFIX + "KeyAgreementSpi$DHwithSHA384KDFAndSharedInfo");
-            provider.addAlgorithm("KeyAgreement.ECCDHWITHSHA384KDF", PREFIX + "KeyAgreementSpi$CDHwithSHA384KDFAndSharedInfo");
+            provider.addAlgorithm("KeyAgreement.ECDHWITHSHA512KDF", PREFIX + "KeyAgreementSpi$DHwithSHA512KDFAndSharedInfo", generalEcAttributes);
+            provider.addAlgorithm("KeyAgreement.ECCDHWITHSHA512KDF", PREFIX + "KeyAgreementSpi$CDHwithSHA512KDFAndSharedInfo", generalEcAttributes);
 
-            provider.addAlgorithm("KeyAgreement.ECDHWITHSHA512KDF", PREFIX + "KeyAgreementSpi$DHwithSHA512KDFAndSharedInfo");
-            provider.addAlgorithm("KeyAgreement.ECCDHWITHSHA512KDF", PREFIX + "KeyAgreementSpi$CDHwithSHA512KDFAndSharedInfo");
+            provider.addAlgorithm("KeyAgreement", X9ObjectIdentifiers.dhSinglePass_stdDH_sha1kdf_scheme, PREFIX + "KeyAgreementSpi$DHwithSHA1KDFAndSharedInfo", generalEcAttributes);
+            provider.addAlgorithm("KeyAgreement", X9ObjectIdentifiers.dhSinglePass_cofactorDH_sha1kdf_scheme, PREFIX + "KeyAgreementSpi$CDHwithSHA1KDFAndSharedInfo", generalEcAttributes);
 
-            provider.addAlgorithm("KeyAgreement", X9ObjectIdentifiers.dhSinglePass_stdDH_sha1kdf_scheme, PREFIX + "KeyAgreementSpi$DHwithSHA1KDFAndSharedInfo");
-            provider.addAlgorithm("KeyAgreement", X9ObjectIdentifiers.dhSinglePass_cofactorDH_sha1kdf_scheme, PREFIX + "KeyAgreementSpi$CDHwithSHA1KDFAndSharedInfo");
+            provider.addAlgorithm("KeyAgreement", SECObjectIdentifiers.dhSinglePass_stdDH_sha224kdf_scheme, PREFIX + "KeyAgreementSpi$DHwithSHA224KDFAndSharedInfo", generalEcAttributes);
+            provider.addAlgorithm("KeyAgreement", SECObjectIdentifiers.dhSinglePass_cofactorDH_sha224kdf_scheme, PREFIX + "KeyAgreementSpi$CDHwithSHA224KDFAndSharedInfo", generalEcAttributes);
 
-            provider.addAlgorithm("KeyAgreement", SECObjectIdentifiers.dhSinglePass_stdDH_sha224kdf_scheme, PREFIX + "KeyAgreementSpi$DHwithSHA224KDFAndSharedInfo");
-            provider.addAlgorithm("KeyAgreement", SECObjectIdentifiers.dhSinglePass_cofactorDH_sha224kdf_scheme, PREFIX + "KeyAgreementSpi$CDHwithSHA224KDFAndSharedInfo");
+            provider.addAlgorithm("KeyAgreement", SECObjectIdentifiers.dhSinglePass_stdDH_sha256kdf_scheme, PREFIX + "KeyAgreementSpi$DHwithSHA256KDFAndSharedInfo", generalEcAttributes);
+            provider.addAlgorithm("KeyAgreement", SECObjectIdentifiers.dhSinglePass_cofactorDH_sha256kdf_scheme, PREFIX + "KeyAgreementSpi$CDHwithSHA256KDFAndSharedInfo", generalEcAttributes);
 
-            provider.addAlgorithm("KeyAgreement", SECObjectIdentifiers.dhSinglePass_stdDH_sha256kdf_scheme, PREFIX + "KeyAgreementSpi$DHwithSHA256KDFAndSharedInfo");
-            provider.addAlgorithm("KeyAgreement", SECObjectIdentifiers.dhSinglePass_cofactorDH_sha256kdf_scheme, PREFIX + "KeyAgreementSpi$CDHwithSHA256KDFAndSharedInfo");
+            provider.addAlgorithm("KeyAgreement", SECObjectIdentifiers.dhSinglePass_stdDH_sha384kdf_scheme, PREFIX + "KeyAgreementSpi$DHwithSHA384KDFAndSharedInfo", generalEcAttributes);
+            provider.addAlgorithm("KeyAgreement", SECObjectIdentifiers.dhSinglePass_cofactorDH_sha384kdf_scheme, PREFIX + "KeyAgreementSpi$CDHwithSHA384KDFAndSharedInfo", generalEcAttributes);
 
-            provider.addAlgorithm("KeyAgreement", SECObjectIdentifiers.dhSinglePass_stdDH_sha384kdf_scheme, PREFIX + "KeyAgreementSpi$DHwithSHA384KDFAndSharedInfo");
-            provider.addAlgorithm("KeyAgreement", SECObjectIdentifiers.dhSinglePass_cofactorDH_sha384kdf_scheme, PREFIX + "KeyAgreementSpi$CDHwithSHA384KDFAndSharedInfo");
+            provider.addAlgorithm("KeyAgreement", SECObjectIdentifiers.dhSinglePass_stdDH_sha512kdf_scheme, PREFIX + "KeyAgreementSpi$DHwithSHA512KDFAndSharedInfo", generalEcAttributes);
+            provider.addAlgorithm("KeyAgreement", SECObjectIdentifiers.dhSinglePass_cofactorDH_sha512kdf_scheme, PREFIX + "KeyAgreementSpi$CDHwithSHA512KDFAndSharedInfo", generalEcAttributes);
 
-            provider.addAlgorithm("KeyAgreement", SECObjectIdentifiers.dhSinglePass_stdDH_sha512kdf_scheme, PREFIX + "KeyAgreementSpi$DHwithSHA512KDFAndSharedInfo");
-            provider.addAlgorithm("KeyAgreement", SECObjectIdentifiers.dhSinglePass_cofactorDH_sha512kdf_scheme, PREFIX + "KeyAgreementSpi$CDHwithSHA512KDFAndSharedInfo");
+            provider.addAlgorithm("KeyAgreement.ECCDHWITHSHA1CKDF", PREFIX + "KeyAgreementSpi$DHwithSHA1CKDF", generalEcAttributes);
+            provider.addAlgorithm("KeyAgreement.ECCDHWITHSHA256CKDF", PREFIX + "KeyAgreementSpi$DHwithSHA256CKDF", generalEcAttributes);
+            provider.addAlgorithm("KeyAgreement.ECCDHWITHSHA384CKDF", PREFIX + "KeyAgreementSpi$DHwithSHA384CKDF", generalEcAttributes);
+            provider.addAlgorithm("KeyAgreement.ECCDHWITHSHA512CKDF", PREFIX + "KeyAgreementSpi$DHwithSHA512CKDF", generalEcAttributes);
 
-            provider.addAlgorithm("KeyAgreement.ECCDHWITHSHA1CKDF", PREFIX + "KeyAgreementSpi$DHwithSHA1CKDF");
-            provider.addAlgorithm("KeyAgreement.ECCDHWITHSHA256CKDF", PREFIX + "KeyAgreementSpi$DHwithSHA256CKDF");
-            provider.addAlgorithm("KeyAgreement.ECCDHWITHSHA384CKDF", PREFIX + "KeyAgreementSpi$DHwithSHA384CKDF");
-            provider.addAlgorithm("KeyAgreement.ECCDHWITHSHA512CKDF", PREFIX + "KeyAgreementSpi$DHwithSHA512CKDF");
+            provider.addAlgorithm("KeyAgreement.ECCDHUWITHSHA1CKDF", PREFIX + "KeyAgreementSpi$DHUwithSHA1CKDF", generalEcAttributes);
+            provider.addAlgorithm("KeyAgreement.ECCDHUWITHSHA224CKDF", PREFIX + "KeyAgreementSpi$DHUwithSHA224CKDF", generalEcAttributes);
+            provider.addAlgorithm("KeyAgreement.ECCDHUWITHSHA256CKDF", PREFIX + "KeyAgreementSpi$DHUwithSHA256CKDF", generalEcAttributes);
+            provider.addAlgorithm("KeyAgreement.ECCDHUWITHSHA384CKDF", PREFIX + "KeyAgreementSpi$DHUwithSHA384CKDF", generalEcAttributes);
+            provider.addAlgorithm("KeyAgreement.ECCDHUWITHSHA512CKDF", PREFIX + "KeyAgreementSpi$DHUwithSHA512CKDF", generalEcAttributes);
 
-            provider.addAlgorithm("KeyAgreement.ECCDHUWITHSHA1CKDF", PREFIX + "KeyAgreementSpi$DHUwithSHA1CKDF");
-            provider.addAlgorithm("KeyAgreement.ECCDHUWITHSHA224CKDF", PREFIX + "KeyAgreementSpi$DHUwithSHA224CKDF");
-            provider.addAlgorithm("KeyAgreement.ECCDHUWITHSHA256CKDF", PREFIX + "KeyAgreementSpi$DHUwithSHA256CKDF");
-            provider.addAlgorithm("KeyAgreement.ECCDHUWITHSHA384CKDF", PREFIX + "KeyAgreementSpi$DHUwithSHA384CKDF");
-            provider.addAlgorithm("KeyAgreement.ECCDHUWITHSHA512CKDF", PREFIX + "KeyAgreementSpi$DHUwithSHA512CKDF");
+            provider.addAlgorithm("KeyAgreement.ECCDHUWITHSHA1KDF", PREFIX + "KeyAgreementSpi$DHUwithSHA1KDF", generalEcAttributes);
+            provider.addAlgorithm("KeyAgreement.ECCDHUWITHSHA224KDF", PREFIX + "KeyAgreementSpi$DHUwithSHA224KDF", generalEcAttributes);
+            provider.addAlgorithm("KeyAgreement.ECCDHUWITHSHA256KDF", PREFIX + "KeyAgreementSpi$DHUwithSHA256KDF", generalEcAttributes);
+            provider.addAlgorithm("KeyAgreement.ECCDHUWITHSHA384KDF", PREFIX + "KeyAgreementSpi$DHUwithSHA384KDF", generalEcAttributes);
+            provider.addAlgorithm("KeyAgreement.ECCDHUWITHSHA512KDF", PREFIX + "KeyAgreementSpi$DHUwithSHA512KDF", generalEcAttributes);
 
-            provider.addAlgorithm("KeyAgreement.ECCDHUWITHSHA1KDF", PREFIX + "KeyAgreementSpi$DHUwithSHA1KDF");
-            provider.addAlgorithm("KeyAgreement.ECCDHUWITHSHA224KDF", PREFIX + "KeyAgreementSpi$DHUwithSHA224KDF");
-            provider.addAlgorithm("KeyAgreement.ECCDHUWITHSHA256KDF", PREFIX + "KeyAgreementSpi$DHUwithSHA256KDF");
-            provider.addAlgorithm("KeyAgreement.ECCDHUWITHSHA384KDF", PREFIX + "KeyAgreementSpi$DHUwithSHA384KDF");
-            provider.addAlgorithm("KeyAgreement.ECCDHUWITHSHA512KDF", PREFIX + "KeyAgreementSpi$DHUwithSHA512KDF");
+            provider.addAlgorithm("KeyAgreement.ECKAEGWITHSHA1KDF", PREFIX + "KeyAgreementSpi$ECKAEGwithSHA1KDF", generalEcAttributes);
+            provider.addAlgorithm("KeyAgreement.ECKAEGWITHSHA224KDF", PREFIX + "KeyAgreementSpi$ECKAEGwithSHA224KDF", generalEcAttributes);
+            provider.addAlgorithm("KeyAgreement.ECKAEGWITHSHA256KDF", PREFIX + "KeyAgreementSpi$ECKAEGwithSHA256KDF", generalEcAttributes);
+            provider.addAlgorithm("KeyAgreement.ECKAEGWITHSHA384KDF", PREFIX + "KeyAgreementSpi$ECKAEGwithSHA384KDF", generalEcAttributes);
+            provider.addAlgorithm("KeyAgreement.ECKAEGWITHSHA512KDF", PREFIX + "KeyAgreementSpi$ECKAEGwithSHA512KDF", generalEcAttributes);
 
-            provider.addAlgorithm("KeyAgreement.ECKAEGWITHSHA1KDF", PREFIX + "KeyAgreementSpi$ECKAEGwithSHA1KDF");
-            provider.addAlgorithm("KeyAgreement.ECKAEGWITHSHA224KDF", PREFIX + "KeyAgreementSpi$ECKAEGwithSHA224KDF");
-            provider.addAlgorithm("KeyAgreement.ECKAEGWITHSHA256KDF", PREFIX + "KeyAgreementSpi$ECKAEGwithSHA256KDF");
-            provider.addAlgorithm("KeyAgreement.ECKAEGWITHSHA384KDF", PREFIX + "KeyAgreementSpi$ECKAEGwithSHA384KDF");
-            provider.addAlgorithm("KeyAgreement.ECKAEGWITHSHA512KDF", PREFIX + "KeyAgreementSpi$ECKAEGwithSHA512KDF");
+            provider.addAlgorithm("KeyAgreement", BSIObjectIdentifiers.ecka_eg_X963kdf_SHA1, PREFIX + "KeyAgreementSpi$ECKAEGwithSHA1KDF", generalEcAttributes);
+            provider.addAlgorithm("KeyAgreement", BSIObjectIdentifiers.ecka_eg_X963kdf_SHA224, PREFIX + "KeyAgreementSpi$ECKAEGwithSHA224KDF", generalEcAttributes);
+            provider.addAlgorithm("KeyAgreement", BSIObjectIdentifiers.ecka_eg_X963kdf_SHA256, PREFIX + "KeyAgreementSpi$ECKAEGwithSHA256KDF", generalEcAttributes);
+            provider.addAlgorithm("KeyAgreement", BSIObjectIdentifiers.ecka_eg_X963kdf_SHA384, PREFIX + "KeyAgreementSpi$ECKAEGwithSHA384KDF", generalEcAttributes);
+            provider.addAlgorithm("KeyAgreement", BSIObjectIdentifiers.ecka_eg_X963kdf_SHA512, PREFIX + "KeyAgreementSpi$ECKAEGwithSHA512KDF", generalEcAttributes);
 
-            provider.addAlgorithm("KeyAgreement", BSIObjectIdentifiers.ecka_eg_X963kdf_SHA1, PREFIX + "KeyAgreementSpi$ECKAEGwithSHA1KDF");
-            provider.addAlgorithm("KeyAgreement", BSIObjectIdentifiers.ecka_eg_X963kdf_SHA224, PREFIX + "KeyAgreementSpi$ECKAEGwithSHA224KDF");
-            provider.addAlgorithm("KeyAgreement", BSIObjectIdentifiers.ecka_eg_X963kdf_SHA256, PREFIX + "KeyAgreementSpi$ECKAEGwithSHA256KDF");
-            provider.addAlgorithm("KeyAgreement", BSIObjectIdentifiers.ecka_eg_X963kdf_SHA384, PREFIX + "KeyAgreementSpi$ECKAEGwithSHA384KDF");
-            provider.addAlgorithm("KeyAgreement", BSIObjectIdentifiers.ecka_eg_X963kdf_SHA512, PREFIX + "KeyAgreementSpi$ECKAEGwithSHA512KDF");
-
-            provider.addAlgorithm("KeyAgreement", BSIObjectIdentifiers.ecka_eg_X963kdf_RIPEMD160, PREFIX + "KeyAgreementSpi$ECKAEGwithRIPEMD160KDF");
-            provider.addAlgorithm("KeyAgreement.ECKAEGWITHRIPEMD160KDF", PREFIX + "KeyAgreementSpi$ECKAEGwithRIPEMD160KDF");
+            provider.addAlgorithm("KeyAgreement", BSIObjectIdentifiers.ecka_eg_X963kdf_RIPEMD160, PREFIX + "KeyAgreementSpi$ECKAEGwithRIPEMD160KDF", generalEcAttributes);
+            provider.addAlgorithm("KeyAgreement.ECKAEGWITHRIPEMD160KDF", PREFIX + "KeyAgreementSpi$ECKAEGwithRIPEMD160KDF", generalEcAttributes);
 
             registerOid(provider, X9ObjectIdentifiers.id_ecPublicKey, "EC", new KeyFactorySpi.EC());
 
@@ -158,25 +157,25 @@
 
             if (!Properties.isOverrideSet("org.bouncycastle.ec.disable_mqv"))
             {
-                provider.addAlgorithm("KeyAgreement.ECMQV", PREFIX + "KeyAgreementSpi$MQV");
+                provider.addAlgorithm("KeyAgreement.ECMQV", PREFIX + "KeyAgreementSpi$MQV", generalEcAttributes);
 
-                provider.addAlgorithm("KeyAgreement.ECMQVWITHSHA1CKDF", PREFIX + "KeyAgreementSpi$MQVwithSHA1CKDF");
-                provider.addAlgorithm("KeyAgreement.ECMQVWITHSHA224CKDF", PREFIX + "KeyAgreementSpi$MQVwithSHA224CKDF");
-                provider.addAlgorithm("KeyAgreement.ECMQVWITHSHA256CKDF", PREFIX + "KeyAgreementSpi$MQVwithSHA256CKDF");
-                provider.addAlgorithm("KeyAgreement.ECMQVWITHSHA384CKDF", PREFIX + "KeyAgreementSpi$MQVwithSHA384CKDF");
-                provider.addAlgorithm("KeyAgreement.ECMQVWITHSHA512CKDF", PREFIX + "KeyAgreementSpi$MQVwithSHA512CKDF");
+                provider.addAlgorithm("KeyAgreement.ECMQVWITHSHA1CKDF", PREFIX + "KeyAgreementSpi$MQVwithSHA1CKDF", generalEcAttributes);
+                provider.addAlgorithm("KeyAgreement.ECMQVWITHSHA224CKDF", PREFIX + "KeyAgreementSpi$MQVwithSHA224CKDF", generalEcAttributes);
+                provider.addAlgorithm("KeyAgreement.ECMQVWITHSHA256CKDF", PREFIX + "KeyAgreementSpi$MQVwithSHA256CKDF", generalEcAttributes);
+                provider.addAlgorithm("KeyAgreement.ECMQVWITHSHA384CKDF", PREFIX + "KeyAgreementSpi$MQVwithSHA384CKDF", generalEcAttributes);
+                provider.addAlgorithm("KeyAgreement.ECMQVWITHSHA512CKDF", PREFIX + "KeyAgreementSpi$MQVwithSHA512CKDF", generalEcAttributes);
 
-                provider.addAlgorithm("KeyAgreement.ECMQVWITHSHA1KDF", PREFIX + "KeyAgreementSpi$MQVwithSHA1KDF");
-                provider.addAlgorithm("KeyAgreement.ECMQVWITHSHA224KDF", PREFIX + "KeyAgreementSpi$MQVwithSHA224KDF");
-                provider.addAlgorithm("KeyAgreement.ECMQVWITHSHA256KDF", PREFIX + "KeyAgreementSpi$MQVwithSHA256KDF");
-                provider.addAlgorithm("KeyAgreement.ECMQVWITHSHA384KDF", PREFIX + "KeyAgreementSpi$MQVwithSHA384KDF");
-                provider.addAlgorithm("KeyAgreement.ECMQVWITHSHA512KDF", PREFIX + "KeyAgreementSpi$MQVwithSHA512KDF");
+                provider.addAlgorithm("KeyAgreement.ECMQVWITHSHA1KDF", PREFIX + "KeyAgreementSpi$MQVwithSHA1KDF", generalEcAttributes);
+                provider.addAlgorithm("KeyAgreement.ECMQVWITHSHA224KDF", PREFIX + "KeyAgreementSpi$MQVwithSHA224KDF", generalEcAttributes);
+                provider.addAlgorithm("KeyAgreement.ECMQVWITHSHA256KDF", PREFIX + "KeyAgreementSpi$MQVwithSHA256KDF", generalEcAttributes);
+                provider.addAlgorithm("KeyAgreement.ECMQVWITHSHA384KDF", PREFIX + "KeyAgreementSpi$MQVwithSHA384KDF", generalEcAttributes);
+                provider.addAlgorithm("KeyAgreement.ECMQVWITHSHA512KDF", PREFIX + "KeyAgreementSpi$MQVwithSHA512KDF", generalEcAttributes);
 
-                provider.addAlgorithm("KeyAgreement." + X9ObjectIdentifiers.mqvSinglePass_sha1kdf_scheme, PREFIX + "KeyAgreementSpi$MQVwithSHA1KDFAndSharedInfo");
-                provider.addAlgorithm("KeyAgreement." + SECObjectIdentifiers.mqvSinglePass_sha224kdf_scheme, PREFIX + "KeyAgreementSpi$MQVwithSHA224KDFAndSharedInfo");
-                provider.addAlgorithm("KeyAgreement." + SECObjectIdentifiers.mqvSinglePass_sha256kdf_scheme, PREFIX + "KeyAgreementSpi$MQVwithSHA256KDFAndSharedInfo");
-                provider.addAlgorithm("KeyAgreement." + SECObjectIdentifiers.mqvSinglePass_sha384kdf_scheme, PREFIX + "KeyAgreementSpi$MQVwithSHA384KDFAndSharedInfo");
-                provider.addAlgorithm("KeyAgreement." + SECObjectIdentifiers.mqvSinglePass_sha512kdf_scheme, PREFIX + "KeyAgreementSpi$MQVwithSHA512KDFAndSharedInfo");
+                provider.addAlgorithm("KeyAgreement." + X9ObjectIdentifiers.mqvSinglePass_sha1kdf_scheme, PREFIX + "KeyAgreementSpi$MQVwithSHA1KDFAndSharedInfo", generalEcAttributes);
+                provider.addAlgorithm("KeyAgreement." + SECObjectIdentifiers.mqvSinglePass_sha224kdf_scheme, PREFIX + "KeyAgreementSpi$MQVwithSHA224KDFAndSharedInfo", generalEcAttributes);
+                provider.addAlgorithm("KeyAgreement." + SECObjectIdentifiers.mqvSinglePass_sha256kdf_scheme, PREFIX + "KeyAgreementSpi$MQVwithSHA256KDFAndSharedInfo", generalEcAttributes);
+                provider.addAlgorithm("KeyAgreement." + SECObjectIdentifiers.mqvSinglePass_sha384kdf_scheme, PREFIX + "KeyAgreementSpi$MQVwithSHA384KDFAndSharedInfo", generalEcAttributes);
+                provider.addAlgorithm("KeyAgreement." + SECObjectIdentifiers.mqvSinglePass_sha512kdf_scheme, PREFIX + "KeyAgreementSpi$MQVwithSHA512KDFAndSharedInfo", generalEcAttributes);
 
                 registerOid(provider, X9ObjectIdentifiers.dhSinglePass_stdDH_sha1kdf_scheme, "EC", new KeyFactorySpi.EC());
             
@@ -210,17 +209,42 @@
             provider.addAlgorithm("KeyPairGenerator.ECDHC", PREFIX + "KeyPairGeneratorSpi$ECDHC");
             provider.addAlgorithm("KeyPairGenerator.ECIES", PREFIX + "KeyPairGeneratorSpi$ECDH");
 
-            provider.addAlgorithm("Cipher.ECIES", PREFIX + "IESCipher$ECIES");
+            provider.addAlgorithm("Cipher.ECIES", PREFIX + "IESCipher$ECIES", generalEcAttributes);
+            provider.addAlgorithm("Cipher.ECIESwithSHA1", PREFIX + "IESCipher$ECIES", generalEcAttributes);
+            provider.addAlgorithm("Cipher.ECIESWITHSHA1", PREFIX + "IESCipher$ECIES", generalEcAttributes);
+            provider.addAlgorithm("Cipher.ECIESwithSHA256", PREFIX + "IESCipher$ECIESwithSHA256", generalEcAttributes);
+            provider.addAlgorithm("Cipher.ECIESWITHSHA256", PREFIX + "IESCipher$ECIESwithSHA256", generalEcAttributes);
+            provider.addAlgorithm("Cipher.ECIESwithSHA384", PREFIX + "IESCipher$ECIESwithSHA384", generalEcAttributes);
+            provider.addAlgorithm("Cipher.ECIESWITHSHA384", PREFIX + "IESCipher$ECIESwithSHA384", generalEcAttributes);
+            provider.addAlgorithm("Cipher.ECIESwithSHA512", PREFIX + "IESCipher$ECIESwithSHA512", generalEcAttributes);
+            provider.addAlgorithm("Cipher.ECIESWITHSHA512", PREFIX + "IESCipher$ECIESwithSHA512", generalEcAttributes);
 
-            provider.addAlgorithm("Cipher.ECIESwithAES-CBC", PREFIX + "IESCipher$ECIESwithAESCBC");
-            provider.addAlgorithm("Cipher.ECIESWITHAES-CBC", PREFIX + "IESCipher$ECIESwithAESCBC");
-            provider.addAlgorithm("Cipher.ECIESwithDESEDE-CBC", PREFIX + "IESCipher$ECIESwithDESedeCBC");
-            provider.addAlgorithm("Cipher.ECIESWITHDESEDE-CBC", PREFIX + "IESCipher$ECIESwithDESedeCBC");
+            provider.addAlgorithm("Cipher.ECIESwithAES-CBC", PREFIX + "IESCipher$ECIESwithAESCBC", generalEcAttributes);
+            provider.addAlgorithm("Cipher.ECIESWITHAES-CBC", PREFIX + "IESCipher$ECIESwithAESCBC", generalEcAttributes);
+            provider.addAlgorithm("Cipher.ECIESwithSHA1andAES-CBC", PREFIX + "IESCipher$ECIESwithAESCBC", generalEcAttributes);
+            provider.addAlgorithm("Cipher.ECIESWITHSHA1ANDAES-CBC", PREFIX + "IESCipher$ECIESwithAESCBC", generalEcAttributes);
+            provider.addAlgorithm("Cipher.ECIESwithSHA256andAES-CBC", PREFIX + "IESCipher$ECIESwithSHA256andAESCBC", generalEcAttributes);
+            provider.addAlgorithm("Cipher.ECIESWITHSHA256ANDAES-CBC", PREFIX + "IESCipher$ECIESwithSHA256andAESCBC", generalEcAttributes);
+            provider.addAlgorithm("Cipher.ECIESwithSHA384andAES-CBC", PREFIX + "IESCipher$ECIESwithSHA384andAESCBC", generalEcAttributes);
+            provider.addAlgorithm("Cipher.ECIESWITHSHA384ANDAES-CBC", PREFIX + "IESCipher$ECIESwithSHA384andAESCBC", generalEcAttributes);
+            provider.addAlgorithm("Cipher.ECIESwithSHA512andAES-CBC", PREFIX + "IESCipher$ECIESwithSHA512andAESCBC", generalEcAttributes);
+            provider.addAlgorithm("Cipher.ECIESWITHSHA512ANDAES-CBC", PREFIX + "IESCipher$ECIESwithSHA512andAESCBC", generalEcAttributes);
 
-            provider.addAlgorithm("Signature.ECDSA", PREFIX + "SignatureSpi$ecDSA");
+            provider.addAlgorithm("Cipher.ECIESwithDESEDE-CBC", PREFIX + "IESCipher$ECIESwithDESedeCBC", generalEcAttributes);
+            provider.addAlgorithm("Cipher.ECIESWITHDESEDE-CBC", PREFIX + "IESCipher$ECIESwithDESedeCBC", generalEcAttributes);
+            provider.addAlgorithm("Cipher.ECIESwithSHA1andDESEDE-CBC", PREFIX + "IESCipher$ECIESwithDESedeCBC", generalEcAttributes);
+            provider.addAlgorithm("Cipher.ECIESWITHSHA1ANDDESEDE-CBC", PREFIX + "IESCipher$ECIESwithDESedeCBC", generalEcAttributes);
+            provider.addAlgorithm("Cipher.ECIESwithSHA256andDESEDE-CBC", PREFIX + "IESCipher$ECIESwithSHA256andDESedeCBC", generalEcAttributes);
+            provider.addAlgorithm("Cipher.ECIESWITHSHA256ANDDESEDE-CBC", PREFIX + "IESCipher$ECIESwithSHA256andDESedeCBC", generalEcAttributes);
+            provider.addAlgorithm("Cipher.ECIESwithSHA384andDESEDE-CBC", PREFIX + "IESCipher$ECIESwithSHA384andDESedeCBC", generalEcAttributes);
+            provider.addAlgorithm("Cipher.ECIESWITHSHA384ANDDESEDE-CBC", PREFIX + "IESCipher$ECIESwithSHA384andDESedeCBC", generalEcAttributes);
+            provider.addAlgorithm("Cipher.ECIESwithSHA512andDESEDE-CBC", PREFIX + "IESCipher$ECIESwithSHA512andDESedeCBC", generalEcAttributes);
+            provider.addAlgorithm("Cipher.ECIESWITHSHA512ANDDESEDE-CBC", PREFIX + "IESCipher$ECIESwithSHA512andDESedeCBC", generalEcAttributes);
 
-            provider.addAlgorithm("Signature.SHA1withECDSA", PREFIX + "SignatureSpi$ecDSA");
-            provider.addAlgorithm("Signature.NONEwithECDSA", PREFIX + "SignatureSpi$ecDSAnone");
+            provider.addAlgorithm("Cipher.ETSIKEMWITHSHA256", PREFIX + "IESKEMCipher$KEMwithSHA256", generalEcAttributes);
+
+            provider.addAlgorithm("Signature.ECDSA", PREFIX + "SignatureSpi$ecDSA", generalEcAttributes);
+            provider.addAlgorithm("Signature.NONEwithECDSA", PREFIX + "SignatureSpi$ecDSAnone", generalEcAttributes);
 
             provider.addAlgorithm("Alg.Alias.Signature.ECDSA", "SHA1withECDSA");
             provider.addAlgorithm("Alg.Alias.Signature.ECDSAwithSHA1", "SHA1withECDSA");
@@ -231,16 +255,16 @@
             provider.addAlgorithm("Alg.Alias.Signature.1.2.840.10045.4.1", "SHA1withECDSA");
             provider.addAlgorithm("Alg.Alias.Signature." + TeleTrusTObjectIdentifiers.ecSignWithSha1, "ECDSA");
 
-            provider.addAlgorithm("Signature.ECDDSA", PREFIX + "SignatureSpi$ecDetDSA");
-            provider.addAlgorithm("Signature.SHA1WITHECDDSA", PREFIX + "SignatureSpi$ecDetDSA");
-            provider.addAlgorithm("Signature.SHA224WITHECDDSA", PREFIX + "SignatureSpi$ecDetDSA224");
-            provider.addAlgorithm("Signature.SHA256WITHECDDSA", PREFIX + "SignatureSpi$ecDetDSA256");
-            provider.addAlgorithm("Signature.SHA384WITHECDDSA", PREFIX + "SignatureSpi$ecDetDSA384");
-            provider.addAlgorithm("Signature.SHA512WITHECDDSA", PREFIX + "SignatureSpi$ecDetDSA512");
-            provider.addAlgorithm("Signature.SHA3-224WITHECDDSA", PREFIX + "SignatureSpi$ecDetDSASha3_224");
-            provider.addAlgorithm("Signature.SHA3-256WITHECDDSA", PREFIX + "SignatureSpi$ecDetDSASha3_256");
-            provider.addAlgorithm("Signature.SHA3-384WITHECDDSA", PREFIX + "SignatureSpi$ecDetDSASha3_384");
-            provider.addAlgorithm("Signature.SHA3-512WITHECDDSA", PREFIX + "SignatureSpi$ecDetDSASha3_512");
+            provider.addAlgorithm("Signature.ECDDSA", PREFIX + "SignatureSpi$ecDetDSA", generalEcAttributes);
+            provider.addAlgorithm("Signature.SHA1WITHECDDSA", PREFIX + "SignatureSpi$ecDetDSA", generalEcAttributes);
+            provider.addAlgorithm("Signature.SHA224WITHECDDSA", PREFIX + "SignatureSpi$ecDetDSA224", generalEcAttributes);
+            provider.addAlgorithm("Signature.SHA256WITHECDDSA", PREFIX + "SignatureSpi$ecDetDSA256", generalEcAttributes);
+            provider.addAlgorithm("Signature.SHA384WITHECDDSA", PREFIX + "SignatureSpi$ecDetDSA384", generalEcAttributes);
+            provider.addAlgorithm("Signature.SHA512WITHECDDSA", PREFIX + "SignatureSpi$ecDetDSA512", generalEcAttributes);
+            provider.addAlgorithm("Signature.SHA3-224WITHECDDSA", PREFIX + "SignatureSpi$ecDetDSASha3_224", generalEcAttributes);
+            provider.addAlgorithm("Signature.SHA3-256WITHECDDSA", PREFIX + "SignatureSpi$ecDetDSASha3_256", generalEcAttributes);
+            provider.addAlgorithm("Signature.SHA3-384WITHECDDSA", PREFIX + "SignatureSpi$ecDetDSASha3_384", generalEcAttributes);
+            provider.addAlgorithm("Signature.SHA3-512WITHECDDSA", PREFIX + "SignatureSpi$ecDetDSASha3_512", generalEcAttributes);
 
             provider.addAlgorithm("Alg.Alias.Signature.DETECDSA", "ECDDSA");
             provider.addAlgorithm("Alg.Alias.Signature.SHA1WITHDETECDSA", "SHA1WITHECDDSA");
@@ -249,22 +273,29 @@
             provider.addAlgorithm("Alg.Alias.Signature.SHA384WITHDETECDSA", "SHA384WITHECDDSA");
             provider.addAlgorithm("Alg.Alias.Signature.SHA512WITHDETECDSA", "SHA512WITHECDDSA");
 
-            addSignatureAlgorithm(provider, "SHA224", "ECDSA", PREFIX + "SignatureSpi$ecDSA224", X9ObjectIdentifiers.ecdsa_with_SHA224);
-            addSignatureAlgorithm(provider, "SHA256", "ECDSA", PREFIX + "SignatureSpi$ecDSA256", X9ObjectIdentifiers.ecdsa_with_SHA256);
-            addSignatureAlgorithm(provider, "SHA384", "ECDSA", PREFIX + "SignatureSpi$ecDSA384", X9ObjectIdentifiers.ecdsa_with_SHA384);
-            addSignatureAlgorithm(provider, "SHA512", "ECDSA", PREFIX + "SignatureSpi$ecDSA512", X9ObjectIdentifiers.ecdsa_with_SHA512);
-            addSignatureAlgorithm(provider, "SHA3-224", "ECDSA", PREFIX + "SignatureSpi$ecDSASha3_224", NISTObjectIdentifiers.id_ecdsa_with_sha3_224);
-            addSignatureAlgorithm(provider, "SHA3-256", "ECDSA", PREFIX + "SignatureSpi$ecDSASha3_256", NISTObjectIdentifiers.id_ecdsa_with_sha3_256);
-            addSignatureAlgorithm(provider, "SHA3-384", "ECDSA", PREFIX + "SignatureSpi$ecDSASha3_384", NISTObjectIdentifiers.id_ecdsa_with_sha3_384);
-            addSignatureAlgorithm(provider, "SHA3-512", "ECDSA", PREFIX + "SignatureSpi$ecDSASha3_512", NISTObjectIdentifiers.id_ecdsa_with_sha3_512);
+            addSignatureAlgorithm(provider, "SHA224", "ECDSA", PREFIX + "SignatureSpi$ecDSA224", X9ObjectIdentifiers.ecdsa_with_SHA224, generalEcAttributes);
+            addSignatureAlgorithm(provider, "SHA256", "ECDSA", PREFIX + "SignatureSpi$ecDSA256", X9ObjectIdentifiers.ecdsa_with_SHA256, generalEcAttributes);
+            addSignatureAlgorithm(provider, "SHA384", "ECDSA", PREFIX + "SignatureSpi$ecDSA384", X9ObjectIdentifiers.ecdsa_with_SHA384, generalEcAttributes);
+            addSignatureAlgorithm(provider, "SHA512", "ECDSA", PREFIX + "SignatureSpi$ecDSA512", X9ObjectIdentifiers.ecdsa_with_SHA512, generalEcAttributes);
+            addSignatureAlgorithm(provider, "SHA3-224", "ECDSA", PREFIX + "SignatureSpi$ecDSASha3_224", NISTObjectIdentifiers.id_ecdsa_with_sha3_224, generalEcAttributes);
+            addSignatureAlgorithm(provider, "SHA3-256", "ECDSA", PREFIX + "SignatureSpi$ecDSASha3_256", NISTObjectIdentifiers.id_ecdsa_with_sha3_256, generalEcAttributes);
+            addSignatureAlgorithm(provider, "SHA3-384", "ECDSA", PREFIX + "SignatureSpi$ecDSASha3_384", NISTObjectIdentifiers.id_ecdsa_with_sha3_384, generalEcAttributes);
+            addSignatureAlgorithm(provider, "SHA3-512", "ECDSA", PREFIX + "SignatureSpi$ecDSASha3_512", NISTObjectIdentifiers.id_ecdsa_with_sha3_512, generalEcAttributes);
+            addSignatureAlgorithm(provider, "SHAKE128", "ECDSA", PREFIX + "SignatureSpi$ecDSAShake128", CMSObjectIdentifiers.id_ecdsa_with_shake128, generalEcAttributes);
+            addSignatureAlgorithm(provider, "SHAKE256", "ECDSA", PREFIX + "SignatureSpi$ecDSAShake256", CMSObjectIdentifiers.id_ecdsa_with_shake256, generalEcAttributes);
+            addSignatureAlgorithm(provider, "RIPEMD160", "ECDSA", PREFIX + "SignatureSpi$ecDSARipeMD160",TeleTrusTObjectIdentifiers.ecSignWithRipemd160, generalEcAttributes);
 
-            addSignatureAlgorithm(provider, "RIPEMD160", "ECDSA", PREFIX + "SignatureSpi$ecDSARipeMD160",TeleTrusTObjectIdentifiers.ecSignWithRipemd160);
+            provider.addAlgorithm("Signature.SHA1WITHECNR", PREFIX + "SignatureSpi$ecNR", generalEcAttributes);
+            provider.addAlgorithm("Signature.SHA224WITHECNR", PREFIX + "SignatureSpi$ecNR224", generalEcAttributes);
+            provider.addAlgorithm("Signature.SHA256WITHECNR", PREFIX + "SignatureSpi$ecNR256", generalEcAttributes);
+            provider.addAlgorithm("Signature.SHA384WITHECNR", PREFIX + "SignatureSpi$ecNR384", generalEcAttributes);
+            provider.addAlgorithm("Signature.SHA512WITHECNR", PREFIX + "SignatureSpi$ecNR512", generalEcAttributes);
 
-            provider.addAlgorithm("Signature.SHA1WITHECNR", PREFIX + "SignatureSpi$ecNR");
-            provider.addAlgorithm("Signature.SHA224WITHECNR", PREFIX + "SignatureSpi$ecNR224");
-            provider.addAlgorithm("Signature.SHA256WITHECNR", PREFIX + "SignatureSpi$ecNR256");
-            provider.addAlgorithm("Signature.SHA384WITHECNR", PREFIX + "SignatureSpi$ecNR384");
-            provider.addAlgorithm("Signature.SHA512WITHECNR", PREFIX + "SignatureSpi$ecNR512");
+            addSignatureAlgorithm(provider, "SHA1", "CVC-ECDSA", PREFIX + "SignatureSpi$ecCVCDSA", EACObjectIdentifiers.id_TA_ECDSA_SHA_1, generalEcAttributes);
+            addSignatureAlgorithm(provider, "SHA224", "CVC-ECDSA", PREFIX + "SignatureSpi$ecCVCDSA224", EACObjectIdentifiers.id_TA_ECDSA_SHA_224, generalEcAttributes);
+            addSignatureAlgorithm(provider, "SHA256", "CVC-ECDSA", PREFIX + "SignatureSpi$ecCVCDSA256", EACObjectIdentifiers.id_TA_ECDSA_SHA_256, generalEcAttributes);
+            addSignatureAlgorithm(provider, "SHA384", "CVC-ECDSA", PREFIX + "SignatureSpi$ecCVCDSA384", EACObjectIdentifiers.id_TA_ECDSA_SHA_384, generalEcAttributes);
+            addSignatureAlgorithm(provider, "SHA512", "CVC-ECDSA", PREFIX + "SignatureSpi$ecCVCDSA512", EACObjectIdentifiers.id_TA_ECDSA_SHA_512, generalEcAttributes);
 
             addSignatureAlgorithm(provider, "SHA1", "CVC-ECDSA", PREFIX + "SignatureSpi$ecCVCDSA", EACObjectIdentifiers.id_TA_ECDSA_SHA_1);
             addSignatureAlgorithm(provider, "SHA224", "CVC-ECDSA", PREFIX + "SignatureSpi$ecCVCDSA224", EACObjectIdentifiers.id_TA_ECDSA_SHA_224);
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/asymmetric/EXTERNAL.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/asymmetric/EXTERNAL.java
new file mode 100644
index 0000000..eefaa09
--- /dev/null
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/asymmetric/EXTERNAL.java
@@ -0,0 +1,128 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.org.bouncycastle.jcajce.provider.asymmetric;
+
+import java.io.IOException;
+import java.security.InvalidKeyException;
+import java.security.Key;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.util.HashMap;
+import java.util.Map;
+
+import com.android.org.bouncycastle.asn1.bc.BCObjectIdentifiers;
+import com.android.org.bouncycastle.asn1.bc.ExternalValue;
+import com.android.org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
+import com.android.org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import com.android.org.bouncycastle.jcajce.ExternalPublicKey;
+import com.android.org.bouncycastle.jcajce.provider.asymmetric.util.BaseKeyFactorySpi;
+import com.android.org.bouncycastle.jcajce.provider.config.ConfigurableProvider;
+import com.android.org.bouncycastle.jcajce.provider.util.AsymmetricAlgorithmProvider;
+import com.android.org.bouncycastle.jcajce.provider.util.AsymmetricKeyInfoConverter;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+public class EXTERNAL
+{
+    private static final String PREFIX = "com.android.org.bouncycastle.jcajce.provider.asymmetric.EXTERNAL";
+
+    private static final Map<String, String> externalAttributes = new HashMap<String, String>();
+
+    static
+    {
+        externalAttributes.put("SupportedKeyClasses", "com.android.org.bouncycastle.jcajce.ExternalPublicKey");
+        externalAttributes.put("SupportedKeyFormats", "X.509");
+    }
+
+    private static AsymmetricKeyInfoConverter baseConverter;
+
+    /**
+     * @hide This class is not part of the Android public SDK API
+     */
+    public static class KeyFactory
+        extends BaseKeyFactorySpi
+    {
+        protected Key engineTranslateKey(Key key)
+            throws InvalidKeyException
+        {
+            try
+            {
+                if (key instanceof PrivateKey)
+                {
+                    return generatePrivate(PrivateKeyInfo.getInstance(key.getEncoded()));
+                }
+                else if (key instanceof PublicKey)
+                {
+                    return generatePublic(SubjectPublicKeyInfo.getInstance(key.getEncoded()));
+                }
+            }
+            catch (IOException e)
+            {
+                throw new InvalidKeyException("key could not be parsed: " + e.getMessage());
+            }
+
+            throw new InvalidKeyException("key not recognized");
+        }
+
+        public PrivateKey generatePrivate(PrivateKeyInfo keyInfo)
+            throws IOException
+        {
+            return baseConverter.generatePrivate(keyInfo);
+        }
+
+        public PublicKey generatePublic(SubjectPublicKeyInfo keyInfo)
+            throws IOException
+        {
+            return baseConverter.generatePublic(keyInfo);
+        }
+    }
+
+    private static class ExternalKeyInfoConverter
+        implements AsymmetricKeyInfoConverter
+    {
+        private final ConfigurableProvider provider;
+
+        public ExternalKeyInfoConverter(ConfigurableProvider provider)
+        {
+            this.provider = provider;
+        }
+
+        public PrivateKey generatePrivate(PrivateKeyInfo keyInfo)
+            throws IOException
+        {
+            throw new UnsupportedOperationException("no support for private key");
+        }
+
+        public PublicKey generatePublic(SubjectPublicKeyInfo keyInfo)
+            throws IOException
+        {
+            ExternalValue extKey = ExternalValue.getInstance(keyInfo.parsePublicKey());
+
+            // TODO: maybe implement some sort of cache lookup?
+
+            return new ExternalPublicKey(extKey);
+        }
+    }
+
+    /**
+     * @hide This class is not part of the Android public SDK API
+     */
+    public static class Mappings
+        extends AsymmetricAlgorithmProvider
+    {
+        public Mappings()
+        {
+        }
+
+        public void configure(ConfigurableProvider provider)
+        {
+            provider.addAlgorithm("KeyFactory.EXTERNAL", PREFIX + "$KeyFactory");
+            provider.addAlgorithm("KeyFactory." + BCObjectIdentifiers.external_value, PREFIX + "$KeyFactory");
+            provider.addAlgorithm("KeyFactory.OID." + BCObjectIdentifiers.external_value, PREFIX + "$KeyFactory");
+
+            baseConverter = new ExternalKeyInfoConverter(provider);
+
+            provider.addKeyInfoConverter(BCObjectIdentifiers.external_value, baseConverter);
+        }
+    }
+}
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/asymmetric/LMS.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/asymmetric/LMS.java
new file mode 100644
index 0000000..07b87da
--- /dev/null
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/asymmetric/LMS.java
@@ -0,0 +1,37 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.org.bouncycastle.jcajce.provider.asymmetric;
+
+import com.android.org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import com.android.org.bouncycastle.jcajce.provider.config.ConfigurableProvider;
+import com.android.org.bouncycastle.jcajce.provider.util.AsymmetricAlgorithmProvider;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+public class LMS
+{
+    private static final String PREFIX = "com.android.org.bouncycastle.pqc.jcajce.provider" + ".lms.";
+
+    /**
+     * @hide This class is not part of the Android public SDK API
+     */
+    public static class Mappings
+        extends AsymmetricAlgorithmProvider
+    {
+        public Mappings()
+        {
+        }
+
+        public void configure(ConfigurableProvider provider)
+        {
+            provider.addAlgorithm("KeyFactory.LMS", PREFIX + "LMSKeyFactorySpi");
+            provider.addAlgorithm("Alg.Alias.KeyFactory." + PKCSObjectIdentifiers.id_alg_hss_lms_hashsig, "LMS");
+
+            provider.addAlgorithm("KeyPairGenerator.LMS", PREFIX + "LMSKeyPairGeneratorSpi");
+            provider.addAlgorithm("Alg.Alias.KeyPairGenerator." + PKCSObjectIdentifiers.id_alg_hss_lms_hashsig, "LMS");
+
+            provider.addAlgorithm("Signature.LMS", PREFIX + "LMSSignatureSpi$generic");
+            provider.addAlgorithm("Alg.Alias.Signature." + PKCSObjectIdentifiers.id_alg_hss_lms_hashsig, "LMS");
+        }
+    }
+}
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/asymmetric/RSA.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/asymmetric/RSA.java
index c4a0400..7c44e45 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/asymmetric/RSA.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/asymmetric/RSA.java
@@ -11,6 +11,7 @@
 // Android-removed: Unsupported algorithms
 // import org.bouncycastle.asn1.teletrust.TeleTrusTObjectIdentifiers;
 import com.android.org.bouncycastle.asn1.x509.X509ObjectIdentifiers;
+// import org.bouncycastle.internal.asn1.cms.CMSObjectIdentifiers;
 import com.android.org.bouncycastle.jcajce.provider.asymmetric.rsa.KeyFactorySpi;
 import com.android.org.bouncycastle.jcajce.provider.config.ConfigurableProvider;
 import com.android.org.bouncycastle.jcajce.provider.util.AsymmetricAlgorithmProvider;
@@ -27,7 +28,7 @@
 
     static
     {
-        generalRsaAttributes.put("SupportedKeyClasses", "javax.crypto.interfaces.RSAPublicKey|javax.crypto.interfaces.RSAPrivateKey");
+        generalRsaAttributes.put("SupportedKeyClasses", "java.security.interfaces.RSAPublicKey|java.security.interfaces.RSAPrivateKey");
         generalRsaAttributes.put("SupportedKeyFormats", "PKCS#8|X.509");
     }
 
@@ -80,8 +81,8 @@
             // BEGIN Android-removed: Unsupported algorithms
             /*
             provider.addAlgorithm("Cipher.RSA/PKCS1", PREFIX + "CipherSpi$PKCS1v1_5Padding");
-            provider.addAlgorithm("Cipher", PKCSObjectIdentifiers.rsaEncryption, PREFIX + "CipherSpi$PKCS1v1_5Padding");
-            provider.addAlgorithm("Cipher", X509ObjectIdentifiers.id_ea_rsa, PREFIX + "CipherSpi$PKCS1v1_5Padding");
+            provider.addAlgorithm("Cipher", PKCSObjectIdentifiers.rsaEncryption, PREFIX + "CipherSpi$PKCS1v1_5Padding", generalRsaAttributes);
+            provider.addAlgorithm("Cipher", X509ObjectIdentifiers.id_ea_rsa, PREFIX + "CipherSpi$PKCS1v1_5Padding", generalRsaAttributes);
             provider.addAlgorithm("Cipher.RSA/1", PREFIX + "CipherSpi$PKCS1v1_5Padding_PrivateOnly");
             provider.addAlgorithm("Cipher.RSA/2", PREFIX + "CipherSpi$PKCS1v1_5Padding_PublicOnly");
             provider.addAlgorithm("Cipher.RSA/OAEP", PREFIX + "CipherSpi$OAEPPadding");
@@ -122,12 +123,12 @@
             registerOidAlgorithmParameters(provider, PKCSObjectIdentifiers.id_RSAES_OAEP, "OAEP");
             registerOidAlgorithmParameters(provider, PKCSObjectIdentifiers.id_RSASSA_PSS, "PSS");
 
-            provider.addAlgorithm("Signature.RSASSA-PSS", PREFIX + "PSSSignatureSpi$PSSwithRSA");
-            provider.addAlgorithm("Signature." + PKCSObjectIdentifiers.id_RSASSA_PSS, PREFIX + "PSSSignatureSpi$PSSwithRSA");
-            provider.addAlgorithm("Signature.OID." + PKCSObjectIdentifiers.id_RSASSA_PSS, PREFIX + "PSSSignatureSpi$PSSwithRSA");
+            provider.addAlgorithm("Signature.RSASSA-PSS", PREFIX + "PSSSignatureSpi$PSSwithRSA", generalRsaAttributes);
+            provider.addAlgorithm("Alg.Alias.Signature." + PKCSObjectIdentifiers.id_RSASSA_PSS, "RSASSA-PSS");
+            provider.addAlgorithm("Alg.Alias.Signature.OID." + PKCSObjectIdentifiers.id_RSASSA_PSS, "RSASSA-PSS");
 
-            provider.addAlgorithm("Signature.RSA", PREFIX + "DigestSignatureSpi$noneRSA");
-            provider.addAlgorithm("Signature.RAWRSASSA-PSS", PREFIX + "PSSSignatureSpi$nonePSS");
+            provider.addAlgorithm("Signature.RSA", PREFIX + "DigestSignatureSpi$noneRSA", generalRsaAttributes);
+            provider.addAlgorithm("Signature.RAWRSASSA-PSS", PREFIX + "PSSSignatureSpi$nonePSS", generalRsaAttributes);
 
             provider.addAlgorithm("Alg.Alias.Signature.RAWRSA", "RSA");
             provider.addAlgorithm("Alg.Alias.Signature.NONEWITHRSA", "RSA");
@@ -137,17 +138,43 @@
             provider.addAlgorithm("Alg.Alias.Signature.NONEWITHRSAANDMGF1", "RAWRSASSA-PSS");
             provider.addAlgorithm("Alg.Alias.Signature.RSAPSS", "RSASSA-PSS");
 
-            addPSSSignature(provider, "SHA224", PREFIX + "PSSSignatureSpi$SHA224withRSA");
-            addPSSSignature(provider, "SHA256", PREFIX + "PSSSignatureSpi$SHA256withRSA");
-            addPSSSignature(provider, "SHA384", PREFIX + "PSSSignatureSpi$SHA384withRSA");
-            addPSSSignature(provider, "SHA512", PREFIX + "PSSSignatureSpi$SHA512withRSA");
-            addPSSSignature(provider, "SHA512(224)", PREFIX + "PSSSignatureSpi$SHA512_224withRSA");
-            addPSSSignature(provider, "SHA512(256)", PREFIX + "PSSSignatureSpi$SHA512_256withRSA");
+            addPSSSignature(provider, "SHA224", "MGF1", PREFIX + "PSSSignatureSpi$SHA224withRSA");
+            addPSSSignature(provider, "SHA256", "MGF1", PREFIX + "PSSSignatureSpi$SHA256withRSA");
+            addPSSSignature(provider, "SHA384", "MGF1", PREFIX + "PSSSignatureSpi$SHA384withRSA");
+            addPSSSignature(provider, "SHA512", "MGF1", PREFIX + "PSSSignatureSpi$SHA512withRSA");
+            addPSSSignature(provider, "SHA512(224)", "MGF1", PREFIX + "PSSSignatureSpi$SHA512_224withRSA");
+            addPSSSignature(provider, "SHA512(256)", "MGF1", PREFIX + "PSSSignatureSpi$SHA512_256withRSA");
 
-            addPSSSignature(provider, "SHA3-224", PREFIX + "PSSSignatureSpi$SHA3_224withRSA");
-            addPSSSignature(provider, "SHA3-256", PREFIX + "PSSSignatureSpi$SHA3_256withRSA");
-            addPSSSignature(provider, "SHA3-384", PREFIX + "PSSSignatureSpi$SHA3_384withRSA");
-            addPSSSignature(provider, "SHA3-512", PREFIX + "PSSSignatureSpi$SHA3_512withRSA");
+            addPSSSignature(provider, "SHA3-224", "MGF1", PREFIX + "PSSSignatureSpi$SHA3_224withRSA");
+            addPSSSignature(provider, "SHA3-256", "MGF1", PREFIX + "PSSSignatureSpi$SHA3_256withRSA");
+            addPSSSignature(provider, "SHA3-384", "MGF1", PREFIX + "PSSSignatureSpi$SHA3_384withRSA");
+            addPSSSignature(provider, "SHA3-512", "MGF1", PREFIX + "PSSSignatureSpi$SHA3_512withRSA");
+            addPSSSignature(provider, "SHAKE128", PREFIX + "PSSSignatureSpi$SHAKE128WithRSAPSS", CMSObjectIdentifiers.id_RSASSA_PSS_SHAKE128);
+            addPSSSignature(provider, "SHAKE256", PREFIX + "PSSSignatureSpi$SHAKE256WithRSAPSS", CMSObjectIdentifiers.id_RSASSA_PSS_SHAKE256);
+
+            addPSSSignature(provider, "SHA224", "SHAKE128", PREFIX + "PSSSignatureSpi$SHA224withRSAandSHAKE128");
+            addPSSSignature(provider, "SHA256", "SHAKE128", PREFIX + "PSSSignatureSpi$SHA256withRSAandSHAKE128");
+            addPSSSignature(provider, "SHA384", "SHAKE128", PREFIX + "PSSSignatureSpi$SHA384withRSAandSHAKE128");
+            addPSSSignature(provider, "SHA512", "SHAKE128", PREFIX + "PSSSignatureSpi$SHA512withRSAandSHAKE128");
+            addPSSSignature(provider, "SHA512(224)", "SHAKE128", PREFIX + "PSSSignatureSpi$SHA512_224withRSAandSHAKE128");
+            addPSSSignature(provider, "SHA512(256)", "SHAKE128", PREFIX + "PSSSignatureSpi$SHA512_256withRSAandSHAKE128");
+
+            addPSSSignature(provider, "SHA224", "SHAKE256", PREFIX + "PSSSignatureSpi$SHA224withRSAandSHAKE256");
+            addPSSSignature(provider, "SHA256", "SHAKE256", PREFIX + "PSSSignatureSpi$SHA256withRSAandSHAKE256");
+            addPSSSignature(provider, "SHA384", "SHAKE256", PREFIX + "PSSSignatureSpi$SHA384withRSAandSHAKE256");
+            addPSSSignature(provider, "SHA512", "SHAKE256", PREFIX + "PSSSignatureSpi$SHA512withRSAandSHAKE256");
+            addPSSSignature(provider, "SHA512(224)", "SHAKE256", PREFIX + "PSSSignatureSpi$SHA512_224withRSAandSHAKE256");
+            addPSSSignature(provider, "SHA512(256)", "SHAKE256", PREFIX + "PSSSignatureSpi$SHA512_256withRSAandSHAKE256");
+
+            addPSSSignature(provider, "SHA3-224", "SHAKE128", PREFIX + "PSSSignatureSpi$SHA3_224withRSAandSHAKE128");
+            addPSSSignature(provider, "SHA3-256", "SHAKE128", PREFIX + "PSSSignatureSpi$SHA3_256withRSAandSHAKE128");
+            addPSSSignature(provider, "SHA3-384", "SHAKE128", PREFIX + "PSSSignatureSpi$SHA3_384withRSAandSHAKE128");
+            addPSSSignature(provider, "SHA3-512", "SHAKE128", PREFIX + "PSSSignatureSpi$SHA3_512withRSAandSHAKE128");
+
+            addPSSSignature(provider, "SHA3-224", "SHAKE256", PREFIX + "PSSSignatureSpi$SHA3_224withRSAandSHAKE256");
+            addPSSSignature(provider, "SHA3-256", "SHAKE256", PREFIX + "PSSSignatureSpi$SHA3_256withRSAandSHAKE256");
+            addPSSSignature(provider, "SHA3-384", "SHAKE256", PREFIX + "PSSSignatureSpi$SHA3_384withRSAandSHAKE256");
+            addPSSSignature(provider, "SHA3-512", "SHAKE256", PREFIX + "PSSSignatureSpi$SHA3_512withRSAandSHAKE256");
 
             if (provider.hasAlgorithm("MessageDigest", "MD2"))
             {
@@ -170,7 +197,9 @@
                 provider.addAlgorithm("Alg.Alias.AlgorithmParameters.SHA1withRSA/PSS", "PSS");
                 provider.addAlgorithm("Alg.Alias.AlgorithmParameters.SHA1WITHRSAANDMGF1", "PSS");
 
-                addPSSSignature(provider, "SHA1", PREFIX + "PSSSignatureSpi$SHA1withRSA");
+                addPSSSignature(provider, "SHA1", "MGF1", PREFIX + "PSSSignatureSpi$SHA1withRSA");
+                addPSSSignature(provider, "SHA1", "SHAKE128", PREFIX + "PSSSignatureSpi$SHA1withRSAandSHAKE128");
+                addPSSSignature(provider, "SHA1", "SHAKE256", PREFIX + "PSSSignatureSpi$SHA1withRSAandSHAKE256");
                 addDigestSignature(provider, "SHA1", PREFIX + "DigestSignatureSpi$SHA1", PKCSObjectIdentifiers.sha1WithRSAEncryption);
                 addISO9796Signature(provider, "SHA1", PREFIX + "ISOSignatureSpi$SHA1WithRSAEncryption");
 
@@ -270,6 +299,7 @@
                 provider.addAlgorithm("Alg.Alias.Signature." + oid, mainName);
                 provider.addAlgorithm("Alg.Alias.Signature.OID." + oid, mainName);
             }
+            provider.addAttributes("Signature." + mainName, generalRsaAttributes);
         }
 
         private void addISO9796Signature(
@@ -280,12 +310,37 @@
             provider.addAlgorithm("Alg.Alias.Signature." + digest + "withRSA/ISO9796-2", digest + "WITHRSA/ISO9796-2");
             provider.addAlgorithm("Alg.Alias.Signature." + digest + "WithRSA/ISO9796-2", digest + "WITHRSA/ISO9796-2");
             provider.addAlgorithm("Signature." + digest + "WITHRSA/ISO9796-2", className);
+            provider.addAttributes("Signature." + digest + "WITHRSA/ISO9796-2", generalRsaAttributes);
+        }
+
+        private void addPSSSignature(
+            ConfigurableProvider provider,
+            String digest, String mgf,
+            String className)
+        {
+            String stem = "WITHRSAAND" + mgf;
+            if (mgf.equals("MGF1"))
+            {
+                provider.addAlgorithm("Alg.Alias.Signature." + digest + "withRSA/PSS", digest + stem);
+                provider.addAlgorithm("Alg.Alias.Signature." + digest + "WithRSA/PSS", digest + stem);
+                provider.addAlgorithm("Alg.Alias.Signature." + digest + "WITHRSA/PSS", digest + stem);
+                provider.addAlgorithm("Alg.Alias.Signature." + digest + "withRSASSA-PSS", digest + stem);
+                provider.addAlgorithm("Alg.Alias.Signature." + digest + "WithRSASSA-PSS", digest + stem);
+                provider.addAlgorithm("Alg.Alias.Signature." + digest + "WITHRSASSA-PSS", digest + stem);
+            }
+
+            provider.addAlgorithm("Alg.Alias.Signature." + digest + "withRSAand" + mgf, digest + stem);
+            provider.addAlgorithm("Alg.Alias.Signature." + digest + "WithRSAAnd" + mgf, digest + stem);
+
+            provider.addAlgorithm("Signature." + digest + "WITHRSAAND" + mgf, className);
+            provider.addAttributes("Signature." + digest + "WITHRSAAND" + mgf, generalRsaAttributes);
         }
 
         private void addPSSSignature(
             ConfigurableProvider provider,
             String digest,
-            String className)
+            String className,
+            ASN1ObjectIdentifier sigOid)
         {
             provider.addAlgorithm("Alg.Alias.Signature." + digest + "withRSA/PSS", digest + "WITHRSAANDMGF1");
             provider.addAlgorithm("Alg.Alias.Signature." + digest + "WithRSA/PSS", digest + "WITHRSAANDMGF1");
@@ -306,6 +361,7 @@
             provider.addAlgorithm("Alg.Alias.Signature." + digest + "withRSA/X9.31", digest + "WITHRSA/X9.31");
             provider.addAlgorithm("Alg.Alias.Signature." + digest + "WithRSA/X9.31", digest + "WITHRSA/X9.31");
             provider.addAlgorithm("Signature." + digest + "WITHRSA/X9.31", className);
+            provider.addAttributes("Signature." + digest + "WITHRSA/X9.31", generalRsaAttributes);
         }
     }
 }
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/asymmetric/dh/KeyAgreementSpi.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/asymmetric/dh/KeyAgreementSpi.java
index 9e812a6..8463920 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/asymmetric/dh/KeyAgreementSpi.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/asymmetric/dh/KeyAgreementSpi.java
@@ -41,6 +41,7 @@
 // import org.bouncycastle.jcajce.spec.DHUParameterSpec;
 // import org.bouncycastle.jcajce.spec.MQVParameterSpec;
 import com.android.org.bouncycastle.jcajce.spec.UserKeyingMaterialSpec;
+import com.android.org.bouncycastle.util.BigIntegers;
 
 /**
  * Diffie-Hellman key agreement. There's actually a better way of doing this
@@ -117,30 +118,9 @@
         //
         int expectedLength = (p.bitLength() + 7) / 8;
 
-        byte[]    tmp = r.toByteArray();
-
-        if (tmp.length == expectedLength)
-        {
-            return tmp;
-        }
-
-        if (tmp[0] == 0 && tmp.length == expectedLength + 1)
-        {
-            byte[]    rv = new byte[tmp.length - 1];
-            
-            System.arraycopy(tmp, 1, rv, 0, rv.length);
-            return rv;
-        }
-
-        // tmp must be shorter than expectedLength
-        // pad to the left with zeros.
-        byte[]    rv = new byte[expectedLength];
-
-        System.arraycopy(tmp, 0, rv, rv.length - tmp.length, tmp.length);
-
-        return rv;
+        return BigIntegers.asUnsignedByteArray(expectedLength, r);
     }
-    
+
     protected Key engineDoPhase(
         Key     key,
         boolean lastPhase) 
@@ -268,7 +248,7 @@
         return super.engineGenerateSecret(algorithm);
     }
 
-    protected void engineInit(
+    protected void doInitFromKey(
         Key                     key,
         AlgorithmParameterSpec  params,
         SecureRandom            random) 
@@ -387,7 +367,7 @@
         this.result = bigIntToBytes(x);
     }
 
-    protected byte[] calcSecret()
+    protected byte[] doCalcSecret()
     {
         return result;
     }
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/asymmetric/dsa/AlgorithmParameterGeneratorSpi.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/asymmetric/dsa/AlgorithmParameterGeneratorSpi.java
index 3655de0..b6647e8 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/asymmetric/dsa/AlgorithmParameterGeneratorSpi.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/asymmetric/dsa/AlgorithmParameterGeneratorSpi.java
@@ -71,7 +71,7 @@
         }
         else
         {
-            pGen = new DSAParametersGenerator(new SHA256Digest());
+            pGen = new DSAParametersGenerator(SHA256Digest.newInstance());
         }
 
         if (random == null)
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/asymmetric/dsa/DSASigner.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/asymmetric/dsa/DSASigner.java
index c7e20d2..0275b58 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/asymmetric/dsa/DSASigner.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/asymmetric/dsa/DSASigner.java
@@ -1,6 +1,6 @@
 /* GENERATED SOURCE. DO NOT MODIFY. */
-package com.android.org.bouncycastle.jcajce.provider.asymmetric.dsa;
 
+package com.android.org.bouncycastle.jcajce.provider.asymmetric.dsa;
 import java.math.BigInteger;
 import java.security.AlgorithmParameters;
 import java.security.InvalidKeyException;
@@ -10,7 +10,6 @@
 import java.security.SignatureException;
 import java.security.SignatureSpi;
 import java.security.spec.AlgorithmParameterSpec;
-
 import com.android.org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
 import com.android.org.bouncycastle.asn1.x509.X509ObjectIdentifiers;
 import com.android.org.bouncycastle.crypto.CipherParameters;
@@ -29,7 +28,6 @@
 // import org.bouncycastle.crypto.util.DigestFactory;
 import com.android.org.bouncycastle.crypto.digests.AndroidDigestFactory;
 import com.android.org.bouncycastle.util.Arrays;
-
 /**
  * @hide This class is not part of the Android public SDK API
  */
@@ -41,7 +39,6 @@
     private DSAExt                  signer;
     private DSAEncoding             encoding = StandardDSAEncoding.INSTANCE;
     private SecureRandom            random;
-
     protected DSASigner(
         Digest digest,
         DSAExt signer)
@@ -49,17 +46,14 @@
         this.digest = digest;
         this.signer = signer;
     }
-
     protected void engineInitVerify(
         PublicKey   publicKey)
         throws InvalidKeyException
     {
         CipherParameters    param = DSAUtil.generatePublicKeyParameter(publicKey);
-
         digest.reset();
         signer.init(false, param);
     }
-
     protected void engineInitSign(
         PrivateKey      privateKey,
         SecureRandom    random)
@@ -68,53 +62,43 @@
         this.random = random;
         engineInitSign(privateKey);
     }
-
     protected void engineInitSign(
         PrivateKey  privateKey)
         throws InvalidKeyException
     {
         CipherParameters    param = DSAUtil.generatePrivateKeyParameter(privateKey);
-
         // Android-added: Check DSA keys when generated
         DSAParameters dsaParam = ((DSAKeyParameters) param).getParameters();
         checkKey(dsaParam);
-
         if (random != null)
         {
             param = new ParametersWithRandom(param, random);
         }
-
         digest.reset();
         signer.init(true, param);
     }
-
     protected void engineUpdate(
         byte    b)
         throws SignatureException
     {
         digest.update(b);
     }
-
     protected void engineUpdate(
         byte[]  b,
         int     off,
-        int     len) 
+        int     len)
         throws SignatureException
     {
         digest.update(b, off, len);
     }
-
     protected byte[] engineSign()
         throws SignatureException
     {
         byte[]  hash = new byte[digest.getDigestSize()];
-
         digest.doFinal(hash, 0);
-
         try
         {
             BigInteger[] sig = signer.generateSignature(hash);
-
             return encoding.encode(signer.getOrder(), sig[0], sig[1]);
         }
         catch (Exception e)
@@ -122,17 +106,13 @@
             throw new SignatureException(e.toString());
         }
     }
-
     protected boolean engineVerify(
-        byte[]  sigBytes) 
+        byte[]  sigBytes)
         throws SignatureException
     {
         byte[]  hash = new byte[digest.getDigestSize()];
-
         digest.doFinal(hash, 0);
-
         BigInteger[] sig;
-
         try
         {
             sig = encoding.decode(signer.getOrder(), sigBytes);
@@ -141,27 +121,22 @@
         {
             throw new SignatureException("error decoding signature bytes.");
         }
-
         return signer.verifySignature(hash, sig[0], sig[1]);
     }
-
     protected AlgorithmParameters engineGetParameters()
     {
         return null;
     }
-
     protected void engineSetParameter(
         AlgorithmParameterSpec params)
     {
         throw new UnsupportedOperationException("engineSetParameter unsupported");
     }
-
     // BEGIN Android-added: Check DSA keys when generated
     protected void checkKey(DSAParameters params) throws InvalidKeyException {
         int valueL = params.getP().bitLength();
         int valueN = params.getQ().bitLength();
         int digestSize = digest.getDigestSize();
-
         // The checks are consistent with DSAParametersGenerator's init method.
         if ((valueL < 1024 || valueL > 3072) || valueL % 1024 != 0) {
             throw new InvalidKeyException("valueL values must be between 1024 and 3072 and a multiple of 1024");
@@ -176,7 +151,6 @@
             throw new InvalidKeyException("Key is too strong for this signature algorithm");
         }
     }
-
     // END Android-added: Check DSA keys when generated
     /**
      * @deprecated replaced with #engineSetParameter(java.security.spec.AlgorithmParameterSpec)
@@ -187,7 +161,6 @@
     {
         throw new UnsupportedOperationException("engineSetParameter unsupported");
     }
-
     /**
      * @deprecated
      */
@@ -196,7 +169,6 @@
     {
         throw new UnsupportedOperationException("engineGetParameter unsupported");
     }
-
     /**
      * @hide This class is not part of the Android public SDK API
      */
@@ -210,7 +182,6 @@
             super(AndroidDigestFactory.getSHA1(), new com.android.org.bouncycastle.crypto.signers.DSASigner());
         }
     }
-
     // BEGIN Android-removed: Unsupported algorithm
     /*
     static public class detDSA
@@ -221,6 +192,14 @@
             super(DigestFactory.createSHA1(), new org.bouncycastle.crypto.signers.DSASigner(new HMacDSAKCalculator(DigestFactory.createSHA1())));
         }
     }
+    static public class dsaRMD160
+        extends DSASigner
+    {
+        public dsaRMD160()
+        {
+            super(new RIPEMD160Digest(), new org.bouncycastle.crypto.signers.DSASigner());
+        }
+    }
     */
     // END Android-removed: Unsupported algorithm
 
@@ -237,7 +216,6 @@
             super(AndroidDigestFactory.getSHA224(), new com.android.org.bouncycastle.crypto.signers.DSASigner());
         }
     }
-
     // BEGIN Android-removed: Unsupported algorithm
     /*
     static public class detDSA224
@@ -250,7 +228,6 @@
     }
     */
     // END Android-removed: Unsupported algorithm
-
     /**
      * @hide This class is not part of the Android public SDK API
      */
@@ -264,7 +241,6 @@
             super(AndroidDigestFactory.getSHA256(), new com.android.org.bouncycastle.crypto.signers.DSASigner());
         }
     }
-
     // BEGIN Android-removed: Unsupported algorithms
     /*
     static public class detDSA256
@@ -275,7 +251,6 @@
             super(DigestFactory.createSHA256(), new org.bouncycastle.crypto.signers.DSASigner(new HMacDSAKCalculator(DigestFactory.createSHA256())));
         }
     }
-
     static public class dsa384
         extends DSASigner
     {
@@ -284,7 +259,6 @@
             super(DigestFactory.createSHA384(), new org.bouncycastle.crypto.signers.DSASigner());
         }
     }
-
     static public class detDSA384
         extends DSASigner
     {
@@ -293,7 +267,6 @@
             super(DigestFactory.createSHA384(), new org.bouncycastle.crypto.signers.DSASigner(new HMacDSAKCalculator(DigestFactory.createSHA384())));
         }
     }
-
     static public class dsa512
         extends DSASigner
     {
@@ -302,7 +275,6 @@
             super(DigestFactory.createSHA512(), new org.bouncycastle.crypto.signers.DSASigner());
         }
     }
-
     static public class detDSA512
         extends DSASigner
     {
@@ -311,7 +283,6 @@
             super(DigestFactory.createSHA512(), new org.bouncycastle.crypto.signers.DSASigner(new HMacDSAKCalculator(DigestFactory.createSHA512())));
         }
     }
-
     static public class dsaSha3_224
         extends DSASigner
     {
@@ -320,7 +291,6 @@
             super(DigestFactory.createSHA3_224(), new org.bouncycastle.crypto.signers.DSASigner());
         }
     }
-
     static public class detDSASha3_224
         extends DSASigner
     {
@@ -329,7 +299,6 @@
             super(DigestFactory.createSHA3_224(), new org.bouncycastle.crypto.signers.DSASigner(new HMacDSAKCalculator(DigestFactory.createSHA3_224())));
         }
     }
-
     static public class dsaSha3_256
         extends DSASigner
     {
@@ -338,7 +307,6 @@
             super(DigestFactory.createSHA3_256(), new org.bouncycastle.crypto.signers.DSASigner());
         }
     }
-
     static public class detDSASha3_256
         extends DSASigner
     {
@@ -347,7 +315,6 @@
             super(DigestFactory.createSHA3_256(), new org.bouncycastle.crypto.signers.DSASigner(new HMacDSAKCalculator(DigestFactory.createSHA3_256())));
         }
     }
-
     static public class dsaSha3_384
         extends DSASigner
     {
@@ -356,7 +323,6 @@
             super(DigestFactory.createSHA3_384(), new org.bouncycastle.crypto.signers.DSASigner());
         }
     }
-
     static public class detDSASha3_384
         extends DSASigner
     {
@@ -365,7 +331,6 @@
             super(DigestFactory.createSHA3_384(), new org.bouncycastle.crypto.signers.DSASigner(new HMacDSAKCalculator(DigestFactory.createSHA3_384())));
         }
     }
-
     static public class dsaSha3_512
         extends DSASigner
     {
@@ -374,7 +339,6 @@
             super(DigestFactory.createSHA3_512(), new org.bouncycastle.crypto.signers.DSASigner());
         }
     }
-
     static public class detDSASha3_512
         extends DSASigner
     {
@@ -385,7 +349,6 @@
     }
     */
     // END Android-removed: Unsupported algorithms
-
     /**
      * @hide This class is not part of the Android public SDK API
      */
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/asymmetric/dsa/KeyPairGeneratorSpi.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/asymmetric/dsa/KeyPairGeneratorSpi.java
index 6ff1651..96e1904 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/asymmetric/dsa/KeyPairGeneratorSpi.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/asymmetric/dsa/KeyPairGeneratorSpi.java
@@ -152,7 +152,7 @@
                         else if (strength > 1024)
                         {
                             dsaParams = new DSAParameterGenerationParameters(strength, 256, certainty, random);
-                            pGen = new DSAParametersGenerator(new SHA256Digest());
+                            pGen = new DSAParametersGenerator(SHA256Digest.newInstance());
                             pGen.init(dsaParams);
                         }
                         else
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/asymmetric/ec/AlgorithmParametersSpi.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/asymmetric/ec/AlgorithmParametersSpi.java
index a1f852f..76a15d8 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/asymmetric/ec/AlgorithmParametersSpi.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/asymmetric/ec/AlgorithmParametersSpi.java
@@ -16,6 +16,7 @@
 import com.android.org.bouncycastle.asn1.x9.X9ECPoint;
 import com.android.org.bouncycastle.jcajce.provider.asymmetric.util.EC5Util;
 import com.android.org.bouncycastle.jcajce.provider.asymmetric.util.ECUtil;
+import com.android.org.bouncycastle.jcajce.provider.config.ProviderConfiguration;
 import com.android.org.bouncycastle.jce.provider.BouncyCastleProvider;
 import com.android.org.bouncycastle.jce.spec.ECNamedCurveSpec;
 import com.android.org.bouncycastle.math.ec.ECCurve;
@@ -41,12 +42,14 @@
         if (algorithmParameterSpec instanceof ECGenParameterSpec)
         {
             ECGenParameterSpec ecGenParameterSpec = (ECGenParameterSpec)algorithmParameterSpec;
-            X9ECParameters params = ECUtils.getDomainParametersFromGenSpec(ecGenParameterSpec);
+            ProviderConfiguration configuration = BouncyCastleProvider.CONFIGURATION;
 
-            if (params == null)
+            X9ECParameters params = ECUtils.getDomainParametersFromGenSpec(ecGenParameterSpec, configuration);
+            if (null == params)
             {
                 throw new InvalidParameterSpecException("EC curve name not recognized: " + ecGenParameterSpec.getName());
             }
+
             curveName = ecGenParameterSpec.getName();
             ECParameterSpec baseSpec = EC5Util.convertToSpec(params);
             ecParameterSpec = new ECNamedCurveSpec(curveName,
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/asymmetric/ec/BCECPrivateKey.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/asymmetric/ec/BCECPrivateKey.java
index ced733f..ddc8420 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/asymmetric/ec/BCECPrivateKey.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/asymmetric/ec/BCECPrivateKey.java
@@ -11,18 +11,20 @@
 import java.security.spec.EllipticCurve;
 import java.util.Enumeration;
 
+import com.android.org.bouncycastle.asn1.ASN1BitString;
 import com.android.org.bouncycastle.asn1.ASN1Encodable;
 import com.android.org.bouncycastle.asn1.ASN1Encoding;
 import com.android.org.bouncycastle.asn1.ASN1Integer;
 import com.android.org.bouncycastle.asn1.ASN1ObjectIdentifier;
 import com.android.org.bouncycastle.asn1.ASN1Primitive;
-import com.android.org.bouncycastle.asn1.DERBitString;
 import com.android.org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
 import com.android.org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 import com.android.org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import com.android.org.bouncycastle.asn1.x9.ECNamedCurveTable;
 import com.android.org.bouncycastle.asn1.x9.X962Parameters;
 import com.android.org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
 import com.android.org.bouncycastle.crypto.params.ECDomainParameters;
+import com.android.org.bouncycastle.crypto.params.ECNamedDomainParameters;
 import com.android.org.bouncycastle.crypto.params.ECPrivateKeyParameters;
 import com.android.org.bouncycastle.jcajce.provider.asymmetric.util.EC5Util;
 import com.android.org.bouncycastle.jcajce.provider.asymmetric.util.ECUtil;
@@ -31,7 +33,9 @@
 import com.android.org.bouncycastle.jce.interfaces.ECPointEncoder;
 import com.android.org.bouncycastle.jce.interfaces.PKCS12BagAttributeCarrier;
 import com.android.org.bouncycastle.jce.provider.BouncyCastleProvider;
+import com.android.org.bouncycastle.jce.spec.ECNamedCurveParameterSpec;
 import com.android.org.bouncycastle.math.ec.ECCurve;
+import com.android.org.bouncycastle.util.Arrays;
 
 /**
  * @hide This class is not part of the Android public SDK API
@@ -41,16 +45,20 @@
 {
     static final long serialVersionUID = 994553197664784084L;
 
-    private String          algorithm = "EC";
-    private boolean         withCompression;
+    private String algorithm = "EC";
+    private boolean withCompression;
 
-    private transient BigInteger              d;
-    private transient ECParameterSpec         ecSpec;
-    private transient ProviderConfiguration   configuration;
-    private transient DERBitString            publicKey;
+    private transient BigInteger d;
+    private transient ECParameterSpec ecSpec;
+    private transient ProviderConfiguration configuration;
+    private transient ASN1BitString publicKey;
+    private transient PrivateKeyInfo privateKeyInfo;
+    private transient byte[] encoding;
 
+    private transient ECPrivateKeyParameters baseKey;
     private transient PKCS12BagAttributeCarrierImpl attrCarrier = new PKCS12BagAttributeCarrierImpl();
 
+
     protected BCECPrivateKey()
     {
     }
@@ -63,6 +71,7 @@
         this.algorithm = key.getAlgorithm();
         this.ecSpec = key.getParams();
         this.configuration = configuration;
+        this.baseKey = convertToBaseKey(this);
     }
 
     public BCECPrivateKey(
@@ -88,6 +97,7 @@
         }
 
         this.configuration = configuration;
+        this.baseKey = convertToBaseKey(this);
     }
 
 
@@ -100,6 +110,7 @@
         this.d = spec.getS();
         this.ecSpec = spec.getParams();
         this.configuration = configuration;
+        this.baseKey = convertToBaseKey(this);
     }
 
     public BCECPrivateKey(
@@ -113,6 +124,7 @@
         this.attrCarrier = key.attrCarrier;
         this.publicKey = key.publicKey;
         this.configuration = key.configuration;
+        this.baseKey = key.baseKey;
     }
 
     public BCECPrivateKey(
@@ -125,6 +137,7 @@
         this.algorithm = algorithm;
         this.d = params.getD();
         this.configuration = configuration;
+        this.baseKey = params;
 
         if (spec == null)
         {
@@ -155,6 +168,7 @@
         this.algorithm = algorithm;
         this.d = params.getD();
         this.configuration = configuration;
+        this.baseKey = params;
 
         if (spec == null)
         {
@@ -193,10 +207,11 @@
         this.d = params.getD();
         this.ecSpec = null;
         this.configuration = configuration;
+        this.baseKey = params;
     }
 
     BCECPrivateKey(
-        String         algorithm,
+        String algorithm,
         PrivateKeyInfo info,
         ProviderConfiguration configuration)
         throws IOException
@@ -217,7 +232,7 @@
         ASN1Encodable privKey = info.parsePrivateKey();
         if (privKey instanceof ASN1Integer)
         {
-            ASN1Integer          derD = ASN1Integer.getInstance(privKey);
+            ASN1Integer derD = ASN1Integer.getInstance(privKey);
 
             this.d = derD.getValue();
         }
@@ -228,6 +243,7 @@
             this.d = ec.getKey();
             this.publicKey = ec.getPublicKey();
         }
+        this.baseKey = convertToBaseKey(this);
     }
 
     public String getAlgorithm()
@@ -253,40 +269,71 @@
      */
     public byte[] getEncoded()
     {
-        X962Parameters  params = ECUtils.getDomainParametersFromName(ecSpec, withCompression);
+        if (encoding == null)
+        {
+            PrivateKeyInfo info = getPrivateKeyInfo();
 
-        int orderBitLength;
-        if (ecSpec == null)
-        {
-            orderBitLength = ECUtil.getOrderBitLength(configuration, null, this.getS());
-        }
-        else
-        {
-            orderBitLength = ECUtil.getOrderBitLength(configuration, ecSpec.getOrder(), this.getS());
-        }
-        
-        PrivateKeyInfo          info;
-        com.android.org.bouncycastle.asn1.sec.ECPrivateKey            keyStructure;
+            if (info == null)
+            {
+                return null;
+            }
 
-        if (publicKey != null)
-        {
-            keyStructure = new com.android.org.bouncycastle.asn1.sec.ECPrivateKey(orderBitLength, this.getS(), publicKey, params);
-        }
-        else
-        {
-            keyStructure = new com.android.org.bouncycastle.asn1.sec.ECPrivateKey(orderBitLength, this.getS(), params);
+            try
+            {
+                encoding = info.getEncoded(ASN1Encoding.DER);
+            }
+            catch (IOException e)
+            {
+                return null;
+            }
         }
 
-        try
-        {
-            info = new PrivateKeyInfo(new AlgorithmIdentifier(X9ObjectIdentifiers.id_ecPublicKey, params), keyStructure);
+        return Arrays.clone(encoding);
+    }
 
-            return info.getEncoded(ASN1Encoding.DER);
-        }
-        catch (IOException e)
+    private PrivateKeyInfo getPrivateKeyInfo()
+    {
+        if (privateKeyInfo == null)
         {
-            return null;
+            X962Parameters params = ECUtils.getDomainParametersFromName(ecSpec, withCompression);
+
+            int orderBitLength;
+            if (ecSpec == null)
+            {
+                orderBitLength = ECUtil.getOrderBitLength(configuration, null, this.getS());
+            }
+            else
+            {
+                orderBitLength = ECUtil.getOrderBitLength(configuration, ecSpec.getOrder(), this.getS());
+            }
+
+            com.android.org.bouncycastle.asn1.sec.ECPrivateKey keyStructure;
+
+            if (publicKey != null)
+            {
+                keyStructure = new com.android.org.bouncycastle.asn1.sec.ECPrivateKey(orderBitLength, this.getS(), publicKey, params);
+            }
+            else
+            {
+                keyStructure = new com.android.org.bouncycastle.asn1.sec.ECPrivateKey(orderBitLength, this.getS(), params);
+            }
+
+            try
+            {
+                privateKeyInfo = new PrivateKeyInfo(new AlgorithmIdentifier(X9ObjectIdentifiers.id_ecPublicKey, params), keyStructure);
+            }
+            catch (IOException e)
+            {
+                return null;
+            }
         }
+
+        return privateKeyInfo;
+    }
+
+    public ECPrivateKeyParameters engineGetKeyParameters()
+    {
+        return baseKey;
     }
 
     public ECParameterSpec getParams()
@@ -300,7 +347,6 @@
         {
             return null;
         }
-        
         return EC5Util.convertSpec(ecSpec);
     }
 
@@ -323,10 +369,10 @@
     {
         return d;
     }
-    
+
     public void setBagAttribute(
         ASN1ObjectIdentifier oid,
-        ASN1Encodable        attribute)
+        ASN1Encodable attribute)
     {
         attrCarrier.setBagAttribute(oid, attribute);
     }
@@ -344,19 +390,37 @@
 
     public void setPointFormat(String style)
     {
-       withCompression = !("UNCOMPRESSED".equalsIgnoreCase(style));
+        withCompression = !("UNCOMPRESSED".equalsIgnoreCase(style));
     }
 
     public boolean equals(Object o)
     {
-        if (!(o instanceof BCECPrivateKey))
+        if (o instanceof ECPrivateKey)
         {
-            return false;
+            ECPrivateKey other = (ECPrivateKey)o;
+
+            PrivateKeyInfo info = this.getPrivateKeyInfo();
+            PrivateKeyInfo otherInfo = (other instanceof BCECPrivateKey) ? ((BCECPrivateKey)other).getPrivateKeyInfo() : PrivateKeyInfo.getInstance(other.getEncoded());
+
+            if (info == null || otherInfo == null)
+            {
+                return false;
+            }
+
+            try
+            {
+                boolean algEquals = Arrays.constantTimeAreEqual(info.getPrivateKeyAlgorithm().getEncoded(), otherInfo.getPrivateKeyAlgorithm().getEncoded());
+                boolean keyEquals = Arrays.constantTimeAreEqual(this.getS().toByteArray(), other.getS().toByteArray());
+
+                return algEquals & keyEquals;
+            }
+            catch (IOException e)
+            {
+                return false;
+            }
         }
 
-        BCECPrivateKey other = (BCECPrivateKey)o;
-
-        return getD().equals(other.getD()) && (engineGetSpec().equals(other.engineGetSpec()));
+        return false;
     }
 
     public int hashCode()
@@ -369,7 +433,7 @@
         return ECUtil.privateKeyToString("EC", d, engineGetSpec());
     }
 
-    private DERBitString getPublicKeyDetails(BCECPublicKey pub)
+    private ASN1BitString getPublicKeyDetails(BCECPublicKey pub)
     {
         try
         {
@@ -406,4 +470,31 @@
 
         out.writeObject(this.getEncoded());
     }
+
+    private static ECPrivateKeyParameters convertToBaseKey(BCECPrivateKey key)
+    {
+        com.android.org.bouncycastle.jce.interfaces.ECPrivateKey k = (com.android.org.bouncycastle.jce.interfaces.ECPrivateKey)key;
+        com.android.org.bouncycastle.jce.spec.ECParameterSpec s = k.getParameters();
+
+        if (s == null)
+        {
+            s = BouncyCastleProvider.CONFIGURATION.getEcImplicitlyCa();
+        }
+
+        if (k.getParameters() instanceof ECNamedCurveParameterSpec)
+        {
+            String name = ((ECNamedCurveParameterSpec)k.getParameters()).getName();
+            if (name != null)
+            {
+                return new ECPrivateKeyParameters(
+                    k.getD(),
+                    new ECNamedDomainParameters(ECNamedCurveTable.getOID(name),
+                        s.getCurve(), s.getG(), s.getN(), s.getH(), s.getSeed()));
+            }
+        }
+
+        return new ECPrivateKeyParameters(
+                k.getD(),
+                new ECDomainParameters(s.getCurve(), s.getG(), s.getN(), s.getH(), s.getSeed()));
+    }
 }
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/asymmetric/ec/BCECPublicKey.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/asymmetric/ec/BCECPublicKey.java
index 94e7415..db8fb19 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/asymmetric/ec/BCECPublicKey.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/asymmetric/ec/BCECPublicKey.java
@@ -10,9 +10,9 @@
 import java.security.spec.ECPublicKeySpec;
 import java.security.spec.EllipticCurve;
 
+import com.android.org.bouncycastle.asn1.ASN1BitString;
 import com.android.org.bouncycastle.asn1.ASN1OctetString;
 import com.android.org.bouncycastle.asn1.ASN1Primitive;
-import com.android.org.bouncycastle.asn1.DERBitString;
 import com.android.org.bouncycastle.asn1.DEROctetString;
 import com.android.org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 import com.android.org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
@@ -29,6 +29,7 @@
 import com.android.org.bouncycastle.jce.interfaces.ECPointEncoder;
 import com.android.org.bouncycastle.jce.provider.BouncyCastleProvider;
 import com.android.org.bouncycastle.math.ec.ECCurve;
+import com.android.org.bouncycastle.util.Arrays;
 import com.android.org.bouncycastle.util.Properties;
 
 /**
@@ -45,6 +46,8 @@
     private transient ECPublicKeyParameters   ecPublicKey;
     private transient ECParameterSpec         ecSpec;
     private transient ProviderConfiguration   configuration;
+    private transient byte[]                  encoding;
+    private transient boolean                 oldPcSet;
 
     public BCECPublicKey(
         String algorithm,
@@ -197,7 +200,7 @@
         ECCurve curve = EC5Util.getCurve(configuration, params);
         ecSpec = EC5Util.convertToSpec(params, curve);
 
-        DERBitString    bits = info.getPublicKeyData();
+        ASN1BitString   bits = info.getPublicKeyData();
         byte[]          data = bits.getBytes();
         ASN1OctetString key = new DEROctetString(data);
 
@@ -239,16 +242,23 @@
 
     public byte[] getEncoded()
     {
-        boolean compress = withCompression || Properties.isOverrideSet("com.android.org.bouncycastle.ec.enable_pc");
+        boolean pcSet = Properties.isOverrideSet("com.android.org.bouncycastle.ec.enable_pc");
+        if (encoding == null || oldPcSet != pcSet)
+        {
+            boolean compress = withCompression || pcSet;
 
-        AlgorithmIdentifier algId = new AlgorithmIdentifier(
-            X9ObjectIdentifiers.id_ecPublicKey,
-            ECUtils.getDomainParametersFromName(ecSpec, compress));
+            AlgorithmIdentifier algId = new AlgorithmIdentifier(
+                X9ObjectIdentifiers.id_ecPublicKey,
+                ECUtils.getDomainParametersFromName(ecSpec, compress));
 
-        byte[] pubKeyOctets = ecPublicKey.getQ().getEncoded(compress);
+            byte[] pubKeyOctets = ecPublicKey.getQ().getEncoded(compress);
 
-        // stored curve is null if ImplicitlyCa
-        return KeyUtil.getEncodedSubjectPublicKeyInfo(algId, pubKeyOctets);
+            // stored curve is null if ImplicitlyCa
+            encoding = KeyUtil.getEncodedSubjectPublicKeyInfo(algId, pubKeyOctets);
+            oldPcSet = pcSet;
+        }
+
+        return Arrays.clone(encoding);
     }
 
     public ECParameterSpec getParams()
@@ -306,18 +316,26 @@
     public void setPointFormat(String style)
     {
        withCompression = !("UNCOMPRESSED".equalsIgnoreCase(style));
+       encoding = null;
     }
 
     public boolean equals(Object o)
     {
-        if (!(o instanceof BCECPublicKey))
+        if (o instanceof BCECPublicKey)
         {
-            return false;
+            BCECPublicKey other = (BCECPublicKey)o;
+
+            return ecPublicKey.getQ().equals(other.ecPublicKey.getQ()) && (engineGetSpec().equals(other.engineGetSpec()));
         }
 
-        BCECPublicKey other = (BCECPublicKey)o;
+        if (o instanceof ECPublicKey)
+        {
+            ECPublicKey other = (ECPublicKey)o;
 
-        return ecPublicKey.getQ().equals(other.ecPublicKey.getQ()) && (engineGetSpec().equals(other.engineGetSpec()));
+            return Arrays.areEqual(getEncoded(), other.getEncoded());
+        }
+
+        return false;
     }
 
     public int hashCode()
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/asymmetric/ec/ECUtils.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/asymmetric/ec/ECUtils.java
index 99bdedb..3cb1f10 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/asymmetric/ec/ECUtils.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/asymmetric/ec/ECUtils.java
@@ -3,9 +3,11 @@
 
 import java.math.BigInteger;
 import java.security.InvalidKeyException;
+import java.security.PrivateKey;
 import java.security.PublicKey;
 import java.security.spec.ECGenParameterSpec;
 import java.security.spec.ECParameterSpec;
+import java.util.Map;
 
 import com.android.org.bouncycastle.asn1.ASN1ObjectIdentifier;
 import com.android.org.bouncycastle.asn1.DERNull;
@@ -15,6 +17,7 @@
 import com.android.org.bouncycastle.crypto.params.AsymmetricKeyParameter;
 import com.android.org.bouncycastle.jcajce.provider.asymmetric.util.EC5Util;
 import com.android.org.bouncycastle.jcajce.provider.asymmetric.util.ECUtil;
+import com.android.org.bouncycastle.jcajce.provider.config.ProviderConfiguration;
 import com.android.org.bouncycastle.jce.spec.ECNamedCurveSpec;
 import com.android.org.bouncycastle.math.ec.ECCurve;
 
@@ -27,39 +30,49 @@
         return (key instanceof BCECPublicKey) ? ((BCECPublicKey)key).engineGetKeyParameters() : ECUtil.generatePublicKeyParameter(key);
     }
 
-    static X9ECParameters getDomainParametersFromGenSpec(ECGenParameterSpec genSpec)
+    static AsymmetricKeyParameter generatePrivateKeyParameter(
+            PrivateKey key)
+        throws InvalidKeyException
     {
-        return getDomainParametersFromName(genSpec.getName());
+        return (key instanceof BCECPrivateKey) ? ((BCECPrivateKey)key).engineGetKeyParameters() : ECUtil.generatePrivateKeyParameter(key);
     }
 
-    static X9ECParameters getDomainParametersFromName(String curveName)
+    static X9ECParameters getDomainParametersFromGenSpec(ECGenParameterSpec genSpec, ProviderConfiguration configuration)
     {
-        X9ECParameters domainParameters;
-        try
+        return getDomainParametersFromName(genSpec.getName(), configuration);
+    }
+
+    static X9ECParameters getDomainParametersFromName(String curveName, ProviderConfiguration configuration)
+    {
+        if (null == curveName || curveName.length() < 1)
         {
-            if (curveName.charAt(0) >= '0' && curveName.charAt(0) <= '2')
+            return null;
+        }
+
+        int spacePos = curveName.indexOf(' ');
+        if (spacePos > 0)
+        {
+            curveName = curveName.substring(spacePos + 1);
+        }
+
+        ASN1ObjectIdentifier oid = getOID(curveName);
+        if (null == oid)
+        {
+            return ECUtil.getNamedCurveByName(curveName);
+        }
+
+        X9ECParameters x9 = ECUtil.getNamedCurveByOid(oid);
+        if (null == x9)
+        {
+            if (null != configuration)
             {
-                ASN1ObjectIdentifier oidID = new ASN1ObjectIdentifier(curveName);
-                domainParameters = ECUtil.getNamedCurveByOid(oidID);
-            }
-            else
-            {
-                if (curveName.indexOf(' ') > 0)
-                {
-                    curveName = curveName.substring(curveName.indexOf(' ') + 1);
-                    domainParameters = ECUtil.getNamedCurveByName(curveName);
-                }
-                else
-                {
-                    domainParameters = ECUtil.getNamedCurveByName(curveName);
-                }
+                Map extraCurves = configuration.getAdditionalECParameters();
+
+                x9 = (X9ECParameters)extraCurves.get(oid);
             }
         }
-        catch (IllegalArgumentException ex)
-        {
-            domainParameters = ECUtil.getNamedCurveByName(curveName);
-        }
-        return domainParameters;
+
+        return x9;
     }
 
     static X962Parameters getDomainParametersFromName(ECParameterSpec ecSpec, boolean withCompression)
@@ -95,4 +108,20 @@
 
         return params;
     }
+
+    private static ASN1ObjectIdentifier getOID(String curveName)
+    {
+        char firstChar = curveName.charAt(0);
+        if (firstChar >= '0' && firstChar <= '2')
+        {
+            try
+            {
+                return new ASN1ObjectIdentifier(curveName);
+            }
+            catch (Exception e)
+            {
+            }
+        }
+        return null;
+    }
 }
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/asymmetric/ec/GMKeyPairGeneratorSpi.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/asymmetric/ec/GMKeyPairGeneratorSpi.java
new file mode 100644
index 0000000..1cbf0a8
--- /dev/null
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/asymmetric/ec/GMKeyPairGeneratorSpi.java
@@ -0,0 +1,259 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.org.bouncycastle.jcajce.provider.asymmetric.ec;
+
+import java.math.BigInteger;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidParameterException;
+import java.security.KeyPair;
+import java.security.SecureRandom;
+import java.security.spec.AlgorithmParameterSpec;
+import java.security.spec.ECGenParameterSpec;
+import java.util.Hashtable;
+
+import com.android.org.bouncycastle.asn1.x9.X9ECParameters;
+import com.android.org.bouncycastle.crypto.AsymmetricCipherKeyPair;
+import com.android.org.bouncycastle.crypto.CryptoServicesRegistrar;
+import com.android.org.bouncycastle.crypto.generators.ECKeyPairGenerator;
+import com.android.org.bouncycastle.crypto.params.ECDomainParameters;
+import com.android.org.bouncycastle.crypto.params.ECKeyGenerationParameters;
+import com.android.org.bouncycastle.crypto.params.ECPrivateKeyParameters;
+import com.android.org.bouncycastle.crypto.params.ECPublicKeyParameters;
+import com.android.org.bouncycastle.jcajce.provider.asymmetric.util.EC5Util;
+import com.android.org.bouncycastle.jcajce.provider.asymmetric.util.ECUtil;
+import com.android.org.bouncycastle.jcajce.provider.config.ProviderConfiguration;
+import com.android.org.bouncycastle.jce.provider.BouncyCastleProvider;
+import com.android.org.bouncycastle.jce.spec.ECNamedCurveGenParameterSpec;
+import com.android.org.bouncycastle.jce.spec.ECNamedCurveSpec;
+import com.android.org.bouncycastle.jce.spec.ECParameterSpec;
+import com.android.org.bouncycastle.math.ec.ECCurve;
+import com.android.org.bouncycastle.math.ec.ECPoint;
+import com.android.org.bouncycastle.util.Integers;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+public abstract class GMKeyPairGeneratorSpi
+    extends java.security.KeyPairGenerator
+{
+    public GMKeyPairGeneratorSpi(String algorithmName)
+    {
+        super(algorithmName);
+    }
+
+    /**
+     * @hide This class is not part of the Android public SDK API
+     */
+    public static class BaseSM2
+        extends GMKeyPairGeneratorSpi
+    {
+        ECKeyGenerationParameters   param;
+        ECKeyPairGenerator          engine = new ECKeyPairGenerator();
+        Object                      ecParams = null;
+        int                         strength = 239;
+        SecureRandom                random = CryptoServicesRegistrar.getSecureRandom();
+        boolean                     initialised = false;
+        String                      algorithm;
+        ProviderConfiguration       configuration;
+
+        static private Hashtable    ecParameters;
+
+        static
+        {
+            ecParameters = new Hashtable();
+
+            ecParameters.put(Integers.valueOf(192), new ECNamedCurveGenParameterSpec("prime192v1")); // a.k.a P-192
+            ecParameters.put(Integers.valueOf(239), new ECNamedCurveGenParameterSpec("prime239v1"));
+            ecParameters.put(Integers.valueOf(256), new ECNamedCurveGenParameterSpec("prime256v1")); // a.k.a P-256
+
+            ecParameters.put(Integers.valueOf(224), new ECNamedCurveGenParameterSpec("P-224"));
+            ecParameters.put(Integers.valueOf(384), new ECNamedCurveGenParameterSpec("P-384"));
+            ecParameters.put(Integers.valueOf(521), new ECNamedCurveGenParameterSpec("P-521"));
+        }
+
+        public BaseSM2()
+        {
+            super("EC");
+            this.algorithm = "EC";
+            this.configuration = BouncyCastleProvider.CONFIGURATION;
+        }
+
+        public BaseSM2(
+            String  algorithm,
+            ProviderConfiguration configuration)
+        {
+            super(algorithm);
+            this.algorithm = algorithm;
+            this.configuration = configuration;
+        }
+
+        public void initialize(
+            int             strength,
+            SecureRandom    random)
+        {
+            this.strength = strength;
+            this.random = random;
+
+            ECNamedCurveGenParameterSpec ecParams = (ECNamedCurveGenParameterSpec)ecParameters.get(Integers.valueOf(strength));
+            if (ecParams == null)
+            {
+                throw new InvalidParameterException("unknown key size.");
+            }
+
+            try
+            {
+                initialize(ecParams, random);
+            }
+            catch (InvalidAlgorithmParameterException e)
+            {
+                throw new InvalidParameterException("key size not configurable.");
+            }
+        }
+
+        public void initialize(
+            AlgorithmParameterSpec  params,
+            SecureRandom            random)
+            throws InvalidAlgorithmParameterException
+        {
+            if (params == null)
+            {
+                ECParameterSpec implicitCA = configuration.getEcImplicitlyCa();
+                if (implicitCA == null)
+                {
+                    throw new InvalidAlgorithmParameterException("null parameter passed but no implicitCA set");
+                }
+
+                this.ecParams = null;
+                this.param = createKeyGenParamsBC(implicitCA, random);
+            }
+            else if (params instanceof ECParameterSpec)
+            {
+                this.ecParams = params;
+                this.param = createKeyGenParamsBC((ECParameterSpec)params, random);
+            }
+            else if (params instanceof java.security.spec.ECParameterSpec)
+            {
+                this.ecParams = params;
+                this.param = createKeyGenParamsJCE((java.security.spec.ECParameterSpec)params, random);
+            }
+            else if (params instanceof ECGenParameterSpec)
+            {
+                initializeNamedCurve(((ECGenParameterSpec)params).getName(), random);
+            }
+            else if (params instanceof ECNamedCurveGenParameterSpec)
+            {
+                initializeNamedCurve(((ECNamedCurveGenParameterSpec)params).getName(), random);
+            }
+            else
+            {
+                String name = ECUtil.getNameFrom(params);
+
+                if (name != null)
+                {
+                    initializeNamedCurve(name, random);
+                }
+                else
+                {
+                    throw new InvalidAlgorithmParameterException("invalid parameterSpec: " + params);
+                }
+            }
+
+            engine.init(param);
+            initialised = true;
+        }
+
+        public KeyPair generateKeyPair()
+        {
+            if (!initialised)
+            {
+                initialize(strength, new SecureRandom());
+            }
+
+            AsymmetricCipherKeyPair     pair = engine.generateKeyPair();
+            ECPublicKeyParameters       pub = (ECPublicKeyParameters)pair.getPublic();
+            ECPrivateKeyParameters      priv = (ECPrivateKeyParameters)pair.getPrivate();
+
+            if (ecParams instanceof ECParameterSpec)
+            {
+                ECParameterSpec p = (ECParameterSpec)ecParams;
+
+                BCECPublicKey pubKey = new BCECPublicKey(algorithm, pub, p, configuration);
+                return new KeyPair(pubKey,
+                                   new BCECPrivateKey(algorithm, priv, pubKey, p, configuration));
+            }
+            else if (ecParams == null)
+            {
+               return new KeyPair(new BCECPublicKey(algorithm, pub, configuration),
+                                   new BCECPrivateKey(algorithm, priv, configuration));
+            }
+            else
+            {
+                java.security.spec.ECParameterSpec p = (java.security.spec.ECParameterSpec)ecParams;
+
+                BCECPublicKey pubKey = new BCECPublicKey(algorithm, pub, p, configuration);
+                
+                return new KeyPair(pubKey, new BCECPrivateKey(algorithm, priv, pubKey, p, configuration));
+            }
+        }
+
+        protected ECKeyGenerationParameters createKeyGenParamsBC(ECParameterSpec p, SecureRandom r)
+        {
+            return new ECKeyGenerationParameters(new ECDomainParameters(p.getCurve(), p.getG(), p.getN(), p.getH()), r);
+        }
+
+        protected ECKeyGenerationParameters createKeyGenParamsJCE(java.security.spec.ECParameterSpec p, SecureRandom r)
+        {
+            if (p instanceof ECNamedCurveSpec)
+            {
+                String curveName = ((ECNamedCurveSpec)p).getName();
+
+                X9ECParameters x9 = ECUtils.getDomainParametersFromName(curveName, configuration);
+                if (null != x9)
+                {
+                    return createKeyGenParamsJCE(x9, r);
+                }
+            }
+
+            ECCurve curve = EC5Util.convertCurve(p.getCurve());
+            ECPoint g = EC5Util.convertPoint(curve, p.getGenerator());
+            BigInteger n = p.getOrder();
+            BigInteger h = BigInteger.valueOf(p.getCofactor());
+            ECDomainParameters dp = new ECDomainParameters(curve, g, n, h);
+            return new ECKeyGenerationParameters(dp, r);
+        }
+
+        protected ECKeyGenerationParameters createKeyGenParamsJCE(X9ECParameters x9, SecureRandom r)
+        {
+            ECDomainParameters dp = new ECDomainParameters(x9.getCurve(), x9.getG(), x9.getN(), x9.getH());
+
+            return new ECKeyGenerationParameters(dp, r);
+        }
+
+        protected void initializeNamedCurve(String curveName, SecureRandom random)
+            throws InvalidAlgorithmParameterException
+        {
+            X9ECParameters x9 = ECUtils.getDomainParametersFromName(curveName, configuration);
+            if (null == x9)
+            {
+                throw new InvalidAlgorithmParameterException("unknown curve name: " + curveName);
+            }
+
+            // Work-around for JDK bug -- it won't look up named curves properly if seed is present
+            byte[] seed = null; //p.getSeed();
+
+            this.ecParams = new ECNamedCurveSpec(curveName, x9.getCurve(), x9.getG(), x9.getN(), x9.getH(), seed);
+            this.param = createKeyGenParamsJCE(x9, random);
+        }
+    }
+
+    /**
+     * @hide This class is not part of the Android public SDK API
+     */
+    public static class SM2
+        extends BaseSM2
+    {
+        public SM2()
+        {
+            super("SM2", BouncyCastleProvider.CONFIGURATION);
+        }
+    }
+}
\ No newline at end of file
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/asymmetric/ec/KeyAgreementSpi.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/asymmetric/ec/KeyAgreementSpi.java
index a2dc9b1..2aee422 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/asymmetric/ec/KeyAgreementSpi.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/asymmetric/ec/KeyAgreementSpi.java
@@ -197,21 +197,18 @@
         return null;
     }
 
-    protected void engineInit(
-        Key key,
-        AlgorithmParameterSpec params,
-        SecureRandom random)
+    protected void doInitFromKey(Key key, AlgorithmParameterSpec parameterSpec, SecureRandom random)
         throws InvalidKeyException, InvalidAlgorithmParameterException
     {
         // Android-removed: Unsupported algorithms
-        // if (params != null &&
-        //     !(params instanceof MQVParameterSpec || params instanceof UserKeyingMaterialSpec || params instanceof DHUParameterSpec))
-        if (params != null && !(params instanceof UserKeyingMaterialSpec))
+        // if (parameterSpec != null &&
+        //     !(parameterSpec instanceof MQVParameterSpec || parameterSpec instanceof UserKeyingMaterialSpec || params instanceof DHUParameterSpec))
+        if (parameterSpec != null && !(parameterSpec instanceof UserKeyingMaterialSpec))
         {
             throw new InvalidAlgorithmParameterException("No algorithm parameters supported");
         }
 
-        initFromKey(key, params);
+        initFromKey(key, parameterSpec);
     }
 
     protected void engineInit(
@@ -251,9 +248,9 @@
             {
                 MQVPrivateKey mqvPrivKey = (MQVPrivateKey)key;
                 staticPrivKey = (ECPrivateKeyParameters)
-                    ECUtil.generatePrivateKeyParameter(mqvPrivKey.getStaticPrivateKey());
+                    ECUtils.generatePrivateKeyParameter(mqvPrivKey.getStaticPrivateKey());
                 ephemPrivKey = (ECPrivateKeyParameters)
-                    ECUtil.generatePrivateKeyParameter(mqvPrivKey.getEphemeralPrivateKey());
+                    ECUtils.generatePrivateKeyParameter(mqvPrivKey.getEphemeralPrivateKey());
 
                 ephemPubKey = null;
                 if (mqvPrivKey.getEphemeralPublicKey() != null)
@@ -267,9 +264,9 @@
                 MQVParameterSpec mqvParameterSpec = (MQVParameterSpec)parameterSpec;
 
                 staticPrivKey = (ECPrivateKeyParameters)
-                    ECUtil.generatePrivateKeyParameter((PrivateKey)key);
+                    ECUtils.generatePrivateKeyParameter((PrivateKey)key);
                 ephemPrivKey = (ECPrivateKeyParameters)
-                    ECUtil.generatePrivateKeyParameter(mqvParameterSpec.getEphemeralPrivateKey());
+                    ECUtils.generatePrivateKeyParameter(mqvParameterSpec.getEphemeralPrivateKey());
 
                 ephemPubKey = null;
                 if (mqvParameterSpec.getEphemeralPublicKey() != null)
@@ -301,9 +298,9 @@
             ECPublicKeyParameters ephemPubKey;
 
             staticPrivKey = (ECPrivateKeyParameters)
-                ECUtil.generatePrivateKeyParameter((PrivateKey)key);
+                ECUtils.generatePrivateKeyParameter((PrivateKey)key);
             ephemPrivKey = (ECPrivateKeyParameters)
-                ECUtil.generatePrivateKeyParameter(dheParameterSpec.getEphemeralPrivateKey());
+                ECUtils.generatePrivateKeyParameter(dheParameterSpec.getEphemeralPrivateKey());
 
             ephemPubKey = null;
             if (dheParameterSpec.getEphemeralPublicKey() != null)
@@ -332,7 +329,7 @@
             {
                 throw new InvalidAlgorithmParameterException("no KDF specified for UserKeyingMaterialSpec");
             }
-            ECPrivateKeyParameters privKey = (ECPrivateKeyParameters)ECUtil.generatePrivateKeyParameter((PrivateKey)key);
+            ECPrivateKeyParameters privKey = (ECPrivateKeyParameters)ECUtils.generatePrivateKeyParameter((PrivateKey)key);
             this.parameters = privKey.getParameters();
             ukmParameters = (parameterSpec instanceof UserKeyingMaterialSpec) ? ((UserKeyingMaterialSpec)parameterSpec).getUserKeyingMaterial() : null;
             ((BasicAgreement)agreement).init(privKey);
@@ -346,7 +343,7 @@
         return fullName.substring(fullName.lastIndexOf('.') + 1);
     }
     
-    protected byte[] calcSecret()
+    protected byte[] doCalcSecret()
     {
         return Arrays.clone(result);
     }
@@ -762,8 +759,8 @@
    		{
    			super("ECKAEGwithSHA1KDF", new ECDHBasicAgreement(),
                    new KDF2BytesGenerator(DigestFactory.createSHA1()));
-   		}
-   	}
+           }
+       }
 
     /**
    	 * KeyAgreement according to BSI TR-03111 chapter 4.3.1
@@ -775,8 +772,8 @@
    		{
    			super("ECKAEGwithRIPEMD160KDF", new ECDHBasicAgreement(),
                    new KDF2BytesGenerator(new RIPEMD160Digest()));
-   		}
-   	}
+           }
+       }
 
     /**
    	 * KeyAgreement according to BSI TR-03111 chapter 4.3.1
@@ -788,8 +785,8 @@
    		{
    			super("ECKAEGwithSHA224KDF", new ECDHBasicAgreement(),
                    new KDF2BytesGenerator(DigestFactory.createSHA224()));
-   		}
-   	}
+           }
+       }
 
 	/**
 	 * KeyAgreement according to BSI TR-03111 chapter 4.3.1
@@ -801,8 +798,8 @@
 		{
 			super("ECKAEGwithSHA256KDF", new ECDHBasicAgreement(),
                 new KDF2BytesGenerator(DigestFactory.createSHA256()));
-		}
-	}
+        }
+    }
 
 	/**
 	 * KeyAgreement according to BSI TR-03111 chapter 4.3.1
@@ -814,8 +811,8 @@
 		{
 			super("ECKAEGwithSHA384KDF", new ECDHBasicAgreement(),
                 new KDF2BytesGenerator(DigestFactory.createSHA384()));
-		}
-	}
+        }
+    }
 
 	/**
 	 * KeyAgreement according to BSI TR-03111 chapter 4.3.1
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/asymmetric/ec/KeyFactorySpi.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/asymmetric/ec/KeyFactorySpi.java
index 72c5880..1f2f88e 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/asymmetric/ec/KeyFactorySpi.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/asymmetric/ec/KeyFactorySpi.java
@@ -241,7 +241,11 @@
 
             try
             {
-                return new BCECPrivateKey(algorithm, new PrivateKeyInfo(new AlgorithmIdentifier(X9ObjectIdentifiers.id_ecPublicKey, ecKey.getParameters()), ecKey), configuration);
+                return new BCECPrivateKey(algorithm,
+                    new PrivateKeyInfo(
+                        new AlgorithmIdentifier(X9ObjectIdentifiers.id_ecPublicKey, ecKey.getParametersObject()),
+                        ecKey),
+                    configuration);
             }
             catch (IOException e)
             {
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/asymmetric/ec/KeyPairGeneratorSpi.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/asymmetric/ec/KeyPairGeneratorSpi.java
index b2dd4cf..756e081 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/asymmetric/ec/KeyPairGeneratorSpi.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/asymmetric/ec/KeyPairGeneratorSpi.java
@@ -9,10 +9,7 @@
 import java.security.spec.AlgorithmParameterSpec;
 import java.security.spec.ECGenParameterSpec;
 import java.util.Hashtable;
-import java.util.Map;
 
-import com.android.org.bouncycastle.asn1.ASN1ObjectIdentifier;
-import com.android.org.bouncycastle.asn1.x9.ECNamedCurveTable;
 import com.android.org.bouncycastle.asn1.x9.X9ECParameters;
 import com.android.org.bouncycastle.crypto.AsymmetricCipherKeyPair;
 import com.android.org.bouncycastle.crypto.CryptoServicesRegistrar;
@@ -63,7 +60,8 @@
 
         static private Hashtable    ecParameters;
 
-        static {
+        static
+        {
             ecParameters = new Hashtable();
 
             ecParameters.put(Integers.valueOf(192), new ECGenParameterSpec("prime192v1")); // a.k.a P-192
@@ -220,13 +218,12 @@
         {
             if (p instanceof ECNamedCurveSpec)
             {
-                X9ECParameters x9P = ECUtils.getDomainParametersFromName(((ECNamedCurveSpec)p).getName());
+                String curveName = ((ECNamedCurveSpec)p).getName();
 
-                if (x9P != null)
+                X9ECParameters x9 = ECUtils.getDomainParametersFromName(curveName, configuration);
+                if (null != x9)
                 {
-                    ECDomainParameters dp = new ECDomainParameters(x9P.getCurve(), x9P.getG(), x9P.getN(), x9P.getH());
-
-                    return new ECKeyGenerationParameters(dp, r);
+                    return createKeyGenParamsJCE(x9, r);
                 }
             }
 
@@ -238,48 +235,27 @@
             return new ECKeyGenerationParameters(dp, r);
         }
 
-        protected ECNamedCurveSpec createNamedCurveSpec(String curveName)
-            throws InvalidAlgorithmParameterException
+        protected ECKeyGenerationParameters createKeyGenParamsJCE(X9ECParameters x9, SecureRandom r)
         {
-            // NOTE: Don't bother with custom curves here as the curve will be converted to JCE type shortly
+            ECDomainParameters dp = new ECDomainParameters(x9.getCurve(), x9.getG(), x9.getN(), x9.getH());
 
-            X9ECParameters p = ECUtils.getDomainParametersFromName(curveName);
-            if (p == null)
-            {
-                try
-                {
-                    // Check whether it's actually an OID string (SunJSSE ServerHandshaker setupEphemeralECDHKeys bug)
-                    p = ECNamedCurveTable.getByOID(new ASN1ObjectIdentifier(curveName));
-                    if (p == null)
-                    {
-                        Map extraCurves = configuration.getAdditionalECParameters();
-
-                        p = (X9ECParameters)extraCurves.get(new ASN1ObjectIdentifier(curveName));
-
-                        if (p == null)
-                        {
-                            throw new InvalidAlgorithmParameterException("unknown curve OID: " + curveName);
-                        }
-                    }
-                }
-                catch (IllegalArgumentException ex)
-                {
-                    throw new InvalidAlgorithmParameterException("unknown curve name: " + curveName);
-                }
-            }
-
-            // Work-around for JDK bug -- it won't look up named curves properly if seed is present
-            byte[] seed = null; //p.getSeed();
-
-            return new ECNamedCurveSpec(curveName, p.getCurve(), p.getG(), p.getN(), p.getH(), seed);
+            return new ECKeyGenerationParameters(dp, r);
         }
 
         protected void initializeNamedCurve(String curveName, SecureRandom random)
             throws InvalidAlgorithmParameterException
         {
-            ECNamedCurveSpec namedCurve = createNamedCurveSpec(curveName);
-            this.ecParams = namedCurve;
-            this.param = createKeyGenParamsJCE(namedCurve, random);
+            X9ECParameters x9 = ECUtils.getDomainParametersFromName(curveName, configuration);
+            if (null == x9)
+            {
+                throw new InvalidAlgorithmParameterException("unknown curve name: " + curveName);
+            }
+
+            // Work-around for JDK bug -- it won't look up named curves properly if seed is present
+            byte[] seed = null; //p.getSeed();
+
+            this.ecParams = new ECNamedCurveSpec(curveName, x9.getCurve(), x9.getG(), x9.getN(), x9.getH(), seed);
+            this.param = createKeyGenParamsJCE(x9, random);
         }
     }
 
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/asymmetric/ec/SignatureSpi.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/asymmetric/ec/SignatureSpi.java
index 4543176..655ffaa 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/asymmetric/ec/SignatureSpi.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/asymmetric/ec/SignatureSpi.java
@@ -12,6 +12,7 @@
 import com.android.org.bouncycastle.crypto.digests.NullDigest;
 // BEGIN Android-removed: Unsupported algorithms
 // import org.bouncycastle.crypto.digests.RIPEMD160Digest;
+// import org.bouncycastle.crypto.digests.SHAKEDigest;
 // END Android-removed: Unsupported algorithms
 import com.android.org.bouncycastle.crypto.params.ParametersWithRandom;
 import com.android.org.bouncycastle.crypto.signers.DSAEncoding;
@@ -26,7 +27,6 @@
 // import org.bouncycastle.crypto.util.DigestFactory;
 import com.android.org.bouncycastle.crypto.digests.AndroidDigestFactory;
 import com.android.org.bouncycastle.jcajce.provider.asymmetric.util.DSABase;
-import com.android.org.bouncycastle.jcajce.provider.asymmetric.util.ECUtil;
 
 /**
  * @hide This class is not part of the Android public SDK API
@@ -52,7 +52,7 @@
         PrivateKey privateKey)
         throws InvalidKeyException
     {
-        CipherParameters param = ECUtil.generatePrivateKeyParameter(privateKey);
+        CipherParameters param = ECUtils.generatePrivateKeyParameter(privateKey);
 
         digest.reset();
 
@@ -297,6 +297,26 @@
         }
     }
 
+    static public class ecDSAShake128
+         extends SignatureSpi
+    {
+        public ecDSAShake128()
+        {
+            // RFC 8702 specifies deterministic DSA
+            super(new SHAKEDigest(128), new ECDSASigner(new HMacDSAKCalculator(new SHAKEDigest(128))), StandardDSAEncoding.INSTANCE);
+        }
+    }
+
+    static public class ecDSAShake256
+        extends SignatureSpi
+    {
+        public ecDSAShake256()
+        {
+            // RFC 8702 specifies deterministic DSA
+            super(new SHAKEDigest(256), new ECDSASigner(new HMacDSAKCalculator(new SHAKEDigest(256))), StandardDSAEncoding.INSTANCE);
+        }
+    }
+
     static public class ecNR
         extends SignatureSpi
     {
@@ -395,6 +415,41 @@
             super(new RIPEMD160Digest(), new ECDSASigner(), PlainDSAEncoding.INSTANCE);
         }
     }
+    static public class ecCVCDSA3_224
+        extends SignatureSpi
+    {
+        public ecCVCDSA3_224()
+        {
+            super(DigestFactory.createSHA3_224(), new ECDSASigner(), PlainDSAEncoding.INSTANCE);
+        }
+    }
+
+    static public class ecCVCDSA3_256
+        extends SignatureSpi
+    {
+        public ecCVCDSA3_256()
+        {
+            super(DigestFactory.createSHA3_256(), new ECDSASigner(), PlainDSAEncoding.INSTANCE);
+        }
+    }
+
+    static public class ecCVCDSA3_384
+        extends SignatureSpi
+    {
+        public ecCVCDSA3_384()
+        {
+            super(DigestFactory.createSHA3_384(), new ECDSASigner(), PlainDSAEncoding.INSTANCE);
+        }
+    }
+
+    static public class ecCVCDSA3_512
+        extends SignatureSpi
+    {
+        public ecCVCDSA3_512()
+        {
+            super(DigestFactory.createSHA3_512(), new ECDSASigner(), PlainDSAEncoding.INSTANCE);
+        }
+    }
     */
     // END Android-removed: Unsupported algorithms
 }
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/asymmetric/rsa/AlgorithmParametersSpi.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/asymmetric/rsa/AlgorithmParametersSpi.java
index 7dcccc8..ed24090 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/asymmetric/rsa/AlgorithmParametersSpi.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/asymmetric/rsa/AlgorithmParametersSpi.java
@@ -12,9 +12,11 @@
 
 import com.android.org.bouncycastle.asn1.ASN1Encoding;
 import com.android.org.bouncycastle.asn1.ASN1Integer;
+import com.android.org.bouncycastle.asn1.ASN1ObjectIdentifier;
 import com.android.org.bouncycastle.asn1.ASN1OctetString;
 import com.android.org.bouncycastle.asn1.DERNull;
 import com.android.org.bouncycastle.asn1.DEROctetString;
+import com.android.org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
 import com.android.org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
 import com.android.org.bouncycastle.asn1.pkcs.RSAESOAEPparams;
 import com.android.org.bouncycastle.asn1.pkcs.RSASSAPSSparams;
@@ -184,16 +186,36 @@
             throws IOException
         {
             PSSParameterSpec pssSpec = currentSpec;
-            AlgorithmIdentifier hashAlgorithm = new AlgorithmIdentifier(
-                                                DigestFactory.getOID(pssSpec.getDigestAlgorithm()),
-                                                DERNull.INSTANCE);
-            MGF1ParameterSpec mgfSpec = (MGF1ParameterSpec)pssSpec.getMGFParameters();
-            AlgorithmIdentifier maskGenAlgorithm = new AlgorithmIdentifier(
-                                                PKCSObjectIdentifiers.id_mgf1,
-                                                new AlgorithmIdentifier(DigestFactory.getOID(mgfSpec.getDigestAlgorithm()), DERNull.INSTANCE));
-            RSASSAPSSparams pssP = new RSASSAPSSparams(hashAlgorithm, maskGenAlgorithm, new ASN1Integer(pssSpec.getSaltLength()), new ASN1Integer(pssSpec.getTrailerField()));
+            ASN1ObjectIdentifier digOid = DigestFactory.getOID(pssSpec.getDigestAlgorithm());
+            AlgorithmIdentifier hashAlgorithm;
+            // RFC 8072
+            if (NISTObjectIdentifiers.id_shake128.equals(digOid) || NISTObjectIdentifiers.id_shake256.equals(digOid))
+            {
+                hashAlgorithm = new AlgorithmIdentifier(digOid);
+            }
+            else
+            {
+                hashAlgorithm = new AlgorithmIdentifier(digOid, DERNull.INSTANCE);
+            }
             
-            return pssP.getEncoded("DER");
+            MGF1ParameterSpec mgfSpec = (MGF1ParameterSpec)pssSpec.getMGFParameters();
+            if (mgfSpec != null)
+            {
+                AlgorithmIdentifier maskGenAlgorithm = new AlgorithmIdentifier(
+                    PKCSObjectIdentifiers.id_mgf1,
+                    new AlgorithmIdentifier(DigestFactory.getOID(mgfSpec.getDigestAlgorithm()), DERNull.INSTANCE));
+                RSASSAPSSparams pssP = new RSASSAPSSparams(hashAlgorithm, maskGenAlgorithm, new ASN1Integer(pssSpec.getSaltLength()), new ASN1Integer(pssSpec.getTrailerField()));
+
+                return pssP.getEncoded("DER");
+            }
+            else
+            {
+                AlgorithmIdentifier maskGenAlgorithm = new AlgorithmIdentifier(
+                    pssSpec.getMGFAlgorithm().equals("SHAKE128") ? NISTObjectIdentifiers.id_shake128 : NISTObjectIdentifiers.id_shake256);
+                RSASSAPSSparams pssP = new RSASSAPSSparams(hashAlgorithm, maskGenAlgorithm, new ASN1Integer(pssSpec.getSaltLength()), new ASN1Integer(pssSpec.getTrailerField()));
+
+                return pssP.getEncoded("DER");
+            }
         }
     
         protected byte[] engineGetEncoded(
@@ -241,17 +263,29 @@
             {
                 RSASSAPSSparams pssP = RSASSAPSSparams.getInstance(params);
 
-                if (!pssP.getMaskGenAlgorithm().getAlgorithm().equals(PKCSObjectIdentifiers.id_mgf1))
+                ASN1ObjectIdentifier mgfOid = pssP.getMaskGenAlgorithm().getAlgorithm();
+                if (mgfOid.equals(PKCSObjectIdentifiers.id_mgf1))
+                {
+                    currentSpec = new PSSParameterSpec(
+                        MessageDigestUtils.getDigestName(pssP.getHashAlgorithm().getAlgorithm()),
+                        PSSParameterSpec.DEFAULT.getMGFAlgorithm(),
+                        new MGF1ParameterSpec(MessageDigestUtils.getDigestName(AlgorithmIdentifier.getInstance(pssP.getMaskGenAlgorithm().getParameters()).getAlgorithm())),
+                        pssP.getSaltLength().intValue(),
+                        pssP.getTrailerField().intValue());
+                }
+                else if (mgfOid.equals(NISTObjectIdentifiers.id_shake128) || mgfOid.equals(NISTObjectIdentifiers.id_shake256))
+                {
+                    currentSpec = new PSSParameterSpec(
+                        MessageDigestUtils.getDigestName(pssP.getHashAlgorithm().getAlgorithm()),
+                        mgfOid.equals(NISTObjectIdentifiers.id_shake128) ? "SHAKE128" : "SHAKE256",
+                        null,
+                        pssP.getSaltLength().intValue(),
+                        pssP.getTrailerField().intValue());
+                }
+                else
                 {
                     throw new IOException("unknown mask generation function: " + pssP.getMaskGenAlgorithm().getAlgorithm());
                 }
-
-                currentSpec = new PSSParameterSpec(
-                                       MessageDigestUtils.getDigestName(pssP.getHashAlgorithm().getAlgorithm()),
-                                       PSSParameterSpec.DEFAULT.getMGFAlgorithm(),
-                                       new MGF1ParameterSpec(MessageDigestUtils.getDigestName(AlgorithmIdentifier.getInstance(pssP.getMaskGenAlgorithm().getParameters()).getAlgorithm())),
-                                       pssP.getSaltLength().intValue(),
-                                       pssP.getTrailerField().intValue());
             }
             catch (ClassCastException e)
             {
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/asymmetric/rsa/CipherSpi.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/asymmetric/rsa/CipherSpi.java
index 60f9296..7802c9e 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/asymmetric/rsa/CipherSpi.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/asymmetric/rsa/CipherSpi.java
@@ -31,7 +31,6 @@
 // Android-removed: Unsupported algorithm
 // import org.bouncycastle.crypto.encodings.ISO9796d1Encoding;
 import com.android.org.bouncycastle.crypto.encodings.OAEPEncoding;
-import com.android.org.bouncycastle.crypto.encodings.PKCS1Encoding;
 import com.android.org.bouncycastle.crypto.engines.RSABlindedEngine;
 import com.android.org.bouncycastle.crypto.params.ParametersWithRandom;
 import com.android.org.bouncycastle.jcajce.provider.asymmetric.util.BaseCipherSpi;
@@ -209,7 +208,7 @@
         }
         else if (pad.equals("PKCS1PADDING"))
         {
-            cipher = new PKCS1Encoding(new RSABlindedEngine());
+            cipher = new CustomPKCS1Encoding(new RSABlindedEngine());
         }
         // BEGIN Android-removed: Unsupported algorithm
         // else if (pad.equals("ISO9796-1PADDING"))
@@ -546,15 +545,26 @@
     {
         try
         {
-            return cipher.processBlock(bOut.getBuf(), 0, bOut.size());
-        }
-        catch (InvalidCipherTextException e)
-        {
-            throw new BadBlockException("unable to decrypt block", e);
-        }
-        catch (ArrayIndexOutOfBoundsException e)
-        {
-            throw new BadBlockException("unable to decrypt block", e);
+            byte[] output;
+            try
+            {
+                output = cipher.processBlock(bOut.getBuf(), 0, bOut.size());
+            }
+            catch (InvalidCipherTextException e)
+            {
+                throw new BadBlockException("unable to decrypt block", e);
+            }
+            catch (ArrayIndexOutOfBoundsException e)
+            {
+                throw new BadBlockException("unable to decrypt block", e);
+            }
+
+            if (output == null)
+            {
+                throw new BadBlockException("unable to decrypt block", null);
+            }
+
+            return output;
         }
         finally
         {
@@ -583,7 +593,7 @@
     {
         public PKCS1v1_5Padding()
         {
-            super(new PKCS1Encoding(new RSABlindedEngine()));
+            super(new CustomPKCS1Encoding(new RSABlindedEngine()));
         }
     }
 
@@ -592,7 +602,7 @@
     {
         public PKCS1v1_5Padding_PrivateOnly()
         {
-            super(false, true, new PKCS1Encoding(new RSABlindedEngine()));
+            super(false, true, new CustomPKCS1Encoding(new RSABlindedEngine()));
         }
     }
 
@@ -601,7 +611,7 @@
     {
         public PKCS1v1_5Padding_PublicOnly()
         {
-            super(true, false, new PKCS1Encoding(new RSABlindedEngine()));
+            super(true, false, new CustomPKCS1Encoding(new RSABlindedEngine()));
         }
     }
 
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/asymmetric/rsa/CustomPkcs1Encoding.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/asymmetric/rsa/CustomPkcs1Encoding.java
new file mode 100644
index 0000000..3cb07f8
--- /dev/null
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/asymmetric/rsa/CustomPkcs1Encoding.java
@@ -0,0 +1,264 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.org.bouncycastle.jcajce.provider.asymmetric.rsa;
+
+import java.security.SecureRandom;
+
+import com.android.org.bouncycastle.crypto.AsymmetricBlockCipher;
+import com.android.org.bouncycastle.crypto.CipherParameters;
+import com.android.org.bouncycastle.crypto.CryptoServicesRegistrar;
+import com.android.org.bouncycastle.crypto.InvalidCipherTextException;
+import com.android.org.bouncycastle.crypto.encodings.PKCS1Encoding;
+import com.android.org.bouncycastle.crypto.params.AsymmetricKeyParameter;
+import com.android.org.bouncycastle.crypto.params.ParametersWithRandom;
+import com.android.org.bouncycastle.util.Arrays;
+import com.android.org.bouncycastle.util.Properties;
+
+/**
+ * this does your basic PKCS 1 v1.5 padding - whether or not you should be using this
+ * depends on your application - see PKCS1 Version 2 for details.
+ */
+class CustomPKCS1Encoding
+    implements AsymmetricBlockCipher
+{
+    private static final int HEADER_LENGTH = 10;
+
+    private SecureRandom random;
+    private AsymmetricBlockCipher engine;
+    private boolean forEncryption;
+    private boolean forPrivateKey;
+    private boolean useStrictLength;
+    private byte[] blockBuffer;
+
+    /**
+     * Basic constructor.
+     *
+     * @param cipher
+     */
+    CustomPKCS1Encoding(AsymmetricBlockCipher cipher)
+    {
+        this.engine = cipher;
+        this.useStrictLength = useStrict();
+    }
+
+    //
+    // for J2ME compatibility
+    //
+    private boolean useStrict()
+    {
+        if (Properties.isOverrideSetTo(PKCS1Encoding.NOT_STRICT_LENGTH_ENABLED_PROPERTY, true))
+        {
+            return false;
+        }
+
+        return !Properties.isOverrideSetTo(PKCS1Encoding.STRICT_LENGTH_ENABLED_PROPERTY, false);
+    }
+
+    public AsymmetricBlockCipher getUnderlyingCipher()
+    {
+        return engine;
+    }
+
+    public void init(boolean forEncryption, CipherParameters param)
+    {
+        AsymmetricKeyParameter kParam;
+
+        if (param instanceof ParametersWithRandom)
+        {
+            ParametersWithRandom rParam = (ParametersWithRandom)param;
+
+            this.random = rParam.getRandom();
+            kParam = (AsymmetricKeyParameter)rParam.getParameters();
+        }
+        else
+        {
+            kParam = (AsymmetricKeyParameter)param;
+            if (!kParam.isPrivate() && forEncryption)
+            {
+                this.random = CryptoServicesRegistrar.getSecureRandom();
+            }
+        }
+
+        engine.init(forEncryption, param);
+
+        this.forPrivateKey = kParam.isPrivate();
+        this.forEncryption = forEncryption;
+        this.blockBuffer = new byte[engine.getOutputBlockSize()];
+    }
+
+    public int getInputBlockSize()
+    {
+        int baseBlockSize = engine.getInputBlockSize();
+
+        if (forEncryption)
+        {
+            return baseBlockSize - HEADER_LENGTH;
+        }
+        else
+        {
+            return baseBlockSize;
+        }
+    }
+
+    public int getOutputBlockSize()
+    {
+        int baseBlockSize = engine.getOutputBlockSize();
+
+        if (forEncryption)
+        {
+            return baseBlockSize;
+        }
+        else
+        {
+            return baseBlockSize - HEADER_LENGTH;
+        }
+    }
+
+    public byte[] processBlock(byte[] in, int inOff, int inLen) throws InvalidCipherTextException
+    {
+        if (forEncryption)
+        {
+            return encodeBlock(in, inOff, inLen);
+        }
+        else
+        {
+            return decodeBlock(in, inOff, inLen);
+        }
+    }
+
+    private byte[] encodeBlock(byte[] in, int inOff, int inLen) throws InvalidCipherTextException
+    {
+        if (inLen > getInputBlockSize())
+        {
+            throw new IllegalArgumentException("input data too large");
+        }
+
+        byte[] block = new byte[engine.getInputBlockSize()];
+
+        if (forPrivateKey)
+        {
+            block[0] = 0x01;                        // type code 1
+
+            for (int i = 1; i != block.length - inLen - 1; i++)
+            {
+                block[i] = (byte)0xFF;
+            }
+        }
+        else
+        {
+            random.nextBytes(block);                // random fill
+
+            block[0] = 0x02;                        // type code 2
+
+            //
+            // a zero byte marks the end of the padding, so all
+            // the pad bytes must be non-zero.
+            //
+            for (int i = 1; i != block.length - inLen - 1; i++)
+            {
+                while (block[i] == 0)
+                {
+                    block[i] = (byte)random.nextInt();
+                }
+            }
+        }
+
+        block[block.length - inLen - 1] = 0x00;       // mark the end of the padding
+        System.arraycopy(in, inOff, block, block.length - inLen, inLen);
+
+        return engine.processBlock(block, 0, block.length);
+    }
+
+    /**
+     * Check the argument is a valid encoding with type 1. Returns the plaintext length if valid, or -1 if invalid.
+     */
+    private static int checkPkcs1Encoding1(byte[] buf)
+    {
+        int foundZeroMask = 0;
+        int lastPadPos = 0;
+
+        // The first byte should be 0x01
+        int badPadSign = -((buf[0] & 0xFF) ^ 0x01);
+
+        // There must be a zero terminator for the padding somewhere
+        for (int i = 1; i < buf.length; ++i)
+        {
+            int padByte = buf[i] & 0xFF;
+            int is0x00Mask = ((padByte ^ 0x00) - 1) >> 31;
+            int is0xFFMask = ((padByte ^ 0xFF) - 1) >> 31;
+            lastPadPos ^= i & ~foundZeroMask & is0x00Mask;
+            foundZeroMask |= is0x00Mask;
+            badPadSign |= ~(foundZeroMask | is0xFFMask);
+        }
+
+        // The header should be at least 10 bytes
+        badPadSign |= lastPadPos - 9;
+
+        int plaintextLength = buf.length - 1 - lastPadPos;
+        return plaintextLength | badPadSign >> 31;
+    }
+
+    /**
+     * Check the argument is a valid encoding with type 2. Returns the plaintext length if valid, or -1 if invalid.
+     */
+    private static int checkPkcs1Encoding2(byte[] buf)
+    {
+        int foundZeroMask = 0;
+        int lastPadPos = 0;
+
+        // The first byte should be 0x02
+        int badPadSign = -((buf[0] & 0xFF) ^ 0x02);
+
+        // There must be a zero terminator for the padding somewhere
+        for (int i = 1; i < buf.length; ++i)
+        {
+            int padByte = buf[i] & 0xFF;
+            int is0x00Mask = ((padByte ^ 0x00) - 1) >> 31;
+            lastPadPos ^= i & ~foundZeroMask & is0x00Mask;
+            foundZeroMask |= is0x00Mask;
+        }
+
+        // The header should be at least 10 bytes
+        badPadSign |= lastPadPos - 9;
+
+        int plaintextLength = buf.length - 1 - lastPadPos;
+        return plaintextLength | badPadSign >> 31;
+    }
+
+    /**
+     * @throws InvalidCipherTextException if the decrypted block is not in PKCS1 format.
+     */
+    private byte[] decodeBlock(byte[] in, int inOff, int inLen)
+        throws InvalidCipherTextException
+    {
+        int strictBlockSize = engine.getOutputBlockSize();
+        byte[] block = engine.processBlock(in, inOff, inLen);
+
+        boolean incorrectLength = useStrictLength & (block.length != strictBlockSize);
+
+        byte[] data = block;
+        if (block.length < strictBlockSize)
+        {
+            data = blockBuffer;
+        }
+
+        int plaintextLength = forPrivateKey ? checkPkcs1Encoding2(data) : checkPkcs1Encoding1(data);
+
+        try
+        {
+            if (plaintextLength < 0 | incorrectLength)
+            {
+                // Special behaviour to avoid throw/catch/throw in CipherSpi
+                return null;
+            }
+
+            byte[] result = new byte[plaintextLength];
+            System.arraycopy(data, data.length - plaintextLength, result, 0, plaintextLength);
+            return result;
+        }
+        finally
+        {
+            Arrays.fill(block, (byte)0);
+            Arrays.fill(blockBuffer, 0, Math.max(0, blockBuffer.length - block.length), (byte)0);
+        }
+    }
+}
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/asymmetric/util/BaseAgreementSpi.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/asymmetric/util/BaseAgreementSpi.java
index 759bdc0..e25076c 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/asymmetric/util/BaseAgreementSpi.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/asymmetric/util/BaseAgreementSpi.java
@@ -1,7 +1,12 @@
 /* GENERATED SOURCE. DO NOT MODIFY. */
 package com.android.org.bouncycastle.jcajce.provider.asymmetric.util;
 
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.Key;
 import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.security.spec.AlgorithmParameterSpec;
 import java.util.HashMap;
 import java.util.Hashtable;
 import java.util.Map;
@@ -27,6 +32,7 @@
 // import org.bouncycastle.crypto.agreement.kdf.DHKEKGenerator;
 import com.android.org.bouncycastle.crypto.params.DESParameters;
 import com.android.org.bouncycastle.crypto.params.KDFParameters;
+import com.android.org.bouncycastle.jcajce.spec.HybridValueParameterSpec;
 import com.android.org.bouncycastle.util.Arrays;
 import com.android.org.bouncycastle.util.Integers;
 import com.android.org.bouncycastle.util.Strings;
@@ -152,6 +158,7 @@
     protected final DerivationFunction kdf;
 
     protected byte[]     ukmParameters;
+    private HybridValueParameterSpec hybridSpec;
 
     public BaseAgreementSpi(String kaAlgorithm, DerivationFunction kdf)
     {
@@ -225,6 +232,40 @@
         }
     }
 
+    protected void engineInit(
+        Key             key,
+        SecureRandom    random)
+        throws InvalidKeyException
+    {
+        try
+        {
+            doInitFromKey(key, null, random);
+        }
+        catch (InvalidAlgorithmParameterException e)
+        {
+            // this should never occur.
+            throw new InvalidKeyException(e.getMessage());
+        }
+    }
+
+    protected void engineInit(
+        Key key,
+        AlgorithmParameterSpec params,
+        SecureRandom random)
+        throws InvalidKeyException, InvalidAlgorithmParameterException
+    {
+        if (params instanceof HybridValueParameterSpec)
+        {
+            this.hybridSpec = (HybridValueParameterSpec)params;
+            doInitFromKey(key, hybridSpec.getBaseParameterSpec(), random);
+        }
+        else
+        {
+            this.hybridSpec = null;
+            doInitFromKey(key, params, random);
+        }
+    }
+
     protected byte[] engineGenerateSecret()
         throws IllegalStateException
     {
@@ -351,5 +392,26 @@
         }
     }
 
-    protected abstract byte[] calcSecret();
+    private byte[] calcSecret()
+    {
+        if (hybridSpec != null)
+        {
+            // Set Z' to Z || T
+            byte[] s = doCalcSecret();
+            byte[] sec = Arrays.concatenate(s, hybridSpec.getT());
+
+            Arrays.clear(s);
+
+            return sec;
+        }
+        else
+        {
+            return doCalcSecret();
+        }
+    }
+
+    protected abstract byte[] doCalcSecret();
+
+    protected abstract void doInitFromKey(Key key, AlgorithmParameterSpec parameterSpec, SecureRandom random)
+        throws InvalidKeyException, InvalidAlgorithmParameterException;
 }
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/asymmetric/util/EC5Util.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/asymmetric/util/EC5Util.java
index 78423c3..c933301 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/asymmetric/util/EC5Util.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/asymmetric/util/EC5Util.java
@@ -20,6 +20,7 @@
 import com.android.org.bouncycastle.asn1.x9.ECNamedCurveTable;
 import com.android.org.bouncycastle.asn1.x9.X962Parameters;
 import com.android.org.bouncycastle.asn1.x9.X9ECParameters;
+import com.android.org.bouncycastle.asn1.x9.X9ECParametersHolder;
 import com.android.org.bouncycastle.crypto.ec.CustomNamedCurves;
 import com.android.org.bouncycastle.crypto.params.ECDomainParameters;
 import com.android.org.bouncycastle.jcajce.provider.config.ProviderConfiguration;
@@ -39,20 +40,41 @@
  */
 public class EC5Util
 {
-    private static Map customCurves = new HashMap();
-
-    static
+    private static class CustomCurves
     {
-        Enumeration e = CustomNamedCurves.getNames();
-        while (e.hasMoreElements())
-        {
-            String name = (String)e.nextElement();
+        private static Map CURVE_MAP = createCurveMap();
 
-            X9ECParameters curveParams = ECNamedCurveTable.getByName(name);
-            if (curveParams != null)  // there may not be a regular curve, may just be a custom curve.
+        private static Map createCurveMap()
+        {
+            Map map = new HashMap();
+
+            Enumeration e = CustomNamedCurves.getNames();
+            while (e.hasMoreElements())
             {
-                customCurves.put(curveParams.getCurve(), CustomNamedCurves.getByName(name).getCurve());
+                String name = (String)e.nextElement();
+
+                X9ECParametersHolder curveParams = ECNamedCurveTable.getByNameLazy(name);
+                if (curveParams != null)  // there may not be a regular curve, may just be a custom curve.
+                {
+                    ECCurve curve = curveParams.getCurve();
+                    if (ECAlgorithms.isFpCurve(curve))
+                    {
+                        map.put(curve, CustomNamedCurves.getByNameLazy(name).getCurve());
+                    }
+                }
             }
+
+            ECCurve c_25519 = CustomNamedCurves.getByNameLazy("Curve25519").getCurve();
+
+            map.put(new ECCurve.Fp(
+                c_25519.getField().getCharacteristic(),
+                c_25519.getA().toBigInteger(),
+                c_25519.getB().toBigInteger(),
+                c_25519.getOrder(),
+                c_25519.getCofactor(),
+                true), c_25519);
+
+            return map;
         }
 
         // BEGIN Android-removed: Unsupported curves
@@ -69,6 +91,11 @@
             ), c_25519);
         */
         // END Android-removed: Unsupported curves
+        static ECCurve substitute(ECCurve c)
+        {
+            ECCurve custom = (ECCurve)CURVE_MAP.get(c);
+            return null != custom ? custom : c;
+        }
     }
 
     public static ECCurve getCurve(
@@ -280,21 +307,14 @@
 
         if (field instanceof ECFieldFp)
         {
-            ECCurve.Fp curve = new ECCurve.Fp(((ECFieldFp)field).getP(), a, b);
-
-            if (customCurves.containsKey(curve))
-            {
-                return (ECCurve)customCurves.get(curve);
-            }
-
-            return curve;
+            return CustomCurves.substitute(new ECCurve.Fp(((ECFieldFp)field).getP(), a, b, null, null));
         }
         else
         {
             ECFieldF2m fieldF2m = (ECFieldF2m)field;
             int m = fieldF2m.getM();
             int ks[] = ECUtil.convertMidTerms(fieldF2m.getMidTermsOfReductionPolynomial());
-            return new ECCurve.F2m(m, ks[0], ks[1], ks[2], a, b); 
+            return new ECCurve.F2m(m, ks[0], ks[1], ks[2], a, b, null, null);
         }
     }
 
@@ -308,7 +328,7 @@
         {
             Polynomial poly = ((PolynomialExtensionField)field).getMinimalPolynomial();
             int[] exponents = poly.getExponentsPresent();
-            int[] ks = Arrays.reverse(Arrays.copyOfRange(exponents, 1, exponents.length - 1));
+            int[] ks = Arrays.reverseInPlace(Arrays.copyOfRange(exponents, 1, exponents.length - 1));
             return new ECFieldF2m(poly.getDegree(), ks);
         }
     }
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/asymmetric/util/ECUtil.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/asymmetric/util/ECUtil.java
index e3ea672..142237f 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/asymmetric/util/ECUtil.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/asymmetric/util/ECUtil.java
@@ -293,6 +293,11 @@
     {
         if (order == null)     // implicitly CA
         {
+            if (configuration == null)
+            {
+                return privateValue.bitLength();   // a guess but better than an exception!
+            }
+
             ECParameterSpec implicitCA = configuration.getEcImplicitlyCa();
 
             if (implicitCA == null)
@@ -311,26 +316,24 @@
     public static ASN1ObjectIdentifier getNamedCurveOid(
         String curveName)
     {
-        String name = curveName;
+        if (null == curveName || curveName.length() < 1)
+        {
+            return null;
+        }
 
-        int spacePos = name.indexOf(' ');
+        int spacePos = curveName.indexOf(' ');
         if (spacePos > 0)
         {
-            name = name.substring(spacePos + 1);
+            curveName = curveName.substring(spacePos + 1);
         }
 
-        try
+        ASN1ObjectIdentifier oid = getOID(curveName);
+        if (null != oid)
         {
-            if (name.charAt(0) >= '0' && name.charAt(0) <= '2')
-            {
-                return new ASN1ObjectIdentifier(name);
-            }
-        }
-        catch (IllegalArgumentException ex)
-        {
+            return oid;
         }
 
-        return ECNamedCurveTable.getOID(name);
+        return ECNamedCurveTable.getOID(curveName);
     }
 
     public static ASN1ObjectIdentifier getNamedCurveOid(
@@ -448,4 +451,20 @@
             }
         });
     }
+
+    private static ASN1ObjectIdentifier getOID(String curveName)
+    {
+        char firstChar = curveName.charAt(0);
+        if (firstChar >= '0' && firstChar <= '2')
+        {
+            try
+            {
+                return new ASN1ObjectIdentifier(curveName);
+            }
+            catch (Exception e)
+            {
+            }
+        }
+        return null;
+    }
 }
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/asymmetric/x509/CertificateFactory.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/asymmetric/x509/CertificateFactory.java
index 1f57708..31acb3b 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/asymmetric/x509/CertificateFactory.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/asymmetric/x509/CertificateFactory.java
@@ -65,10 +65,11 @@
     }
 
     private java.security.cert.Certificate readPEMCertificate(
-        InputStream in)
+        InputStream in,
+        boolean isFirst)
         throws IOException, CertificateParsingException
     {
-        return getCertificate(PEM_CERT_PARSER.readPEMObject(in));
+        return getCertificate(PEM_CERT_PARSER.readPEMObject(in, isFirst));
     }
 
     private java.security.cert.Certificate getCertificate(ASN1Sequence seq)
@@ -123,10 +124,11 @@
     }
     
     private CRL readPEMCRL(
-        InputStream in)
+        InputStream in,
+        boolean isFirst)
         throws IOException, CRLException
     {
-        return getCRL(PEM_CRL_PARSER.readPEMObject(in));
+        return getCRL(PEM_CRL_PARSER.readPEMObject(in, isFirst));
     }
 
     private CRL readDERCRL(
@@ -181,6 +183,14 @@
         InputStream in)
         throws CertificateException
     {
+        return doGenerateCertificate(in, true);
+    }
+
+   private java.security.cert.Certificate doGenerateCertificate(
+            InputStream in,
+            boolean isFirst)
+            throws CertificateException
+   {
         if (currentStream == null)
         {
             currentStream = in;
@@ -254,7 +264,7 @@
 
             if (tag != 0x30)  // assume ascii PEM encoded.
             {
-                return readPEMCertificate(pis);
+                return readPEMCertificate(pis, isFirst);
             }
             else
             {
@@ -286,7 +296,8 @@
 
         // Android-changed: Read from original stream
         // while ((cert = engineGenerateCertificate(in)) != null)
-        while ((cert = engineGenerateCertificate(inStream)) != null)
+        // if we do read some certificates we'll return them even if junk at end of file
+        while ((cert = doGenerateCertificate(inStream, certs.isEmpty())) != null)
         {
             certs.add(cert);
         }
@@ -302,6 +313,18 @@
         InputStream in)
         throws CRLException
     {
+        return doGenerateCRL(in, true);
+    }
+
+    /**
+     * Generates a certificate revocation list (CRL) object and initializes
+     * it with the data read from the input stream inStream.
+     */
+    private CRL doGenerateCRL(
+        InputStream in,
+        boolean     isFirst)
+        throws CRLException
+    {
         if (currentCrlStream == null)
         {
             currentCrlStream = in;
@@ -353,7 +376,7 @@
             pis.reset();
             if (tag != 0x30)  // assume ascii PEM encoded.
             {
-                return readPEMCRL(pis);
+                return readPEMCRL(pis, isFirst);
             }
             else
             {       // lazy evaluate to help processing of large CRLs
@@ -387,7 +410,8 @@
         List crls = new ArrayList();
         BufferedInputStream in = new BufferedInputStream(inStream);
 
-        while ((crl = engineGenerateCRL(in)) != null)
+        // if we do read some certificates we'll return them even if junk at end of file
+        while ((crl = doGenerateCRL(in, crls.isEmpty())) != null)
         {
             crls.add(crl);
         }
@@ -435,7 +459,7 @@
         return new PKIXCertPath(certificates);
     }
 
-    private class ExCertificateException
+    private static class ExCertificateException
         extends CertificateException
     {
         private Throwable cause;
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/asymmetric/x509/PEMUtil.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/asymmetric/x509/PEMUtil.java
index 452d386..2c682dd 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/asymmetric/x509/PEMUtil.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/asymmetric/x509/PEMUtil.java
@@ -14,7 +14,7 @@
      * current PEM object.
      *
      */
-    private class Boundaries
+    private static class Boundaries
     {
         private final String _header;
         private final String _footer;
@@ -113,7 +113,8 @@
     }
 
     ASN1Sequence readPEMObject(
-        InputStream in)
+        InputStream in,
+        boolean     isFirst)
         throws IOException
     {
         String line;
@@ -132,6 +133,11 @@
 
         if (header == null)
         {
+            if (!isFirst)
+            {
+                // just ignore the data
+                return null;
+            }
             throw new IOException("malformed PEM data: no header found");
         }
 
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/asymmetric/x509/X509CRLImpl.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/asymmetric/x509/X509CRLImpl.java
index fcf84c8..1ffd603 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/asymmetric/x509/X509CRLImpl.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/asymmetric/x509/X509CRLImpl.java
@@ -29,6 +29,7 @@
 
 import javax.security.auth.x500.X500Principal;
 
+import com.android.org.bouncycastle.asn1.ASN1BitString;
 import com.android.org.bouncycastle.asn1.ASN1Encodable;
 import com.android.org.bouncycastle.asn1.ASN1Encoding;
 import com.android.org.bouncycastle.asn1.ASN1InputStream;
@@ -37,7 +38,6 @@
 import com.android.org.bouncycastle.asn1.ASN1OctetString;
 import com.android.org.bouncycastle.asn1.ASN1Primitive;
 import com.android.org.bouncycastle.asn1.ASN1Sequence;
-import com.android.org.bouncycastle.asn1.DERBitString;
 import com.android.org.bouncycastle.asn1.util.ASN1Dump;
 import com.android.org.bouncycastle.asn1.x500.X500Name;
 import com.android.org.bouncycastle.asn1.x509.AlgorithmIdentifier;
@@ -159,19 +159,6 @@
         return null;
     }
 
-    public byte[] getEncoded()
-        throws CRLException
-    {
-        try
-        {
-            return c.getEncoded(ASN1Encoding.DER);
-        }
-        catch (IOException e)
-        {
-            throw new CRLException(e.toString());
-        }
-    }
-
     public void verify(PublicKey key)
         throws CRLException, NoSuchAlgorithmException,
         InvalidKeyException, NoSuchProviderException, SignatureException
@@ -256,7 +243,7 @@
         {
             List<PublicKey> pubKeys = ((CompositePublicKey)key).getPublicKeys();
             ASN1Sequence keySeq = ASN1Sequence.getInstance(c.getSignatureAlgorithm().getParameters());
-            ASN1Sequence sigSeq = ASN1Sequence.getInstance(DERBitString.getInstance(c.getSignature()).getBytes());
+            ASN1Sequence sigSeq = ASN1Sequence.getInstance(ASN1BitString.getInstance(c.getSignature()).getBytes());
 
             boolean success = false;
             for (int i = 0; i != pubKeys.size(); i++)
@@ -278,7 +265,7 @@
                     checkSignature(
                         (PublicKey)pubKeys.get(i), signature,
                         sigAlg.getParameters(),
-                        DERBitString.getInstance(sigSeq.getObjectAt(i)).getBytes());
+                        ASN1BitString.getInstance(sigSeq.getObjectAt(i)).getBytes());
                     success = true;
                 }
                 catch (SignatureException e)
@@ -300,7 +287,7 @@
         else if (X509SignatureUtil.isCompositeAlgorithm(c.getSignatureAlgorithm()))
         {
             ASN1Sequence keySeq = ASN1Sequence.getInstance(c.getSignatureAlgorithm().getParameters());
-            ASN1Sequence sigSeq = ASN1Sequence.getInstance(DERBitString.getInstance(c.getSignature()).getBytes());
+            ASN1Sequence sigSeq = ASN1Sequence.getInstance(ASN1BitString.getInstance(c.getSignature()).getBytes());
 
             boolean success = false;
             for (int i = 0; i != sigSeq.size(); i++)
@@ -317,7 +304,7 @@
                     checkSignature(
                         key, signature,
                         sigAlg.getParameters(),
-                        DERBitString.getInstance(sigSeq.getObjectAt(i)).getBytes());
+                        ASN1BitString.getInstance(sigSeq.getObjectAt(i)).getBytes());
 
                     success = true;
                 }
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/asymmetric/x509/X509CRLInternal.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/asymmetric/x509/X509CRLInternal.java
index 73cf1bf..3c2aa2e 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/asymmetric/x509/X509CRLInternal.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/asymmetric/x509/X509CRLInternal.java
@@ -6,25 +6,42 @@
 import com.android.org.bouncycastle.asn1.x509.CertificateList;
 import com.android.org.bouncycastle.jcajce.util.JcaJceHelper;
 
+/**
+ * This class exists to let {@link #equals(Object)} and {@link #hashCode()} methods be delegated efficiently
+ * to the platform default implementations (especially important for compatibility of {@link #hashCode()}
+ * calculations). Those methods fall back to calling {@link #getEncoded()} for third-party subclasses, and
+ * this class allows us to avoid cloning the return value of {@link #getEncoded()} for those callers.
+ */
 class X509CRLInternal extends X509CRLImpl
 {
     private final byte[] encoding;
+    private final CRLException exception;
 
     X509CRLInternal(JcaJceHelper bcHelper, CertificateList c, String sigAlgName, byte[] sigAlgParams, boolean isIndirect,
-        byte[] encoding)
+        byte[] encoding, CRLException exception)
     {
         super(bcHelper, c, sigAlgName, sigAlgParams, isIndirect);
 
         this.encoding = encoding;
+        this.exception = exception;
     }
 
     public byte[] getEncoded() throws CRLException
     {
+        if (null != exception)
+        {
+            throw exception;
+        }
+
         if (null == encoding)
         {
             throw new CRLException();
         }
 
+        /*
+         * NOTE: Don't clone this return value. See class javadoc for details. Any necessary cloning is
+         * handled by the X509CRLObject that is holding this instance.
+         */
         return encoding;
     }
 }
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/asymmetric/x509/X509CRLObject.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/asymmetric/x509/X509CRLObject.java
index c9635a4..e8fe588 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/asymmetric/x509/X509CRLObject.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/asymmetric/x509/X509CRLObject.java
@@ -1,6 +1,7 @@
 /* GENERATED SOURCE. DO NOT MODIFY. */
 package com.android.org.bouncycastle.jcajce.provider.asymmetric.x509;
 
+import java.io.IOException;
 import java.security.cert.CRLException;
 
 import com.android.org.bouncycastle.asn1.ASN1BitString;
@@ -10,6 +11,7 @@
 import com.android.org.bouncycastle.asn1.x509.Extension;
 import com.android.org.bouncycastle.asn1.x509.IssuingDistributionPoint;
 import com.android.org.bouncycastle.jcajce.util.JcaJceHelper;
+import com.android.org.bouncycastle.util.Arrays;
 
 class X509CRLObject
     extends X509CRLImpl
@@ -25,6 +27,11 @@
         super(bcHelper, c, createSigAlgName(c), createSigAlgParams(c), isIndirectCRL(c));
     }
 
+    public byte[] getEncoded() throws CRLException
+    {
+        return Arrays.clone(getInternalCRL().getEncoded());
+    }
+
     public boolean equals(Object other)
     {
         if (this == other)
@@ -51,6 +58,8 @@
                     return false;
                 }
             }
+
+            return getInternalCRL().equals(otherBC.getInternalCRL());
         }
 
         return getInternalCRL().equals(other);
@@ -77,17 +86,19 @@
             }
         }
 
-        byte[] encoding;
+        byte[] encoding = null;
+        CRLException exception = null;
         try
         {
-            encoding = getEncoded();
+            encoding = c.getEncoded(ASN1Encoding.DER);
         }
-        catch (CRLException e)
+        catch (IOException e)
         {
-            encoding = null;
+            exception = new X509CRLException(e);
         }
 
-        X509CRLInternal temp = new X509CRLInternal(bcHelper, c, sigAlgName,sigAlgParams, isIndirect, encoding);
+        X509CRLInternal temp = new X509CRLInternal(bcHelper, c, sigAlgName,sigAlgParams, isIndirect, encoding,
+            exception);
 
         synchronized (cacheLock)
         {
@@ -108,7 +119,7 @@
         }
         catch (Exception e)
         {
-            throw new CRLException("CRL contents invalid: " + e);
+            throw new X509CRLException("CRL contents invalid: " + e.getMessage(), e);
         }
     }
 
@@ -147,4 +158,26 @@
             throw new ExtCRLException("Exception reading IssuingDistributionPoint", e);
         }
     }
+
+    private static class X509CRLException
+        extends CRLException
+    {
+        private final Throwable cause;
+
+        X509CRLException(String msg, Throwable cause)
+        {
+            super(msg);
+            this.cause = cause;
+        }
+
+        X509CRLException(Throwable cause)
+        {
+            this.cause = cause;
+        }
+
+        public Throwable getCause()
+        {
+            return cause;
+        }
+    }
 }
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/asymmetric/x509/X509CertificateImpl.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/asymmetric/x509/X509CertificateImpl.java
index 6a3aa06..31c393b 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/asymmetric/x509/X509CertificateImpl.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/asymmetric/x509/X509CertificateImpl.java
@@ -32,16 +32,17 @@
 
 import javax.security.auth.x500.X500Principal;
 
+import com.android.org.bouncycastle.asn1.ASN1BitString;
 import com.android.org.bouncycastle.asn1.ASN1Encodable;
 import com.android.org.bouncycastle.asn1.ASN1Encoding;
+import com.android.org.bouncycastle.asn1.ASN1IA5String;
 import com.android.org.bouncycastle.asn1.ASN1InputStream;
+import com.android.org.bouncycastle.asn1.ASN1Integer;
 import com.android.org.bouncycastle.asn1.ASN1ObjectIdentifier;
 import com.android.org.bouncycastle.asn1.ASN1OctetString;
 import com.android.org.bouncycastle.asn1.ASN1Primitive;
 import com.android.org.bouncycastle.asn1.ASN1Sequence;
 import com.android.org.bouncycastle.asn1.ASN1String;
-import com.android.org.bouncycastle.asn1.DERBitString;
-import com.android.org.bouncycastle.asn1.DERIA5String;
 import com.android.org.bouncycastle.asn1.DERNull;
 import com.android.org.bouncycastle.asn1.DEROctetString;
 import com.android.org.bouncycastle.asn1.misc.MiscObjectIdentifiers;
@@ -65,6 +66,7 @@
 import com.android.org.bouncycastle.jce.X509Principal;
 import com.android.org.bouncycastle.jce.provider.BouncyCastleProvider;
 import com.android.org.bouncycastle.util.Arrays;
+import com.android.org.bouncycastle.util.Exceptions;
 import com.android.org.bouncycastle.util.Integers;
 import com.android.org.bouncycastle.util.Properties;
 import com.android.org.bouncycastle.util.Strings;
@@ -79,7 +81,6 @@
     protected boolean[] keyUsage;
     protected String sigAlgName;
     protected byte[] sigAlgParams;
-
     X509CertificateImpl(JcaJceHelper bcHelper, com.android.org.bouncycastle.asn1.x509.Certificate c,
         BasicConstraints basicConstraints, boolean[] keyUsage, String sigAlgName, byte[] sigAlgParams)
     {
@@ -230,7 +231,7 @@
 
     public boolean[] getIssuerUniqueID()
     {
-        DERBitString    id = c.getTBSCertificate().getIssuerUniqueId();
+        ASN1BitString    id = c.getTBSCertificate().getIssuerUniqueId();
 
         if (id != null)
         {
@@ -250,7 +251,7 @@
 
     public boolean[] getSubjectUniqueID()
     {
-        DERBitString    id = c.getTBSCertificate().getSubjectUniqueId();
+        ASN1BitString id = c.getTBSCertificate().getSubjectUniqueId();
 
         if (id != null)
         {
@@ -298,29 +299,21 @@
             throw new CertificateParsingException("error processing extended key usage extension");
         }
     }
-    
+
     public int getBasicConstraints()
     {
-        if (basicConstraints != null)
+        if (basicConstraints == null || !basicConstraints.isCA())
         {
-            if (basicConstraints.isCA())
-            {
-                if (basicConstraints.getPathLenConstraint() == null)
-                {
-                    return Integer.MAX_VALUE;
-                }
-                else
-                {
-                    return basicConstraints.getPathLenConstraint().intValue();
-                }
-            }
-            else
-            {
-                return -1;
-            }
+            return -1;
         }
 
-        return -1;
+        ASN1Integer pathLenConstraint = basicConstraints.getPathLenConstraintInteger();
+        if (pathLenConstraint == null)
+        {
+            return Integer.MAX_VALUE;
+        }
+
+        return pathLenConstraint.intPositiveValueExact();
     }
 
     public Collection getSubjectAlternativeNames()
@@ -375,7 +368,7 @@
             }
             catch (Exception e)
             {
-                throw new IllegalStateException("error parsing " + e.toString());
+                throw Exceptions.illegalStateException("error parsing " + e.getMessage(), e);
             }
         }
 
@@ -461,20 +454,7 @@
         }
         catch (IOException e)
         {
-            return null;   // should never happen...
-        }
-    }
-
-    public byte[] getEncoded()
-        throws CertificateEncodingException
-    {
-        try
-        {
-            return c.getEncoded(ASN1Encoding.DER);
-        }
-        catch (IOException e)
-        {
-            throw new CertificateEncodingException(e.toString());
+            throw Exceptions.illegalStateException("failed to recover public key: " + e.getMessage(), e);
         }
     }
 
@@ -527,15 +507,15 @@
                         }
                         else if (oid.equals(MiscObjectIdentifiers.netscapeCertType))
                         {
-                            buf.append(new NetscapeCertType(DERBitString.getInstance(dIn.readObject()))).append(nl);
+                            buf.append(new NetscapeCertType(ASN1BitString.getInstance(dIn.readObject()))).append(nl);
                         }
                         else if (oid.equals(MiscObjectIdentifiers.netscapeRevocationURL))
                         {
-                            buf.append(new NetscapeRevocationURL(DERIA5String.getInstance(dIn.readObject()))).append(nl);
+                            buf.append(new NetscapeRevocationURL(ASN1IA5String.getInstance(dIn.readObject()))).append(nl);
                         }
                         else if (oid.equals(MiscObjectIdentifiers.verisignCzagExtension))
                         {
-                            buf.append(new VerisignCzagExtension(DERIA5String.getInstance(dIn.readObject()))).append(nl);
+                            buf.append(new VerisignCzagExtension(ASN1IA5String.getInstance(dIn.readObject()))).append(nl);
                         }
                         else 
                         {
@@ -647,7 +627,7 @@
         {
             List<PublicKey> pubKeys = ((CompositePublicKey)key).getPublicKeys();
             ASN1Sequence keySeq = ASN1Sequence.getInstance(c.getSignatureAlgorithm().getParameters());
-            ASN1Sequence sigSeq = ASN1Sequence.getInstance(DERBitString.getInstance(c.getSignature()).getBytes());
+            ASN1Sequence sigSeq = ASN1Sequence.getInstance(ASN1BitString.getInstance(c.getSignature()).getBytes());
 
             boolean success = false;
             for (int i = 0; i != pubKeys.size(); i++)
@@ -668,7 +648,7 @@
                     checkSignature(
                         (PublicKey)pubKeys.get(i), signature,
                         sigAlg.getParameters(),
-                        DERBitString.getInstance(sigSeq.getObjectAt(i)).getBytes());
+                        ASN1BitString.getInstance(sigSeq.getObjectAt(i)).getBytes());
                     success = true;
                 }
                 catch (SignatureException e)
@@ -690,7 +670,7 @@
         else if (X509SignatureUtil.isCompositeAlgorithm(c.getSignatureAlgorithm()))
         {
             ASN1Sequence keySeq = ASN1Sequence.getInstance(c.getSignatureAlgorithm().getParameters());
-            ASN1Sequence sigSeq = ASN1Sequence.getInstance(DERBitString.getInstance(c.getSignature()).getBytes());
+            ASN1Sequence sigSeq = ASN1Sequence.getInstance(ASN1BitString.getInstance(c.getSignature()).getBytes());
 
             boolean success = false;
             for (int i = 0; i != sigSeq.size(); i++)
@@ -707,7 +687,7 @@
                     checkSignature(
                         key, signature,
                         sigAlg.getParameters(),
-                        DERBitString.getInstance(sigSeq.getObjectAt(i)).getBytes());
+                        ASN1BitString.getInstance(sigSeq.getObjectAt(i)).getBytes());
 
                     success = true;
                 }
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/asymmetric/x509/X509CertificateInternal.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/asymmetric/x509/X509CertificateInternal.java
index e74b8be..fc59572 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/asymmetric/x509/X509CertificateInternal.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/asymmetric/x509/X509CertificateInternal.java
@@ -6,25 +6,43 @@
 import com.android.org.bouncycastle.asn1.x509.BasicConstraints;
 import com.android.org.bouncycastle.jcajce.util.JcaJceHelper;
 
+/**
+ * This class exists to let {@link #equals(Object)} and {@link #hashCode()} methods be delegated efficiently
+ * to the platform default implementations (especially important for compatibility of {@link #hashCode()}
+ * calculations). Those methods fall back to calling {@link #getEncoded()} for third-party subclasses, and
+ * this class allows us to avoid cloning the return value of {@link #getEncoded()} for those callers.
+ */
 class X509CertificateInternal extends X509CertificateImpl
 {
     private final byte[] encoding;
+    private final CertificateEncodingException exception;
 
     X509CertificateInternal(JcaJceHelper bcHelper, com.android.org.bouncycastle.asn1.x509.Certificate c,
-        BasicConstraints basicConstraints, boolean[] keyUsage, String sigAlgName, byte[] sigAlgParams, byte[] encoding)
+        BasicConstraints basicConstraints, boolean[] keyUsage, String sigAlgName, byte[] sigAlgParams, byte[] encoding,
+        CertificateEncodingException exception)
     {
         super(bcHelper, c, basicConstraints, keyUsage, sigAlgName, sigAlgParams);
 
         this.encoding = encoding;
+        this.exception = exception;
     }
 
     public byte[] getEncoded() throws CertificateEncodingException
     {
+        if (null != exception)
+        {
+            throw exception;
+        }
+
         if (null == encoding)
         {
             throw new CertificateEncodingException();
         }
 
+        /*
+         * NOTE: Don't clone this return value. See class javadoc for details. Any necessary cloning is
+         * handled by the X509CertificateObject that is holding this instance.
+         */
         return encoding;
     }
 }
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/asymmetric/x509/X509CertificateObject.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/asymmetric/x509/X509CertificateObject.java
index bfaf29f..037a74c 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/asymmetric/x509/X509CertificateObject.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/asymmetric/x509/X509CertificateObject.java
@@ -1,57 +1,22 @@
 /* GENERATED SOURCE. DO NOT MODIFY. */
 package com.android.org.bouncycastle.jcajce.provider.asymmetric.x509;
 
-import java.io.ByteArrayOutputStream;
 import java.io.IOException;
-import java.math.BigInteger;
-import java.net.InetAddress;
-import java.net.UnknownHostException;
-import java.security.InvalidKeyException;
-import java.security.NoSuchAlgorithmException;
-import java.security.NoSuchProviderException;
-import java.security.Principal;
-import java.security.Provider;
 import java.security.PublicKey;
-import java.security.Signature;
-import java.security.SignatureException;
 import java.security.cert.CertificateEncodingException;
-import java.security.cert.CertificateException;
 import java.security.cert.CertificateExpiredException;
 import java.security.cert.CertificateNotYetValidException;
 import java.security.cert.CertificateParsingException;
-import java.security.cert.X509Certificate;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
 import java.util.Date;
 import java.util.Enumeration;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
 
 import javax.security.auth.x500.X500Principal;
 
 import com.android.org.bouncycastle.asn1.ASN1BitString;
 import com.android.org.bouncycastle.asn1.ASN1Encodable;
 import com.android.org.bouncycastle.asn1.ASN1Encoding;
-import com.android.org.bouncycastle.asn1.ASN1InputStream;
 import com.android.org.bouncycastle.asn1.ASN1ObjectIdentifier;
-import com.android.org.bouncycastle.asn1.ASN1OutputStream;
 import com.android.org.bouncycastle.asn1.ASN1Primitive;
-import com.android.org.bouncycastle.asn1.ASN1Sequence;
-import com.android.org.bouncycastle.asn1.ASN1String;
-import com.android.org.bouncycastle.asn1.DERBitString;
-import com.android.org.bouncycastle.asn1.DERIA5String;
-import com.android.org.bouncycastle.asn1.DERNull;
-import com.android.org.bouncycastle.asn1.DEROctetString;
-import com.android.org.bouncycastle.asn1.misc.MiscObjectIdentifiers;
-import com.android.org.bouncycastle.asn1.misc.NetscapeCertType;
-import com.android.org.bouncycastle.asn1.misc.NetscapeRevocationURL;
-import com.android.org.bouncycastle.asn1.misc.VerisignCzagExtension;
-import com.android.org.bouncycastle.asn1.util.ASN1Dump;
-import com.android.org.bouncycastle.asn1.x500.X500Name;
-import com.android.org.bouncycastle.asn1.x500.style.RFC4519Style;
-import com.android.org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 import com.android.org.bouncycastle.asn1.x509.BasicConstraints;
 import com.android.org.bouncycastle.asn1.x509.Extension;
 import com.android.org.bouncycastle.asn1.x509.Extensions;
@@ -62,12 +27,8 @@
 // END Android-added: Unknown reason
 import com.android.org.bouncycastle.jcajce.provider.asymmetric.util.PKCS12BagAttributeCarrierImpl;
 import com.android.org.bouncycastle.jcajce.util.JcaJceHelper;
-import com.android.org.bouncycastle.jce.X509Principal;
 import com.android.org.bouncycastle.jce.interfaces.PKCS12BagAttributeCarrier;
-import com.android.org.bouncycastle.jce.provider.BouncyCastleProvider;
-import com.android.org.bouncycastle.util.Integers;
-import com.android.org.bouncycastle.util.Strings;
-import com.android.org.bouncycastle.util.encoders.Hex;
+import com.android.org.bouncycastle.util.Arrays;
 
 class X509CertificateObject
     extends X509CertificateImpl
@@ -254,6 +215,8 @@
                     return false;
                 }
             }
+
+            return getInternalCertificate().equals(otherBC.getInternalCertificate());
         }
 
         return getInternalCertificate().equals(other);
@@ -318,18 +281,19 @@
             }
         }
 
-        byte[] encoding;
+        byte[] encoding = null;
+        CertificateEncodingException exception = null;
         try
         {
-            encoding = getEncoded();
+            encoding = c.getEncoded(ASN1Encoding.DER);
         }
-        catch (CertificateEncodingException e)
+        catch (IOException e)
         {
-            encoding = null;
+            exception = new X509CertificateEncodingException(e);
         }
 
         X509CertificateInternal temp = new X509CertificateInternal(bcHelper, c, basicConstraints, keyUsage, sigAlgName,
-            sigAlgParams, encoding);
+            sigAlgParams, encoding, exception);
 
         synchronized (cacheLock)
         {
@@ -371,7 +335,7 @@
                 return null;
             }
 
-            ASN1BitString bits = DERBitString.getInstance(ASN1Primitive.fromByteArray(extOctets));
+            ASN1BitString bits = ASN1BitString.getInstance(ASN1Primitive.fromByteArray(extOctets));
 
             byte[] bytes = bits.getBytes();
             int length = (bytes.length * 8) - bits.getPadBits();
@@ -420,4 +384,20 @@
             throw new CertificateParsingException("cannot construct SigAlgParams: " + e);
         }
     }
+
+    private static class X509CertificateEncodingException
+        extends CertificateEncodingException
+    {
+        private final Throwable cause;
+
+        X509CertificateEncodingException(Throwable cause)
+        {
+            this.cause = cause;
+        }
+
+        public Throwable getCause()
+        {
+            return cause;
+        }
+    }
 }
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/asymmetric/x509/X509SignatureUtil.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/asymmetric/x509/X509SignatureUtil.java
index 9903c5f..47bf765 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/asymmetric/x509/X509SignatureUtil.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/asymmetric/x509/X509SignatureUtil.java
@@ -181,6 +181,11 @@
 
     static void prettyPrintSignature(byte[] sig, StringBuffer buf, String nl)
     {
+        // -DM Hex.toHexString
+        // -DM Hex.toHexString
+        // -DM Hex.toHexString
+        // -DM Hex.toHexString
+
         if (sig.length > 20)
         {
             buf.append("            Signature: ").append(Hex.toHexString(sig, 0, 20)).append(nl);
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/config/ConfigurableProvider.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/config/ConfigurableProvider.java
index d6dc21e..76bfd53 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/config/ConfigurableProvider.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/config/ConfigurableProvider.java
@@ -47,8 +47,12 @@
 
     void addAlgorithm(String key, String value);
 
+    void addAlgorithm(String key, String value, Map<String, String> attributes);
+
     void addAlgorithm(String type, ASN1ObjectIdentifier oid, String className);
 
+    void addAlgorithm(String type, ASN1ObjectIdentifier oid, String className, Map<String, String> attributes);
+
     boolean hasAlgorithm(String type, String name);
 
     void addKeyInfoConverter(ASN1ObjectIdentifier oid, AsymmetricKeyInfoConverter keyInfoConverter);
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/digest/BCMessageDigest.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/digest/BCMessageDigest.java
index 2bb2007..24225e3 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/digest/BCMessageDigest.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/digest/BCMessageDigest.java
@@ -1,6 +1,7 @@
 /* GENERATED SOURCE. DO NOT MODIFY. */
 package com.android.org.bouncycastle.jcajce.provider.digest;
 
+import java.security.DigestException;
 import java.security.MessageDigest;
 
 import com.android.org.bouncycastle.crypto.Digest;
@@ -69,4 +70,16 @@
 
         return digestBytes;
     }
+
+    public int engineDigest(byte[] buf, int off, int len) throws DigestException
+    {
+        if (len < digestSize)
+            throw new DigestException("partial digests not returned");
+        if (buf.length - off < digestSize)
+            throw new DigestException("insufficient space in the output buffer to store the digest");
+
+        digest.doFinal(buf, off);
+
+        return digestSize;
+    }
 }
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/digest/DigestAlgorithmProvider.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/digest/DigestAlgorithmProvider.java
index 6597e1d..33183e1 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/digest/DigestAlgorithmProvider.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/digest/DigestAlgorithmProvider.java
@@ -24,6 +24,19 @@
         provider.addAlgorithm("Alg.Alias.KeyGenerator.HMAC/" + algorithm, mainName);
     }
 
+    protected void addKMACAlgorithm(
+        ConfigurableProvider provider,
+        String algorithm,
+        String algorithmClassName,
+        String keyGeneratorClassName)
+    {
+        String mainName = "KMAC" + algorithm;
+
+        provider.addAlgorithm("Mac." + mainName, algorithmClassName);
+        provider.addAlgorithm("KeyGenerator." + mainName, keyGeneratorClassName);
+        provider.addAlgorithm("Alg.Alias.KeyGenerator.KMAC" + algorithm, mainName);
+    }
+
     protected void addHMACAlias(
         ConfigurableProvider provider,
         String algorithm,
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/digest/Haraka.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/digest/Haraka.java
new file mode 100644
index 0000000..2d335a8
--- /dev/null
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/digest/Haraka.java
@@ -0,0 +1,80 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.org.bouncycastle.jcajce.provider.digest;
+
+import com.android.org.bouncycastle.crypto.digests.Haraka256Digest;
+import com.android.org.bouncycastle.crypto.digests.Haraka512Digest;
+import com.android.org.bouncycastle.jcajce.provider.config.ConfigurableProvider;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+public class Haraka
+{
+    private Haraka()
+    {
+
+    }
+
+    /**
+     * @hide This class is not part of the Android public SDK API
+     */
+    static public class Digest256
+        extends BCMessageDigest
+        implements Cloneable
+    {
+        public Digest256()
+        {
+            super(new Haraka256Digest());
+        }
+
+        public Object clone()
+            throws CloneNotSupportedException
+        {
+            Digest256 d = (Digest256)super.clone();
+            d.digest = new Haraka256Digest((Haraka256Digest)digest);
+
+            return d;
+        }
+    }
+
+    /**
+     * @hide This class is not part of the Android public SDK API
+     */
+    static public class Digest512
+        extends BCMessageDigest
+        implements Cloneable
+    {
+        public Digest512()
+        {
+            super(new Haraka512Digest());
+        }
+
+        public Object clone()
+            throws CloneNotSupportedException
+        {
+            Digest512 d = (Digest512)super.clone();
+            d.digest = new Haraka512Digest((Haraka512Digest)digest);
+
+            return d;
+        }
+    }
+
+    /**
+     * @hide This class is not part of the Android public SDK API
+     */
+    public static class Mappings
+        extends DigestAlgorithmProvider
+    {
+        private static final String PREFIX = Haraka.class.getName();
+
+        public Mappings()
+        {
+        }
+
+        public void configure(ConfigurableProvider provider)
+        {
+            provider.addAlgorithm("MessageDigest.HARAKA-256", PREFIX + "$Digest256");
+            provider.addAlgorithm("MessageDigest.HARAKA-512", PREFIX + "$Digest512");
+        }
+    }
+}
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/digest/SHA256.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/digest/SHA256.java
index a4e8a76..aed2538 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/digest/SHA256.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/digest/SHA256.java
@@ -30,14 +30,14 @@
     {
         public Digest()
         {
-            super(new SHA256Digest());
+            super(SHA256Digest.newInstance());
         }
 
         public Object clone()
             throws CloneNotSupportedException
         {
             Digest d = (Digest)super.clone();
-            d.digest = new SHA256Digest((SHA256Digest)digest);
+            d.digest = SHA256Digest.newInstance(digest);
 
             return d;
         }
@@ -51,7 +51,7 @@
     {
         public HashMac()
         {
-            super(new HMac(new SHA256Digest()));
+            super(new HMac(SHA256Digest.newInstance()));
         }
     }
 
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/digest/SHA512.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/digest/SHA512.java
index cdc7018..4f0aa21 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/digest/SHA512.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/digest/SHA512.java
@@ -190,10 +190,16 @@
 
             provider.addAlgorithm("MessageDigest.SHA-512/224", PREFIX + "$DigestT224");
             provider.addAlgorithm("Alg.Alias.MessageDigest.SHA512/224", "SHA-512/224");
+            provider.addAlgorithm("Alg.Alias.MessageDigest.SHA512224", "SHA-512/224");
+            provider.addAlgorithm("Alg.Alias.MessageDigest.SHA-512(224)", "SHA-512/224");
+            provider.addAlgorithm("Alg.Alias.MessageDigest.SHA512(224)", "SHA-512/224");
             provider.addAlgorithm("Alg.Alias.MessageDigest." + NISTObjectIdentifiers.id_sha512_224, "SHA-512/224");
 
             provider.addAlgorithm("MessageDigest.SHA-512/256", PREFIX + "$DigestT256");
+            provider.addAlgorithm("Alg.Alias.MessageDigest.SHA512/256", "SHA-512/256");
             provider.addAlgorithm("Alg.Alias.MessageDigest.SHA512256", "SHA-512/256");
+            provider.addAlgorithm("Alg.Alias.MessageDigest.SHA-512(256)", "SHA-512/256");
+            provider.addAlgorithm("Alg.Alias.MessageDigest.SHA512(256)", "SHA-512/256");
             provider.addAlgorithm("Alg.Alias.MessageDigest." + NISTObjectIdentifiers.id_sha512_256, "SHA-512/256");
 
             provider.addAlgorithm("Mac.OLDHMACSHA512", PREFIX + "$OldSHA512");
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/drbg/EntropyDaemon.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/drbg/EntropyDaemon.java
new file mode 100644
index 0000000..1d0586d
--- /dev/null
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/drbg/EntropyDaemon.java
@@ -0,0 +1,66 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.org.bouncycastle.jcajce.provider.drbg;
+
+import java.util.LinkedList;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+class EntropyDaemon
+    implements Runnable
+{
+    private static final Logger LOG = Logger.getLogger(EntropyDaemon.class.getName());
+
+    private final LinkedList<Runnable> tasks = new LinkedList<Runnable>();
+
+    public EntropyDaemon()
+    {
+    }
+
+    void addTask(Runnable task)
+    {
+        synchronized (tasks)
+        {
+            tasks.add(task);
+        }
+    }
+
+    public void run()
+    {
+        while (!Thread.currentThread().isInterrupted())
+        {
+            Runnable task;
+            synchronized (tasks)
+            {
+                task = tasks.poll();
+            }
+
+            if (task != null)
+            {
+                try
+                {
+                    task.run();
+                }
+                catch (Throwable e)
+                {
+                    // ignore
+                }
+            }
+            else
+            {
+                try
+                {
+                    Thread.sleep(5000);
+                }
+                catch (InterruptedException e)
+                {
+                    Thread.currentThread().interrupt();
+                }
+            }
+        }
+
+        if (LOG.isLoggable(Level.FINE))
+        {
+            LOG.fine("entropy thread interrupted - exiting");
+        }
+    }
+}
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/keystore/BC.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/keystore/BC.java
index aac3db9..aa6e941 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/keystore/BC.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/keystore/BC.java
@@ -3,6 +3,7 @@
 
 import com.android.org.bouncycastle.jcajce.provider.config.ConfigurableProvider;
 import com.android.org.bouncycastle.jcajce.provider.util.AsymmetricAlgorithmProvider;
+import com.android.org.bouncycastle.util.Properties;
 
 /**
  * @hide This class is not part of the Android public SDK API
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/keystore/bc/BcKeyStoreSpi.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/keystore/bc/BcKeyStoreSpi.java
index 9605ced..2138148 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/keystore/bc/BcKeyStoreSpi.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/keystore/bc/BcKeyStoreSpi.java
@@ -58,6 +58,7 @@
 import com.android.org.bouncycastle.jce.interfaces.BCKeyStore;
 import com.android.org.bouncycastle.jce.provider.BouncyCastleProvider;
 import com.android.org.bouncycastle.util.Arrays;
+import com.android.org.bouncycastle.util.Properties;
 import com.android.org.bouncycastle.util.io.Streams;
 import com.android.org.bouncycastle.util.io.TeeOutputStream;
 
@@ -398,6 +399,11 @@
     {
         byte[]      enc = key.getEncoded();
 
+        if (enc == null)
+        {
+            throw new IOException("unable to store encoding of protected key");
+        }
+
         if (key instanceof PrivateKey)
         {
             dOut.write(KEY_PRIVATE);
@@ -676,9 +682,18 @@
         Certificate[]   chain) 
         throws KeyStoreException
     {
-        if ((key instanceof PrivateKey) && (chain == null))
+        if ((key instanceof PrivateKey))
         {
-            throw new KeyStoreException("no certificate chain for private key");
+            if (chain == null)
+            {
+                throw new KeyStoreException("no certificate chain for private key");
+            }
+            if (key.getEncoded() == null)
+            {
+                // we ingore the password as the key is already protected.
+                table.put(alias, new StoreEntry(alias, new Date(), KEY, key, chain));
+                return;
+            }
         }
 
         try
@@ -1129,6 +1144,10 @@
         public Version1()
         {
             super(1);
+            if (!Properties.isOverrideSet("com.android.org.bouncycastle.bks.enable_v1"))
+            {
+                 throw new IllegalStateException("BKS-V1 not enabled");
+            }
         }
     }
 
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/keystore/pkcs12/PKCS12KeyStoreSpi.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/keystore/pkcs12/PKCS12KeyStoreSpi.java
index 06fbb96..e308252 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/keystore/pkcs12/PKCS12KeyStoreSpi.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/keystore/pkcs12/PKCS12KeyStoreSpi.java
@@ -3,6 +3,7 @@
 
 import java.io.BufferedInputStream;
 import java.io.ByteArrayInputStream;
+import java.io.EOFException;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
@@ -47,6 +48,7 @@
 import javax.crypto.spec.PBEKeySpec;
 import javax.crypto.spec.PBEParameterSpec;
 
+import com.android.org.bouncycastle.asn1.ASN1BMPString;
 import com.android.org.bouncycastle.asn1.ASN1Encodable;
 import com.android.org.bouncycastle.asn1.ASN1EncodableVector;
 import com.android.org.bouncycastle.asn1.ASN1Encoding;
@@ -66,6 +68,7 @@
 // Android-removed: Unsupported algorithms
 // import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers;
 // import org.bouncycastle.asn1.cryptopro.GOST28147Parameters;
+// import org.bouncycastle.asn1.misc.MiscObjectIdentifiers;
 import com.android.org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
 // import org.bouncycastle.asn1.ntt.NTTObjectIdentifiers;
 import com.android.org.bouncycastle.asn1.oiw.OIWObjectIdentifiers;
@@ -84,9 +87,13 @@
 import com.android.org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 import com.android.org.bouncycastle.asn1.x509.AuthorityKeyIdentifier;
 import com.android.org.bouncycastle.asn1.x509.DigestInfo;
+import com.android.org.bouncycastle.asn1.x509.ExtendedKeyUsage;
 import com.android.org.bouncycastle.asn1.x509.Extension;
+import com.android.org.bouncycastle.asn1.x509.Extensions;
+import com.android.org.bouncycastle.asn1.x509.KeyPurposeId;
 import com.android.org.bouncycastle.asn1.x509.SubjectKeyIdentifier;
 import com.android.org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import com.android.org.bouncycastle.asn1.x509.TBSCertificate;
 import com.android.org.bouncycastle.asn1.x509.X509ObjectIdentifiers;
 import com.android.org.bouncycastle.crypto.CryptoServicesRegistrar;
 import com.android.org.bouncycastle.crypto.Digest;
@@ -97,6 +104,9 @@
 import com.android.org.bouncycastle.jcajce.PKCS12StoreParameter;
 // Android-removed: Unsupported algorithms
 // import org.bouncycastle.jcajce.spec.GOST28147ParameterSpec;
+// import org.bouncycastle.jcajce.BCLoadStoreParameter;
+import com.android.org.bouncycastle.jcajce.provider.keystore.util.AdaptingKeyStoreSpi;
+import com.android.org.bouncycastle.jcajce.provider.keystore.util.ParameterUtil;
 import com.android.org.bouncycastle.jcajce.spec.PBKDF2KeySpec;
 import com.android.org.bouncycastle.jcajce.util.BCJcaJceHelper;
 import com.android.org.bouncycastle.jcajce.util.DefaultJcaJceHelper;
@@ -133,7 +143,7 @@
     private static final DefaultSecretKeyProvider keySizeProvider = new DefaultSecretKeyProvider();
 
     private IgnoresCaseHashtable keys = new IgnoresCaseHashtable();
-    private Hashtable localIds = new Hashtable();
+    private IgnoresCaseHashtable localIds = new IgnoresCaseHashtable();
     private IgnoresCaseHashtable certs = new IgnoresCaseHashtable();
     private Hashtable chainCerts = new Hashtable();
     private Hashtable keyCerts = new Hashtable();
@@ -257,6 +267,12 @@
         this.random = rand;
     }
 
+    public boolean engineProbe(InputStream stream)
+        throws IOException
+    {
+        return false;
+    }
+
     public Enumeration engineAliases()
     {
         Hashtable tab = new Hashtable();
@@ -295,25 +311,23 @@
         String alias)
         throws KeyStoreException
     {
-        Key k = (Key)keys.remove(alias);
-
-        Certificate c = (Certificate)certs.remove(alias);
-
-        if (c != null)
+        Certificate cert = (Certificate)certs.remove(alias);
+        if (cert != null)
         {
-            chainCerts.remove(new CertId(c.getPublicKey()));
+            chainCerts.remove(new CertId(cert.getPublicKey()));
         }
 
-        if (k != null)
+        Key key = (Key)keys.remove(alias);
+        if (key != null)
         {
             String id = (String)localIds.remove(alias);
             if (id != null)
             {
-                c = (Certificate)keyCerts.remove(id);
-            }
-            if (c != null)
-            {
-                chainCerts.remove(new CertId(c.getPublicKey()));
+                Certificate keyCert = (Certificate)keyCerts.remove(id);
+                if (keyCert != null)
+                {
+                    chainCerts.remove(new CertId(keyCert.getPublicKey()));
+                }
             }
         }
     }
@@ -658,7 +672,7 @@
 
         try
         {
-            SecretKeyFactory keyFact =  helper.createSecretKeyFactory(algorithm);
+            SecretKeyFactory keyFact = helper.createSecretKeyFactory(algorithm);
             PBEParameterSpec defParams = new PBEParameterSpec(
                 pbeParams.getIV(),
                 pbeParams.getIterations().intValue());
@@ -708,7 +722,7 @@
                 throw new IOException("exception decrypting data - " + e.toString());
             }
         }
-        else  if (algorithm.equals(PKCSObjectIdentifiers.id_PBES2))
+        else if (algorithm.equals(PKCSObjectIdentifiers.id_PBES2))
         {
             try
             {
@@ -771,6 +785,31 @@
         return cipher;
     }
 
+  
+    // BEGIN Android-removed: Unsupported algorithms
+    /*
+    public void engineLoad(KeyStore.LoadStoreParameter loadStoreParameter)
+        throws IOException, NoSuchAlgorithmException, CertificateException
+    {
+        if (loadStoreParameter == null)
+        {
+            engineLoad(null, null);
+        }
+        else if (loadStoreParameter instanceof BCLoadStoreParameter)
+        {
+            BCLoadStoreParameter bcParam = (BCLoadStoreParameter)loadStoreParameter;
+
+            engineLoad(bcParam.getInputStream(), ParameterUtil.extractPassword(loadStoreParameter));
+        }
+        else
+        {
+            throw new IllegalArgumentException(
+                "no support for 'param' of type " + loadStoreParameter.getClass().getName());
+        }
+    }
+    */
+    // END Android-removed: Unsupported algorithms
+
     public void engineLoad(
         InputStream stream,
         char[] password)
@@ -786,7 +825,10 @@
         bufIn.mark(10);
 
         int head = bufIn.read();
-
+        if (head < 0)
+        {
+            throw new EOFException("no data in keystore stream");
+        }
         if (head != 0x30)
         {
             throw new IOException("stream does not represent a PKCS12 key store");
@@ -795,7 +837,7 @@
         bufIn.reset();
 
         ASN1InputStream bIn = new ASN1InputStream(bufIn);
-        
+
         Pfx bag;
         try
         {
@@ -872,7 +914,7 @@
         // END Android-removed: keep v1.61 behaviour to keep backwards-compatibility
 
         keys = new IgnoresCaseHashtable();
-        localIds = new Hashtable();
+        localIds = new IgnoresCaseHashtable();
 
         if (info.getContentType().equals(data))
         {
@@ -892,86 +934,19 @@
                         SafeBag b = SafeBag.getInstance(seq.getObjectAt(j));
                         if (b.getBagId().equals(pkcs8ShroudedKeyBag))
                         {
-                            com.android.org.bouncycastle.asn1.pkcs.EncryptedPrivateKeyInfo eIn = com.android.org.bouncycastle.asn1.pkcs.EncryptedPrivateKeyInfo.getInstance(b.getBagValue());
-                            PrivateKey privKey = unwrapKey(eIn.getEncryptionAlgorithm(), eIn.getEncryptedData(), password, wrongPKCS12Zero);
-
-                            //
-                            // set the attributes on the key
-                            //
-                            String alias = null;
-                            ASN1OctetString localId = null;
-
-                            if (b.getBagAttributes() != null)
-                            {
-                                Enumeration e = b.getBagAttributes().getObjects();
-                                while (e.hasMoreElements())
-                                {
-                                    ASN1Sequence sq = (ASN1Sequence)e.nextElement();
-                                    ASN1ObjectIdentifier aOid = (ASN1ObjectIdentifier)sq.getObjectAt(0);
-                                    ASN1Set attrSet = (ASN1Set)sq.getObjectAt(1);
-                                    ASN1Primitive attr = null;
-
-                                    if (attrSet.size() > 0)
-                                    {
-                                        attr = (ASN1Primitive)attrSet.getObjectAt(0);
-
-                                        if (privKey instanceof PKCS12BagAttributeCarrier)
-                                        {
-                                            PKCS12BagAttributeCarrier bagAttr = (PKCS12BagAttributeCarrier)privKey;
-                                            ASN1Encodable existing = bagAttr.getBagAttribute(aOid);
-                                            if (existing != null)
-                                            {
-                                                // OK, but the value has to be the same
-                                                if (!existing.toASN1Primitive().equals(attr))
-                                                {
-                                                    throw new IOException(
-                                                        "attempt to add existing attribute with different value");
-                                                }
-                                            }
-                                            else
-                                            {
-                                                bagAttr.setBagAttribute(aOid, attr);
-                                            }
-                                        }
-                                    }
-
-                                    if (aOid.equals(pkcs_9_at_friendlyName))
-                                    {
-                                        alias = ((DERBMPString)attr).getString();
-                                        keys.put(alias, privKey);
-                                    }
-                                    else if (aOid.equals(pkcs_9_at_localKeyId))
-                                    {
-                                        localId = (ASN1OctetString)attr;
-                                    }
-                                }
-                            }
-
-                            if (localId != null)
-                            {
-                                String name = new String(Hex.encode(localId.getOctets()));
-
-                                if (alias == null)
-                                {
-                                    keys.put(name, privKey);
-                                }
-                                else
-                                {
-                                    localIds.put(alias, name);
-                                }
-                            }
-                            else
-                            {
-                                unmarkedKey = true;
-                                keys.put("unmarked", privKey);
-                            }
+                            unmarkedKey = processShroudedKeyBag(b, password, wrongPKCS12Zero);
                         }
                         else if (b.getBagId().equals(certBag))
                         {
                             chain.addElement(b);
                         }
+                        else if (b.getBagId().equals(keyBag))
+                        {
+                            processKeyBag(b);
+                        }
                         else
                         {
+                            // -DM 2 System.out.println
                             System.out.println("extra in data " + b.getBagId());
                             System.out.println(ASN1Dump.dumpAsString(b));
                         }
@@ -987,137 +962,21 @@
                     for (int j = 0; j != seq.size(); j++)
                     {
                         SafeBag b = SafeBag.getInstance(seq.getObjectAt(j));
-
                         if (b.getBagId().equals(certBag))
                         {
                             chain.addElement(b);
                         }
                         else if (b.getBagId().equals(pkcs8ShroudedKeyBag))
                         {
-                            com.android.org.bouncycastle.asn1.pkcs.EncryptedPrivateKeyInfo eIn = com.android.org.bouncycastle.asn1.pkcs.EncryptedPrivateKeyInfo.getInstance(b.getBagValue());
-                            PrivateKey privKey = unwrapKey(eIn.getEncryptionAlgorithm(), eIn.getEncryptedData(), password, wrongPKCS12Zero);
-
-                            //
-                            // set the attributes on the key
-                            //
-                            PKCS12BagAttributeCarrier bagAttr = (PKCS12BagAttributeCarrier)privKey;
-                            String alias = null;
-                            ASN1OctetString localId = null;
-
-                            Enumeration e = b.getBagAttributes().getObjects();
-                            while (e.hasMoreElements())
-                            {
-                                ASN1Sequence sq = (ASN1Sequence)e.nextElement();
-                                ASN1ObjectIdentifier aOid = (ASN1ObjectIdentifier)sq.getObjectAt(0);
-                                ASN1Set attrSet = (ASN1Set)sq.getObjectAt(1);
-                                ASN1Primitive attr = null;
-
-                                if (attrSet.size() > 0)
-                                {
-                                    attr = (ASN1Primitive)attrSet.getObjectAt(0);
-
-                                    ASN1Encodable existing = bagAttr.getBagAttribute(aOid);
-                                    if (existing != null)
-                                    {
-                                        // OK, but the value has to be the same
-                                        if (!existing.toASN1Primitive().equals(attr))
-                                        {
-                                            throw new IOException(
-                                                "attempt to add existing attribute with different value");
-                                        }
-                                    }
-                                    else
-                                    {
-                                        bagAttr.setBagAttribute(aOid, attr);
-                                    }
-                                }
-
-                                if (aOid.equals(pkcs_9_at_friendlyName))
-                                {
-                                    alias = ((DERBMPString)attr).getString();
-                                    keys.put(alias, privKey);
-                                }
-                                else if (aOid.equals(pkcs_9_at_localKeyId))
-                                {
-                                    localId = (ASN1OctetString)attr;
-                                }
-                            }
-
-                            String name = new String(Hex.encode(localId.getOctets()));
-
-                            if (alias == null)
-                            {
-                                keys.put(name, privKey);
-                            }
-                            else
-                            {
-                                localIds.put(alias, name);
-                            }
+                            unmarkedKey = processShroudedKeyBag(b, password, wrongPKCS12Zero);
                         }
                         else if (b.getBagId().equals(keyBag))
                         {
-                            com.android.org.bouncycastle.asn1.pkcs.PrivateKeyInfo kInfo = com.android.org.bouncycastle.asn1.pkcs.PrivateKeyInfo.getInstance(b.getBagValue());
-                            PrivateKey privKey = BouncyCastleProvider.getPrivateKey(kInfo);
-
-                            //
-                            // set the attributes on the key
-                            //
-                            PKCS12BagAttributeCarrier bagAttr = (PKCS12BagAttributeCarrier)privKey;
-                            String alias = null;
-                            ASN1OctetString localId = null;
-
-                            Enumeration e = b.getBagAttributes().getObjects();
-                            while (e.hasMoreElements())
-                            {
-                                ASN1Sequence sq = ASN1Sequence.getInstance(e.nextElement());
-                                ASN1ObjectIdentifier aOid = ASN1ObjectIdentifier.getInstance(sq.getObjectAt(0));
-                                ASN1Set attrSet = ASN1Set.getInstance(sq.getObjectAt(1));
-                                ASN1Primitive attr = null;
-
-                                if (attrSet.size() > 0)
-                                {
-                                    attr = (ASN1Primitive)attrSet.getObjectAt(0);
-
-                                    ASN1Encodable existing = bagAttr.getBagAttribute(aOid);
-                                    if (existing != null)
-                                    {
-                                        // OK, but the value has to be the same
-                                        if (!existing.toASN1Primitive().equals(attr))
-                                        {
-                                            throw new IOException(
-                                                "attempt to add existing attribute with different value");
-                                        }
-                                    }
-                                    else
-                                    {
-                                        bagAttr.setBagAttribute(aOid, attr);
-                                    }
-
-                                    if (aOid.equals(pkcs_9_at_friendlyName))
-                                    {
-                                        alias = ((DERBMPString)attr).getString();
-                                        keys.put(alias, privKey);
-                                    }
-                                    else if (aOid.equals(pkcs_9_at_localKeyId))
-                                    {
-                                        localId = (ASN1OctetString)attr;
-                                    }
-                                }
-                            }
-
-                            String name = new String(Hex.encode(localId.getOctets()));
-
-                            if (alias == null)
-                            {
-                                keys.put(name, privKey);
-                            }
-                            else
-                            {
-                                localIds.put(alias, name);
-                            }
+                            processKeyBag(b);
                         }
                         else
                         {
+                            // -DM 2 System.out.println
                             System.out.println("extra in encryptedData " + b.getBagId());
                             System.out.println(ASN1Dump.dumpAsString(b));
                         }
@@ -1125,6 +984,7 @@
                 }
                 else
                 {
+                    // -DM 2 System.out.println
                     System.out.println("extra " + c[i].getContentType().getId());
                     System.out.println("extra " + ASN1Dump.dumpAsString(c[i].getContent()));
                 }
@@ -1185,6 +1045,17 @@
                             ASN1Encodable existing = bagAttr.getBagAttribute(oid);
                             if (existing != null)
                             {
+                                // we've found more than one - one might be incorrect
+                                if (oid.equals(pkcs_9_at_localKeyId))
+                                {
+                                    // -DM Hex.toHexString
+                                    String id = Hex.toHexString(((ASN1OctetString)attr).getOctets());
+                                    if (!(keys.keys.containsKey(id) || localIds.keys.containsKey(id)))
+                                    {
+                                        continue; // ignore this one - it's not valid
+                                    }
+                                }
+
                                 // OK, but the value has to be the same
                                 if (!existing.toASN1Primitive().equals(attr))
                                 {
@@ -1194,13 +1065,20 @@
                             }
                             else
                             {
-                                bagAttr.setBagAttribute(oid, attr);
+                                if (attrSet.size() > 1)
+                                {
+                                    bagAttr.setBagAttribute(oid, attrSet);
+                                }
+                                else
+                                {
+                                    bagAttr.setBagAttribute(oid, attr);
+                                }
                             }
                         }
 
                         if (oid.equals(pkcs_9_at_friendlyName))
                         {
-                            alias = ((DERBMPString)attr).getString();
+                            alias = ((ASN1BMPString)attr).getString();
                         }
                         else if (oid.equals(pkcs_9_at_localKeyId))
                         {
@@ -1241,6 +1119,149 @@
         }
     }
 
+    private boolean processShroudedKeyBag(SafeBag b, char[] password, boolean wrongPKCS12Zero)
+        throws IOException
+    {
+        com.android.org.bouncycastle.asn1.pkcs.EncryptedPrivateKeyInfo eIn = com.android.org.bouncycastle.asn1.pkcs.EncryptedPrivateKeyInfo.getInstance(b.getBagValue());
+        PrivateKey privKey = unwrapKey(eIn.getEncryptionAlgorithm(), eIn.getEncryptedData(), password, wrongPKCS12Zero);
+
+        //
+        // set the attributes on the key
+        //
+        String alias = null;
+        ASN1OctetString localId = null;
+
+        if (b.getBagAttributes() != null)
+        {
+            Enumeration e = b.getBagAttributes().getObjects();
+            while (e.hasMoreElements())
+            {
+                ASN1Sequence sq = (ASN1Sequence)e.nextElement();
+                ASN1ObjectIdentifier aOid = (ASN1ObjectIdentifier)sq.getObjectAt(0);
+                ASN1Set attrSet = (ASN1Set)sq.getObjectAt(1);
+                ASN1Primitive attr = null;
+
+                if (attrSet.size() > 0)
+                {
+                    attr = (ASN1Primitive)attrSet.getObjectAt(0);
+
+                    if (privKey instanceof PKCS12BagAttributeCarrier)
+                    {
+                        PKCS12BagAttributeCarrier bagAttr = (PKCS12BagAttributeCarrier)privKey;
+                        ASN1Encodable existing = bagAttr.getBagAttribute(aOid);
+                        if (existing != null)
+                        {
+                            // OK, but the value has to be the same
+                            if (!existing.toASN1Primitive().equals(attr))
+                            {
+                                throw new IOException(
+                                    "attempt to add existing attribute with different value");
+                            }
+                        }
+                        else
+                        {
+                            bagAttr.setBagAttribute(aOid, attr);
+                        }
+                    }
+                }
+
+                if (aOid.equals(pkcs_9_at_friendlyName))
+                {
+                    alias = ((ASN1BMPString)attr).getString();
+                    keys.put(alias, privKey);
+                }
+                else if (aOid.equals(pkcs_9_at_localKeyId))
+                {
+                    localId = (ASN1OctetString)attr;
+                }
+            }
+        }
+
+        if (localId != null)
+        {
+            String name = new String(Hex.encode(localId.getOctets()));
+
+            if (alias == null)
+            {
+                keys.put(name, privKey);
+            }
+            else
+            {
+                localIds.put(alias, name);
+            }
+            return false;  // key properly marked
+        }
+        else
+        {
+            keys.put("unmarked", privKey);
+            return true;  // key properly marked
+        }
+    }
+
+    private void processKeyBag(SafeBag b)
+        throws IOException
+    {
+        com.android.org.bouncycastle.asn1.pkcs.PrivateKeyInfo kInfo = com.android.org.bouncycastle.asn1.pkcs.PrivateKeyInfo.getInstance(b.getBagValue());
+        PrivateKey privKey = BouncyCastleProvider.getPrivateKey(kInfo);
+
+        //
+        // set the attributes on the key
+        //
+        PKCS12BagAttributeCarrier bagAttr = (PKCS12BagAttributeCarrier)privKey;
+        String alias = null;
+        ASN1OctetString localId = null;
+
+        Enumeration e = b.getBagAttributes().getObjects();
+        while (e.hasMoreElements())
+        {
+            ASN1Sequence sq = ASN1Sequence.getInstance(e.nextElement());
+            ASN1ObjectIdentifier aOid = ASN1ObjectIdentifier.getInstance(sq.getObjectAt(0));
+            ASN1Set attrSet = ASN1Set.getInstance(sq.getObjectAt(1));
+            ASN1Primitive attr = null;
+
+            if (attrSet.size() > 0)
+            {
+                attr = (ASN1Primitive)attrSet.getObjectAt(0);
+
+                ASN1Encodable existing = bagAttr.getBagAttribute(aOid);
+                if (existing != null)
+                {
+                    // OK, but the value has to be the same
+                    if (!existing.toASN1Primitive().equals(attr))
+                    {
+                        throw new IOException(
+                            "attempt to add existing attribute with different value");
+                    }
+                }
+                else
+                {
+                    bagAttr.setBagAttribute(aOid, attr);
+                }
+
+                if (aOid.equals(pkcs_9_at_friendlyName))
+                {
+                    alias = ((ASN1BMPString)attr).getString();
+                    keys.put(alias, privKey);
+                }
+                else if (aOid.equals(pkcs_9_at_localKeyId))
+                {
+                    localId = (ASN1OctetString)attr;
+                }
+            }
+        }
+
+        String name = new String(Hex.encode(localId.getOctets()));
+
+        if (alias == null)
+        {
+            keys.put(name, privKey);
+        }
+        else
+        {
+            localIds.put(alias, name);
+        }
+    }
+
     private int validateIterationCount(BigInteger i)
     {
         int count = i.intValue();
@@ -1322,7 +1343,55 @@
         // See CtsKeystoreTestCases:android.keystore.cts.KeyStoreTest
         if (password == null)
         {
-            throw new NullPointerException("No password supplied for PKCS#12 KeyStore.");
+            if (password == null)
+            {
+                Enumeration cs = certs.keys();
+
+                ASN1EncodableVector certSeq = new ASN1EncodableVector();
+
+                while (cs.hasMoreElements())
+                {
+                    try
+                    {
+                        String certId = (String)cs.nextElement();
+                        Certificate cert = (Certificate)certs.get(certId);
+
+                        SafeBag sBag = createSafeBag(certId, cert);
+
+                        certSeq.add(sBag);
+                    }
+                    catch (CertificateEncodingException e)
+                    {
+                        throw new IOException("Error encoding certificate: " + e.toString());
+                    }
+                }
+
+                if (useDEREncoding)
+                {
+                    ContentInfo bagInfo = new ContentInfo(PKCSObjectIdentifiers.data, new DEROctetString(new DERSequence(certSeq).getEncoded()));
+
+                    Pfx pfx = new Pfx(new ContentInfo(PKCSObjectIdentifiers.data, new DEROctetString(new DERSequence(bagInfo).getEncoded())), null);
+
+                    pfx.encodeTo(stream, ASN1Encoding.DER);
+                }
+                else
+                {
+                    ContentInfo bagInfo = new ContentInfo(PKCSObjectIdentifiers.data, new BEROctetString(new BERSequence(certSeq).getEncoded()));
+
+                    Pfx pfx = new Pfx(new ContentInfo(PKCSObjectIdentifiers.data, new BEROctetString(new BERSequence(bagInfo).getEncoded())), null);
+
+                    pfx.encodeTo(stream, ASN1Encoding.BER);
+                }
+
+                return;
+            }
+        }
+        else
+        {
+            if (password == null)
+            {
+                throw new NullPointerException("no password supplied for PKCS#12 KeyStore");
+            }
         }
         /*
         if (keys.size() == 0)
@@ -1409,7 +1478,7 @@
                 //
                 // make sure we are using the local alias on store
                 //
-                DERBMPString nm = (DERBMPString)bagAttrs.getBagAttribute(pkcs_9_at_friendlyName);
+                ASN1BMPString nm = (ASN1BMPString)bagAttrs.getBagAttribute(pkcs_9_at_friendlyName);
                 if (nm == null || !nm.getString().equals(name))
                 {
                     bagAttrs.setBagAttribute(pkcs_9_at_friendlyName, new DERBMPString(name));
@@ -1500,7 +1569,7 @@
                     //
                     // make sure we are using the local alias on store
                     //
-                    DERBMPString nm = (DERBMPString)bagAttrs.getBagAttribute(pkcs_9_at_friendlyName);
+                    ASN1BMPString nm = (ASN1BMPString)bagAttrs.getBagAttribute(pkcs_9_at_friendlyName);
                     if (nm == null || !nm.getString().equals(name))
                     {
                         bagAttrs.setBagAttribute(pkcs_9_at_friendlyName, new DERBMPString(name));
@@ -1633,6 +1702,7 @@
                     }
                 }
 
+
                 SafeBag sBag = new SafeBag(certBag, cBag.toASN1Primitive(), new DERSet(fName));
 
                 certSeq.add(sBag);
@@ -1706,7 +1776,7 @@
             //
             // make sure we are using the local alias on store
             //
-            DERBMPString nm = (DERBMPString)bagAttrs.getBagAttribute(pkcs_9_at_friendlyName);
+            ASN1BMPString nm = (ASN1BMPString)bagAttrs.getBagAttribute(pkcs_9_at_friendlyName);
             if (nm == null || !nm.getString().equals(certId))
             {
                 if (certId != null)
@@ -1750,6 +1820,43 @@
             fName.add(new DERSequence(fSeq));
         }
 
+        // Android-removed: unsupported
+        // add the trusted usage attribute - needed for Oracle key stores
+        // if (cert instanceof X509Certificate)
+        // {
+        //     TBSCertificate tbsCert = TBSCertificate.getInstance(((X509Certificate)cert).getTBSCertificate());
+        //     Extensions exts = tbsCert.getExtensions();
+        //     if (exts != null)
+        //     {
+        //         Extension extUsage = exts.getExtension(Extension.extendedKeyUsage);
+        //         if (extUsage != null)
+        //         {
+        //             ASN1EncodableVector fSeq = new ASN1EncodableVector();
+
+        //             // oracle trusted key usage OID.
+        //             fSeq.add(MiscObjectIdentifiers.id_oracle_pkcs12_trusted_key_usage);
+        //             fSeq.add(new DERSet(ExtendedKeyUsage.getInstance(extUsage.getParsedValue()).getUsages()));
+        //             fName.add(new DERSequence(fSeq));
+        //         }
+        //         else
+        //         {
+        //             ASN1EncodableVector fSeq = new ASN1EncodableVector();
+
+        //             fSeq.add(MiscObjectIdentifiers.id_oracle_pkcs12_trusted_key_usage);
+        //             fSeq.add(new DERSet(KeyPurposeId.anyExtendedKeyUsage));
+        //             fName.add(new DERSequence(fSeq));
+        //         }
+        //     }
+        //     else
+        //     {
+        //         ASN1EncodableVector fSeq = new ASN1EncodableVector();
+
+        //         fSeq.add(MiscObjectIdentifiers.id_oracle_pkcs12_trusted_key_usage);
+        //         fSeq.add(new DERSet(KeyPurposeId.anyExtendedKeyUsage));
+        //         fName.add(new DERSequence(fSeq));
+        //     }
+        // }
+
         return new SafeBag(certBag, cBag.toASN1Primitive(), new DERSet(fName));
     }
 
@@ -1757,19 +1864,19 @@
     {
         Set usedSet = new HashSet();
 
-        for (Enumeration en = keys.keys(); en.hasMoreElements();)
+        for (Enumeration en = keys.keys(); en.hasMoreElements(); )
         {
             String alias = (String)en.nextElement();
 
-                Certificate[] certs = engineGetCertificateChain(alias);
+            Certificate[] certs = engineGetCertificateChain(alias);
 
-                for (int i = 0; i != certs.length; i++)
-                {
-                    usedSet.add(certs[i]);
-                }
+            for (int i = 0; i != certs.length; i++)
+            {
+                usedSet.add(certs[i]);
+            }
         }
 
-        for (Enumeration en = certs.keys(); en.hasMoreElements();)
+        for (Enumeration en = certs.keys(); en.hasMoreElements(); )
         {
             String alias = (String)en.nextElement();
 
@@ -1801,6 +1908,7 @@
         return mac.doFinal();
     }
 
+    // Android-changed: Use default provider for JCA algorithms instead of BC
     /**
      * @hide This class is not part of the Android public SDK API
      */
@@ -1810,7 +1918,7 @@
         public BCPKCS12KeyStore()
         {
             // Android-changed: Use default provider for JCA algorithms instead of BC
-            // Was: super(new BCJcaJceHelper(), pbeWithSHAAnd3_KeyTripleDES_CBC, pbeWithSHAAnd40BitRC2_CBC);
+            // Was: super(new BCJcaJceHelper(), new PKCS12KeyStoreSpi(new BCJcaJceHelper(), pbeWithSHAAnd3_KeyTripleDES_CBC, pbeWithSHAAnd40BitRC2_CBC));
             super(new DefaultJcaJceHelper(), pbeWithSHAAnd3_KeyTripleDES_CBC, pbeWithSHAAnd40BitRC2_CBC);
         }
     }
@@ -1818,29 +1926,29 @@
     // BEGIN Android-removed: Unsupported algorithms
     /*
     public static class BCPKCS12KeyStore3DES
-        extends PKCS12KeyStoreSpi
+        extends AdaptingKeyStoreSpi
     {
         public BCPKCS12KeyStore3DES()
         {
-            super(new BCJcaJceHelper(), pbeWithSHAAnd3_KeyTripleDES_CBC, pbeWithSHAAnd3_KeyTripleDES_CBC);
+            super(new BCJcaJceHelper(), new PKCS12KeyStoreSpi(new BCJcaJceHelper(), pbeWithSHAAnd3_KeyTripleDES_CBC, pbeWithSHAAnd3_KeyTripleDES_CBC));
         }
     }
 
     public static class DefPKCS12KeyStore
-        extends PKCS12KeyStoreSpi
+        extends AdaptingKeyStoreSpi
     {
         public DefPKCS12KeyStore()
         {
-            super(new DefaultJcaJceHelper(), pbeWithSHAAnd3_KeyTripleDES_CBC, pbeWithSHAAnd40BitRC2_CBC);
+            super(new DefaultJcaJceHelper(), new PKCS12KeyStoreSpi(new DefaultJcaJceHelper(), pbeWithSHAAnd3_KeyTripleDES_CBC, pbeWithSHAAnd40BitRC2_CBC));
         }
     }
 
     public static class DefPKCS12KeyStore3DES
-        extends PKCS12KeyStoreSpi
+        extends AdaptingKeyStoreSpi
     {
         public DefPKCS12KeyStore3DES()
         {
-            super(new DefaultJcaJceHelper(), pbeWithSHAAnd3_KeyTripleDES_CBC, pbeWithSHAAnd3_KeyTripleDES_CBC);
+            super(new DefaultJcaJceHelper(), new PKCS12KeyStoreSpi(new DefaultJcaJceHelper(), pbeWithSHAAnd3_KeyTripleDES_CBC, pbeWithSHAAnd3_KeyTripleDES_CBC));
         }
     }
     */
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/keystore/util/AdaptingKeyStoreSpi.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/keystore/util/AdaptingKeyStoreSpi.java
new file mode 100644
index 0000000..1a10ae1
--- /dev/null
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/keystore/util/AdaptingKeyStoreSpi.java
@@ -0,0 +1,186 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.org.bouncycastle.jcajce.provider.keystore.util;
+
+import java.io.BufferedInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.security.Key;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.KeyStoreSpi;
+import java.security.NoSuchAlgorithmException;
+import java.security.UnrecoverableKeyException;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateException;
+import java.util.Date;
+import java.util.Enumeration;
+
+import com.android.org.bouncycastle.jcajce.provider.keystore.pkcs12.PKCS12KeyStoreSpi;
+import com.android.org.bouncycastle.jcajce.util.JcaJceHelper;
+import com.android.org.bouncycastle.util.Properties;
+
+/**
+ * Implements a certificate only JKS key store.
+ * @hide This class is not part of the Android public SDK API
+ */
+public class AdaptingKeyStoreSpi
+    extends KeyStoreSpi
+{
+    public static final String COMPAT_OVERRIDE = "keystore.type.compat";
+
+    private final JKSKeyStoreSpi jksStore;
+    private final KeyStoreSpi primaryStore;
+
+    private KeyStoreSpi keyStoreSpi;
+
+    public AdaptingKeyStoreSpi(JcaJceHelper helper, KeyStoreSpi primaryStore)
+    {
+        this.jksStore = new JKSKeyStoreSpi(helper);
+        this.primaryStore = primaryStore;
+        this.keyStoreSpi = primaryStore;
+    }
+
+    public boolean engineProbe(InputStream stream)
+        throws IOException
+    {
+        if (keyStoreSpi instanceof PKCS12KeyStoreSpi)
+        {
+            return ((PKCS12KeyStoreSpi)keyStoreSpi).engineProbe(stream);
+        }
+        return false;
+    }
+
+    public Key engineGetKey(String alias, char[] password)
+        throws NoSuchAlgorithmException, UnrecoverableKeyException
+    {
+        return keyStoreSpi.engineGetKey(alias, password);
+    }
+
+    public Certificate[] engineGetCertificateChain(String alias)
+    {
+        return keyStoreSpi.engineGetCertificateChain(alias);
+    }
+
+    public Certificate engineGetCertificate(String alias)
+    {
+        return keyStoreSpi.engineGetCertificate(alias);
+    }
+
+    public Date engineGetCreationDate(String alias)
+    {
+        return keyStoreSpi.engineGetCreationDate(alias);
+    }
+
+    public void engineSetKeyEntry(String alias, Key key, char[] password, Certificate[] chain)
+        throws KeyStoreException
+    {
+        keyStoreSpi.engineSetKeyEntry(alias, key, password, chain);
+    }
+
+    public void engineSetKeyEntry(String alias, byte[] key, Certificate[] chain)
+        throws KeyStoreException
+    {
+        keyStoreSpi.engineSetKeyEntry(alias, key, chain);
+    }
+
+    public void engineSetCertificateEntry(String alias, Certificate cert)
+        throws KeyStoreException
+    {
+        keyStoreSpi.engineSetCertificateEntry(alias, cert);
+    }
+
+    public void engineDeleteEntry(String alias)
+        throws KeyStoreException
+    {
+        keyStoreSpi.engineDeleteEntry(alias);
+    }
+
+    public Enumeration<String> engineAliases()
+    {
+        return keyStoreSpi.engineAliases();
+    }
+
+    public boolean engineContainsAlias(String alias)
+    {
+        return keyStoreSpi.engineContainsAlias(alias);
+    }
+
+    public int engineSize()
+    {
+        return keyStoreSpi.engineSize();
+    }
+
+    public boolean engineIsKeyEntry(String alias)
+    {
+        return keyStoreSpi.engineIsKeyEntry(alias);
+    }
+
+    public boolean engineIsCertificateEntry(String alias)
+    {
+        return keyStoreSpi.engineIsCertificateEntry(alias);
+    }
+
+    public String engineGetCertificateAlias(Certificate cert)
+    {
+        return keyStoreSpi.engineGetCertificateAlias(cert);
+    }
+
+    public void engineStore(OutputStream stream, char[] password)
+        throws IOException, NoSuchAlgorithmException, CertificateException
+    {
+        keyStoreSpi.engineStore(stream, password);
+    }
+
+    public void engineStore(KeyStore.LoadStoreParameter parameter)
+        throws IOException, NoSuchAlgorithmException, CertificateException
+    {
+        keyStoreSpi.engineStore(parameter);
+    }
+
+    public void engineLoad(InputStream stream, char[] password)
+        throws IOException, NoSuchAlgorithmException, CertificateException
+    {
+        if (stream == null)
+        {
+            keyStoreSpi = primaryStore;
+            keyStoreSpi.engineLoad(null, password);
+        }
+        else
+        {
+            // the FIPS BCFKS/JKS compatibility is explicit and doesn't use the override.
+            if (Properties.isOverrideSet(COMPAT_OVERRIDE) || !(primaryStore instanceof PKCS12KeyStoreSpi))
+            {
+                if (!stream.markSupported())
+                {
+                    stream = new BufferedInputStream(stream);
+                }
+
+                stream.mark(8);
+                if (jksStore.engineProbe(stream))
+                {
+                    keyStoreSpi = jksStore;
+                }
+                else
+                {
+                    keyStoreSpi = primaryStore;
+                }
+
+                stream.reset();
+            }
+            else
+            {
+                keyStoreSpi = primaryStore;
+            }
+
+            keyStoreSpi.engineLoad(stream, password);
+        }
+    }
+
+    public void engineLoad(KeyStore.LoadStoreParameter parameter)
+        throws IOException, NoSuchAlgorithmException, CertificateException
+    {
+        keyStoreSpi.engineLoad(parameter);
+    }
+}
+
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/keystore/util/JKSKeyStoreSpi.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/keystore/util/JKSKeyStoreSpi.java
new file mode 100644
index 0000000..d27ee78
--- /dev/null
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/keystore/util/JKSKeyStoreSpi.java
@@ -0,0 +1,428 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.org.bouncycastle.jcajce.provider.keystore.util;
+
+import java.io.ByteArrayInputStream;
+import java.io.DataInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.security.Key;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.KeyStoreSpi;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.UnrecoverableKeyException;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.util.Date;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.Map;
+
+import com.android.org.bouncycastle.crypto.Digest;
+import com.android.org.bouncycastle.jcajce.BCLoadStoreParameter;
+import com.android.org.bouncycastle.jcajce.provider.util.DigestFactory;
+import com.android.org.bouncycastle.jcajce.util.JcaJceHelper;
+import com.android.org.bouncycastle.util.Arrays;
+import com.android.org.bouncycastle.util.Strings;
+import com.android.org.bouncycastle.util.io.Streams;
+
+/**
+ * Implements a certificate only JKS key store.
+ * @hide This class is not part of the Android public SDK API
+ */
+public class JKSKeyStoreSpi
+    extends KeyStoreSpi
+{
+    private static final String NOT_IMPLEMENTED_MESSAGE = "BC JKS store is read-only and only supports certificate entries";
+
+    private final Hashtable<String, BCJKSTrustedCertEntry> certificateEntries = new Hashtable<String, BCJKSTrustedCertEntry>();
+    private final JcaJceHelper helper;
+
+    public JKSKeyStoreSpi(JcaJceHelper helper)
+    {
+        this.helper = helper;
+    }
+
+    public boolean engineProbe(InputStream stream)
+        throws IOException
+    {
+        DataInputStream storeStream;
+        if (stream instanceof DataInputStream)
+        {
+            storeStream = (DataInputStream)stream;
+        }
+        else
+        {
+            storeStream = new DataInputStream(stream);
+        }
+
+        int magic = storeStream.readInt();
+        int storeVersion = storeStream.readInt();
+        return magic == (int)0x0000feedfeedL && (storeVersion == 1 || storeVersion == 2);
+    }
+
+    public Key engineGetKey(String alias, char[] password)
+        throws NoSuchAlgorithmException, UnrecoverableKeyException
+    {
+        return null;  // by definition
+    }
+
+    public Certificate[] engineGetCertificateChain(String alias)
+    {
+        return null;  // by definition
+    }
+
+    public Certificate engineGetCertificate(String alias)
+    {
+        synchronized (certificateEntries)
+        {
+            BCJKSTrustedCertEntry ent = certificateEntries.get(alias);
+            if (ent != null)
+            {
+                return ent.cert;
+            }
+        }
+        return null;
+    }
+
+    public Date engineGetCreationDate(String alias)
+    {
+        synchronized (certificateEntries)
+        {
+            BCJKSTrustedCertEntry ent = certificateEntries.get(alias);
+            if (ent != null)
+            {
+                return ent.date;
+            }
+        }
+        return null;
+    }
+
+    public void engineSetKeyEntry(String alias, Key key, char[] password, Certificate[] chain)
+        throws KeyStoreException
+    {
+        throw new KeyStoreException(NOT_IMPLEMENTED_MESSAGE);
+    }
+
+    public void engineSetKeyEntry(String alias, byte[] key, Certificate[] chain)
+        throws KeyStoreException
+    {
+        throw new KeyStoreException(NOT_IMPLEMENTED_MESSAGE);
+    }
+
+    public void engineSetCertificateEntry(String alias, Certificate cert)
+        throws KeyStoreException
+    {
+        throw new KeyStoreException(NOT_IMPLEMENTED_MESSAGE);
+    }
+
+    public void engineDeleteEntry(String alias)
+        throws KeyStoreException
+    {
+        throw new KeyStoreException(NOT_IMPLEMENTED_MESSAGE);
+    }
+
+    public Enumeration<String> engineAliases()
+    {
+        synchronized (certificateEntries)
+        {
+            return certificateEntries.keys();
+        }
+    }
+
+    public boolean engineContainsAlias(String alias)
+    {
+        if (alias == null)
+        {
+            throw new NullPointerException("alias value is null");
+        }
+
+        synchronized (certificateEntries)
+        {
+            return certificateEntries.containsKey(alias);
+        }
+    }
+
+    public int engineSize()
+    {
+        return certificateEntries.size();
+    }
+
+    public boolean engineIsKeyEntry(String alias)
+    {
+        return false;    // by definition
+    }
+
+    public boolean engineIsCertificateEntry(String alias)
+    {
+        synchronized (certificateEntries)
+        {
+            return certificateEntries.containsKey(alias);
+        }
+    }
+
+    public String engineGetCertificateAlias(Certificate cert)
+    {
+        synchronized (certificateEntries)
+        {
+            for (Iterator<Map.Entry<String, BCJKSTrustedCertEntry>> it = certificateEntries.entrySet().iterator(); it.hasNext(); )
+            {
+                Map.Entry<String, BCJKSTrustedCertEntry> entry = it.next();
+                if (entry.getValue().cert.equals(cert))
+                {
+                    return entry.getKey();
+                }
+            }
+            return null;
+        }
+    }
+
+    public void engineStore(OutputStream stream, char[] password)
+        throws IOException, NoSuchAlgorithmException, CertificateException
+    {
+        throw new IOException(NOT_IMPLEMENTED_MESSAGE);
+    }
+
+    public void engineLoad(KeyStore.LoadStoreParameter loadStoreParameter)
+        throws IOException, NoSuchAlgorithmException, CertificateException
+    {
+        if (loadStoreParameter == null)
+        {
+            engineLoad(null, null);
+        }
+        else if (loadStoreParameter instanceof BCLoadStoreParameter)
+        {
+            BCLoadStoreParameter bcParam = (BCLoadStoreParameter)loadStoreParameter;
+
+            engineLoad(bcParam.getInputStream(), ParameterUtil.extractPassword(loadStoreParameter));
+        }
+        else
+        {
+            throw new IllegalArgumentException(
+                "no support for 'param' of type " + loadStoreParameter.getClass().getName());
+        }
+    }
+
+    public void engineLoad(InputStream stream, char[] password)
+        throws IOException, NoSuchAlgorithmException, CertificateException
+    {
+        if (stream == null)
+        {
+            return;
+        }
+
+        ErasableByteStream storeStream = validateStream(stream, password);
+
+        synchronized (certificateEntries)
+        {
+            try
+            {
+                DataInputStream dIn = new DataInputStream(storeStream);
+
+                int magic = dIn.readInt();
+                int storeVersion = dIn.readInt();
+                if (magic == (int)0x0000feedfeedL)
+                {
+                    CertificateFactory certFact = null;
+                    Hashtable certFactories = null;
+
+                    switch (storeVersion)
+                    {
+                    case 1:  // all certs X.509
+                        certFact = createCertFactory("X.509");
+                        break;
+                    case 2:  // provision for format in store.
+                        certFactories = new Hashtable();
+                        break;
+                    default:
+                        throw new IllegalStateException("unable to discern store version");
+                    }
+
+                    int numEntries = dIn.readInt();
+                    for (int t = 0; t < numEntries; t++)
+                    {
+                        int tag = dIn.readInt();
+                        switch (tag)
+                        {
+                        case 1: // we can't process keys
+                            throw new IOException(NOT_IMPLEMENTED_MESSAGE);
+                        case 2: // certificate
+                            String alias = dIn.readUTF();
+                            Date date = new Date(dIn.readLong());
+
+                            if (storeVersion == 2)
+                            {
+                                String certFormat = dIn.readUTF();
+                                if (certFactories.containsKey(certFormat))
+                                {
+                                    certFact = (CertificateFactory)certFactories.get(certFormat);
+                                }
+                                else
+                                {
+                                    certFact = createCertFactory(certFormat);
+                                    certFactories.put(certFormat, certFact);
+                                }
+                            }
+
+                            int l = dIn.readInt();
+                            byte[] certData = new byte[l];
+                            dIn.readFully(certData);
+
+                            ErasableByteStream certStream = new ErasableByteStream(certData, 0, certData.length);
+                            Certificate cert;
+                            try
+                            {
+                                cert = certFact.generateCertificate(certStream);
+
+                                if (certStream.available() != 0)
+                                {
+                                    throw new IOException("password incorrect or store tampered with");
+                                }
+                            }
+                            finally
+                            {
+                                certStream.erase();
+                            }
+
+                            certificateEntries.put(alias, new BCJKSTrustedCertEntry(date, cert));
+                            break;
+                        default:
+                            throw new IllegalStateException("unable to discern entry type");
+                        }
+                    }
+                }
+
+                if (storeStream.available() != 0)
+                {
+                    throw new IOException("password incorrect or store tampered with");
+                }
+            }
+            finally
+            {
+                storeStream.erase();
+            }
+        }
+    }
+
+    private CertificateFactory createCertFactory(String certFormat)
+        throws CertificateException
+    {
+        if (helper != null)
+        {
+            try
+            {
+                return helper.createCertificateFactory(certFormat);
+            }
+            catch (NoSuchProviderException e)
+            {
+                throw new CertificateException(e.toString());
+            }
+        }
+        else
+        {
+            return CertificateFactory.getInstance(certFormat);
+        }
+    }
+
+    /**
+     * Process password updates the digest with the password.
+     *
+     * @param digest   The digest instance.
+     * @param password The password.
+     */
+    private void addPassword(Digest digest, char[] password)
+        throws IOException
+    {
+        for (int i = 0; i < password.length; ++i)
+        {
+            digest.update((byte)(password[i] >> 8));
+            digest.update((byte)password[i]);
+        }
+
+        //
+        // This "Mighty Aphrodite" string goes all the way back to the
+        // first java betas in the mid 90's, why who knows? But see
+        // https://cryptosense.com/mighty-aphrodite-dark-secrets-of-the-java-keystore/
+        //
+        digest.update(Strings.toByteArray("Mighty Aphrodite"), 0, 16);
+    }
+
+    /**
+     * Validate password takes the checksum of the store and will either.
+     * 1. If password is null, load the store into memory, return the result.
+     * 2. If password is not null, load the store into memory, test the checksum, and if successful return
+     * a new input stream instance of the store.
+     * 3. Fail if there is a password and an invalid checksum.
+     *
+     * @param inputStream The input stream.
+     * @param password    the password.
+     * @return Either the passed in input stream or a new input stream.
+     * @throws IOException
+     */
+    private ErasableByteStream validateStream(InputStream inputStream, char[] password)
+        throws IOException
+    {
+        Digest checksumCalculator = DigestFactory.getDigest("SHA-1");
+        byte[] rawStore = Streams.readAll(inputStream);
+
+        if (password != null)
+        {
+            addPassword(checksumCalculator, password);
+            checksumCalculator.update(rawStore, 0, rawStore.length - checksumCalculator.getDigestSize());
+
+            byte[] checksum = new byte[checksumCalculator.getDigestSize()];
+
+            checksumCalculator.doFinal(checksum, 0);
+
+            byte[] streamChecksum = new byte[checksum.length];
+            System.arraycopy(rawStore, rawStore.length - checksum.length, streamChecksum, 0, checksum.length);
+
+            if (!Arrays.constantTimeAreEqual(checksum, streamChecksum))
+            {
+                Arrays.fill(rawStore, (byte)0);
+                throw new IOException("password incorrect or store tampered with");
+            }
+
+            return new ErasableByteStream(rawStore, 0, rawStore.length - checksum.length);
+        }
+
+        return new ErasableByteStream(rawStore, 0, rawStore.length - checksumCalculator.getDigestSize());
+    }
+
+    /**
+     * BCJKSTrustedCertEntry is a internal container for the certificate entry.
+     */
+    private static final class BCJKSTrustedCertEntry
+    {
+        final Date date;
+        final Certificate cert;
+
+        public BCJKSTrustedCertEntry(Date date, Certificate cert)
+        {
+            this.date = date;
+            this.cert = cert;
+        }
+    }
+
+    private static final class ErasableByteStream
+        extends ByteArrayInputStream
+    {
+        public ErasableByteStream(byte[] buf, int offSet, int length)
+        {
+            super(buf, offSet, length);
+        }
+
+        public void erase()
+        {
+            // this will also erase the checksum from memory.
+            Arrays.fill(buf, (byte)0);
+        }
+    }
+
+
+}
+
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/keystore/util/ParameterUtil.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/keystore/util/ParameterUtil.java
new file mode 100644
index 0000000..7bf9e06
--- /dev/null
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/keystore/util/ParameterUtil.java
@@ -0,0 +1,53 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.org.bouncycastle.jcajce.provider.keystore.util;
+
+import java.io.IOException;
+import java.security.KeyStore;
+
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.callback.PasswordCallback;
+import javax.security.auth.callback.UnsupportedCallbackException;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+public class ParameterUtil
+{
+    public static char[] extractPassword(KeyStore.LoadStoreParameter bcParam)
+        throws IOException
+    {
+        KeyStore.ProtectionParameter protParam = bcParam.getProtectionParameter();
+
+        if (protParam == null)
+        {
+            return null;
+        }
+        else if (protParam instanceof KeyStore.PasswordProtection)
+        {
+            return ((KeyStore.PasswordProtection)protParam).getPassword();
+        }
+        else if (protParam instanceof KeyStore.CallbackHandlerProtection)
+        {
+            CallbackHandler handler = ((KeyStore.CallbackHandlerProtection)protParam).getCallbackHandler();
+
+            PasswordCallback passwordCallback = new PasswordCallback("password: ", false);
+
+            try
+            {
+                handler.handle(new Callback[]{passwordCallback});
+
+                return passwordCallback.getPassword();
+            }
+            catch (UnsupportedCallbackException e)
+            {
+                throw new IllegalArgumentException("PasswordCallback not recognised: " + e.getMessage(), e);
+            }
+        }
+        else
+        {
+            throw new IllegalArgumentException(
+                "no support for protection parameter of type " + protParam.getClass().getName());
+        }
+    }
+}
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/symmetric/AES.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/symmetric/AES.java
index 9684f35..7837d1c 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/symmetric/AES.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/symmetric/AES.java
@@ -19,16 +19,13 @@
 import javax.crypto.NoSuchPaddingException;
 // END Android-added: Needed for setting padding with GCM
 import com.android.org.bouncycastle.asn1.bc.BCObjectIdentifiers;
-// Android-removed: Unsupported algorithms
-// import org.bouncycastle.asn1.cms.CCMParameters;
-import com.android.org.bouncycastle.asn1.cms.GCMParameters;
 import com.android.org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
 import com.android.org.bouncycastle.crypto.BlockCipher;
-import com.android.org.bouncycastle.crypto.BufferedBlockCipher;
 import com.android.org.bouncycastle.crypto.CipherKeyGenerator;
 import com.android.org.bouncycastle.crypto.CipherParameters;
 import com.android.org.bouncycastle.crypto.CryptoServicesRegistrar;
 import com.android.org.bouncycastle.crypto.DataLengthException;
+import com.android.org.bouncycastle.crypto.DefaultBufferedBlockCipher;
 import com.android.org.bouncycastle.crypto.InvalidCipherTextException;
 import com.android.org.bouncycastle.crypto.Mac;
 import com.android.org.bouncycastle.crypto.engines.AESEngine;
@@ -43,9 +40,13 @@
 import com.android.org.bouncycastle.crypto.modes.CBCBlockCipher;
 // Android-removed: Unsupported algorithms
 // import org.bouncycastle.crypto.modes.CCMBlockCipher;
+// import org.bouncycastle.crypto.modes.CCMModeCipher;
 import com.android.org.bouncycastle.crypto.modes.CFBBlockCipher;
 import com.android.org.bouncycastle.crypto.modes.GCMBlockCipher;
 import com.android.org.bouncycastle.crypto.modes.OFBBlockCipher;
+// Android-removed: Unsupported algorithms
+// import org.bouncycastle.internal.asn1.cms.CCMParameters;
+import com.android.org.bouncycastle.internal.asn1.cms.GCMParameters;
 import com.android.org.bouncycastle.jcajce.provider.config.ConfigurableProvider;
 // Android-removed: Unsupported algorithms
 // import org.bouncycastle.jcajce.provider.symmetric.util.BaseAlgorithmParameterGenerator;
@@ -78,7 +79,7 @@
     private AES()
     {
     }
-    
+
     /**
      * @hide This class is not part of the Android public SDK API
      */
@@ -91,7 +92,7 @@
             {
                 public BlockCipher get()
                 {
-                    return new AESEngine();
+                    return AESEngine.newInstance();
                 }
             });
         }
@@ -101,11 +102,11 @@
      * @hide This class is not part of the Android public SDK API
      */
     public static class CBC
-       extends BaseBlockCipher
+        extends BaseBlockCipher
     {
         public CBC()
         {
-            super(new CBCBlockCipher(new AESEngine()), 128);
+            super(CBCBlockCipher.newInstance(AESEngine.newInstance()), 128);
         }
     }
 
@@ -117,7 +118,7 @@
     {
         public CFB()
         {
-            super(new BufferedBlockCipher(new CFBBlockCipher(new AESEngine(), 128)), 128);
+            super(new DefaultBufferedBlockCipher(CFBBlockCipher.newInstance(AESEngine.newInstance(), 128)), 128);
         }
     }
 
@@ -129,7 +130,7 @@
     {
         public OFB()
         {
-            super(new BufferedBlockCipher(new OFBBlockCipher(new AESEngine(), 128)), 128);
+            super(new DefaultBufferedBlockCipher(new OFBBlockCipher(AESEngine.newInstance(), 128)), 128);
         }
     }
 
@@ -161,7 +162,7 @@
     {
         public CCM()
         {
-            super(new CCMBlockCipher(new AESEngine()), false, 12);
+            super(CCMBlockCipher.newInstance(AESEngine.newInstance()), false, 12);
         }
     }
 
@@ -170,7 +171,7 @@
     {
         public AESCMAC()
         {
-            super(new CMac(new AESEngine()));
+            super(new CMac(AESEngine.newInstance()));
         }
     }
 
@@ -179,7 +180,7 @@
     {
         public AESGMAC()
         {
-            super(new GMac(new GCMBlockCipher(new AESEngine())));
+            super(new GMac(GCMBlockCipher.newInstance(AESEngine.newInstance())));
         }
     }
 
@@ -194,7 +195,7 @@
         private static class CCMMac
             implements Mac
         {
-            private final CCMBlockCipher ccm = new CCMBlockCipher(new AESEngine());
+            private final CCMModeCipher ccm = CCMBlockCipher.newInstance(AESEngine.newInstance());
 
             private int macLength = 8;
 
@@ -249,7 +250,7 @@
     }
 
     static public class KeyFactory
-         extends BaseSecretKeyFactory
+        extends BaseSecretKeyFactory
     {
         public KeyFactory()
         {
@@ -262,7 +263,7 @@
     {
         public Poly1305()
         {
-            super(new org.bouncycastle.crypto.macs.Poly1305(new AESEngine()));
+            super(new org.bouncycastle.crypto.macs.Poly1305(AESEngine.newInstance()));
         }
     }
 
@@ -305,7 +306,7 @@
     {
         public RFC3211Wrap()
         {
-            super(new RFC3211WrapEngine(new AESEngine()), 16);
+            super(new RFC3211WrapEngine(AESEngine.newInstance()), 16);
         }
     }
 
@@ -314,7 +315,7 @@
     {
         public RFC5649Wrap()
         {
-            super(new RFC5649WrapEngine(new AESEngine()));
+            super(new RFC5649WrapEngine(AESEngine.newInstance()));
         }
     }
     */
@@ -329,7 +330,7 @@
     {
         public PBEWithAESCBC()
         {
-            super(new CBCBlockCipher(new AESEngine()));
+            super(CBCBlockCipher.newInstance(AESEngine.newInstance()));
         }
     }
 
@@ -342,7 +343,7 @@
     {
         public PBEWithSHA1AESCBC128()
         {
-            super(new CBCBlockCipher(new AESEngine()), PKCS12, SHA1, 128, 16);
+            super(CBCBlockCipher.newInstance(AESEngine.newInstance()), PKCS12, SHA1, 128, 16);
         }
     }
 
@@ -354,7 +355,7 @@
     {
         public PBEWithSHA1AESCBC192()
         {
-            super(new CBCBlockCipher(new AESEngine()), PKCS12, SHA1, 192, 16);
+            super(CBCBlockCipher.newInstance(AESEngine.newInstance()), PKCS12, SHA1, 192, 16);
         }
     }
 
@@ -366,7 +367,7 @@
     {
         public PBEWithSHA1AESCBC256()
         {
-            super(new CBCBlockCipher(new AESEngine()), PKCS12, SHA1, 256, 16);
+            super(CBCBlockCipher.newInstance(AESEngine.newInstance()), PKCS12, SHA1, 256, 16);
         }
     }
 
@@ -379,7 +380,7 @@
     {
         public PBEWithSHA256AESCBC128()
         {
-            super(new CBCBlockCipher(new AESEngine()), PKCS12, SHA256, 128, 16);
+            super(CBCBlockCipher.newInstance(AESEngine.newInstance()), PKCS12, SHA256, 128, 16);
         }
     }
 
@@ -391,7 +392,7 @@
     {
         public PBEWithSHA256AESCBC192()
         {
-            super(new CBCBlockCipher(new AESEngine()), PKCS12, SHA256, 192, 16);
+            super(CBCBlockCipher.newInstance(AESEngine.newInstance()), PKCS12, SHA256, 192, 16);
         }
     }
 
@@ -403,7 +404,7 @@
     {
         public PBEWithSHA256AESCBC256()
         {
-            super(new CBCBlockCipher(new AESEngine()), PKCS12, SHA256, 256, 16);
+            super(CBCBlockCipher.newInstance(AESEngine.newInstance()), PKCS12, SHA256, 256, 16);
         }
     }
 
@@ -472,7 +473,7 @@
             super("PBEWithSHA1And128BitAES-CBC-BC", null, true, PKCS12, SHA1, 128, 128);
         }
     }
-    
+
     /**
      * PBEWithSHA1And192BitAES-BC
      * @hide This class is not part of the Android public SDK API
@@ -485,7 +486,7 @@
             super("PBEWithSHA1And192BitAES-CBC-BC", null, true, PKCS12, SHA1, 192, 128);
         }
     }
-    
+
     /**
      * PBEWithSHA1And256BitAES-BC
      * @hide This class is not part of the Android public SDK API
@@ -498,7 +499,7 @@
             super("PBEWithSHA1And256BitAES-CBC-BC", null, true, PKCS12, SHA1, 256, 128);
         }
     }
-    
+
     /**
      * PBEWithSHA256And128BitAES-BC
      * @hide This class is not part of the Android public SDK API
@@ -511,7 +512,7 @@
             super("PBEWithSHA256And128BitAES-CBC-BC", null, true, PKCS12, SHA256, 128, 128);
         }
     }
-    
+
     /**
      * PBEWithSHA256And192BitAES-BC
      * @hide This class is not part of the Android public SDK API
@@ -524,7 +525,7 @@
             super("PBEWithSHA256And192BitAES-CBC-BC", null, true, PKCS12, SHA256, 192, 128);
         }
     }
-    
+
     /**
      * PBEWithSHA256And256BitAES-BC
      * @hide This class is not part of the Android public SDK API
@@ -537,7 +538,7 @@
             super("PBEWithSHA256And256BitAES-CBC-BC", null, true, PKCS12, SHA256, 256, 128);
         }
     }
-    
+
     /**
      * PBEWithMD5And128BitAES-OpenSSL
      * @hide This class is not part of the Android public SDK API
@@ -550,7 +551,7 @@
             super("PBEWithMD5And128BitAES-CBC-OpenSSL", null, true, OPENSSL, MD5, 128, 128);
         }
     }
-    
+
     /**
      * PBEWithMD5And192BitAES-OpenSSL
      * @hide This class is not part of the Android public SDK API
@@ -563,7 +564,7 @@
             super("PBEWithMD5And192BitAES-CBC-OpenSSL", null, true, OPENSSL, MD5, 192, 128);
         }
     }
-    
+
     /**
      * PBEWithMD5And256BitAES-OpenSSL
      * @hide This class is not part of the Android public SDK API
@@ -592,7 +593,7 @@
 
         protected AlgorithmParameters engineGenerateParameters()
         {
-            byte[]  iv = new byte[16];
+            byte[] iv = new byte[16];
 
             if (random == null)
             {
@@ -631,7 +632,7 @@
 
         protected AlgorithmParameters engineGenerateParameters()
         {
-            byte[]  iv = new byte[12];
+            byte[] iv = new byte[12];
 
             if (random == null)
             {
@@ -670,7 +671,7 @@
 
         protected AlgorithmParameters engineGenerateParameters()
         {
-            byte[]  nonce = new byte[12];
+            byte[] nonce = new byte[12];
 
             if (random == null)
             {
@@ -778,7 +779,7 @@
         {
             if (paramSpec == AlgorithmParameterSpec.class || GcmSpecUtil.isGcmSpec(paramSpec))
             {
-                if (GcmSpecUtil.gcmSpecExists())
+                if (GcmSpecUtil.gcmSpecExtractable())
                 {
                     return GcmSpecUtil.extractGcmSpec(gcmParams.toASN1Primitive());
                 }
@@ -865,7 +866,7 @@
         {
             if (paramSpec == AlgorithmParameterSpec.class || GcmSpecUtil.isGcmSpec(paramSpec))
             {
-                if (GcmSpecUtil.gcmSpecExists())
+                if (GcmSpecUtil.gcmSpecExtractable())
                 {
                     return GcmSpecUtil.extractGcmSpec(ccmParams.toASN1Primitive());
                 }
@@ -893,7 +894,7 @@
         extends SymmetricAlgorithmProvider
     {
         private static final String PREFIX = AES.class.getName();
-        
+
         /**
          * These three got introduced in some messages as a result of a typo in an
          * early document. We don't produce anything using these OID values, but we'll
@@ -1055,31 +1056,31 @@
             provider.addAlgorithm("Cipher.PBEWITHSHA256AND128BITAES-CBC-BC", PREFIX + "$PBEWithSHA256AESCBC128");
             provider.addAlgorithm("Cipher.PBEWITHSHA256AND192BITAES-CBC-BC", PREFIX + "$PBEWithSHA256AESCBC192");
             provider.addAlgorithm("Cipher.PBEWITHSHA256AND256BITAES-CBC-BC", PREFIX + "$PBEWithSHA256AESCBC256");
-            
-            provider.addAlgorithm("Alg.Alias.Cipher.PBEWITHSHA1AND128BITAES-CBC-BC","PBEWITHSHAAND128BITAES-CBC-BC");
-            provider.addAlgorithm("Alg.Alias.Cipher.PBEWITHSHA1AND192BITAES-CBC-BC","PBEWITHSHAAND192BITAES-CBC-BC");
-            provider.addAlgorithm("Alg.Alias.Cipher.PBEWITHSHA1AND256BITAES-CBC-BC","PBEWITHSHAAND256BITAES-CBC-BC");
-            provider.addAlgorithm("Alg.Alias.Cipher.PBEWITHSHA-1AND128BITAES-CBC-BC","PBEWITHSHAAND128BITAES-CBC-BC");
-            provider.addAlgorithm("Alg.Alias.Cipher.PBEWITHSHA-1AND192BITAES-CBC-BC","PBEWITHSHAAND192BITAES-CBC-BC");
-            provider.addAlgorithm("Alg.Alias.Cipher.PBEWITHSHA-1AND256BITAES-CBC-BC","PBEWITHSHAAND256BITAES-CBC-BC");
-            provider.addAlgorithm("Alg.Alias.Cipher.PBEWITHSHAAND128BITAES-BC","PBEWITHSHAAND128BITAES-CBC-BC");
+
+            provider.addAlgorithm("Alg.Alias.Cipher.PBEWITHSHA1AND128BITAES-CBC-BC", "PBEWITHSHAAND128BITAES-CBC-BC");
+            provider.addAlgorithm("Alg.Alias.Cipher.PBEWITHSHA1AND192BITAES-CBC-BC", "PBEWITHSHAAND192BITAES-CBC-BC");
+            provider.addAlgorithm("Alg.Alias.Cipher.PBEWITHSHA1AND256BITAES-CBC-BC", "PBEWITHSHAAND256BITAES-CBC-BC");
+            provider.addAlgorithm("Alg.Alias.Cipher.PBEWITHSHA-1AND128BITAES-CBC-BC", "PBEWITHSHAAND128BITAES-CBC-BC");
+            provider.addAlgorithm("Alg.Alias.Cipher.PBEWITHSHA-1AND192BITAES-CBC-BC", "PBEWITHSHAAND192BITAES-CBC-BC");
+            provider.addAlgorithm("Alg.Alias.Cipher.PBEWITHSHA-1AND256BITAES-CBC-BC", "PBEWITHSHAAND256BITAES-CBC-BC");
+            provider.addAlgorithm("Alg.Alias.Cipher.PBEWITHSHAAND128BITAES-BC", "PBEWITHSHAAND128BITAES-CBC-BC");
             provider.addAlgorithm("Alg.Alias.Cipher.PBEWITHSHAAND192BITAES-BC", "PBEWITHSHAAND192BITAES-CBC-BC");
             provider.addAlgorithm("Alg.Alias.Cipher.PBEWITHSHAAND256BITAES-BC", "PBEWITHSHAAND256BITAES-CBC-BC");
-            provider.addAlgorithm("Alg.Alias.Cipher.PBEWITHSHA1AND128BITAES-BC","PBEWITHSHAAND128BITAES-CBC-BC");
-            provider.addAlgorithm("Alg.Alias.Cipher.PBEWITHSHA1AND192BITAES-BC","PBEWITHSHAAND192BITAES-CBC-BC");
-            provider.addAlgorithm("Alg.Alias.Cipher.PBEWITHSHA1AND256BITAES-BC","PBEWITHSHAAND256BITAES-CBC-BC");
-            provider.addAlgorithm("Alg.Alias.Cipher.PBEWITHSHA-1AND128BITAES-BC","PBEWITHSHAAND128BITAES-CBC-BC");
-            provider.addAlgorithm("Alg.Alias.Cipher.PBEWITHSHA-1AND192BITAES-BC","PBEWITHSHAAND192BITAES-CBC-BC");
-            provider.addAlgorithm("Alg.Alias.Cipher.PBEWITHSHA-1AND256BITAES-BC","PBEWITHSHAAND256BITAES-CBC-BC");
-            provider.addAlgorithm("Alg.Alias.Cipher.PBEWITHSHA-256AND128BITAES-CBC-BC","PBEWITHSHA256AND128BITAES-CBC-BC");
-            provider.addAlgorithm("Alg.Alias.Cipher.PBEWITHSHA-256AND192BITAES-CBC-BC","PBEWITHSHA256AND192BITAES-CBC-BC");
-            provider.addAlgorithm("Alg.Alias.Cipher.PBEWITHSHA-256AND256BITAES-CBC-BC","PBEWITHSHA256AND256BITAES-CBC-BC");
-            provider.addAlgorithm("Alg.Alias.Cipher.PBEWITHSHA256AND128BITAES-BC","PBEWITHSHA256AND128BITAES-CBC-BC");
-            provider.addAlgorithm("Alg.Alias.Cipher.PBEWITHSHA256AND192BITAES-BC","PBEWITHSHA256AND192BITAES-CBC-BC");
-            provider.addAlgorithm("Alg.Alias.Cipher.PBEWITHSHA256AND256BITAES-BC","PBEWITHSHA256AND256BITAES-CBC-BC");
-            provider.addAlgorithm("Alg.Alias.Cipher.PBEWITHSHA-256AND128BITAES-BC","PBEWITHSHA256AND128BITAES-CBC-BC");
-            provider.addAlgorithm("Alg.Alias.Cipher.PBEWITHSHA-256AND192BITAES-BC","PBEWITHSHA256AND192BITAES-CBC-BC");
-            provider.addAlgorithm("Alg.Alias.Cipher.PBEWITHSHA-256AND256BITAES-BC","PBEWITHSHA256AND256BITAES-CBC-BC");
+            provider.addAlgorithm("Alg.Alias.Cipher.PBEWITHSHA1AND128BITAES-BC", "PBEWITHSHAAND128BITAES-CBC-BC");
+            provider.addAlgorithm("Alg.Alias.Cipher.PBEWITHSHA1AND192BITAES-BC", "PBEWITHSHAAND192BITAES-CBC-BC");
+            provider.addAlgorithm("Alg.Alias.Cipher.PBEWITHSHA1AND256BITAES-BC", "PBEWITHSHAAND256BITAES-CBC-BC");
+            provider.addAlgorithm("Alg.Alias.Cipher.PBEWITHSHA-1AND128BITAES-BC", "PBEWITHSHAAND128BITAES-CBC-BC");
+            provider.addAlgorithm("Alg.Alias.Cipher.PBEWITHSHA-1AND192BITAES-BC", "PBEWITHSHAAND192BITAES-CBC-BC");
+            provider.addAlgorithm("Alg.Alias.Cipher.PBEWITHSHA-1AND256BITAES-BC", "PBEWITHSHAAND256BITAES-CBC-BC");
+            provider.addAlgorithm("Alg.Alias.Cipher.PBEWITHSHA-256AND128BITAES-CBC-BC", "PBEWITHSHA256AND128BITAES-CBC-BC");
+            provider.addAlgorithm("Alg.Alias.Cipher.PBEWITHSHA-256AND192BITAES-CBC-BC", "PBEWITHSHA256AND192BITAES-CBC-BC");
+            provider.addAlgorithm("Alg.Alias.Cipher.PBEWITHSHA-256AND256BITAES-CBC-BC", "PBEWITHSHA256AND256BITAES-CBC-BC");
+            provider.addAlgorithm("Alg.Alias.Cipher.PBEWITHSHA256AND128BITAES-BC", "PBEWITHSHA256AND128BITAES-CBC-BC");
+            provider.addAlgorithm("Alg.Alias.Cipher.PBEWITHSHA256AND192BITAES-BC", "PBEWITHSHA256AND192BITAES-CBC-BC");
+            provider.addAlgorithm("Alg.Alias.Cipher.PBEWITHSHA256AND256BITAES-BC", "PBEWITHSHA256AND256BITAES-CBC-BC");
+            provider.addAlgorithm("Alg.Alias.Cipher.PBEWITHSHA-256AND128BITAES-BC", "PBEWITHSHA256AND128BITAES-CBC-BC");
+            provider.addAlgorithm("Alg.Alias.Cipher.PBEWITHSHA-256AND192BITAES-BC", "PBEWITHSHA256AND192BITAES-CBC-BC");
+            provider.addAlgorithm("Alg.Alias.Cipher.PBEWITHSHA-256AND256BITAES-BC", "PBEWITHSHA256AND256BITAES-CBC-BC");
 
             provider.addAlgorithm("Cipher.PBEWITHMD5AND128BITAES-CBC-OPENSSL", PREFIX + "$PBEWithAESCBC");
             provider.addAlgorithm("Cipher.PBEWITHMD5AND192BITAES-CBC-OPENSSL", PREFIX + "$PBEWithAESCBC");
@@ -1093,48 +1094,48 @@
             provider.addAlgorithm("SecretKeyFactory.PBEWITHMD5AND128BITAES-CBC-OPENSSL", PREFIX + "$PBEWithMD5And128BitAESCBCOpenSSL");
             provider.addAlgorithm("SecretKeyFactory.PBEWITHMD5AND192BITAES-CBC-OPENSSL", PREFIX + "$PBEWithMD5And192BitAESCBCOpenSSL");
             provider.addAlgorithm("SecretKeyFactory.PBEWITHMD5AND256BITAES-CBC-OPENSSL", PREFIX + "$PBEWithMD5And256BitAESCBCOpenSSL");
-            
+
             provider.addAlgorithm("SecretKeyFactory.PBEWITHSHAAND128BITAES-CBC-BC", PREFIX + "$PBEWithSHAAnd128BitAESBC");
             provider.addAlgorithm("SecretKeyFactory.PBEWITHSHAAND192BITAES-CBC-BC", PREFIX + "$PBEWithSHAAnd192BitAESBC");
             provider.addAlgorithm("SecretKeyFactory.PBEWITHSHAAND256BITAES-CBC-BC", PREFIX + "$PBEWithSHAAnd256BitAESBC");
             provider.addAlgorithm("SecretKeyFactory.PBEWITHSHA256AND128BITAES-CBC-BC", PREFIX + "$PBEWithSHA256And128BitAESBC");
             provider.addAlgorithm("SecretKeyFactory.PBEWITHSHA256AND192BITAES-CBC-BC", PREFIX + "$PBEWithSHA256And192BitAESBC");
             provider.addAlgorithm("SecretKeyFactory.PBEWITHSHA256AND256BITAES-CBC-BC", PREFIX + "$PBEWithSHA256And256BitAESBC");
-            provider.addAlgorithm("Alg.Alias.SecretKeyFactory.PBEWITHSHA1AND128BITAES-CBC-BC","PBEWITHSHAAND128BITAES-CBC-BC");
-            provider.addAlgorithm("Alg.Alias.SecretKeyFactory.PBEWITHSHA1AND192BITAES-CBC-BC","PBEWITHSHAAND192BITAES-CBC-BC");
-            provider.addAlgorithm("Alg.Alias.SecretKeyFactory.PBEWITHSHA1AND256BITAES-CBC-BC","PBEWITHSHAAND256BITAES-CBC-BC");
-            provider.addAlgorithm("Alg.Alias.SecretKeyFactory.PBEWITHSHA-1AND128BITAES-CBC-BC","PBEWITHSHAAND128BITAES-CBC-BC");
-            provider.addAlgorithm("Alg.Alias.SecretKeyFactory.PBEWITHSHA-1AND192BITAES-CBC-BC","PBEWITHSHAAND192BITAES-CBC-BC");
-            provider.addAlgorithm("Alg.Alias.SecretKeyFactory.PBEWITHSHA-1AND256BITAES-CBC-BC","PBEWITHSHAAND256BITAES-CBC-BC");
-            provider.addAlgorithm("Alg.Alias.SecretKeyFactory.PBEWITHSHA-256AND128BITAES-CBC-BC","PBEWITHSHA256AND128BITAES-CBC-BC");
-            provider.addAlgorithm("Alg.Alias.SecretKeyFactory.PBEWITHSHA-256AND192BITAES-CBC-BC","PBEWITHSHA256AND192BITAES-CBC-BC");
-            provider.addAlgorithm("Alg.Alias.SecretKeyFactory.PBEWITHSHA-256AND256BITAES-CBC-BC","PBEWITHSHA256AND256BITAES-CBC-BC");
-            provider.addAlgorithm("Alg.Alias.SecretKeyFactory.PBEWITHSHA-256AND128BITAES-BC","PBEWITHSHA256AND128BITAES-CBC-BC");
-            provider.addAlgorithm("Alg.Alias.SecretKeyFactory.PBEWITHSHA-256AND192BITAES-BC","PBEWITHSHA256AND192BITAES-CBC-BC");
-            provider.addAlgorithm("Alg.Alias.SecretKeyFactory.PBEWITHSHA-256AND256BITAES-BC","PBEWITHSHA256AND256BITAES-CBC-BC");
+            provider.addAlgorithm("Alg.Alias.SecretKeyFactory.PBEWITHSHA1AND128BITAES-CBC-BC", "PBEWITHSHAAND128BITAES-CBC-BC");
+            provider.addAlgorithm("Alg.Alias.SecretKeyFactory.PBEWITHSHA1AND192BITAES-CBC-BC", "PBEWITHSHAAND192BITAES-CBC-BC");
+            provider.addAlgorithm("Alg.Alias.SecretKeyFactory.PBEWITHSHA1AND256BITAES-CBC-BC", "PBEWITHSHAAND256BITAES-CBC-BC");
+            provider.addAlgorithm("Alg.Alias.SecretKeyFactory.PBEWITHSHA-1AND128BITAES-CBC-BC", "PBEWITHSHAAND128BITAES-CBC-BC");
+            provider.addAlgorithm("Alg.Alias.SecretKeyFactory.PBEWITHSHA-1AND192BITAES-CBC-BC", "PBEWITHSHAAND192BITAES-CBC-BC");
+            provider.addAlgorithm("Alg.Alias.SecretKeyFactory.PBEWITHSHA-1AND256BITAES-CBC-BC", "PBEWITHSHAAND256BITAES-CBC-BC");
+            provider.addAlgorithm("Alg.Alias.SecretKeyFactory.PBEWITHSHA-256AND128BITAES-CBC-BC", "PBEWITHSHA256AND128BITAES-CBC-BC");
+            provider.addAlgorithm("Alg.Alias.SecretKeyFactory.PBEWITHSHA-256AND192BITAES-CBC-BC", "PBEWITHSHA256AND192BITAES-CBC-BC");
+            provider.addAlgorithm("Alg.Alias.SecretKeyFactory.PBEWITHSHA-256AND256BITAES-CBC-BC", "PBEWITHSHA256AND256BITAES-CBC-BC");
+            provider.addAlgorithm("Alg.Alias.SecretKeyFactory.PBEWITHSHA-256AND128BITAES-BC", "PBEWITHSHA256AND128BITAES-CBC-BC");
+            provider.addAlgorithm("Alg.Alias.SecretKeyFactory.PBEWITHSHA-256AND192BITAES-BC", "PBEWITHSHA256AND192BITAES-CBC-BC");
+            provider.addAlgorithm("Alg.Alias.SecretKeyFactory.PBEWITHSHA-256AND256BITAES-BC", "PBEWITHSHA256AND256BITAES-CBC-BC");
             provider.addAlgorithm("Alg.Alias.SecretKeyFactory", BCObjectIdentifiers.bc_pbe_sha1_pkcs12_aes128_cbc, "PBEWITHSHAAND128BITAES-CBC-BC");
             provider.addAlgorithm("Alg.Alias.SecretKeyFactory", BCObjectIdentifiers.bc_pbe_sha1_pkcs12_aes192_cbc, "PBEWITHSHAAND192BITAES-CBC-BC");
             provider.addAlgorithm("Alg.Alias.SecretKeyFactory", BCObjectIdentifiers.bc_pbe_sha1_pkcs12_aes256_cbc, "PBEWITHSHAAND256BITAES-CBC-BC");
             provider.addAlgorithm("Alg.Alias.SecretKeyFactory", BCObjectIdentifiers.bc_pbe_sha256_pkcs12_aes128_cbc, "PBEWITHSHA256AND128BITAES-CBC-BC");
             provider.addAlgorithm("Alg.Alias.SecretKeyFactory", BCObjectIdentifiers.bc_pbe_sha256_pkcs12_aes192_cbc, "PBEWITHSHA256AND192BITAES-CBC-BC");
             provider.addAlgorithm("Alg.Alias.SecretKeyFactory", BCObjectIdentifiers.bc_pbe_sha256_pkcs12_aes256_cbc, "PBEWITHSHA256AND256BITAES-CBC-BC");
-            
+
             provider.addAlgorithm("Alg.Alias.AlgorithmParameters.PBEWITHSHAAND128BITAES-CBC-BC", "PKCS12PBE");
             provider.addAlgorithm("Alg.Alias.AlgorithmParameters.PBEWITHSHAAND192BITAES-CBC-BC", "PKCS12PBE");
             provider.addAlgorithm("Alg.Alias.AlgorithmParameters.PBEWITHSHAAND256BITAES-CBC-BC", "PKCS12PBE");
             provider.addAlgorithm("Alg.Alias.AlgorithmParameters.PBEWITHSHA256AND128BITAES-CBC-BC", "PKCS12PBE");
             provider.addAlgorithm("Alg.Alias.AlgorithmParameters.PBEWITHSHA256AND192BITAES-CBC-BC", "PKCS12PBE");
             provider.addAlgorithm("Alg.Alias.AlgorithmParameters.PBEWITHSHA256AND256BITAES-CBC-BC", "PKCS12PBE");
-            provider.addAlgorithm("Alg.Alias.AlgorithmParameters.PBEWITHSHA1AND128BITAES-CBC-BC","PKCS12PBE");
-            provider.addAlgorithm("Alg.Alias.AlgorithmParameters.PBEWITHSHA1AND192BITAES-CBC-BC","PKCS12PBE");
-            provider.addAlgorithm("Alg.Alias.AlgorithmParameters.PBEWITHSHA1AND256BITAES-CBC-BC","PKCS12PBE");
-            provider.addAlgorithm("Alg.Alias.AlgorithmParameters.PBEWITHSHA-1AND128BITAES-CBC-BC","PKCS12PBE");
-            provider.addAlgorithm("Alg.Alias.AlgorithmParameters.PBEWITHSHA-1AND192BITAES-CBC-BC","PKCS12PBE");
-            provider.addAlgorithm("Alg.Alias.AlgorithmParameters.PBEWITHSHA-1AND256BITAES-CBC-BC","PKCS12PBE");
-            provider.addAlgorithm("Alg.Alias.AlgorithmParameters.PBEWITHSHA-256AND128BITAES-CBC-BC","PKCS12PBE");
-            provider.addAlgorithm("Alg.Alias.AlgorithmParameters.PBEWITHSHA-256AND192BITAES-CBC-BC","PKCS12PBE");
-            provider.addAlgorithm("Alg.Alias.AlgorithmParameters.PBEWITHSHA-256AND256BITAES-CBC-BC","PKCS12PBE"); 
-            
+            provider.addAlgorithm("Alg.Alias.AlgorithmParameters.PBEWITHSHA1AND128BITAES-CBC-BC", "PKCS12PBE");
+            provider.addAlgorithm("Alg.Alias.AlgorithmParameters.PBEWITHSHA1AND192BITAES-CBC-BC", "PKCS12PBE");
+            provider.addAlgorithm("Alg.Alias.AlgorithmParameters.PBEWITHSHA1AND256BITAES-CBC-BC", "PKCS12PBE");
+            provider.addAlgorithm("Alg.Alias.AlgorithmParameters.PBEWITHSHA-1AND128BITAES-CBC-BC", "PKCS12PBE");
+            provider.addAlgorithm("Alg.Alias.AlgorithmParameters.PBEWITHSHA-1AND192BITAES-CBC-BC", "PKCS12PBE");
+            provider.addAlgorithm("Alg.Alias.AlgorithmParameters.PBEWITHSHA-1AND256BITAES-CBC-BC", "PKCS12PBE");
+            provider.addAlgorithm("Alg.Alias.AlgorithmParameters.PBEWITHSHA-256AND128BITAES-CBC-BC", "PKCS12PBE");
+            provider.addAlgorithm("Alg.Alias.AlgorithmParameters.PBEWITHSHA-256AND192BITAES-CBC-BC", "PKCS12PBE");
+            provider.addAlgorithm("Alg.Alias.AlgorithmParameters.PBEWITHSHA-256AND256BITAES-CBC-BC", "PKCS12PBE");
+
             provider.addAlgorithm("Alg.Alias.AlgorithmParameters." + BCObjectIdentifiers.bc_pbe_sha1_pkcs12_aes128_cbc.getId(), "PKCS12PBE");
             provider.addAlgorithm("Alg.Alias.AlgorithmParameters." + BCObjectIdentifiers.bc_pbe_sha1_pkcs12_aes192_cbc.getId(), "PKCS12PBE");
             provider.addAlgorithm("Alg.Alias.AlgorithmParameters." + BCObjectIdentifiers.bc_pbe_sha1_pkcs12_aes256_cbc.getId(), "PKCS12PBE");
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/symmetric/GcmSpecUtil.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/symmetric/GcmSpecUtil.java
deleted file mode 100644
index a29228d..0000000
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/symmetric/GcmSpecUtil.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/* GENERATED SOURCE. DO NOT MODIFY. */
-package com.android.org.bouncycastle.jcajce.provider.symmetric;
-
-import java.lang.reflect.Constructor;
-import java.lang.reflect.Method;
-import java.security.spec.AlgorithmParameterSpec;
-import java.security.spec.InvalidParameterSpecException;
-
-import com.android.org.bouncycastle.asn1.ASN1Primitive;
-import com.android.org.bouncycastle.asn1.cms.GCMParameters;
-import com.android.org.bouncycastle.jcajce.provider.symmetric.util.ClassUtil;
-import com.android.org.bouncycastle.util.Integers;
-
-class GcmSpecUtil
-{
-    static final Class gcmSpecClass = ClassUtil.loadClass(GcmSpecUtil.class, "javax.crypto.spec.GCMParameterSpec");
-
-    static boolean gcmSpecExists()
-    {
-        return gcmSpecClass != null;
-    }
-
-    static boolean isGcmSpec(AlgorithmParameterSpec paramSpec)
-    {
-        return gcmSpecClass != null && gcmSpecClass.isInstance(paramSpec);
-    }
-
-    static boolean isGcmSpec(Class paramSpecClass)
-    {
-        return gcmSpecClass == paramSpecClass;
-    }
-
-    static AlgorithmParameterSpec extractGcmSpec(ASN1Primitive spec)
-        throws InvalidParameterSpecException
-    {
-        try
-        {
-            GCMParameters gcmParams = GCMParameters.getInstance(spec);
-            Constructor constructor = gcmSpecClass.getConstructor(new Class[]{Integer.TYPE, byte[].class});
-
-            return (AlgorithmParameterSpec)constructor.newInstance(new Object[] { Integers.valueOf(gcmParams.getIcvLen() * 8), gcmParams.getNonce() });
-        }
-        catch (NoSuchMethodException e)
-        {
-            throw new InvalidParameterSpecException("No constructor found!");   // should never happen
-        }
-        catch (Exception e)
-        {
-            throw new InvalidParameterSpecException("Construction failed: " + e.getMessage());   // should never happen
-        }
-    }
-
-    static GCMParameters extractGcmParameters(AlgorithmParameterSpec paramSpec)
-        throws InvalidParameterSpecException
-    {
-        try
-        {
-            Method tLen = gcmSpecClass.getDeclaredMethod("getTLen", new Class[0]);
-            Method iv= gcmSpecClass.getDeclaredMethod("getIV", new Class[0]);
-
-            return new GCMParameters((byte[])iv.invoke(paramSpec, new Object[0]), ((Integer)tLen.invoke(paramSpec, new Object[0])).intValue() / 8);
-        }
-        catch (Exception e)
-        {
-            throw new InvalidParameterSpecException("Cannot process GCMParameterSpec");
-        }
-    }
-}
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/symmetric/PBEPBKDF2.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/symmetric/PBEPBKDF2.java
index 15880ad..f0467a1 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/symmetric/PBEPBKDF2.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/symmetric/PBEPBKDF2.java
@@ -621,6 +621,15 @@
             super("PBKDF2", PKCS5S2_UTF8, SM3);
         }
     }
+
+    public static class PBKDF2withSM3
+        extends BasePBKDF2
+    {
+        public PBKDF2withSM3()
+        {
+            super("PBKDF2", PKCS5S2_UTF8, SM3);
+        }
+    }
     */
     // END Android-removed: Unsupported algorithms
 
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/symmetric/util/BCPBEKey.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/symmetric/util/BCPBEKey.java
index 8013761..b3f7300 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/symmetric/util/BCPBEKey.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/symmetric/util/BCPBEKey.java
@@ -73,19 +73,23 @@
 
     public String getAlgorithm()
     {
+        String rv = this.algorithm;
+
         checkDestroyed(this);
 
-        return algorithm;
+        return rv;
     }
 
     public String getFormat()
     {
+        checkDestroyed(this);
+
         return "RAW";
     }
 
     public byte[] getEncoded()
     {
-        checkDestroyed(this);
+        byte[] enc;
 
         if (param != null)
         {
@@ -100,58 +104,72 @@
                 kParam = (KeyParameter)param;
             }
             
-            return kParam.getKey();
+            enc = kParam.getKey();
         }
         else
         {
             if (type == PBE.PKCS12)
             {
-                return PBEParametersGenerator.PKCS12PasswordToBytes(password);
+                enc = PBEParametersGenerator.PKCS12PasswordToBytes(password);
             }
             else if (type == PBE.PKCS5S2_UTF8)
             {
-                return PBEParametersGenerator.PKCS5PasswordToUTF8Bytes(password);
+                enc = PBEParametersGenerator.PKCS5PasswordToUTF8Bytes(password);
             }
             else
             {   
-                return PBEParametersGenerator.PKCS5PasswordToBytes(password);
+                enc = PBEParametersGenerator.PKCS5PasswordToBytes(password);
             }
         }
+
+        checkDestroyed(this);
+
+        return enc;
     }
     
     int getType()
     {
+        int rv = type;
+
         checkDestroyed(this);
 
-        return type;
+        return rv;
     }
     
     int getDigest()
     {
+        int rv = digest;
+
         checkDestroyed(this);
 
-        return digest;
+        return rv;
     }
     
     int getKeySize()
     {
+        int rv = keySize;
+
         checkDestroyed(this);
 
-        return keySize;
+        return rv;
     }
     
     public int getIvSize()
     {
+        int rv = ivSize;
+
         checkDestroyed(this);
 
-        return ivSize;
+        return rv;
     }
     
     public CipherParameters getParam()
     {
+        CipherParameters rv = param;
+
         checkDestroyed(this);
 
-        return param;
+        return rv;
     }
 
     /* (non-Javadoc)
@@ -159,14 +177,16 @@
      */
     public char[] getPassword()
     {
+        char[] clone = Arrays.clone(password);
+
         checkDestroyed(this);
 
-        if (password == null)
+        if (clone == null)
         {
             throw new IllegalStateException("no password available");
         }
 
-        return Arrays.clone(password);
+        return clone;
     }
 
     /* (non-Javadoc)
@@ -174,9 +194,11 @@
      */
     public byte[] getSalt()
     {
+        byte[] clone = Arrays.clone(salt);
+
         checkDestroyed(this);
 
-        return Arrays.clone(salt);
+        return clone;
     }
 
     /* (non-Javadoc)
@@ -184,16 +206,20 @@
      */
     public int getIterationCount()
     {
+        int rv = iterationCount;
+
         checkDestroyed(this);
 
-        return iterationCount;
+        return rv;
     }
     
     public ASN1ObjectIdentifier getOID()
     {
+        ASN1ObjectIdentifier rv = oid;
+
         checkDestroyed(this);
 
-        return oid;
+        return rv;
     }
     
     public void setTryWrongPKCS12Zero(boolean tryWrong)
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/symmetric/util/BaseBlockCipher.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/symmetric/util/BaseBlockCipher.java
index 51310cb..bf16ee9 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/symmetric/util/BaseBlockCipher.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/symmetric/util/BaseBlockCipher.java
@@ -29,17 +29,20 @@
 // import javax.crypto.spec.RC5ParameterSpec;
 
 import com.android.org.bouncycastle.asn1.DEROctetString;
-import com.android.org.bouncycastle.asn1.cms.GCMParameters;
 import com.android.org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
 import com.android.org.bouncycastle.crypto.BlockCipher;
 import com.android.org.bouncycastle.crypto.BufferedBlockCipher;
 import com.android.org.bouncycastle.crypto.CipherParameters;
 import com.android.org.bouncycastle.crypto.CryptoServicesRegistrar;
 import com.android.org.bouncycastle.crypto.DataLengthException;
+import com.android.org.bouncycastle.crypto.DefaultBufferedBlockCipher;
 import com.android.org.bouncycastle.crypto.InvalidCipherTextException;
 import com.android.org.bouncycastle.crypto.OutputLengthException;
 // Android-removed: Unsupported algorithms
 // import org.bouncycastle.crypto.engines.DSTU7624Engine;
+// import org.bouncycastle.crypto.fpe.FPEEngine;
+// import org.bouncycastle.crypto.fpe.FPEFF1Engine;
+// import org.bouncycastle.crypto.fpe.FPEFF3_1Engine;
 import com.android.org.bouncycastle.crypto.modes.AEADBlockCipher;
 import com.android.org.bouncycastle.crypto.modes.AEADCipher;
 import com.android.org.bouncycastle.crypto.modes.CBCBlockCipher;
@@ -51,6 +54,7 @@
 // import org.bouncycastle.crypto.modes.GCFBBlockCipher;
 import com.android.org.bouncycastle.crypto.modes.GCMBlockCipher;
 // Android-removed: Unsupported algorithms
+// import org.bouncycastle.crypto.modes.GCMSIVBlockCipher;
 // import org.bouncycastle.crypto.modes.GOFBBlockCipher;
 // import org.bouncycastle.crypto.modes.KCCMBlockCipher;
 // import org.bouncycastle.crypto.modes.KCTRBlockCipher;
@@ -60,6 +64,8 @@
 // Android-removed: Unsupported algorithms
 // import org.bouncycastle.crypto.modes.OpenPGPCFBBlockCipher;
 // import org.bouncycastle.crypto.modes.PGPCFBBlockCipher;
+import com.android.org.bouncycastle.crypto.paddings.PKCS7Padding;
+// import org.bouncycastle.crypto.params.FPEParameters;
 import com.android.org.bouncycastle.crypto.modes.SICBlockCipher;
 import com.android.org.bouncycastle.crypto.paddings.BlockCipherPadding;
 import com.android.org.bouncycastle.crypto.paddings.ISO10126d2Padding;
@@ -85,6 +91,8 @@
 // Android-removed: Unsupported algorithms
 // import org.bouncycastle.jcajce.spec.GOST28147ParameterSpec;
 // import org.bouncycastle.jcajce.spec.RepeatedSecretKeySpec;
+import com.android.org.bouncycastle.internal.asn1.cms.GCMParameters;
+// import org.bouncycastle.jcajce.spec.FPEParameterSpec;
 import com.android.org.bouncycastle.util.Arrays;
 import com.android.org.bouncycastle.util.Strings;
 
@@ -96,7 +104,6 @@
     implements PBE
 {
     private static final int BUF_SIZE = 512;
-    private static final Class gcmSpecClass = ClassUtil.loadClass(BaseBlockCipher.class, "javax.crypto.spec.GCMParameterSpec");
 
     //
     // specs we can handle.
@@ -106,7 +113,7 @@
             // Android-removed: Unsupported alhorithms
             // RC2ParameterSpec.class,
             // RC5ParameterSpec.class,
-            gcmSpecClass,
+            GcmSpecUtil.gcmSpecClass,
             // Android-removed: unsupported algorithms
             // GOST28147ParameterSpec.class,
             IvParameterSpec.class,
@@ -170,7 +177,14 @@
         AEADBlockCipher engine)
     {
         this.baseEngine = engine.getUnderlyingCipher();
-        this.ivLength = baseEngine.getBlockSize();
+        if (engine.getAlgorithmName().indexOf("GCM") >= 0)
+        {
+            this.ivLength = 12;
+        }
+        else
+        {
+            this.ivLength = baseEngine.getBlockSize();
+        }
         this.cipher = new AEADGenericBlockCipher(engine);
     }
 
@@ -352,7 +366,7 @@
         {
             ivLength = baseEngine.getBlockSize();
             cipher = new BufferedGenericBlockCipher(
-                new CBCBlockCipher(baseEngine));
+                CBCBlockCipher.newInstance(baseEngine));
         }
         else if (modeName.startsWith("OFB"))
         {
@@ -378,12 +392,12 @@
                 int wordSize = Integer.parseInt(modeName.substring(3));
 
                 cipher = new BufferedGenericBlockCipher(
-                    new CFBBlockCipher(baseEngine, wordSize));
+                    CFBBlockCipher.newInstance(baseEngine, wordSize));
             }
             else
             {
                 cipher = new BufferedGenericBlockCipher(
-                    new CFBBlockCipher(baseEngine, 8 * baseEngine.getBlockSize()));
+                    CFBBlockCipher.newInstance(baseEngine, 8 * baseEngine.getBlockSize()));
             }
         }
         // BEGIN Android-removed: Unsupported modes
@@ -407,6 +421,18 @@
             cipher = new BufferedGenericBlockCipher(
                 new OpenPGPCFBBlockCipher(baseEngine));
         }
+        else if (modeName.equals("FF1"))
+        {
+            ivLength = 0;
+            cipher = new BufferedFPEBlockCipher(
+                new FPEFF1Engine(baseEngine));
+        }
+        else if (modeName.equals("FF3-1"))
+        {
+            ivLength = 0;
+            cipher = new BufferedFPEBlockCipher(
+                new FPEFF3_1Engine(baseEngine));
+        }
         else if (modeName.equals("SIC"))
         {
             ivLength = baseEngine.getBlockSize();
@@ -415,8 +441,8 @@
                 throw new IllegalArgumentException("Warning: SIC-Mode can become a twotime-pad if the blocksize of the cipher is too small. Use a cipher with a block size of at least 128 bits (e.g. AES)");
             }
             fixedIv = false;
-            cipher = new BufferedGenericBlockCipher(new BufferedBlockCipher(
-                new SICBlockCipher(baseEngine)));
+            cipher = new BufferedGenericBlockCipher(new DefaultBufferedBlockCipher(
+                SICBlockCipher.newInstance(baseEngine)));
         }
         */
         // END Android-removed: Unsupported modes
@@ -428,14 +454,14 @@
             /*
             if (baseEngine instanceof DSTU7624Engine)
             {
-                cipher = new BufferedGenericBlockCipher(new BufferedBlockCipher(
+                cipher = new BufferedGenericBlockCipher(new DefaultBufferedBlockCipher(
                     new KCTRBlockCipher(baseEngine)));
             }
             else
             {
             */
-                cipher = new BufferedGenericBlockCipher(new BufferedBlockCipher(
-                    new SICBlockCipher(baseEngine)));
+                cipher = new BufferedGenericBlockCipher(new DefaultBufferedBlockCipher(
+                    SICBlockCipher.newInstance(baseEngine)));
             /*
             }
             */
@@ -445,13 +471,13 @@
         else if (modeName.equals("GOFB"))
         {
             ivLength = baseEngine.getBlockSize();
-            cipher = new BufferedGenericBlockCipher(new BufferedBlockCipher(
+            cipher = new BufferedGenericBlockCipher(new DefaultBufferedBlockCipher(
                 new GOFBBlockCipher(baseEngine)));
         }
         else if (modeName.equals("GCFB"))
         {
             ivLength = baseEngine.getBlockSize();
-            cipher = new BufferedGenericBlockCipher(new BufferedBlockCipher(
+            cipher = new BufferedGenericBlockCipher(new DefaultBufferedBlockCipher(
                 new GCFBBlockCipher(baseEngine)));
         }
         */
@@ -459,7 +485,7 @@
         else if (modeName.equals("CTS"))
         {
             ivLength = baseEngine.getBlockSize();
-            cipher = new BufferedGenericBlockCipher(new CTSBlockCipher(new CBCBlockCipher(baseEngine)));
+            cipher = new BufferedGenericBlockCipher(new CTSBlockCipher(CBCBlockCipher.newInstance(baseEngine)));
         }
         else if (modeName.equals("CCM"))
         {
@@ -473,7 +499,7 @@
             else
             {
             */
-                cipher = new AEADGenericBlockCipher(new CCMBlockCipher(baseEngine));
+                cipher = new AEADGenericBlockCipher(CCMBlockCipher.newInstance(baseEngine));
             /*
             }
             */
@@ -509,12 +535,13 @@
             /*
             if (baseEngine instanceof DSTU7624Engine)
             {
+                ivLength = baseEngine.getBlockSize();
                 cipher = new AEADGenericBlockCipher(new KGCMBlockCipher(baseEngine));
             }
             else
             {
             */
-                cipher = new AEADGenericBlockCipher(new GCMBlockCipher(baseEngine));
+                cipher = new AEADGenericBlockCipher(GCMBlockCipher.newInstance(baseEngine));
             /*
             }
             */
@@ -540,7 +567,7 @@
         {
             if (cipher.wrapOnNoPadding())
             {
-                cipher = new BufferedGenericBlockCipher(new BufferedBlockCipher(cipher.getUnderlyingCipher()));
+                cipher = new BufferedGenericBlockCipher(new DefaultBufferedBlockCipher(cipher.getUnderlyingCipher()));
             }
         }
         else if (paddingName.equals("WITHCTS") || paddingName.equals("CTSPADDING") || paddingName.equals("CS3PADDING"))
@@ -849,7 +876,7 @@
             GOST28147ParameterSpec gost28147Param = (GOST28147ParameterSpec)params;
 
             param = new ParametersWithSBox(
-                new KeyParameter(key.getEncoded()), ((GOST28147ParameterSpec)params).getSbox());
+                new KeyParameter(key.getEncoded()), ((GOST28147ParameterSpec)params).getSBox());
 
             if (gost28147Param.getIV() != null && ivLength != 0)
             {
@@ -922,9 +949,15 @@
                 ivParam = (ParametersWithIV)param;
             }
         }
+        else if (params instanceof FPEParameterSpec)
+        {
+            FPEParameterSpec spec = (FPEParameterSpec)params;
+
+            param = new FPEParameters((KeyParameter)param, spec.getRadixConverter(), spec.getTweak(), spec.isUsingInverseFunction());
+        }
         */
         // END Android-removed: Unsupported algorithms
-        else if (gcmSpecClass != null && gcmSpecClass.isInstance(params))
+        else if (GcmSpecUtil.isGcmSpec(params))
         {
             if (!isAEADModeName(modeName) && !(cipher instanceof AEADGenericBlockCipher))
             {
@@ -1048,7 +1081,7 @@
                 // need to pick up IV and SBox.
                 GOST28147ParameterSpec gost28147Param = (GOST28147ParameterSpec)params;
 
-                param = new ParametersWithSBox(param, gost28147Param.getSbox());
+                param = new ParametersWithSBox(param, gost28147Param.getSBox());
 
                 if (gost28147Param.getIV() != null && ivLength != 0)
                 {
@@ -1075,7 +1108,7 @@
                 // need to pick up IV and SBox.
                 GOST28147ParameterSpec gost28147Param = (GOST28147ParameterSpec)params;
 
-                param = new ParametersWithSBox(param, gost28147Param.getSbox());
+                param = new ParametersWithSBox(param, gost28147Param.getSBox());
 
                 if (gost28147Param.getIV() != null && ivLength != 0)
                 {
@@ -1351,7 +1384,7 @@
 
         BufferedGenericBlockCipher(com.android.org.bouncycastle.crypto.BlockCipher cipher)
         {
-            this.cipher = new PaddedBufferedBlockCipher(cipher);
+            this(cipher, new PKCS7Padding());
         }
 
         BufferedGenericBlockCipher(com.android.org.bouncycastle.crypto.BlockCipher cipher, BlockCipherPadding padding)
@@ -1421,6 +1454,85 @@
         }
     }
 
+    // BEGIN Android-removed: unsupported
+    // private static class BufferedFPEBlockCipher
+    //     implements GenericBlockCipher
+    // {
+    //     private FPEEngine cipher;
+    //     private ErasableOutputStream eOut = new ErasableOutputStream();
+
+    //     BufferedFPEBlockCipher(FPEEngine cipher)
+    //     {
+    //         this.cipher = cipher;
+    //     }
+
+    //     public void init(boolean forEncryption, CipherParameters params)
+    //         throws IllegalArgumentException
+    //     {
+    //         cipher.init(forEncryption, params);
+    //     }
+
+    //     public boolean wrapOnNoPadding()
+    //     {
+    //         return false;
+    //     }
+
+    //     public String getAlgorithmName()
+    //     {
+    //         return cipher.getAlgorithmName();
+    //     }
+
+    //     public org.bouncycastle.crypto.BlockCipher getUnderlyingCipher()
+    //     {
+    //         throw new IllegalStateException("not applicable for FPE");
+    //     }
+
+    //     public int getOutputSize(int len)
+    //     {
+    //         return eOut.size() + len;
+    //     }
+
+    //     public int getUpdateOutputSize(int len)
+    //     {
+    //         return 0;
+    //     }
+
+    //     public void updateAAD(byte[] input, int offset, int length)
+    //     {
+    //         throw new UnsupportedOperationException("AAD is not supported in the current mode.");
+    //     }
+
+    //     public int processByte(byte in, byte[] out, int outOff)
+    //         throws DataLengthException
+    //     {
+    //         eOut.write(in);
+
+    //         return 0;
+    //     }
+
+    //     public int processBytes(byte[] in, int inOff, int len, byte[] out, int outOff)
+    //         throws DataLengthException
+    //     {
+    //         eOut.write(in, inOff, len);
+
+    //         return 0;
+    //     }
+
+    //     public int doFinal(byte[] out, int outOff)
+    //         throws IllegalStateException, BadPaddingException
+    //     {
+    //         try
+    //         {
+    //             return cipher.processBlock(eOut.getBuf(), 0, eOut.size(), out, outOff);
+    //         }
+    //         finally
+    //         {
+    //             eOut.erase();
+    //         }
+    //     }
+    // }
+    // END Android-removed: unsupported
+
     private static class AEADGenericBlockCipher
         implements GenericBlockCipher
     {
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/symmetric/util/BaseMac.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/symmetric/util/BaseMac.java
index d7b1c4e..5d5855f 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/symmetric/util/BaseMac.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/symmetric/util/BaseMac.java
@@ -36,8 +36,6 @@
 public class BaseMac
     extends MacSpi implements PBE
 {
-    private static final Class gcmSpecClass = ClassUtil.loadClass(BaseMac.class, "javax.crypto.spec.GCMParameterSpec");
-
     private Mac macEngine;
 
     private int scheme = PKCS12;
@@ -215,7 +213,7 @@
         {
             param = new KeyParameter(key.getEncoded());
         }
-        else if (gcmSpecClass != null && gcmSpecClass.isAssignableFrom(params.getClass()))
+        else if (GcmSpecUtil.isGcmSpec(params))
         {
             param = GcmSpecUtil.extractAeadParameters(keyParam, params);
         }
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/symmetric/util/ClassUtil.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/symmetric/util/ClassUtil.java
index c306ae7..9b1cd28 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/symmetric/util/ClassUtil.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/symmetric/util/ClassUtil.java
@@ -27,7 +27,9 @@
                     {
                         try
                         {
-                            return Class.forName(className);
+                            ClassLoader classLoader = ClassLoader.getSystemClassLoader();
+
+                            return classLoader.loadClass(className);
                         }
                         catch (Exception e)
                         {
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/symmetric/util/GcmSpecUtil.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/symmetric/util/GcmSpecUtil.java
index 0099755..bdb07a5 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/symmetric/util/GcmSpecUtil.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/symmetric/util/GcmSpecUtil.java
@@ -11,9 +11,9 @@
 import java.security.spec.InvalidParameterSpecException;
 
 import com.android.org.bouncycastle.asn1.ASN1Primitive;
-import com.android.org.bouncycastle.asn1.cms.GCMParameters;
 import com.android.org.bouncycastle.crypto.params.AEADParameters;
 import com.android.org.bouncycastle.crypto.params.KeyParameter;
+import com.android.org.bouncycastle.internal.asn1.cms.GCMParameters;
 import com.android.org.bouncycastle.util.Integers;
 
 /**
@@ -21,25 +21,48 @@
  */
 public class GcmSpecUtil
 {
-    static final Class gcmSpecClass = ClassUtil.loadClass(GcmSpecUtil.class, "javax.crypto.spec.GCMParameterSpec");
-
-    static final Method tLen;
-    static final Method iv;
+    static final Class gcmSpecClass;
+    private static final Constructor constructor;
+    private static final Method tLen;
+    private static final Method iv;
 
     static
     {
+        gcmSpecClass = ClassUtil.loadClass(GcmSpecUtil.class, "javax.crypto.spec.GCMParameterSpec");
+
         if (gcmSpecClass != null)
         {
+            constructor = extractConstructor();
             tLen = extractMethod("getTLen");
             iv = extractMethod("getIV");
         }
         else
         {
+            constructor = null;
             tLen = null;
             iv = null;
         }
     }
 
+    private static Constructor extractConstructor()
+    {
+        try
+        {
+            return (Constructor)AccessController.doPrivileged(new PrivilegedExceptionAction()
+            {
+                public Object run()
+                    throws Exception
+                {
+                    return gcmSpecClass.getConstructor(new Class[]{ Integer.TYPE, byte[].class });
+                }
+            });
+        }
+        catch (PrivilegedActionException e)
+        {
+            return null;
+        }
+    }
+
     private static Method extractMethod(final String name)
     {
         try
@@ -64,6 +87,11 @@
         return gcmSpecClass != null;
     }
 
+    public static boolean gcmSpecExtractable()
+    {
+        return constructor != null;
+    }
+
     public static boolean isGcmSpec(AlgorithmParameterSpec paramSpec)
     {
         return gcmSpecClass != null && gcmSpecClass.isInstance(paramSpec);
@@ -80,14 +108,9 @@
         try
         {
             GCMParameters gcmParams = GCMParameters.getInstance(spec);
-            Constructor constructor = gcmSpecClass.getConstructor(new Class[]{Integer.TYPE, byte[].class});
 
             return (AlgorithmParameterSpec)constructor.newInstance(new Object[] { Integers.valueOf(gcmParams.getIcvLen() * 8), gcmParams.getNonce() });
         }
-        catch (NoSuchMethodException e)
-        {
-            throw new InvalidParameterSpecException("No constructor found!");   // should never happen
-        }
         catch (Exception e)
         {
             throw new InvalidParameterSpecException("Construction failed: " + e.getMessage());   // should never happen
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/symmetric/util/PBE.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/symmetric/util/PBE.java
index f61f91e..3977c7b 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/symmetric/util/PBE.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/symmetric/util/PBE.java
@@ -15,6 +15,7 @@
 import javax.crypto.spec.PBEParameterSpec;
 
 import com.android.org.bouncycastle.crypto.CipherParameters;
+import com.android.org.bouncycastle.crypto.CryptoServicePurpose;
 import com.android.org.bouncycastle.crypto.PBEParametersGenerator;
 // Android-removed: Unsupported algorithms
 // import org.bouncycastle.crypto.digests.GOST3411Digest;
@@ -107,25 +108,25 @@
                 {
                 // Android-removed: Unsupported algorithms
                 // case MD2:
-                //     generator = new PKCS5S2ParametersGenerator(new MD2Digest());
+                    // generator = new PKCS5S2ParametersGenerator(new MD2Digest(CryptoServicePurpose.PRF));
                 //     break;
                 case MD5:
                     // Android-changed: Use Android digests
-                    // generator = new PKCS5S2ParametersGenerator(DigestFactory.createMD5());
+                    // generator = new PKCS5S2ParametersGenerator(DigestFactory.createMD5PRF());
                     generator = new PKCS5S2ParametersGenerator(AndroidDigestFactory.getMD5());
                     break;
                 case SHA1:
                     // Android-changed: Use Android digests
-                    // generator = new PKCS5S2ParametersGenerator(DigestFactory.createSHA1());
+                    // generator = new PKCS5S2ParametersGenerator(DigestFactory.createSHA1PRF());
                     generator = new PKCS5S2ParametersGenerator(AndroidDigestFactory.getSHA1());
                     break;
                 // BEGIN Android-removed: Unsupported algorithms
                 /*
                 case RIPEMD160:
-                    generator = new PKCS5S2ParametersGenerator(new RIPEMD160Digest());
+                    generator = new PKCS5S2ParametersGenerator(new RIPEMD160Digest(CryptoServicePurpose.PRF));
                     break;
                 case TIGER:
-                    generator = new PKCS5S2ParametersGenerator(new TigerDigest());
+                    generator = new PKCS5S2ParametersGenerator(new TigerDigest(CryptoServicePurpose.PRF));
                     break;
                 */
                 // END Android-removed: Unsupported algorithms
@@ -136,36 +137,43 @@
                     break;
                 // Android-removed: Unsupported algorithms
                 // case GOST3411:
+                //     generator = new PKCS5S2ParametersGenerator(new GOST3411Digest(CryptoServicePurpose.PRF));
+                //     break;
+                // Android-removed: Unsupported algorithms
+                // case GOST3411:
                 //     generator = new PKCS5S2ParametersGenerator(new GOST3411Digest());
                 //     break;
                 case SHA224:
                     // Android-changed: Use Android digests
-                    // generator = new PKCS5S2ParametersGenerator(DigestFactory.createSHA224());
+                    // generator = new PKCS5S2ParametersGenerator(DigestFactory.createSHA224PRF());
                     generator = new PKCS5S2ParametersGenerator(AndroidDigestFactory.getSHA224());
                     break;
                 case SHA384:
                     // Android-changed: Use Android digests
-                    // generator = new PKCS5S2ParametersGenerator(DigestFactory.createSHA384());
+                    // generator = new PKCS5S2ParametersGenerator(DigestFactory.createSHA384PRF());
                     generator = new PKCS5S2ParametersGenerator(AndroidDigestFactory.getSHA384());
                     break;
                 case SHA512:
                     // Android-changed: Use Android digests
-                    // generator = new PKCS5S2ParametersGenerator(DigestFactory.createSHA512());
+                    // generator = new PKCS5S2ParametersGenerator(DigestFactory.createSHA512PRF());
                     generator = new PKCS5S2ParametersGenerator(AndroidDigestFactory.getSHA512());
                     break;
                 // BEGIN Android-removed: Unsupported algorithms
                 /*
                 case SHA3_224:
-                    generator = new PKCS5S2ParametersGenerator(DigestFactory.createSHA3_224());
+                    generator = new PKCS5S2ParametersGenerator(DigestFactory.createSHA3_224PRF());
                     break;
                 case SHA3_256:
-                     generator = new PKCS5S2ParametersGenerator(DigestFactory.createSHA3_256());
+                     generator = new PKCS5S2ParametersGenerator(DigestFactory.createSHA3_256PRF());
                      break;
                 case SHA3_384:
-                    generator = new PKCS5S2ParametersGenerator(DigestFactory.createSHA3_384());
+                    generator = new PKCS5S2ParametersGenerator(DigestFactory.createSHA3_384PRF());
                     break;
                 case SHA3_512:
-                    generator = new PKCS5S2ParametersGenerator(DigestFactory.createSHA3_512());
+                    generator = new PKCS5S2ParametersGenerator(DigestFactory.createSHA3_512PRF());
+                    break;
+                case SM3:
+                    generator = new PKCS5S2ParametersGenerator(new SM3Digest(CryptoServicePurpose.PRF));
                     break;
                 case SM3:
                     generator = new PKCS5S2ParametersGenerator(new SM3Digest());
@@ -182,50 +190,54 @@
                 {
                 // Android-removed: Unsupported algorithms
                 // case MD2:
-                //     generator = new PKCS12ParametersGenerator(new MD2Digest());
+                    // generator = new PKCS12ParametersGenerator(new MD2Digest(CryptoServicePurpose.PRF));
                 //     break;
                 case MD5:
                     // Android-changed: Use Android digests
-                    // generator = new PKCS12ParametersGenerator(DigestFactory.createMD5());
+                    // generator = new PKCS12ParametersGenerator(DigestFactory.createMD5PRF());
                     generator = new PKCS12ParametersGenerator(AndroidDigestFactory.getMD5());
                     break;
                 case SHA1:
                     // Android-changed: Use Android digests
-                    // generator = new PKCS12ParametersGenerator(DigestFactory.createSHA1());
+                    // generator = new PKCS12ParametersGenerator(DigestFactory.createSHA1PRF());
                     generator = new PKCS12ParametersGenerator(AndroidDigestFactory.getSHA1());
                     break;
                 // BEGIN Android-removed: Unsupported algorithms
                 /*
                 case RIPEMD160:
-                    generator = new PKCS12ParametersGenerator(new RIPEMD160Digest());
+                    generator = new PKCS12ParametersGenerator(new RIPEMD160Digest(CryptoServicePurpose.PRF));
                     break;
                 case TIGER:
-                    generator = new PKCS12ParametersGenerator(new TigerDigest());
+                    generator = new PKCS12ParametersGenerator(new TigerDigest(CryptoServicePurpose.PRF));
                     break;
                 */
                 // END Android-removed: Unsupported algorithms
                 case SHA256:
                     // Android-changed: Use Android digests
-                    // generator = new PKCS12ParametersGenerator(DigestFactory.createSHA256());
+                    // generator = new PKCS12ParametersGenerator(DigestFactory.createSHA256PRF());
                     generator = new PKCS12ParametersGenerator(AndroidDigestFactory.getSHA256());
                     break;
                 // Android-removed: Unsupported algorithms
                 // case GOST3411:
+                //     generator = new PKCS12ParametersGenerator(new GOST3411Digest(CryptoServicePurpose.PRF));
+                //     break;
+                // Android-removed: Unsupported algorithms
+                // case GOST3411:
                 //     generator = new PKCS12ParametersGenerator(new GOST3411Digest());
                 //     break;
                 case SHA224:
                     // Android-changed: Use Android digests
-                    // generator = new PKCS12ParametersGenerator(DigestFactory.createSHA224());
+                    // generator = new PKCS12ParametersGenerator(DigestFactory.createSHA224PRF());
                     generator = new PKCS12ParametersGenerator(AndroidDigestFactory.getSHA224());
                     break;
                 case SHA384:
                     // Android-changed: Use Android digests
-                    // generator = new PKCS12ParametersGenerator(DigestFactory.createSHA384());
+                    // generator = new PKCS12ParametersGenerator(DigestFactory.createSHA384PRF());
                     generator = new PKCS12ParametersGenerator(AndroidDigestFactory.getSHA384());
                     break;
                 case SHA512:
                     // Android-changed: Use Android digests
-                    // generator = new PKCS12ParametersGenerator(DigestFactory.createSHA512());
+                    // generator = new PKCS12ParametersGenerator(DigestFactory.createSHA512PRF());
                     generator = new PKCS12ParametersGenerator(AndroidDigestFactory.getSHA512());
                     break;
                 default:
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/util/AsymmetricAlgorithmProvider.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/util/AsymmetricAlgorithmProvider.java
index 82b7984..b73bdc2 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/util/AsymmetricAlgorithmProvider.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/util/AsymmetricAlgorithmProvider.java
@@ -1,6 +1,8 @@
 /* GENERATED SOURCE. DO NOT MODIFY. */
 package com.android.org.bouncycastle.jcajce.provider.util;
 
+import java.util.Map;
+
 import com.android.org.bouncycastle.asn1.ASN1ObjectIdentifier;
 import com.android.org.bouncycastle.jcajce.provider.config.ConfigurableProvider;
 
@@ -21,6 +23,24 @@
         provider.addAlgorithm("Alg.Alias.Signature.OID." + oid, algorithm);
     }
 
+    protected void addSignatureAlias(
+        ConfigurableProvider provider,
+        String algorithm,
+        ASN1ObjectIdentifier oid)
+    {
+        provider.addAlgorithm("Alg.Alias.Signature." + oid, algorithm);
+        provider.addAlgorithm("Alg.Alias.Signature.OID." + oid, algorithm);
+    }
+
+    protected void addSignatureAlgorithm(
+        ConfigurableProvider provider,
+        String digest,
+        String algorithm,
+        String className)
+    {
+        addSignatureAlgorithm(provider, digest, algorithm, className, null);
+    }
+
     protected void addSignatureAlgorithm(
         ConfigurableProvider provider,
         String digest,
@@ -37,8 +57,103 @@
         provider.addAlgorithm("Alg.Alias.Signature." + jdk11Variation1, mainName);
         provider.addAlgorithm("Alg.Alias.Signature." + jdk11Variation2, mainName);
         provider.addAlgorithm("Alg.Alias.Signature." + alias, mainName);
-        provider.addAlgorithm("Alg.Alias.Signature." + oid, mainName);
-        provider.addAlgorithm("Alg.Alias.Signature.OID." + oid, mainName);
+        if (oid != null)
+        {
+            provider.addAlgorithm("Alg.Alias.Signature." + oid, mainName);
+            provider.addAlgorithm("Alg.Alias.Signature.OID." + oid, mainName);
+        }
+    }
+
+    protected void addSignatureAlgorithm(
+        ConfigurableProvider provider,
+        String digest,
+        String algorithm,
+        String className,
+        ASN1ObjectIdentifier oid,
+        Map<String, String> attributes)
+    {
+        String mainName = digest + "WITH" + algorithm;
+        String jdk11Variation1 = digest + "with" + algorithm;
+        String jdk11Variation2 = digest + "With" + algorithm;
+        String alias = digest + "/" + algorithm;
+
+        provider.addAlgorithm("Signature." + mainName, className);
+        provider.addAlgorithm("Alg.Alias.Signature." + jdk11Variation1, mainName);
+        provider.addAlgorithm("Alg.Alias.Signature." + jdk11Variation2, mainName);
+        provider.addAlgorithm("Alg.Alias.Signature." + alias, mainName);
+        if (oid != null)
+        {
+            provider.addAlgorithm("Alg.Alias.Signature." + oid, mainName);
+            provider.addAlgorithm("Alg.Alias.Signature.OID." + oid, mainName);
+        }
+        provider.addAttributes("Signature." + mainName, attributes);
+    }
+
+    protected void addKeyPairGeneratorAlgorithm(
+        ConfigurableProvider provider,
+        String algorithm,
+        String className,
+        ASN1ObjectIdentifier oid)
+    {
+        provider.addAlgorithm("KeyPairGenerator." + algorithm, className);
+        if (oid != null)
+        {
+            provider.addAlgorithm("Alg.Alias.KeyPairGenerator." + oid, algorithm);
+            provider.addAlgorithm("Alg.Alias.KeyPairGenerator.OID." + oid, algorithm);
+        }
+    }
+
+    protected void addKeyFactoryAlgorithm(
+        ConfigurableProvider provider,
+        String algorithm,
+        String className,
+        ASN1ObjectIdentifier oid,
+        AsymmetricKeyInfoConverter keyInfoConverter)
+    {
+        provider.addAlgorithm("KeyFactory." + algorithm, className);
+        if (oid != null)
+        {
+            provider.addAlgorithm("Alg.Alias.KeyFactory." + oid, algorithm);
+            provider.addAlgorithm("Alg.Alias.KeyFactory.OID." + oid, algorithm);
+
+            provider.addKeyInfoConverter(oid, keyInfoConverter);
+        }
+    }
+
+    protected void addKeyGeneratorAlgorithm(
+        ConfigurableProvider provider,
+        String algorithm,
+        String className,
+        ASN1ObjectIdentifier oid)
+    {
+        provider.addAlgorithm("KeyGenerator." + algorithm, className);
+        if (oid != null)
+        {
+            provider.addAlgorithm("Alg.Alias.KeyGenerator." + oid, algorithm);
+            provider.addAlgorithm("Alg.Alias.KeyGenerator.OID." + oid, algorithm);
+        }
+    }
+
+    protected void addCipherAlgorithm(
+        ConfigurableProvider provider,
+        String algorithm,
+        String className,
+        ASN1ObjectIdentifier oid)
+    {
+        provider.addAlgorithm("Cipher." + algorithm, className);
+        if (oid != null)
+        {
+            provider.addAlgorithm("Alg.Alias.Cipher." + oid, algorithm);
+            provider.addAlgorithm("Alg.Alias.Cipher.OID." + oid, algorithm);
+        }
+    }
+
+    protected void registerKeyFactoryOid(ConfigurableProvider provider, ASN1ObjectIdentifier oid, String name, AsymmetricKeyInfoConverter keyFactory)
+    {
+        provider.addAlgorithm("Alg.Alias.KeyFactory." + oid, name);
+        provider.addAlgorithm("Alg.Alias.KeyFactory.OID." + oid, name);
+
+        provider.addKeyInfoConverter(oid, keyFactory);
     }
 
     protected void registerOid(ConfigurableProvider provider, ASN1ObjectIdentifier oid, String name, AsymmetricKeyInfoConverter keyFactory)
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/util/DigestFactory.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/util/DigestFactory.java
index d30500a..2575cc6 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/util/DigestFactory.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/util/DigestFactory.java
@@ -35,6 +35,8 @@
     private static Set sha3_256 = new HashSet();
     private static Set sha3_384 = new HashSet();
     private static Set sha3_512 = new HashSet();
+    private static Set shake128 = new HashSet();
+    private static Set shake256 = new HashSet();
     */
     // END Android-removed: Unsupported algorithms
 
@@ -86,6 +88,18 @@
 
         sha3_512.add("SHA3-512");
         sha3_512.add(NISTObjectIdentifiers.id_sha3_512.getId());
+
+        shake128.add("SHAKE128");
+        shake128.add(NISTObjectIdentifiers.id_shake128.getId());
+
+        shake256.add("SHAKE256");
+        shake256.add(NISTObjectIdentifiers.id_shake256.getId());
+
+        oids.put("SHAKE128", NISTObjectIdentifiers.id_shake128);
+        oids.put(NISTObjectIdentifiers.id_shake128.getId(), NISTObjectIdentifiers.id_shake128);
+
+        oids.put("SHAKE256", NISTObjectIdentifiers.id_shake256);
+        oids.put(NISTObjectIdentifiers.id_shake256.getId(), NISTObjectIdentifiers.id_shake256);
         */
         // END Android-removed: Unsupported algorithms
 
@@ -202,6 +216,14 @@
         {
             return org.bouncycastle.crypto.util.DigestFactory.createSHA3_512();
         }
+        if (shake128.contains(digestName))
+        {
+            return org.bouncycastle.crypto.util.DigestFactory.createSHAKE128();
+        }
+        if (shake256.contains(digestName))
+        {
+            return org.bouncycastle.crypto.util.DigestFactory.createSHAKE256();
+        }
         */
         // END Android-removed: Unsupported algorithms
 
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/spec/FPEParameterSpec.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/spec/FPEParameterSpec.java
new file mode 100644
index 0000000..8a95618
--- /dev/null
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/spec/FPEParameterSpec.java
@@ -0,0 +1,55 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.org.bouncycastle.jcajce.spec;
+
+import java.security.spec.AlgorithmParameterSpec;
+
+import com.android.org.bouncycastle.crypto.util.RadixConverter;
+import com.android.org.bouncycastle.util.Arrays;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+public class FPEParameterSpec
+    implements AlgorithmParameterSpec
+{
+    private final RadixConverter radixConverter;
+    private final byte[] tweak;
+    private final boolean useInverse;
+
+    public FPEParameterSpec(int radix, byte[] tweak)
+    {
+        this(radix, tweak, false);
+    }
+
+    public FPEParameterSpec(int radix, byte[] tweak, boolean useInverse)
+    {
+        this(new RadixConverter(radix), tweak, useInverse);
+    }
+
+    public FPEParameterSpec(RadixConverter radixConverter, byte[] tweak, boolean useInverse)
+    {
+        this.radixConverter = radixConverter;
+        this.tweak = Arrays.clone(tweak);
+        this.useInverse = useInverse;
+    }
+
+    public int getRadix()
+    {
+        return radixConverter.getRadix();
+    }
+
+    public RadixConverter getRadixConverter()
+    {
+        return radixConverter;
+    }
+
+    public byte[] getTweak()
+    {
+        return Arrays.clone(tweak);
+    }
+
+    public boolean isUsingInverseFunction()
+    {
+        return useInverse;
+    }
+}
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/spec/HybridValueParameterSpec.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/spec/HybridValueParameterSpec.java
new file mode 100644
index 0000000..a684e6a
--- /dev/null
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/spec/HybridValueParameterSpec.java
@@ -0,0 +1,99 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.org.bouncycastle.jcajce.spec;
+
+import java.security.spec.AlgorithmParameterSpec;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import javax.security.auth.Destroyable;
+
+import com.android.org.bouncycastle.util.Arrays;
+
+/**
+ * SP 800-56C Hybrid Value spec, to allow the secret in a key agreement to be
+ * created as "Z | T" where T is some other secret value as described in Section 2.
+ * <p>
+ * Get methods throw IllegalStateException if destroy() is called.
+ * </p>
+ * @hide This class is not part of the Android public SDK API
+ */
+public class HybridValueParameterSpec
+    implements AlgorithmParameterSpec, Destroyable
+{
+    private final AtomicBoolean hasBeenDestroyed = new AtomicBoolean(false);
+
+    private volatile byte[] t;
+    private volatile AlgorithmParameterSpec baseSpec;
+
+    /**
+     * Create a spec with T set to t and the spec for the KDF in the agreement to baseSpec.
+     * Note: the t value is not copied.
+     *
+     * @param t a shared secret to be concatenated with the agreement's Z value.
+     * @param baseSpec the base spec for the agreements KDF.
+     */
+    public HybridValueParameterSpec(byte[] t, AlgorithmParameterSpec baseSpec)
+    {
+        this.t = t;
+        this.baseSpec = baseSpec;
+    }
+
+    /**
+     * Return a reference to the T value.
+     *
+     * @return a reference to T.
+     */
+    public byte[] getT()
+    {
+        byte[] tVal = t;
+
+        checkDestroyed();
+        
+        return tVal;
+    }
+
+    /**
+     * Return the base parameter spec.
+     *
+     * @return base spec to be applied to the KDF.
+     */
+    public AlgorithmParameterSpec getBaseParameterSpec()
+    {
+        AlgorithmParameterSpec rv = this.baseSpec;
+
+        checkDestroyed();
+
+        return rv;
+    }
+
+    /**
+     * Return true if the destroy() method is called and the contents are
+     * erased.
+     *
+     * @return true if destroyed, false otherwise.
+     */
+    public boolean isDestroyed()
+    {
+        return this.hasBeenDestroyed.get();
+    }
+
+    /**
+     * Destroy this parameter spec, explicitly erasing its contents.
+     */
+    public void destroy()
+    {
+        if (!hasBeenDestroyed.getAndSet(true))
+        {
+            Arrays.clear(t);
+            this.t = null;
+            this.baseSpec = null;
+        }
+    }
+
+    private void checkDestroyed()
+    {
+        if (isDestroyed())
+        {
+            throw new IllegalStateException("spec has been destroyed");
+        }
+    }
+}
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/spec/IESKEMParameterSpec.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/spec/IESKEMParameterSpec.java
new file mode 100644
index 0000000..fc07eeb
--- /dev/null
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/spec/IESKEMParameterSpec.java
@@ -0,0 +1,53 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.org.bouncycastle.jcajce.spec;
+
+import java.security.spec.AlgorithmParameterSpec;
+
+import com.android.org.bouncycastle.util.Arrays;
+
+/**
+ * Parameter spec for an integrated encryptor KEM, as in IEEE_Std_1609_2
+ * @hide This class is not part of the Android public SDK API
+ */
+public class IESKEMParameterSpec
+    implements AlgorithmParameterSpec
+{
+    private final byte[] recipientInfo;
+    private final boolean usePointCompression;
+
+
+    /**
+     * Set the IESKEM parameters.
+     *
+     * @param recipientInfo recipient data.
+     */
+    public IESKEMParameterSpec(
+        byte[] recipientInfo)
+    {
+        this(recipientInfo, false);
+    }
+
+    /**
+     * Set the IESKEM parameters - specifying point compression.
+     *
+     * @param recipientInfo recipient data.
+     * @param usePointCompression use point compression on output (ignored on input).
+     */
+    public IESKEMParameterSpec(
+        byte[] recipientInfo,
+        boolean usePointCompression)
+    {
+        this.recipientInfo = Arrays.clone(recipientInfo);
+        this.usePointCompression = usePointCompression;
+    }
+
+    public byte[] getRecipientInfo()
+    {
+        return Arrays.clone(recipientInfo);
+    }
+
+    public boolean hasUsePointCompression()
+    {
+        return usePointCompression;
+    }
+}
\ No newline at end of file
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/spec/KEMExtractSpec.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/spec/KEMExtractSpec.java
new file mode 100644
index 0000000..868909f
--- /dev/null
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/spec/KEMExtractSpec.java
@@ -0,0 +1,52 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.org.bouncycastle.jcajce.spec;
+
+import java.security.PrivateKey;
+import java.security.spec.AlgorithmParameterSpec;
+
+import com.android.org.bouncycastle.util.Arrays;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+public class KEMExtractSpec
+    implements AlgorithmParameterSpec
+{
+    private final PrivateKey privateKey;
+    private final byte[] encapsulation;
+    private final String keyAlgorithmName;
+    private final int keySizeInBits;
+
+    public KEMExtractSpec(PrivateKey privateKey, byte[] encapsulation, String keyAlgorithmName)
+    {
+        this(privateKey, encapsulation, keyAlgorithmName, 256);
+    }
+
+    public KEMExtractSpec(PrivateKey privateKey, byte[] encapsulation, String keyAlgorithmName, int keySizeInBits)
+    {
+        this.privateKey = privateKey;
+        this.encapsulation = Arrays.clone(encapsulation);
+        this.keyAlgorithmName = keyAlgorithmName;
+        this.keySizeInBits = keySizeInBits;
+    }
+
+    public byte[] getEncapsulation()
+    {
+        return Arrays.clone(encapsulation);
+    }
+
+    public PrivateKey getPrivateKey()
+    {
+        return privateKey;
+    }
+
+    public String getKeyAlgorithmName()
+    {
+        return keyAlgorithmName;
+    }
+
+    public int getKeySize()
+    {
+        return keySizeInBits;
+    }
+}
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/spec/KEMGenerateSpec.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/spec/KEMGenerateSpec.java
new file mode 100644
index 0000000..81457a3
--- /dev/null
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/spec/KEMGenerateSpec.java
@@ -0,0 +1,43 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.org.bouncycastle.jcajce.spec;
+
+import java.security.PublicKey;
+import java.security.spec.AlgorithmParameterSpec;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+public class KEMGenerateSpec
+    implements AlgorithmParameterSpec
+{
+    private final PublicKey publicKey;
+    private final String keyAlgorithmName;
+    private final int keySizeInBits;
+
+    public KEMGenerateSpec(PublicKey publicKey, String keyAlgorithmName)
+    {
+        this(publicKey, keyAlgorithmName, 256);
+    }
+
+    public KEMGenerateSpec(PublicKey publicKey, String keyAlgorithmName, int keySizeInBits)
+    {
+        this.publicKey = publicKey;
+        this.keyAlgorithmName = keyAlgorithmName;
+        this.keySizeInBits = keySizeInBits;
+    }
+
+    public PublicKey getPublicKey()
+    {
+        return publicKey;
+    }
+
+    public String getKeyAlgorithmName()
+    {
+        return keyAlgorithmName;
+    }
+
+    public int getKeySize()
+    {
+        return keySizeInBits;
+    }
+}
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/spec/RawEncodedKeySpec.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/spec/RawEncodedKeySpec.java
new file mode 100644
index 0000000..d7ff7a0
--- /dev/null
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/spec/RawEncodedKeySpec.java
@@ -0,0 +1,27 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.org.bouncycastle.jcajce.spec;
+
+import java.security.spec.EncodedKeySpec;
+
+/**
+ * An encoded key spec that just wraps the minimal data for a public/private key representation.
+ * @hide This class is not part of the Android public SDK API
+ */
+public class RawEncodedKeySpec
+    extends EncodedKeySpec
+{
+    /**
+     * Base constructor - just the minimal data.
+     *
+     * @param bytes the public/private key data.
+     */
+    public RawEncodedKeySpec(byte[] bytes)
+    {
+        super(bytes);
+    }
+
+    public String getFormat()
+    {
+        return "RAW";
+    }
+}
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/spec/UserKeyingMaterialSpec.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/spec/UserKeyingMaterialSpec.java
index 36b9234..802c6e0 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/spec/UserKeyingMaterialSpec.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/spec/UserKeyingMaterialSpec.java
@@ -12,14 +12,47 @@
     implements AlgorithmParameterSpec
 {
     private final byte[] userKeyingMaterial;
+    private final byte[] salt;
 
+    /**
+     * Base constructor.
+     *
+     * @param userKeyingMaterial the bytes to be mixed in to the key agreement's KDF.
+     */
     public UserKeyingMaterialSpec(byte[] userKeyingMaterial)
     {
-        this.userKeyingMaterial = Arrays.clone(userKeyingMaterial);
+        this(userKeyingMaterial, null);
     }
 
+    /**
+     * Base constructor.
+     *
+     * @param userKeyingMaterial the bytes to be mixed in to the key agreement's KDF.
+     * @param salt the salt to use with the underlying KDF.
+     */
+    public UserKeyingMaterialSpec(byte[] userKeyingMaterial, byte[] salt)
+    {
+        this.userKeyingMaterial = Arrays.clone(userKeyingMaterial);
+        this.salt = Arrays.clone(salt);
+    }
+
+    /**
+     * Return a copy of the key material in this object.
+     *
+     * @return the user keying material.
+     */
     public byte[] getUserKeyingMaterial()
     {
         return Arrays.clone(userKeyingMaterial);
     }
+
+    /**
+     * Return a copy of the salt in this object.
+     *
+     * @return the KDF salt.
+     */
+    public byte[] getSalt()
+    {
+        return Arrays.clone(salt);
+    }
 }
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/util/ECKeyUtil.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/util/ECKeyUtil.java
index a0831b6..6869bf5 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/util/ECKeyUtil.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/util/ECKeyUtil.java
@@ -12,6 +12,7 @@
 import com.android.org.bouncycastle.asn1.x9.ECNamedCurveTable;
 import com.android.org.bouncycastle.asn1.x9.X962Parameters;
 import com.android.org.bouncycastle.asn1.x9.X9ECParameters;
+import com.android.org.bouncycastle.asn1.x9.X9ECParametersHolder;
 import com.android.org.bouncycastle.asn1.x9.X9ECPoint;
 import com.android.org.bouncycastle.crypto.ec.CustomNamedCurves;
 
@@ -70,10 +71,10 @@
             {
                 ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier)params.getParameters();
 
-                X9ECParameters x9 = CustomNamedCurves.getByOID(oid);
+                X9ECParametersHolder x9 = CustomNamedCurves.getByOIDLazy(oid);
                 if (x9 == null)
                 {
-                    x9 = ECNamedCurveTable.getByOID(oid);
+                    x9 = ECNamedCurveTable.getByOIDLazy(oid);
                 }
                 curve = x9.getCurve();
             }
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/util/MessageDigestUtils.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/util/MessageDigestUtils.java
index 05cfd6c..9f95c95 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/util/MessageDigestUtils.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/util/MessageDigestUtils.java
@@ -15,6 +15,9 @@
 import com.android.org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
 // Android-removed: Unsupported algorithms
 // import org.bouncycastle.asn1.teletrust.TeleTrusTObjectIdentifiers;
+// import org.bouncycastle.asn1.DERNull;
+// import org.bouncycastle.asn1.misc.MiscObjectIdentifiers;
+// import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 
 /**
  * @hide This class is not part of the Android public SDK API
@@ -23,6 +26,9 @@
 {
     private static Map<ASN1ObjectIdentifier, String> digestOidMap = new HashMap<ASN1ObjectIdentifier, String>();
 
+    // Android-removed: Unsupported algorithms
+    // private static Map<String, AlgorithmIdentifier> digestAlgIdMap = new HashMap<String, AlgorithmIdentifier>();
+
     static
     {
         // BEGIN Android-removed: Unsupported algorithms
@@ -37,6 +43,8 @@
         digestOidMap.put(NISTObjectIdentifiers.id_sha512, "SHA-512");
         // BEGIN Android-removed: Unsupported algorithms
         /*
+        digestOidMap.put(NISTObjectIdentifiers.id_sha512_224, "SHA-512(224)");
+        digestOidMap.put(NISTObjectIdentifiers.id_sha512_256, "SHA-512(256)");
         digestOidMap.put(TeleTrusTObjectIdentifiers.ripemd128, "RIPEMD-128");
         digestOidMap.put(TeleTrusTObjectIdentifiers.ripemd160, "RIPEMD-160");
         digestOidMap.put(TeleTrusTObjectIdentifiers.ripemd256, "RIPEMD-128");
@@ -49,11 +57,51 @@
         digestOidMap.put(NISTObjectIdentifiers.id_sha3_256, "SHA3-256");
         digestOidMap.put(NISTObjectIdentifiers.id_sha3_384, "SHA3-384");
         digestOidMap.put(NISTObjectIdentifiers.id_sha3_512, "SHA3-512");
+        digestOidMap.put(NISTObjectIdentifiers.id_shake128, "SHAKE128");
+        digestOidMap.put(NISTObjectIdentifiers.id_shake256, "SHAKE256");
         digestOidMap.put(GMObjectIdentifiers.sm3, "SM3");
+        digestOidMap.put(MiscObjectIdentifiers.blake3_256, "BLAKE3-256");
+
+        digestAlgIdMap.put("SHA-1", new AlgorithmIdentifier(OIWObjectIdentifiers.idSHA1, DERNull.INSTANCE));
+        digestAlgIdMap.put("SHA-224", new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha224));
+        digestAlgIdMap.put("SHA224", new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha224));
+        digestAlgIdMap.put("SHA-256", new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha256));
+        digestAlgIdMap.put("SHA256", new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha256));
+        digestAlgIdMap.put("SHA-384", new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha384));
+        digestAlgIdMap.put("SHA384", new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha384));
+        digestAlgIdMap.put("SHA-512", new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha512));
+        digestAlgIdMap.put("SHA512", new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha512));
+        digestAlgIdMap.put("SHA3-224", new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha3_224));
+        digestAlgIdMap.put("SHA3-256", new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha3_256));
+        digestAlgIdMap.put("SHA3-384", new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha3_384));
+        digestAlgIdMap.put("SHA3-512", new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha3_512));
+        digestAlgIdMap.put("BLAKE3-256", new AlgorithmIdentifier(MiscObjectIdentifiers.blake3_256));
         */
         // END Android-removed: Unsupported algorithms
     }
 
+    // BEGIN Android-removed: Unsupported algorithms
+    /*
+     * Attempt to find a standard JCA name for the digest represented by the passed in OID.
+     *
+     * @param digestName name of the digest algorithm of interest.
+     * @return an algorithm identifier representing the digest.
+    public static AlgorithmIdentifier getDigestAlgID(String digestName)
+    {
+        if (digestAlgIdMap.containsKey(digestName))
+        {
+            return (AlgorithmIdentifier)digestAlgIdMap.get(digestName);
+        }
+        throw new IllegalArgumentException("unknown digest: " + digestName);
+    }
+
+    public static AlgorithmIdentifier getDigestAlgID(String digestName)
+    {
+        throw new IllegalArgumentException("unknown digest: " + digestName);
+    }
+    */
+    // END Android-removed: Unsupported algorithms
+
     /**
      * Attempt to find a standard JCA name for the digest represented by the passed in OID.
      *
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jce/ECNamedCurveTable.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jce/ECNamedCurveTable.java
index 6924428..63ca22c 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jce/ECNamedCurveTable.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jce/ECNamedCurveTable.java
@@ -23,32 +23,35 @@
     public static ECNamedCurveParameterSpec getParameterSpec(
         String  name)
     {
-        X9ECParameters  ecP = com.android.org.bouncycastle.crypto.ec.CustomNamedCurves.getByName(name);
+        ASN1ObjectIdentifier oid;
+        try
+        {
+            oid = possibleOID(name) ? new ASN1ObjectIdentifier(name) : null;
+        }
+        catch (IllegalArgumentException e)
+        {
+            oid = null;
+        }
+
+        X9ECParameters ecP;
+        if (oid != null)
+        {
+            ecP = com.android.org.bouncycastle.crypto.ec.CustomNamedCurves.getByOID(oid);
+        }
+        else
+        {
+            ecP = com.android.org.bouncycastle.crypto.ec.CustomNamedCurves.getByName(name);
+        }
+
         if (ecP == null)
         {
-            try
+            if (oid != null)
             {
-                ecP = com.android.org.bouncycastle.crypto.ec.CustomNamedCurves.getByOID(new ASN1ObjectIdentifier(name));
+                ecP = com.android.org.bouncycastle.asn1.x9.ECNamedCurveTable.getByOID(oid);
             }
-            catch (IllegalArgumentException e)
-            {
-                // ignore - not an oid
-            }
-
-            if (ecP == null)
+            else
             {
                 ecP = com.android.org.bouncycastle.asn1.x9.ECNamedCurveTable.getByName(name);
-                if (ecP == null)
-                {
-                    try
-                    {
-                        ecP = com.android.org.bouncycastle.asn1.x9.ECNamedCurveTable.getByOID(new ASN1ObjectIdentifier(name));
-                    }
-                    catch (IllegalArgumentException e)
-                    {
-                        // ignore - not an oid
-                    }
-                }
             }
         }
 
@@ -75,4 +78,21 @@
     {
         return com.android.org.bouncycastle.asn1.x9.ECNamedCurveTable.getNames();
     }
+
+    private static boolean possibleOID(
+        String identifier)
+    {
+        if (identifier.length() < 3 || identifier.charAt(1) != '.')
+        {
+            return false;
+        }
+
+        char first = identifier.charAt(0);
+        if (first < '0' || first > '2')
+        {
+            return false;
+        }
+
+        return true;
+    }
 }
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jce/netscape/NetscapeCertRequest.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jce/netscape/NetscapeCertRequest.java
index f6bcc66..c81ca6d 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jce/netscape/NetscapeCertRequest.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jce/netscape/NetscapeCertRequest.java
@@ -18,6 +18,7 @@
 
 import com.android.org.bouncycastle.asn1.ASN1EncodableVector;
 import com.android.org.bouncycastle.asn1.ASN1Encoding;
+import com.android.org.bouncycastle.asn1.ASN1IA5String;
 import com.android.org.bouncycastle.asn1.ASN1InputStream;
 import com.android.org.bouncycastle.asn1.ASN1Object;
 import com.android.org.bouncycastle.asn1.ASN1Primitive;
@@ -105,7 +106,7 @@
                         + pkac.size());
             }
 
-            challenge = ((DERIA5String)pkac.getObjectAt(1)).getString();
+            challenge = ((ASN1IA5String)pkac.getObjectAt(1)).getString();
 
             //this could be dangerous, as ASN.1 decoding/encoding
             //could potentially alter the bytes
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jce/provider/BouncyCastleProvider.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jce/provider/BouncyCastleProvider.java
index 798bb8e..7507797 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jce/provider/BouncyCastleProvider.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jce/provider/BouncyCastleProvider.java
@@ -13,12 +13,20 @@
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.logging.Level;
+import java.util.logging.Logger;
 
 import com.android.org.bouncycastle.asn1.ASN1ObjectIdentifier;
+// import org.bouncycastle.asn1.bc.BCObjectIdentifiers;
 // import org.bouncycastle.asn1.isara.IsaraObjectIdentifiers;
 // import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
 import com.android.org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
 import com.android.org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import com.android.org.bouncycastle.crypto.CryptoServiceConstraintsException;
+import com.android.org.bouncycastle.crypto.CryptoServiceProperties;
+import com.android.org.bouncycastle.crypto.CryptoServicePurpose;
+import com.android.org.bouncycastle.crypto.CryptoServicesRegistrar;
 import com.android.org.bouncycastle.jcajce.provider.config.ConfigurableProvider;
 import com.android.org.bouncycastle.jcajce.provider.config.ProviderConfiguration;
 import com.android.org.bouncycastle.jcajce.provider.symmetric.util.ClassUtil;
@@ -35,6 +43,16 @@
 // import org.bouncycastle.pqc.jcajce.provider.xmss.XMSSKeyFactorySpi;
 // import org.bouncycastle.pqc.jcajce.provider.xmss.XMSSMTKeyFactorySpi;
 // import org.bouncycastle.pqc.jcajce.provider.lms.LMSKeyFactorySpi;
+// import org.bouncycastle.pqc.jcajce.provider.bike.BIKEKeyFactorySpi;
+// import org.bouncycastle.pqc.jcajce.provider.cmce.CMCEKeyFactorySpi;
+// import org.bouncycastle.pqc.jcajce.provider.dilithium.DilithiumKeyFactorySpi;
+// import org.bouncycastle.pqc.jcajce.provider.falcon.FalconKeyFactorySpi;
+// import org.bouncycastle.pqc.jcajce.provider.hqc.HQCKeyFactorySpi;
+// import org.bouncycastle.pqc.jcajce.provider.kyber.KyberKeyFactorySpi;
+// import org.bouncycastle.pqc.jcajce.provider.ntru.NTRUKeyFactorySpi;
+// import org.bouncycastle.pqc.jcajce.provider.picnic.PicnicKeyFactorySpi;
+// import org.bouncycastle.pqc.jcajce.provider.sphincsplus.SPHINCSPlusKeyFactorySpi;
+import com.android.org.bouncycastle.util.Strings;
 
 /**
  * To add the provider at runtime use:
@@ -64,7 +82,9 @@
 public final class BouncyCastleProvider extends Provider
     implements ConfigurableProvider
 {
-    private static String info = "BouncyCastle Security Provider v1.68";
+    private static final Logger LOG = Logger.getLogger(BouncyCastleProvider.class.getName());
+
+    private static String info = "BouncyCastle Security Provider v1.77";
 
     public static final String PROVIDER_NAME = "BC";
 
@@ -89,17 +109,27 @@
     private static final String[] SYMMETRIC_MACS =
     {
         // Android-removed: Unsupported algorithms
-        // "SipHash", "Poly1305"
+        // "SipHash", "SipHash128", "Poly1305"
     };
 
-    private static final String[] SYMMETRIC_CIPHERS =
+    private static final CryptoServiceProperties[] SYMMETRIC_CIPHERS =
     {
         // Android-changed: Unsupported algorithms
-        // "AES", "ARC4", "ARIA", "Blowfish", "Camellia", "CAST5", "CAST6", "ChaCha", "DES", "DESede",
-        // "GOST28147", "Grainv1", "Grain128", "HC128", "HC256", "IDEA", "Noekeon", "RC2", "RC5",
-        // "RC6", "Rijndael", "Salsa20", "SEED", "Serpent", "Shacal2", "Skipjack", "SM4", "TEA", "Twofish", "Threefish",
-        // "VMPC", "VMPCKSA3", "XTEA", "XSalsa20", "OpenSSLPBKDF", "DSTU7624", "GOST3412_2015"
-        "AES", "ARC4", "Blowfish", "DES", "DESede", "RC2", "Twofish",
+        // TODO: these numbers need a bit more work, we cap at 256 bits.
+        // service("ARIA", 256), service("Camellia", 256), service("CAST5", 128),
+        // service("CAST6", 256), service("ChaCha", 128), service("GOST28147", 128), 
+        // service("Grainv1", 128), service("Grain128", 128), service("HC128", 128), 
+        // service("HC256", 256), service("IDEA", 128), service("Noekeon", 128), 
+        // service("RC6", 256), service("Rijndael", 256), service("Salsa20", 128), 
+        // service("SEED", 128), service("Serpent", 256), service("Shacal2", 128),
+        // service("Skipjack", 80), service("SM4", 128), service("TEA", 128), 
+        // service("RC5", 128), service("Threefish", 128), service("VMPC", 128), 
+        // service("VMPCKSA3", 128), service("XTEA", 128), service("XSalsa20", 128),
+        // service("OpenSSLPBKDF", 128), service("DSTU7624", 256), service("GOST3412_2015", 256),
+        //  service("Zuc", 128)
+        service("AES", 256), service("ARC4", 20), service("Blowfish", 128),
+        service("DES", 56),  service("DESede", 112), service("RC2", 128), 
+        service("Twofish", 256)
     };
 
      /*
@@ -112,14 +142,14 @@
     private static final String[] ASYMMETRIC_GENERIC =
     {
         // Android-changed: Unsupported algorithms
-        // "X509", "IES", "COMPOSITE"
+        // "X509", "IES", "COMPOSITE", "EXTERNAL"
         "X509"
     };
 
     private static final String[] ASYMMETRIC_CIPHERS =
     {
         // Android-changed: Unsupported algorithms
-        // "DSA", "DH", "EC", "RSA", "GOST", "ECGOST", "ElGamal", "DSTU4145", "GM", "EdEC"
+        // "DSA", "DH", "EC", "RSA", "GOST", "ECGOST", "ElGamal", "DSTU4145", "GM", "EdEC", "LMS", "SPHINCSPlus", "Dilithium", "Falcon", "NTRU"
         "DSA", "DH", "EC", "RSA",
     };
 
@@ -155,6 +185,8 @@
     //     "DRBG"
     // };
 
+    private Map<String, Service> serviceMap = new ConcurrentHashMap<String, Service>();
+
     /**
      * Construct a new provider.  This should only be required when
      * using runtime registration of the provider using the
@@ -163,7 +195,7 @@
     @android.compat.annotation.UnsupportedAppUsage
     public BouncyCastleProvider()
     {
-        super(PROVIDER_NAME, 1.68, info);
+        super(PROVIDER_NAME, 1.77, info);
 
         AccessController.doPrivileged(new PrivilegedAction()
         {
@@ -229,13 +261,9 @@
         put("Cipher.OLDPBEWITHSHAANDTWOFISH-CBC", "org.bouncycastle.jce.provider.BrokenJCEBlockCipher$OldPBEWithSHAAndTwofish");
 
         // Certification Path API
-        put("CertPathValidator.RFC3281", "org.bouncycastle.jce.provider.PKIXAttrCertPathValidatorSpi");
-        put("CertPathBuilder.RFC3281", "org.bouncycastle.jce.provider.PKIXAttrCertPathBuilderSpi");
-        put("CertPathValidator.RFC3280", "org.bouncycastle.jce.provider.PKIXCertPathValidatorSpi");
-        put("CertPathBuilder.RFC3280", "org.bouncycastle.jce.provider.PKIXCertPathBuilderSpi");
-
         if (revChkClass != null)
         {
+            put("CertPathValidator.RFC3281", "org.bouncycastle.jce.provider.PKIXAttrCertPathValidatorSpi");
             put("CertPathBuilder.RFC3281", "org.bouncycastle.jce.provider.PKIXAttrCertPathBuilderSpi");
             put("CertPathValidator.RFC3280", "org.bouncycastle.jce.provider.PKIXCertPathValidatorSpi_8");
             put("CertPathBuilder.RFC3280", "org.bouncycastle.jce.provider.PKIXCertPathBuilderSpi_8");
@@ -262,33 +290,149 @@
         // put("CertStore.Multi", "org.bouncycastle.jce.provider.MultiCertStoreSpi");
         // put("Alg.Alias.CertStore.X509LDAP", "LDAP");
         // END Android-removed: Unsupported algorithms
+
+        getService("SecureRandom", "DEFAULT");  // prime for new SecureRandom() on 1.8 JVMs.
+    }
+
+    public final Service getService(final String type, final String algorithm)
+    {
+        String upperCaseAlgName = Strings.toUpperCase(algorithm);
+        final String key = type + "." + upperCaseAlgName;
+
+        Service service = serviceMap.get(key);
+
+        if (service == null)
+        {
+            synchronized (this)
+            {
+                if (!serviceMap.containsKey(key))
+                {
+                    service = AccessController.doPrivileged(new PrivilegedAction<Service>()
+                    {
+                        @Override
+                        public Service run()
+                        {
+                            Service service = BouncyCastleProvider.super.getService(type, algorithm);
+                            if (service == null)
+                            {
+                                return null;
+                            }
+                            serviceMap.put(key, service);
+                            // remove legacy entry and swap to service entry
+                            BouncyCastleProvider.super.remove(service.getType() + "." + service.getAlgorithm());
+                            BouncyCastleProvider.super.putService(service);
+
+                            return service;
+                        }
+                    });
+                }
+                else
+                {
+                    service = serviceMap.get(key);
+                }
+            }
+        }
+
+        return service;
     }
 
     private void loadAlgorithms(String packageName, String[] names)
     {
         for (int i = 0; i != names.length; i++)
         {
-            Class clazz = ClassUtil.loadClass(BouncyCastleProvider.class, packageName + names[i] + "$Mappings");
+            loadServiceClass(packageName, names[i]);
+        }
+    }
 
-            if (clazz != null)
+    private void loadAlgorithms(String packageName, CryptoServiceProperties[] services)
+    {
+        for (int i = 0; i != services.length; i++)
+        {
+            CryptoServiceProperties service = services[i];
+            try
             {
-                try
+                CryptoServicesRegistrar.checkConstraints(service);
+
+                loadServiceClass(packageName, service.getServiceName());
+            }
+            catch (CryptoServiceConstraintsException e)
+            {
+                if (LOG.isLoggable(Level.FINE))
                 {
-                    ((AlgorithmProvider)clazz.newInstance()).configure(this);
-                }
-                catch (Exception e)
-                {   // this should never ever happen!!
-                    throw new InternalError("cannot create instance of "
-                        + packageName + names[i] + "$Mappings : " + e);
+                    LOG.fine("service for " + service.getServiceName() + " ignored due to constraints");
                 }
             }
         }
     }
 
+    private void loadServiceClass(String packageName, String serviceName)
+    {
+        Class clazz = ClassUtil.loadClass(BouncyCastleProvider.class, packageName + serviceName + "$Mappings");
+
+        if (clazz != null)
+        {
+            try
+            {
+                ((AlgorithmProvider)clazz.newInstance()).configure(this);
+            }
+            catch (Exception e)
+            {   // this should never ever happen!!
+                throw new InternalError("cannot create instance of "
+                    + packageName + serviceName + "$Mappings : " + e);
+            }
+        }
+    }
     // BEGIN Android-removed: Unsupported algorithms
     /*
+
     private void loadPQCKeys()
     {
+        addKeyInfoConverter(BCObjectIdentifiers.sphincsPlus, new SPHINCSPlusKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.sphincsPlus_sha2_128s_r3, new SPHINCSPlusKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.sphincsPlus_sha2_128f_r3, new SPHINCSPlusKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.sphincsPlus_shake_128s_r3, new SPHINCSPlusKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.sphincsPlus_shake_128f_r3, new SPHINCSPlusKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.sphincsPlus_haraka_128s_r3, new SPHINCSPlusKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.sphincsPlus_haraka_128f_r3, new SPHINCSPlusKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.sphincsPlus_sha2_192s_r3, new SPHINCSPlusKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.sphincsPlus_sha2_192f_r3, new SPHINCSPlusKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.sphincsPlus_shake_192s_r3, new SPHINCSPlusKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.sphincsPlus_shake_192f_r3, new SPHINCSPlusKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.sphincsPlus_haraka_192s_r3, new SPHINCSPlusKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.sphincsPlus_haraka_192f_r3, new SPHINCSPlusKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.sphincsPlus_sha2_256s_r3, new SPHINCSPlusKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.sphincsPlus_sha2_256f_r3, new SPHINCSPlusKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.sphincsPlus_shake_256s_r3, new SPHINCSPlusKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.sphincsPlus_shake_256f_r3, new SPHINCSPlusKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.sphincsPlus_haraka_256s_r3, new SPHINCSPlusKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.sphincsPlus_haraka_256f_r3, new SPHINCSPlusKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.sphincsPlus_sha2_128s_r3_simple, new SPHINCSPlusKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.sphincsPlus_sha2_128f_r3_simple, new SPHINCSPlusKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.sphincsPlus_shake_128s_r3_simple, new SPHINCSPlusKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.sphincsPlus_shake_128f_r3_simple, new SPHINCSPlusKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.sphincsPlus_haraka_128s_r3_simple, new SPHINCSPlusKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.sphincsPlus_haraka_128f_r3_simple, new SPHINCSPlusKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.sphincsPlus_sha2_192s_r3_simple, new SPHINCSPlusKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.sphincsPlus_sha2_192f_r3_simple, new SPHINCSPlusKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.sphincsPlus_shake_192s_r3_simple, new SPHINCSPlusKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.sphincsPlus_shake_192f_r3_simple, new SPHINCSPlusKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.sphincsPlus_haraka_192s_r3_simple, new SPHINCSPlusKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.sphincsPlus_haraka_192f_r3_simple, new SPHINCSPlusKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.sphincsPlus_sha2_256s_r3_simple, new SPHINCSPlusKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.sphincsPlus_sha2_256f_r3_simple, new SPHINCSPlusKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.sphincsPlus_shake_256s_r3_simple, new SPHINCSPlusKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.sphincsPlus_shake_256f_r3_simple, new SPHINCSPlusKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.sphincsPlus_haraka_256s_r3_simple, new SPHINCSPlusKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.sphincsPlus_haraka_256f_r3_simple, new SPHINCSPlusKeyFactorySpi());
+
+        addKeyInfoConverter(BCObjectIdentifiers.sphincsPlus_sha2_128s, new SPHINCSPlusKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.sphincsPlus_sha2_192s, new SPHINCSPlusKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.sphincsPlus_sha2_256s, new SPHINCSPlusKeyFactorySpi());
+        addKeyInfoConverter(new ASN1ObjectIdentifier("1.3.9999.6.4.10"), new SPHINCSPlusKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.sphincsPlus_shake_128f, new SPHINCSPlusKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.sphincsPlus_shake_192f, new SPHINCSPlusKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.sphincsPlus_shake_256f, new SPHINCSPlusKeyFactorySpi());
+
         addKeyInfoConverter(PQCObjectIdentifiers.sphincs256, new Sphincs256KeyFactorySpi());
         addKeyInfoConverter(PQCObjectIdentifiers.newHope, new NHKeyFactorySpi());
         addKeyInfoConverter(PQCObjectIdentifiers.xmss, new XMSSKeyFactorySpi());
@@ -301,6 +445,37 @@
         addKeyInfoConverter(PQCObjectIdentifiers.qTESLA_p_I, new QTESLAKeyFactorySpi());
         addKeyInfoConverter(PQCObjectIdentifiers.qTESLA_p_III, new QTESLAKeyFactorySpi());
         addKeyInfoConverter(PKCSObjectIdentifiers.id_alg_hss_lms_hashsig, new LMSKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.picnic_key, new PicnicKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.falcon_512, new FalconKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.falcon_1024, new FalconKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.dilithium2, new DilithiumKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.dilithium3, new DilithiumKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.dilithium5, new DilithiumKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.dilithium2_aes, new DilithiumKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.dilithium3_aes, new DilithiumKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.dilithium5_aes, new DilithiumKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.kyber512, new KyberKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.kyber768, new KyberKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.kyber1024, new KyberKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.mceliece348864_r3, new CMCEKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.mceliece460896_r3, new CMCEKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.mceliece6688128_r3, new CMCEKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.mceliece6960119_r3, new CMCEKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.mceliece8192128_r3, new CMCEKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.bike128, new BIKEKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.bike192, new BIKEKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.bike256, new BIKEKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.hqc128, new HQCKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.hqc192, new HQCKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.hqc256, new HQCKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.kyber1024, new KyberKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.kyber512_aes, new KyberKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.kyber768_aes, new KyberKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.kyber1024_aes, new KyberKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.ntruhps2048509, new NTRUKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.ntruhps2048677, new NTRUKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.ntruhps4096821, new NTRUKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.ntruhrss701, new NTRUKeyFactorySpi());
     }
     */
     // END Android-removed: Unsupported algorithms
@@ -328,12 +503,25 @@
         put(key, value);
     }
 
+    public void addAlgorithm(String key, String value, Map<String, String> attributes)
+    {
+        addAlgorithm(key, value);
+        addAttributes(key, attributes);
+    }
+
     public void addAlgorithm(String type, ASN1ObjectIdentifier oid, String className)
     {
         addAlgorithm(type + "." + oid, className);
         addAlgorithm(type + ".OID." + oid, className);
     }
 
+    public void addAlgorithm(String type, ASN1ObjectIdentifier oid, String className, Map<String, String> attributes)
+    {
+        addAlgorithm(type, oid, className);
+        addAttributes(type + "." + oid, attributes);
+        addAttributes(type + ".OID." + oid, attributes);
+    }
+    
     public void addKeyInfoConverter(ASN1ObjectIdentifier oid, AsymmetricKeyInfoConverter keyInfoConverter)
     {
         synchronized (keyInfoConverters)
@@ -349,6 +537,8 @@
 
     public void addAttributes(String key, Map<String, String> attributeMap)
     {
+        put(key + " ImplementedIn", "Software");
+
         for (Iterator it = attributeMap.keySet().iterator(); it.hasNext();)
         {
             String attributeName = (String)it.next();
@@ -389,6 +579,11 @@
         }
         // Android-removed: see above
         /*
+        if (publicKeyInfo.getAlgorithm().getAlgorithm().on(BCObjectIdentifiers.picnic_key))
+        {
+            return new PicnicKeyFactorySpi().generatePublic(publicKeyInfo);
+        }
+
         AsymmetricKeyInfoConverter converter = getAsymmetricKeyInfoConverter(publicKeyInfo.getAlgorithm().getAlgorithm());
 
         if (converter == null)
@@ -462,4 +657,42 @@
         return privateProvider;
     }
     // END Android-added: Allow algorithms to be provided privately for BC internals.
+    private static CryptoServiceProperties service(String name, int bitsOfSecurity)
+    {
+        return new JcaCryptoService(name, bitsOfSecurity);
+    }
+
+    private static class JcaCryptoService
+        implements CryptoServiceProperties
+    {
+
+        private final String name;
+        private final int bitsOfSecurity;
+
+        JcaCryptoService(String name, int bitsOfSecurity)
+        {
+            this.name = name;
+            this.bitsOfSecurity = bitsOfSecurity;
+        }
+
+        public int bitsOfSecurity()
+        {
+            return bitsOfSecurity;
+        }
+
+        public String getServiceName()
+        {
+            return name;
+        }
+
+        public CryptoServicePurpose getPurpose()
+        {
+            return CryptoServicePurpose.ANY;
+        }
+
+        public Object getParams()
+        {
+            return null;
+        }
+    }
 }
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jce/provider/CertPathValidatorUtilities.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jce/provider/CertPathValidatorUtilities.java
index 80bf4e9..96915f1 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jce/provider/CertPathValidatorUtilities.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jce/provider/CertPathValidatorUtilities.java
@@ -45,7 +45,6 @@
 import com.android.org.bouncycastle.asn1.ASN1Encodable;
 import com.android.org.bouncycastle.asn1.ASN1Enumerated;
 import com.android.org.bouncycastle.asn1.ASN1GeneralizedTime;
-import com.android.org.bouncycastle.asn1.ASN1InputStream;
 import com.android.org.bouncycastle.asn1.ASN1Integer;
 import com.android.org.bouncycastle.asn1.ASN1ObjectIdentifier;
 import com.android.org.bouncycastle.asn1.ASN1OctetString;
@@ -55,7 +54,6 @@
 import com.android.org.bouncycastle.asn1.ASN1String;
 import com.android.org.bouncycastle.asn1.DEROctetString;
 import com.android.org.bouncycastle.asn1.DERSequence;
-import com.android.org.bouncycastle.asn1.isismtt.ISISMTTObjectIdentifiers;
 import com.android.org.bouncycastle.asn1.x500.X500Name;
 import com.android.org.bouncycastle.asn1.x500.style.RFC4519Style;
 import com.android.org.bouncycastle.asn1.x509.AlgorithmIdentifier;
@@ -69,6 +67,7 @@
 import com.android.org.bouncycastle.asn1.x509.GeneralNames;
 import com.android.org.bouncycastle.asn1.x509.PolicyInformation;
 import com.android.org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import com.android.org.bouncycastle.internal.asn1.isismtt.ISISMTTObjectIdentifiers;
 import com.android.org.bouncycastle.jcajce.PKIXCRLStore;
 import com.android.org.bouncycastle.jcajce.PKIXCRLStoreSelector;
 import com.android.org.bouncycastle.jcajce.PKIXCertRevocationCheckerParameters;
@@ -84,7 +83,6 @@
 import com.android.org.bouncycastle.util.Store;
 import com.android.org.bouncycastle.util.StoreException;
 import com.android.org.bouncycastle.x509.X509AttributeCertificate;
-import com.android.org.bouncycastle.x509.extension.X509ExtensionUtil;
 
 class CertPathValidatorUtilities
 {
@@ -777,7 +775,7 @@
 
                     for (int j = 0; j < genNames.length; j++)
                     {
-                        GeneralName name = genNames[i];
+                        GeneralName name = genNames[j];
                         if (name.getTagNo() == GeneralName.uniformResourceIdentifier)
                         {
                             try
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jce/provider/JCEDHPrivateKey.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jce/provider/JCEDHPrivateKey.java
index 1c024a8..106e077 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jce/provider/JCEDHPrivateKey.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jce/provider/JCEDHPrivateKey.java
@@ -20,7 +20,7 @@
 import com.android.org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
 import com.android.org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
 import com.android.org.bouncycastle.asn1.x509.AlgorithmIdentifier;
-import com.android.org.bouncycastle.asn1.x9.DHDomainParameters;
+import com.android.org.bouncycastle.asn1.x9.DomainParameters;
 import com.android.org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
 import com.android.org.bouncycastle.crypto.params.DHPrivateKeyParameters;
 import com.android.org.bouncycastle.jcajce.provider.asymmetric.util.PKCS12BagAttributeCarrierImpl;
@@ -85,9 +85,9 @@
         }
         else if (id.equals(X9ObjectIdentifiers.dhpublicnumber))
         {
-            DHDomainParameters params = DHDomainParameters.getInstance(seq);
+            DomainParameters params = DomainParameters.getInstance(seq);
 
-            this.dhSpec = new DHParameterSpec(params.getP().getValue(), params.getG().getValue());
+            this.dhSpec = new DHParameterSpec(params.getP(), params.getG());
         }
         else
         {
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jce/provider/JCEDHPublicKey.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jce/provider/JCEDHPublicKey.java
index 156ebba..3170816 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jce/provider/JCEDHPublicKey.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jce/provider/JCEDHPublicKey.java
@@ -17,7 +17,7 @@
 import com.android.org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
 import com.android.org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 import com.android.org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
-import com.android.org.bouncycastle.asn1.x9.DHDomainParameters;
+import com.android.org.bouncycastle.asn1.x9.DomainParameters;
 import com.android.org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
 import com.android.org.bouncycastle.crypto.params.DHPublicKeyParameters;
 import com.android.org.bouncycastle.jcajce.provider.asymmetric.util.KeyUtil;
@@ -80,8 +80,8 @@
 
         this.y = derY.getValue();
 
-        ASN1Sequence seq = ASN1Sequence.getInstance(info.getAlgorithmId().getParameters());
-        ASN1ObjectIdentifier id = info.getAlgorithmId().getAlgorithm();
+        ASN1Sequence seq = ASN1Sequence.getInstance(info.getAlgorithm().getParameters());
+        ASN1ObjectIdentifier id = info.getAlgorithm().getAlgorithm();
 
         // we need the PKCS check to handle older keys marked with the X9 oid.
         if (id.equals(PKCSObjectIdentifiers.dhKeyAgreement) || isPKCSParam(seq))
@@ -99,9 +99,9 @@
         }
         else if (id.equals(X9ObjectIdentifiers.dhpublicnumber))
         {
-            DHDomainParameters params = DHDomainParameters.getInstance(seq);
+            DomainParameters params = DomainParameters.getInstance(seq);
 
-            this.dhSpec = new DHParameterSpec(params.getP().getValue(), params.getG().getValue());
+            this.dhSpec = new DHParameterSpec(params.getP(), params.getG());
         }
         else
         {
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jce/provider/JCEECPrivateKey.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jce/provider/JCEECPrivateKey.java
index 5a165bd..7afb23d 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jce/provider/JCEECPrivateKey.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jce/provider/JCEECPrivateKey.java
@@ -7,24 +7,21 @@
 import java.math.BigInteger;
 import java.security.interfaces.ECPrivateKey;
 import java.security.spec.ECParameterSpec;
-import java.security.spec.ECPoint;
 import java.security.spec.ECPrivateKeySpec;
 import java.security.spec.EllipticCurve;
 import java.util.Enumeration;
 
+import com.android.org.bouncycastle.asn1.ASN1BitString;
 import com.android.org.bouncycastle.asn1.ASN1Encodable;
 import com.android.org.bouncycastle.asn1.ASN1Encoding;
 import com.android.org.bouncycastle.asn1.ASN1Integer;
 import com.android.org.bouncycastle.asn1.ASN1ObjectIdentifier;
 import com.android.org.bouncycastle.asn1.ASN1Primitive;
-import com.android.org.bouncycastle.asn1.ASN1Sequence;
-import com.android.org.bouncycastle.asn1.DERBitString;
 import com.android.org.bouncycastle.asn1.DERNull;
 // Android-removed: Unsupported algorithms
 // import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers;
 // import org.bouncycastle.asn1.cryptopro.ECGOST3410NamedCurves;
 import com.android.org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
-import com.android.org.bouncycastle.asn1.sec.ECPrivateKeyStructure;
 import com.android.org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 import com.android.org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
 import com.android.org.bouncycastle.asn1.x9.X962Parameters;
@@ -53,7 +50,7 @@
     private ECParameterSpec ecSpec;
     private boolean         withCompression;
 
-    private DERBitString publicKey;
+    private ASN1BitString publicKey;
 
     private PKCS12BagAttributeCarrierImpl attrCarrier = new PKCS12BagAttributeCarrierImpl();
 
@@ -218,6 +215,7 @@
             else
             */
             // END Android-removed: Unsupported algorithms
+            if (ecP != null)
             {
                 EllipticCurve ellipticCurve = EC5Util.convertCurve(ecP.getCurve(), ecP.getSeed());
 
@@ -254,7 +252,7 @@
         }
         else
         {
-            ECPrivateKeyStructure ec = new ECPrivateKeyStructure((ASN1Sequence)privKey);
+            com.android.org.bouncycastle.asn1.sec.ECPrivateKey ec = com.android.org.bouncycastle.asn1.sec.ECPrivateKey.getInstance(privKey);
 
             this.d = ec.getKey();
             this.publicKey = ec.getPublicKey();
@@ -314,15 +312,25 @@
         }
         
         PrivateKeyInfo          info;
-        ECPrivateKeyStructure keyStructure;
+        com.android.org.bouncycastle.asn1.sec.ECPrivateKey keyStructure;
 
-        if (publicKey != null)
+        int orderBitLength;
+        if (ecSpec == null)
         {
-            keyStructure = new ECPrivateKeyStructure(this.getS(), publicKey, params);
+            orderBitLength = ECUtil.getOrderBitLength(null, null, this.getS());
         }
         else
         {
-            keyStructure = new ECPrivateKeyStructure(this.getS(), params);
+            orderBitLength = ECUtil.getOrderBitLength(null, ecSpec.getOrder(), this.getS());
+        }
+
+        if (publicKey != null)
+        {
+            keyStructure = new com.android.org.bouncycastle.asn1.sec.ECPrivateKey(orderBitLength, this.getS(), publicKey, params);
+        }
+        else
+        {
+            keyStructure = new com.android.org.bouncycastle.asn1.sec.ECPrivateKey(orderBitLength, this.getS(), params);
         }
 
         try
@@ -434,7 +442,7 @@
 
     }
 
-    private DERBitString getPublicKeyDetails(JCEECPublicKey   pub)
+    private ASN1BitString getPublicKeyDetails(JCEECPublicKey   pub)
     {
         try
         {
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jce/provider/JCEECPublicKey.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jce/provider/JCEECPublicKey.java
index 8fcdeb0..df9978f 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jce/provider/JCEECPublicKey.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jce/provider/JCEECPublicKey.java
@@ -11,11 +11,11 @@
 import java.security.spec.ECPublicKeySpec;
 import java.security.spec.EllipticCurve;
 
+import com.android.org.bouncycastle.asn1.ASN1BitString;
 import com.android.org.bouncycastle.asn1.ASN1Encodable;
 import com.android.org.bouncycastle.asn1.ASN1ObjectIdentifier;
 import com.android.org.bouncycastle.asn1.ASN1OctetString;
 import com.android.org.bouncycastle.asn1.ASN1Primitive;
-import com.android.org.bouncycastle.asn1.DERBitString;
 import com.android.org.bouncycastle.asn1.DERNull;
 import com.android.org.bouncycastle.asn1.DEROctetString;
 // Android-removed: Unsupported algorithms
@@ -192,7 +192,7 @@
 
         if (algID.getAlgorithm().equals(CryptoProObjectIdentifiers.gostR3410_2001))
         {
-            DERBitString bits = info.getPublicKeyData();
+            ASN1BitString bits = info.getPublicKeyData();
             ASN1OctetString key;
             this.algorithm = "ECGOST3410";
 
@@ -273,7 +273,7 @@
                     ecP.getH().intValue());
             }
 
-            DERBitString    bits = info.getPublicKeyData();
+            ASN1BitString   bits = info.getPublicKeyData();
             byte[]          data = bits.getBytes();
             ASN1OctetString key = new DEROctetString(data);
 
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jce/provider/PKIXCRLUtil.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jce/provider/PKIXCRLUtil.java
index 897d0ce..0d5c595 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jce/provider/PKIXCRLUtil.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jce/provider/PKIXCRLUtil.java
@@ -41,8 +41,9 @@
         for (Iterator it = initialSet.iterator(); it.hasNext();)
         {
             X509CRL crl = (X509CRL)it.next();
-
-            if (crl.getNextUpdate().after(validityDate))
+            
+            Date nextUpdate = crl.getNextUpdate();
+            if (nextUpdate == null || nextUpdate.after(validityDate))
             {
                 X509Certificate cert = crlselect.getCertificateChecking();
 
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jce/provider/RFC3280CertPathUtilities.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jce/provider/RFC3280CertPathUtilities.java
index 6df4ad1..3a40cf9 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jce/provider/RFC3280CertPathUtilities.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jce/provider/RFC3280CertPathUtilities.java
@@ -2,7 +2,6 @@
 package com.android.org.bouncycastle.jce.provider;
 
 import java.io.IOException;
-import java.math.BigInteger;
 import java.security.GeneralSecurityException;
 import java.security.PublicKey;
 import java.security.cert.CertPath;
@@ -2099,18 +2098,12 @@
             throw new ExtCertPathValidatorException("Basic constraints extension cannot be decoded.", e, certPath,
                 index);
         }
-        if (bc != null)
+        if (bc != null && bc.isCA())  // if there is a path len constraint and we're not a CA, ignore it! (yes, it happens).
         {
-            BigInteger _pathLengthConstraint = bc.getPathLenConstraint();
-
-            if (_pathLengthConstraint != null)
+            ASN1Integer pathLenConstraint = bc.getPathLenConstraintInteger();
+            if (pathLenConstraint != null)
             {
-                int _plc = _pathLengthConstraint.intValue();
-
-                if (_plc < maxPathLength)
-                {
-                    return _plc;
-                }
+                maxPathLength = Math.min(maxPathLength, pathLenConstraint.intPositiveValueExact());
             }
         }
         return maxPathLength;
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jce/provider/X509CRLEntryObject.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jce/provider/X509CRLEntryObject.java
index 6fd0239..01641ce 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jce/provider/X509CRLEntryObject.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jce/provider/X509CRLEntryObject.java
@@ -24,7 +24,6 @@
 import com.android.org.bouncycastle.asn1.x509.GeneralName;
 import com.android.org.bouncycastle.asn1.x509.GeneralNames;
 import com.android.org.bouncycastle.asn1.x509.TBSCertList;
-import com.android.org.bouncycastle.asn1.x509.X509Extension;
 import com.android.org.bouncycastle.util.Strings;
 
 /**
@@ -288,11 +287,11 @@
                         buf.append("                       critical(").append(ext.isCritical()).append(") ");
                         try
                         {
-                            if (oid.equals(X509Extension.reasonCode))
+                            if (oid.equals(Extension.reasonCode))
                             {
                                 buf.append(CRLReason.getInstance(ASN1Enumerated.getInstance(dIn.readObject()))).append(nl);
                             }
-                            else if (oid.equals(X509Extension.certificateIssuer))
+                            else if (oid.equals(Extension.certificateIssuer))
                             {
                                 buf.append("Certificate issuer: ").append(GeneralNames.getInstance(dIn.readObject())).append(nl);
                             }
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jce/provider/X509CertificateObject.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jce/provider/X509CertificateObject.java
index ad690b6..201ed8d 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jce/provider/X509CertificateObject.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jce/provider/X509CertificateObject.java
@@ -35,13 +35,13 @@
 import com.android.org.bouncycastle.asn1.ASN1BitString;
 import com.android.org.bouncycastle.asn1.ASN1Encodable;
 import com.android.org.bouncycastle.asn1.ASN1Encoding;
+import com.android.org.bouncycastle.asn1.ASN1IA5String;
 import com.android.org.bouncycastle.asn1.ASN1InputStream;
+import com.android.org.bouncycastle.asn1.ASN1Integer;
 import com.android.org.bouncycastle.asn1.ASN1ObjectIdentifier;
 import com.android.org.bouncycastle.asn1.ASN1Primitive;
 import com.android.org.bouncycastle.asn1.ASN1Sequence;
 import com.android.org.bouncycastle.asn1.ASN1String;
-import com.android.org.bouncycastle.asn1.DERBitString;
-import com.android.org.bouncycastle.asn1.DERIA5String;
 import com.android.org.bouncycastle.asn1.DERNull;
 import com.android.org.bouncycastle.asn1.DEROctetString;
 import com.android.org.bouncycastle.asn1.misc.MiscObjectIdentifiers;
@@ -110,7 +110,7 @@
             byte[] bytes = this.getExtensionBytes("2.5.29.15");
             if (bytes != null)
             {
-                ASN1BitString bits = DERBitString.getInstance(ASN1Primitive.fromByteArray(bytes));
+                ASN1BitString bits = ASN1BitString.getInstance(ASN1Primitive.fromByteArray(bytes));
 
                 bytes = bits.getBytes();
                 int length = (bytes.length * 8) - bits.getPadBits();
@@ -293,7 +293,7 @@
 
     public boolean[] getIssuerUniqueID()
     {
-        DERBitString    id = c.getTBSCertificate().getIssuerUniqueId();
+        ASN1BitString    id = c.getTBSCertificate().getIssuerUniqueId();
 
         if (id != null)
         {
@@ -313,7 +313,7 @@
 
     public boolean[] getSubjectUniqueID()
     {
-        DERBitString    id = c.getTBSCertificate().getSubjectUniqueId();
+        ASN1BitString    id = c.getTBSCertificate().getSubjectUniqueId();
 
         if (id != null)
         {
@@ -367,26 +367,18 @@
     
     public int getBasicConstraints()
     {
-        if (basicConstraints != null)
+        if (basicConstraints == null || !basicConstraints.isCA())
         {
-            if (basicConstraints.isCA())
-            {
-                if (basicConstraints.getPathLenConstraint() == null)
-                {
-                    return Integer.MAX_VALUE;
-                }
-                else
-                {
-                    return basicConstraints.getPathLenConstraint().intValue();
-                }
-            }
-            else
-            {
-                return -1;
-            }
+            return -1;
         }
 
-        return -1;
+        ASN1Integer pathLenConstraint = basicConstraints.getPathLenConstraintInteger();
+        if (pathLenConstraint == null)
+        {
+            return Integer.MAX_VALUE;
+        }
+
+        return pathLenConstraint.intPositiveValueExact();
     }
 
     public Collection getSubjectAlternativeNames()
@@ -712,15 +704,15 @@
                         }
                         else if (oid.equals(MiscObjectIdentifiers.netscapeCertType))
                         {
-                            buf.append(new NetscapeCertType((DERBitString)dIn.readObject())).append(nl);
+                            buf.append(new NetscapeCertType((ASN1BitString)dIn.readObject())).append(nl);
                         }
                         else if (oid.equals(MiscObjectIdentifiers.netscapeRevocationURL))
                         {
-                            buf.append(new NetscapeRevocationURL((DERIA5String)dIn.readObject())).append(nl);
+                            buf.append(new NetscapeRevocationURL((ASN1IA5String)dIn.readObject())).append(nl);
                         }
                         else if (oid.equals(MiscObjectIdentifiers.verisignCzagExtension))
                         {
-                            buf.append(new VerisignCzagExtension((DERIA5String)dIn.readObject())).append(nl);
+                            buf.append(new VerisignCzagExtension((ASN1IA5String)dIn.readObject())).append(nl);
                         }
                         else 
                         {
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jce/spec/ECNamedCurveSpec.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jce/spec/ECNamedCurveSpec.java
index 62566fb..4028dbe 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jce/spec/ECNamedCurveSpec.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jce/spec/ECNamedCurveSpec.java
@@ -45,7 +45,7 @@
         {
             Polynomial poly = ((PolynomialExtensionField)field).getMinimalPolynomial();
             int[] exponents = poly.getExponentsPresent();
-            int[] ks = Arrays.reverse(Arrays.copyOfRange(exponents, 1, exponents.length - 1));
+            int[] ks = Arrays.reverseInPlace(Arrays.copyOfRange(exponents, 1, exponents.length - 1));
             return new ECFieldF2m(poly.getDegree(), ks);
         }
     }
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/math/ec/ECCurve.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/math/ec/ECCurve.java
index 1d0c04e..46fdafd 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/math/ec/ECCurve.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/math/ec/ECCurve.java
@@ -3,9 +3,14 @@
 
 import java.math.BigInteger;
 import java.security.SecureRandom;
+import java.util.Collections;
+import java.util.HashSet;
 import java.util.Hashtable;
 import java.util.Random;
+import java.util.Set;
 
+import com.android.org.bouncycastle.crypto.CryptoServicesRegistrar;
+import com.android.org.bouncycastle.math.Primes;
 import com.android.org.bouncycastle.math.ec.endo.ECEndomorphism;
 import com.android.org.bouncycastle.math.ec.endo.GLVEndomorphism;
 import com.android.org.bouncycastle.math.field.FiniteField;
@@ -13,6 +18,7 @@
 import com.android.org.bouncycastle.math.raw.Nat;
 import com.android.org.bouncycastle.util.BigIntegers;
 import com.android.org.bouncycastle.util.Integers;
+import com.android.org.bouncycastle.util.Properties;
 
 /**
  * base class for an elliptic curve
@@ -677,6 +683,8 @@
     public static class Fp extends AbstractFp
     {
         private static final int FP_DEFAULT_COORDS = ECCurve.COORD_JACOBIAN_MODIFIED;
+        private static final Set<BigInteger> knownQs = Collections.synchronizedSet(new HashSet<BigInteger>());
+        private static final BigIntegers.Cache validatedQs = new BigIntegers.Cache();
 
         BigInteger q, r;
         ECPoint.Fp infinity;
@@ -691,9 +699,44 @@
 
         public Fp(BigInteger q, BigInteger a, BigInteger b, BigInteger order, BigInteger cofactor)
         {
+            this(q, a, b, order, cofactor, false);
+        }
+
+        public Fp(BigInteger q, BigInteger a, BigInteger b, BigInteger order, BigInteger cofactor, boolean isInternal)
+        {
             super(q);
 
-            this.q = q;
+            if (isInternal)
+            {
+                this.q = q;
+                knownQs.add(q);
+            }
+            else if (knownQs.contains(q) || validatedQs.contains(q))
+            {
+                this.q = q;
+            }
+            else
+            {
+                int maxBitLength = Properties.asInteger("com.android.org.bouncycastle.ec.fp_max_size", 1042); // 2 * 521
+                int certainty = Properties.asInteger("com.android.org.bouncycastle.ec.fp_certainty", 100);
+
+                int qBitLength = q.bitLength();
+                if (maxBitLength < qBitLength)
+                {
+                    throw new IllegalArgumentException("Fp q value out of range");
+                }
+
+                if (Primes.hasAnySmallFactors(q) || !Primes.isMRProbablePrime(
+                    q, CryptoServicesRegistrar.getSecureRandom(), getNumberOfIterations(qBitLength, certainty)))
+                {
+                    throw new IllegalArgumentException("Fp q value not prime");
+                }
+
+                validatedQs.add(q);
+
+                this.q = q;
+            }
+
             this.r = ECFieldElement.Fp.calculateResidue(q);
             this.infinity = new ECPoint.Fp(this, null, null);
 
@@ -750,6 +793,11 @@
 
         public ECFieldElement fromBigInteger(BigInteger x)
         {
+            if (x == null || x.signum() < 0 || x.compareTo(q) >= 0)
+            {
+                throw new IllegalArgumentException("x value invalid for Fp field element");
+            }
+
             return new ECFieldElement.Fp(this.q, this.r, x);
         }
 
@@ -809,32 +857,11 @@
 
         private static FiniteField buildField(int m, int k1, int k2, int k3)
         {
-            if (k1 == 0)
-            {
-                throw new IllegalArgumentException("k1 must be > 0");
-            }
+            int[] exponents = (k2 | k3) == 0
+                ? new int[]{ 0, k1, m }
+                : new int[]{ 0, k1, k2, k3, m };
 
-            if (k2 == 0)
-            {
-                if (k3 != 0)
-                {
-                    throw new IllegalArgumentException("k3 must be 0 if k2 == 0");
-                }
-
-                return FiniteFields.getBinaryExtensionField(new int[]{ 0, k1, m });
-            }
-
-            if (k2 <= k1)
-            {
-                throw new IllegalArgumentException("k2 must be > k1");
-            }
-
-            if (k3 <= k2)
-            {
-                throw new IllegalArgumentException("k3 must be > k2");
-            }
-
-            return FiniteFields.getBinaryExtensionField(new int[]{ 0, k1, k2, k3, m });
+            return FiniteFields.getBinaryExtensionField(exponents);
         }
 
         protected AbstractF2m(int m, int k1, int k2, int k3)
@@ -927,7 +954,7 @@
                 y = this.getB().sqrt();
             }
             else
-            {
+            { 
                 ECFieldElement beta = x.square().invert().multiply(this.getB()).add(this.getA()).add(x);
                 ECFieldElement z = solveQuadraticEquation(beta);
                 if (z != null)
@@ -1288,7 +1315,16 @@
 
         public ECFieldElement fromBigInteger(BigInteger x)
         {
-            return new ECFieldElement.F2m(this.m, this.k1, this.k2, this.k3, x);
+            if (x == null || x.signum() < 0 || x.bitLength() > m)
+            {
+                throw new IllegalArgumentException("x value invalid in F2m field element");
+            }
+
+            int[] ks = (k2 | k3) == 0
+                ? new int[]{ k1 }
+                : new int[]{ k1, k2, k3 };
+
+            return new ECFieldElement.F2m(m, ks, new LongArray(x));
         }
 
         protected ECPoint createRawPoint(ECFieldElement x, ECFieldElement y)
@@ -1403,4 +1439,36 @@
             };
         }
     }
+
+    private static int getNumberOfIterations(int bits, int certainty)
+    {
+        /*
+         * NOTE: We enforce a minimum 'certainty' of 100 for bits >= 1024 (else 80). Where the
+         * certainty is higher than the FIPS 186-4 tables (C.2/C.3) cater to, extra iterations
+         * are added at the "worst case rate" for the excess.
+         */
+        if (bits >= 1536)
+        {
+            return  certainty <= 100 ? 3
+                :   certainty <= 128 ? 4
+                :   4 + (certainty - 128 + 1) / 2;
+        }
+        else if (bits >= 1024)
+        {
+            return  certainty <= 100 ? 4
+                :   certainty <= 112 ? 5
+                :   5 + (certainty - 112 + 1) / 2;
+        }
+        else if (bits >= 512)
+        {
+            return  certainty <= 80  ? 5
+                :   certainty <= 100 ? 7
+                :   7 + (certainty - 100 + 1) / 2;
+        }
+        else
+        {
+            return  certainty <= 80  ? 40
+                :   40 + (certainty - 80 + 1) / 2;
+        }
+    }
 }
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/math/ec/ECFieldElement.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/math/ec/ECFieldElement.java
index 84b197c..307d661 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/math/ec/ECFieldElement.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/math/ec/ECFieldElement.java
@@ -120,21 +120,8 @@
             return null;
         }
 
-        /**
-         * @deprecated Use ECCurve.fromBigInteger to construct field elements
-         */
-        public Fp(BigInteger q, BigInteger x)
-        {
-            this(q, calculateResidue(q), x);
-        }
-
         Fp(BigInteger q, BigInteger r, BigInteger x)
         {
-            if (x == null || x.signum() < 0 || x.compareTo(q) >= 0)
-            {
-                throw new IllegalArgumentException("x value invalid in Fp field element");
-            }
-
             this.q = q;
             this.r = r;
             this.x = x;
@@ -626,59 +613,6 @@
          */
         LongArray x;
 
-        /**
-         * Constructor for PPB.
-         * @param m  The exponent <code>m</code> of
-         * <code>F<sub>2<sup>m</sup></sub></code>.
-         * @param k1 The integer <code>k1</code> where <code>x<sup>m</sup> +
-         * x<sup>k3</sup> + x<sup>k2</sup> + x<sup>k1</sup> + 1</code>
-         * represents the reduction polynomial <code>f(z)</code>.
-         * @param k2 The integer <code>k2</code> where <code>x<sup>m</sup> +
-         * x<sup>k3</sup> + x<sup>k2</sup> + x<sup>k1</sup> + 1</code>
-         * represents the reduction polynomial <code>f(z)</code>.
-         * @param k3 The integer <code>k3</code> where <code>x<sup>m</sup> +
-         * x<sup>k3</sup> + x<sup>k2</sup> + x<sup>k1</sup> + 1</code>
-         * represents the reduction polynomial <code>f(z)</code>.
-         * @param x The BigInteger representing the value of the field element.
-         * @deprecated Use ECCurve.fromBigInteger to construct field elements
-         */
-        public F2m(
-            int m, 
-            int k1, 
-            int k2, 
-            int k3,
-            BigInteger x)
-        {
-            if (x == null || x.signum() < 0 || x.bitLength() > m)
-            {
-                throw new IllegalArgumentException("x value invalid in F2m field element");
-            }
-
-            if ((k2 == 0) && (k3 == 0))
-            {
-                this.representation = TPB;
-                this.ks = new int[]{ k1 }; 
-            }
-            else
-            {
-                if (k2 >= k3)
-                {
-                    throw new IllegalArgumentException(
-                            "k2 must be smaller than k3");
-                }
-                if (k2 <= 0)
-                {
-                    throw new IllegalArgumentException(
-                            "k2 must be larger than 0");
-                }
-                this.representation = PPB;
-                this.ks = new int[]{ k1, k2, k3 }; 
-            }
-
-            this.m = m;
-            this.x = new LongArray(x);
-        }
-
         F2m(int m, int[] ks, LongArray x)
         {
             this.m = m;
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/math/ec/Tnaf.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/math/ec/Tnaf.java
index db41bfb..0b2b68a 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/math/ec/Tnaf.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/math/ec/Tnaf.java
@@ -3,6 +3,8 @@
 
 import java.math.BigInteger;
 
+import com.android.org.bouncycastle.util.BigIntegers;
+
 /**
  * Class holding methods for point multiplication based on the window
  * &tau;-adic nonadjacent form (WTNAF). The algorithms are based on the
@@ -29,20 +31,19 @@
     public static final byte WIDTH = 4;
 
     /**
-     * 2<sup>4</sup>
-     */
-    public static final byte POW_2_WIDTH = 16;
-
-    /**
      * The <code>&alpha;<sub>u</sub></code>'s for <code>a=0</code> as an array
      * of <code>ZTauElement</code>s.
      */
-    public static final ZTauElement[] alpha0 = {
-        null,
-        new ZTauElement(ECConstants.ONE, ECConstants.ZERO), null,
-        new ZTauElement(MINUS_THREE, MINUS_ONE), null,
-        new ZTauElement(MINUS_ONE, MINUS_ONE), null,
-        new ZTauElement(ECConstants.ONE, MINUS_ONE), null
+    public static final ZTauElement[] alpha0 =
+    {
+        null, new ZTauElement(ECConstants.ONE, ECConstants.ZERO),
+        null, new ZTauElement(MINUS_THREE, MINUS_ONE),
+        null, new ZTauElement(MINUS_ONE, MINUS_ONE),
+        null, new ZTauElement(ECConstants.ONE, MINUS_ONE),
+        null, new ZTauElement(MINUS_ONE, ECConstants.ONE),
+        null, new ZTauElement(ECConstants.ONE, ECConstants.ONE),
+        null, new ZTauElement(ECConstants.THREE, ECConstants.ONE),
+        null, new ZTauElement(MINUS_ONE, ECConstants.ZERO),
     };
 
     /**
@@ -57,11 +58,16 @@
      * The <code>&alpha;<sub>u</sub></code>'s for <code>a=1</code> as an array
      * of <code>ZTauElement</code>s.
      */
-    public static final ZTauElement[] alpha1 = {null,
-        new ZTauElement(ECConstants.ONE, ECConstants.ZERO), null,
-        new ZTauElement(MINUS_THREE, ECConstants.ONE), null,
-        new ZTauElement(MINUS_ONE, ECConstants.ONE), null,
-        new ZTauElement(ECConstants.ONE, ECConstants.ONE), null
+    public static final ZTauElement[] alpha1 =
+    {
+        null, new ZTauElement(ECConstants.ONE, ECConstants.ZERO),
+        null, new ZTauElement(MINUS_THREE, ECConstants.ONE),
+        null, new ZTauElement(MINUS_ONE, ECConstants.ONE),
+        null, new ZTauElement(ECConstants.ONE, ECConstants.ONE),
+        null, new ZTauElement(MINUS_ONE, MINUS_ONE),
+        null, new ZTauElement(ECConstants.ONE, MINUS_ONE),
+        null, new ZTauElement(ECConstants.THREE, MINUS_ONE),
+        null, new ZTauElement(MINUS_ONE, ECConstants.ZERO),
     };
 
     /**
@@ -82,31 +88,29 @@
      */
     public static BigInteger norm(final byte mu, ZTauElement lambda)
     {
-        BigInteger norm;
-
         // s1 = u^2
         BigInteger s1 = lambda.u.multiply(lambda.u);
 
         // s2 = u * v
-        BigInteger s2 = lambda.u.multiply(lambda.v);
+//        BigInteger s2 = lambda.u.multiply(lambda.v);
 
         // s3 = 2 * v^2
-        BigInteger s3 = lambda.v.multiply(lambda.v).shiftLeft(1);
+//        BigInteger s3 = lambda.v.multiply(lambda.v).shiftLeft(1);
 
         if (mu == 1)
         {
-            norm = s1.add(s2).add(s3);
+//            return s1.add(s2).add(s3);
+            return lambda.v.shiftLeft(1).add(lambda.u).multiply(lambda.v).add(s1);
         }
         else if (mu == -1)
         {
-            norm = s1.subtract(s2).add(s3);
+//            return s1.subtract(s2).add(s3);
+            return lambda.v.shiftLeft(1).subtract(lambda.u).multiply(lambda.v).add(s1);
         }
         else
         {
             throw new IllegalArgumentException("mu must be 1 or -1");
         }
-
-        return norm;
     }
 
     /**
@@ -452,10 +456,7 @@
             throw new IllegalArgumentException("mu must be 1 or -1");
         }
 
-        BigInteger u0;
-        BigInteger u1;
-        BigInteger u2;
-
+        BigInteger u0, u1, u2;
         if (doV)
         {
             u0 = ECConstants.TWO;
@@ -470,26 +471,18 @@
         for (int i = 1; i < k; i++)
         {
             // u2 = mu*u1 - 2*u0;
-            BigInteger s = null;
-            if (mu == 1)
+            BigInteger s = u1;
+            if (mu < 0)
             {
-                s = u1;
+                s = s.negate();
             }
-            else
-            {
-                // mu == -1
-                s = u1.negate();
-            }
-            
+
             u2 = s.subtract(u0.shiftLeft(1));
             u0 = u1;
             u1 = u2;
-//            System.out.println(i + ": " + u2);
-//            System.out.println();
         }
 
-        BigInteger[] retVal = {u0, u1};
-        return retVal;
+        return new BigInteger[]{ u0, u1 };
     }
 
     /**
@@ -520,11 +513,7 @@
             BigInteger[] us = getLucas(mu, w, false);
             BigInteger twoToW = ECConstants.ZERO.setBit(w);
             BigInteger u1invert = us[1].modInverse(twoToW);
-            BigInteger tw;
-            tw = ECConstants.TWO.multiply(us[0]).multiply(u1invert).mod(twoToW);
-//            System.out.println("mu = " + mu);
-//            System.out.println("tw = " + tw);
-            return tw;
+            return us[0].shiftLeft(1).multiply(u1invert).mod(twoToW);
         }
     }
 
@@ -543,22 +532,7 @@
             throw new IllegalArgumentException("si is defined for Koblitz curves only");
         }
 
-        int m = curve.getFieldSize();
-        int a = curve.getA().toBigInteger().intValue();
-        byte mu = getMu(a);
-        int shifts = getShiftsForCofactor(curve.getCofactor());
-        int index = m + 3 - a;
-        BigInteger[] ui = getLucas(mu, index, false);
-        if (mu == 1)
-        {
-            ui[0] = ui[0].negate();
-            ui[1] = ui[1].negate();
-        }
-
-        BigInteger dividend0 = ECConstants.ONE.add(ui[1]).shiftRight(shifts);
-        BigInteger dividend1 = ECConstants.ONE.add(ui[0]).shiftRight(shifts).negate();
-
-        return new BigInteger[] { dividend0, dividend1 };
+        return getSi(curve.getFieldSize(), curve.getA().toBigInteger().intValue(), curve.getCofactor());
     }
 
     public static BigInteger[] getSi(int fieldSize, int curveA, BigInteger cofactor)
@@ -609,9 +583,11 @@
      * modular reduction.
      * @return <code>&rho; := k partmod (&tau;<sup>m</sup> - 1)/(&tau; - 1)</code>
      */
-    public static ZTauElement partModReduction(BigInteger k, int m, byte a,
-            BigInteger[] s, byte mu, byte c)
+    public static ZTauElement partModReduction(ECCurve.AbstractF2m curve, BigInteger k, byte a, byte mu, byte c)
     {
+        int m = curve.getFieldSize();
+        BigInteger[] s = curve.getSi();
+
         // d0 = s[0] + mu*s[1]; mu is either 1 or -1
         BigInteger d0;
         if (mu == 1)
@@ -623,20 +599,29 @@
             d0 = s[0].subtract(s[1]);
         }
 
-        BigInteger[] v = getLucas(mu, m, true);
-        BigInteger vm = v[1];
+        BigInteger vm;
+        if (curve.isKoblitz())
+        {
+            /*
+             * Jerome A. Solinas, "Improved Algorithms for Arithmetic on Anomalous Binary Curves", (21).
+             */
+            vm = ECConstants.ONE.shiftLeft(m).add(ECConstants.ONE).subtract(
+                curve.getOrder().multiply(curve.getCofactor()));
+        }
+        else
+        {
+            BigInteger[] v = getLucas(mu, m, true);
+            vm = v[1];
+        }
 
-        SimpleBigDecimal lambda0 = approximateDivisionByN(
-                k, s[0], vm, a, m, c);
-        
-        SimpleBigDecimal lambda1 = approximateDivisionByN(
-                k, s[1], vm, a, m, c);
+        SimpleBigDecimal lambda0 = approximateDivisionByN(k, s[0], vm, a, m, c);
+        SimpleBigDecimal lambda1 = approximateDivisionByN(k, s[1], vm, a, m, c);
 
         ZTauElement q = round(lambda0, lambda1, mu);
 
         // r0 = n - d0*q0 - 2*s1*q1
         BigInteger r0 = k.subtract(d0.multiply(q.u)).subtract(
-                BigInteger.valueOf(2).multiply(s[1]).multiply(q.v));
+            s[1].multiply(q.v).shiftLeft(1));
 
         // r1 = s1*q0 - s0*q1
         BigInteger r1 = s[1].multiply(q.u).subtract(s[0].multiply(q.v));
@@ -655,11 +640,10 @@
     public static ECPoint.AbstractF2m multiplyRTnaf(ECPoint.AbstractF2m p, BigInteger k)
     {
         ECCurve.AbstractF2m curve = (ECCurve.AbstractF2m) p.getCurve();
-        int m = curve.getFieldSize();
         int a = curve.getA().toBigInteger().intValue();
         byte mu = getMu(a);
-        BigInteger[] s = curve.getSi();
-        ZTauElement rho = partModReduction(k, m, (byte)a, s, mu, (byte)10);
+
+        ZTauElement rho = partModReduction(curve, k, (byte)a, mu, (byte)10);
 
         return multiplyTnaf(p, rho);
     }
@@ -676,12 +660,11 @@
     public static ECPoint.AbstractF2m multiplyTnaf(ECPoint.AbstractF2m p, ZTauElement lambda)
     {
         ECCurve.AbstractF2m curve = (ECCurve.AbstractF2m)p.getCurve();
+        ECPoint.AbstractF2m pNeg = (ECPoint.AbstractF2m)p.negate();
         byte mu = getMu(curve.getA());
         byte[] u = tauAdicNaf(mu, lambda);
 
-        ECPoint.AbstractF2m q = multiplyFromTnaf(p, u);
-
-        return q;
+        return multiplyFromTnaf(p, pNeg, u);
     }
 
     /**
@@ -693,11 +676,10 @@
     * @param u The the TNAF of <code>&lambda;</code>..
     * @return <code>&lambda; * p</code>
     */
-    public static ECPoint.AbstractF2m multiplyFromTnaf(ECPoint.AbstractF2m p, byte[] u)
+    public static ECPoint.AbstractF2m multiplyFromTnaf(ECPoint.AbstractF2m p, ECPoint.AbstractF2m pNeg, byte[] u)
     {
         ECCurve curve = p.getCurve();
         ECPoint.AbstractF2m q = (ECPoint.AbstractF2m)curve.getInfinity();
-        ECPoint.AbstractF2m pNeg = (ECPoint.AbstractF2m)p.negate();
         int tauCount = 0;
         for (int i = u.length - 1; i >= 0; i--)
         {
@@ -733,10 +715,9 @@
      * @return The <code>[&tau;]</code>-adic window NAF of
      * <code>&lambda;</code>.
      */
-    public static byte[] tauAdicWNaf(byte mu, ZTauElement lambda,
-            byte width, BigInteger pow2w, BigInteger tw, ZTauElement[] alpha)
+    public static byte[] tauAdicWNaf(byte mu, ZTauElement lambda, int width, int tw, ZTauElement[] alpha)
     {
-        if (!((mu == 1) || (mu == -1)))
+        if (!(mu == 1 || mu == -1))
         {
             throw new IllegalArgumentException("mu must be 1 or -1");
         }
@@ -752,75 +733,72 @@
         // The array holding the TNAF
         byte[] u = new byte[maxLength];
 
-        // 2^(width - 1)
-        BigInteger pow2wMin1 = pow2w.shiftRight(1);
+        int pow2Width = 1 << width;
+        int pow2Mask = pow2Width - 1;
+        int s = 32 - width;
 
         // Split lambda into two BigIntegers to simplify calculations
-        BigInteger r0 = lambda.u;
-        BigInteger r1 = lambda.v;
-        int i = 0;
+        BigInteger R0 = lambda.u;
+        BigInteger R1 = lambda.v;
+        int uPos = 0;
 
         // while lambda <> (0, 0)
-        while (!((r0.equals(ECConstants.ZERO))&&(r1.equals(ECConstants.ZERO))))
+        while (R0.bitLength() > 62 || R1.bitLength() > 62)
         {
-            // if r0 is odd
-            if (r0.testBit(0))
+            if (R0.testBit(0))
             {
-                // uUnMod = r0 + r1*tw mod 2^width
-                BigInteger uUnMod
-                    = r0.add(r1.multiply(tw)).mod(pow2w);
-                
-                byte uLocal;
-                // if uUnMod >= 2^(width - 1)
-                if (uUnMod.compareTo(pow2wMin1) >= 0)
-                {
-                    uLocal = (byte) uUnMod.subtract(pow2w).intValue();
-                }
-                else
-                {
-                    uLocal = (byte) uUnMod.intValue();
-                }
-                // uLocal is now in [-2^(width-1), 2^(width-1)-1]
+                int uVal = R0.intValue() + (R1.intValue() * tw);
+                int alphaPos = uVal & pow2Mask;
 
-                u[i] = uLocal;
-                boolean s = true;
-                if (uLocal < 0)
-                {
-                    s = false;
-                    uLocal = (byte)-uLocal;
-                }
-                // uLocal is now >= 0
-
-                if (s)
-                {
-                    r0 = r0.subtract(alpha[uLocal].u);
-                    r1 = r1.subtract(alpha[uLocal].v);
-                }
-                else
-                {
-                    r0 = r0.add(alpha[uLocal].u);
-                    r1 = r1.add(alpha[uLocal].v);
-                }
-            }
-            else
-            {
-                u[i] = 0;
+                u[uPos] = (byte)((uVal << s) >> s);
+                R0 = R0.subtract(alpha[alphaPos].u);
+                R1 = R1.subtract(alpha[alphaPos].v);
             }
 
-            BigInteger t = r0;
+            ++uPos;
 
+            BigInteger t = R0.shiftRight(1);
             if (mu == 1)
             {
-                r0 = r1.add(r0.shiftRight(1));
+                R0 = R1.add(t);
             }
-            else
+            else // mu == -1
             {
-                // mu == -1
-                r0 = r1.subtract(r0.shiftRight(1));
+                R0 = R1.subtract(t);
             }
-            r1 = t.shiftRight(1).negate();
-            i++;
+            R1 = t.negate();
         }
+
+        long r0_64 = BigIntegers.longValueExact(R0);
+        long r1_64 = BigIntegers.longValueExact(R1);
+
+        // while lambda <> (0, 0)
+        while ((r0_64 | r1_64) != 0L)
+        {
+            if ((r0_64 & 1L) != 0L)
+            {
+                int uVal = (int)r0_64 + ((int)r1_64 * tw);
+                int alphaPos = uVal & pow2Mask;
+
+                u[uPos] = (byte)((uVal << s) >> s);
+                r0_64 -= alpha[alphaPos].u.intValue();
+                r1_64 -= alpha[alphaPos].v.intValue();
+            }
+
+            ++uPos;
+
+            long t_64 = r0_64 >> 1;
+            if (mu == 1)
+            {
+                r0_64 = r1_64 + t_64;
+            }
+            else // mu == -1
+            {
+                r0_64 = r1_64 - t_64;
+            }
+            r1_64 = -t_64;
+        }
+        
         return u;
     }
 
@@ -832,6 +810,7 @@
      */
     public static ECPoint.AbstractF2m[] getPreComp(ECPoint.AbstractF2m p, byte a)
     {
+        ECPoint.AbstractF2m pNeg = (ECPoint.AbstractF2m)p.negate();
         byte[][] alphaTnaf = (a == 0) ? Tnaf.alpha0Tnaf : Tnaf.alpha1Tnaf;
 
         ECPoint.AbstractF2m[] pu = new ECPoint.AbstractF2m[(alphaTnaf.length + 1) >>> 1];
@@ -840,7 +819,7 @@
         int precompLen = alphaTnaf.length;
         for (int i = 3; i < precompLen; i += 2)
         {
-            pu[i >>> 1] = Tnaf.multiplyFromTnaf(p, alphaTnaf[i]);
+            pu[i >>> 1] = Tnaf.multiplyFromTnaf(p, pNeg, alphaTnaf[i]);
         }
 
         p.getCurve().normalizeAll(pu);
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/math/ec/WTauNafMultiplier.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/math/ec/WTauNafMultiplier.java
index 8ce3931..be24e23 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/math/ec/WTauNafMultiplier.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/math/ec/WTauNafMultiplier.java
@@ -31,12 +31,10 @@
 
         ECPoint.AbstractF2m p = (ECPoint.AbstractF2m)point;
         ECCurve.AbstractF2m curve = (ECCurve.AbstractF2m)p.getCurve();
-        int m = curve.getFieldSize();
         byte a = curve.getA().toBigInteger().byteValue();
         byte mu = Tnaf.getMu(a);
-        BigInteger[] s = curve.getSi();
 
-        ZTauElement rho = Tnaf.partModReduction(k, m, a, s, mu, (byte)10);
+        ZTauElement rho = Tnaf.partModReduction(curve, k, a, mu, (byte)10);
 
         return multiplyWTnaf(p, rho, a, mu);
     }
@@ -57,8 +55,7 @@
 
         BigInteger tw = Tnaf.getTw(mu, Tnaf.WIDTH);
 
-        byte[]u = Tnaf.tauAdicWNaf(mu, lambda, Tnaf.WIDTH,
-            BigInteger.valueOf(Tnaf.POW_2_WIDTH), tw, alpha);
+        byte[] u = Tnaf.tauAdicWNaf(mu, lambda, Tnaf.WIDTH, tw.intValue(), alpha);
 
         return multiplyFromWTnaf(p, u);
     }
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/math/ec/custom/sec/SecP256K1Field.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/math/ec/custom/sec/SecP256K1Field.java
index 0d2ca9c..dc8c19a 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/math/ec/custom/sec/SecP256K1Field.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/math/ec/custom/sec/SecP256K1Field.java
@@ -102,6 +102,12 @@
         reduce(tt, z);
     }
 
+    public static void multiply(int[] x, int[] y, int[] z, int[] tt)
+    {
+        Nat256.mul(x, y, tt);
+        reduce(tt, z);
+    }
+
     public static void multiplyAddToExt(int[] x, int[] y, int[] zz)
     {
         int c = Nat256.mulAddTo(x, y, zz);
@@ -175,6 +181,12 @@
         reduce(tt, z);
     }
 
+    public static void square(int[] x, int[] z, int[] tt)
+    {
+        Nat256.square(x, tt);
+        reduce(tt, z);
+    }
+
     public static void squareN(int[] x, int n, int[] z)
     {
 //        assert n > 0;
@@ -190,6 +202,20 @@
         }
     }
 
+    public static void squareN(int[] x, int n, int[] z, int[] tt)
+    {
+//        assert n > 0;
+
+        Nat256.square(x, tt);
+        reduce(tt, z);
+
+        while (--n > 0)
+        {
+            Nat256.square(z, tt);
+            reduce(tt, z);
+        }
+    }
+
     public static void subtract(int[] x, int[] y, int[] z)
     {
         int c = Nat256.sub(x, y, z);
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/math/ec/custom/sec/SecP256K1FieldElement.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/math/ec/custom/sec/SecP256K1FieldElement.java
index f5077be..ed3c080 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/math/ec/custom/sec/SecP256K1FieldElement.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/math/ec/custom/sec/SecP256K1FieldElement.java
@@ -150,49 +150,51 @@
             return this;
         }
 
+        int[] tt0 = Nat256.createExt();
+
         int[] x2 = Nat256.create();
-        SecP256K1Field.square(x1, x2);
-        SecP256K1Field.multiply(x2, x1, x2);
+        SecP256K1Field.square(x1, x2, tt0);
+        SecP256K1Field.multiply(x2, x1, x2, tt0);
         int[] x3 = Nat256.create();
-        SecP256K1Field.square(x2, x3);
-        SecP256K1Field.multiply(x3, x1, x3);
+        SecP256K1Field.square(x2, x3, tt0);
+        SecP256K1Field.multiply(x3, x1, x3, tt0);
         int[] x6 = Nat256.create();
-        SecP256K1Field.squareN(x3, 3, x6);
-        SecP256K1Field.multiply(x6, x3, x6);
+        SecP256K1Field.squareN(x3, 3, x6, tt0);
+        SecP256K1Field.multiply(x6, x3, x6, tt0);
         int[] x9 = x6;
-        SecP256K1Field.squareN(x6, 3, x9);
-        SecP256K1Field.multiply(x9, x3, x9);
+        SecP256K1Field.squareN(x6, 3, x9, tt0);
+        SecP256K1Field.multiply(x9, x3, x9, tt0);
         int[] x11 = x9;
-        SecP256K1Field.squareN(x9, 2, x11);
-        SecP256K1Field.multiply(x11, x2, x11);
+        SecP256K1Field.squareN(x9, 2, x11, tt0);
+        SecP256K1Field.multiply(x11, x2, x11, tt0);
         int[] x22 = Nat256.create();
-        SecP256K1Field.squareN(x11, 11, x22);
-        SecP256K1Field.multiply(x22, x11, x22);
+        SecP256K1Field.squareN(x11, 11, x22, tt0);
+        SecP256K1Field.multiply(x22, x11, x22, tt0);
         int[] x44 = x11;
-        SecP256K1Field.squareN(x22, 22, x44);
-        SecP256K1Field.multiply(x44, x22, x44);
+        SecP256K1Field.squareN(x22, 22, x44, tt0);
+        SecP256K1Field.multiply(x44, x22, x44, tt0);
         int[] x88 = Nat256.create();
-        SecP256K1Field.squareN(x44, 44, x88);
-        SecP256K1Field.multiply(x88, x44, x88);
+        SecP256K1Field.squareN(x44, 44, x88, tt0);
+        SecP256K1Field.multiply(x88, x44, x88, tt0);
         int[] x176 = Nat256.create();
-        SecP256K1Field.squareN(x88, 88, x176);
-        SecP256K1Field.multiply(x176, x88, x176);
+        SecP256K1Field.squareN(x88, 88, x176, tt0);
+        SecP256K1Field.multiply(x176, x88, x176, tt0);
         int[] x220 = x88;
-        SecP256K1Field.squareN(x176, 44, x220);
-        SecP256K1Field.multiply(x220, x44, x220);
+        SecP256K1Field.squareN(x176, 44, x220, tt0);
+        SecP256K1Field.multiply(x220, x44, x220, tt0);
         int[] x223 = x44;
-        SecP256K1Field.squareN(x220, 3, x223);
-        SecP256K1Field.multiply(x223, x3, x223);
+        SecP256K1Field.squareN(x220, 3, x223, tt0);
+        SecP256K1Field.multiply(x223, x3, x223, tt0);
 
         int[] t1 = x223;
-        SecP256K1Field.squareN(t1, 23, t1);
-        SecP256K1Field.multiply(t1, x22, t1);
-        SecP256K1Field.squareN(t1, 6, t1);
-        SecP256K1Field.multiply(t1, x2, t1);
-        SecP256K1Field.squareN(t1, 2, t1);
+        SecP256K1Field.squareN(t1, 23, t1, tt0);
+        SecP256K1Field.multiply(t1, x22, t1, tt0);
+        SecP256K1Field.squareN(t1, 6, t1, tt0);
+        SecP256K1Field.multiply(t1, x2, t1, tt0);
+        SecP256K1Field.squareN(t1, 2, t1, tt0);
 
         int[] t2 = x2;
-        SecP256K1Field.square(t1, t2);
+        SecP256K1Field.square(t1, t2, tt0);
 
         return Nat256.eq(x1, t2) ? new SecP256K1FieldElement(t1) : null;
     }
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/math/ec/custom/sec/SecP256K1Point.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/math/ec/custom/sec/SecP256K1Point.java
index 733663c..c2c01f5 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/math/ec/custom/sec/SecP256K1Point.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/math/ec/custom/sec/SecP256K1Point.java
@@ -52,6 +52,7 @@
         SecP256K1FieldElement Z2 = (SecP256K1FieldElement)b.getZCoord(0);
 
         int c;
+        int[] tt0 = Nat256.createExt();
         int[] tt1 = Nat256.createExt();
         int[] t2 = Nat256.create();
         int[] t3 = Nat256.create();
@@ -67,13 +68,13 @@
         else
         {
             S2 = t3;
-            SecP256K1Field.square(Z1.x, S2);
+            SecP256K1Field.square(Z1.x, S2, tt0);
 
             U2 = t2;
-            SecP256K1Field.multiply(S2, X2.x, U2);
+            SecP256K1Field.multiply(S2, X2.x, U2, tt0);
 
-            SecP256K1Field.multiply(S2, Z1.x, S2);
-            SecP256K1Field.multiply(S2, Y2.x, S2);
+            SecP256K1Field.multiply(S2, Z1.x, S2, tt0);
+            SecP256K1Field.multiply(S2, Y2.x, S2, tt0);
         }
 
         boolean Z2IsOne = Z2.isOne();
@@ -86,13 +87,13 @@
         else
         {
             S1 = t4;
-            SecP256K1Field.square(Z2.x, S1);
+            SecP256K1Field.square(Z2.x, S1, tt0);
 
             U1 = tt1;
-            SecP256K1Field.multiply(S1, X1.x, U1);
+            SecP256K1Field.multiply(S1, X1.x, U1, tt0);
 
-            SecP256K1Field.multiply(S1, Z2.x, S1);
-            SecP256K1Field.multiply(S1, Y1.x, S1);
+            SecP256K1Field.multiply(S1, Z2.x, S1, tt0);
+            SecP256K1Field.multiply(S1, Y1.x, S1, tt0);
         }
 
         int[] H = Nat256.create();
@@ -115,13 +116,13 @@
         }
 
         int[] HSquared = t3;
-        SecP256K1Field.square(H, HSquared);
+        SecP256K1Field.square(H, HSquared, tt0);
 
         int[] G = Nat256.create();
-        SecP256K1Field.multiply(HSquared, H, G);
+        SecP256K1Field.multiply(HSquared, H, G, tt0);
 
         int[] V = t3;
-        SecP256K1Field.multiply(HSquared, U1, V);
+        SecP256K1Field.multiply(HSquared, U1, V, tt0);
 
         SecP256K1Field.negate(G, G);
         Nat256.mul(S1, G, tt1);
@@ -130,7 +131,7 @@
         SecP256K1Field.reduce32(c, G);
 
         SecP256K1FieldElement X3 = new SecP256K1FieldElement(t4);
-        SecP256K1Field.square(R, X3.x);
+        SecP256K1Field.square(R, X3.x, tt0);
         SecP256K1Field.subtract(X3.x, G, X3.x);
 
         SecP256K1FieldElement Y3 = new SecP256K1FieldElement(G);
@@ -141,11 +142,11 @@
         SecP256K1FieldElement Z3 = new SecP256K1FieldElement(H);
         if (!Z1IsOne)
         {
-            SecP256K1Field.multiply(Z3.x, Z1.x, Z3.x);
+            SecP256K1Field.multiply(Z3.x, Z1.x, Z3.x, tt0);
         }
         if (!Z2IsOne)
         {
-            SecP256K1Field.multiply(Z3.x, Z2.x, Z3.x);
+            SecP256K1Field.multiply(Z3.x, Z2.x, Z3.x, tt0);
         }
 
         ECFieldElement[] zs = new ECFieldElement[] { Z3 };
@@ -172,20 +173,21 @@
         SecP256K1FieldElement X1 = (SecP256K1FieldElement)this.x, Z1 = (SecP256K1FieldElement)this.zs[0];
 
         int c;
+        int[] tt0 = Nat256.createExt();
 
         int[] Y1Squared = Nat256.create();
-        SecP256K1Field.square(Y1.x, Y1Squared);
+        SecP256K1Field.square(Y1.x, Y1Squared, tt0);
 
         int[] T = Nat256.create();
-        SecP256K1Field.square(Y1Squared, T);
+        SecP256K1Field.square(Y1Squared, T, tt0);
 
         int[] M = Nat256.create();
-        SecP256K1Field.square(X1.x, M);
+        SecP256K1Field.square(X1.x, M, tt0);
         c = Nat256.addBothTo(M, M, M);
         SecP256K1Field.reduce32(c, M);
 
         int[] S = Y1Squared;
-        SecP256K1Field.multiply(Y1Squared, X1.x, S);
+        SecP256K1Field.multiply(Y1Squared, X1.x, S, tt0);
         c = Nat.shiftUpBits(8, S, 2, 0);
         SecP256K1Field.reduce32(c, S);
 
@@ -194,20 +196,20 @@
         SecP256K1Field.reduce32(c, t1);
 
         SecP256K1FieldElement X3 = new SecP256K1FieldElement(T);
-        SecP256K1Field.square(M, X3.x);
+        SecP256K1Field.square(M, X3.x, tt0);
         SecP256K1Field.subtract(X3.x, S, X3.x);
         SecP256K1Field.subtract(X3.x, S, X3.x);
 
         SecP256K1FieldElement Y3 = new SecP256K1FieldElement(S);
         SecP256K1Field.subtract(S, X3.x, Y3.x);
-        SecP256K1Field.multiply(Y3.x, M, Y3.x);
+        SecP256K1Field.multiply(Y3.x, M, Y3.x, tt0);
         SecP256K1Field.subtract(Y3.x, t1, Y3.x);
 
         SecP256K1FieldElement Z3 = new SecP256K1FieldElement(M);
         SecP256K1Field.twice(Y1.x, Z3.x);
         if (!Z1.isOne())
         {
-            SecP256K1Field.multiply(Z3.x, Z1.x, Z3.x);
+            SecP256K1Field.multiply(Z3.x, Z1.x, Z3.x, tt0);
         }
 
         return new SecP256K1Point(curve, X3, Y3, new ECFieldElement[] { Z3 });
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/math/ec/custom/sec/SecP256R1Field.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/math/ec/custom/sec/SecP256R1Field.java
index 9d3b898..56b9382 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/math/ec/custom/sec/SecP256R1Field.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/math/ec/custom/sec/SecP256R1Field.java
@@ -98,6 +98,12 @@
         reduce(tt, z);
     }
 
+    public static void multiply(int[] x, int[] y, int[] z, int[] tt)
+    {
+        Nat256.mul(x, y, tt);
+        reduce(tt, z);
+    }
+
     public static void multiplyAddToExt(int[] x, int[] y, int[] zz)
     {
         int c = Nat256.mulAddTo(x, y, zz);
@@ -244,6 +250,12 @@
         reduce(tt, z);
     }
 
+    public static void square(int[] x, int[] z, int[] tt)
+    {
+        Nat256.square(x, tt);
+        reduce(tt, z);
+    }
+
     public static void squareN(int[] x, int n, int[] z)
     {
 //        assert n > 0;
@@ -259,6 +271,20 @@
         }
     }
 
+    public static void squareN(int[] x, int n, int[] z, int[] tt)
+    {
+//        assert n > 0;
+
+        Nat256.square(x, tt);
+        reduce(tt, z);
+
+        while (--n > 0)
+        {
+            Nat256.square(z, tt);
+            reduce(tt, z);
+        }
+    }
+
     public static void subtract(int[] x, int[] y, int[] z)
     {
         int c = Nat256.sub(x, y, z);
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/math/ec/custom/sec/SecP256R1FieldElement.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/math/ec/custom/sec/SecP256R1FieldElement.java
index e84cd47..5f879db 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/math/ec/custom/sec/SecP256R1FieldElement.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/math/ec/custom/sec/SecP256R1FieldElement.java
@@ -141,32 +141,33 @@
             return this;
         }
 
+        int[] tt0 = Nat256.createExt();
         int[] t1 = Nat256.create();
         int[] t2 = Nat256.create();
 
-        SecP256R1Field.square(x1, t1);
-        SecP256R1Field.multiply(t1, x1, t1);
+        SecP256R1Field.square(x1, t1, tt0);
+        SecP256R1Field.multiply(t1, x1, t1, tt0);
 
-        SecP256R1Field.squareN(t1, 2, t2);
-        SecP256R1Field.multiply(t2, t1, t2);
+        SecP256R1Field.squareN(t1, 2, t2, tt0);
+        SecP256R1Field.multiply(t2, t1, t2, tt0);
 
-        SecP256R1Field.squareN(t2, 4, t1);
-        SecP256R1Field.multiply(t1, t2, t1);
+        SecP256R1Field.squareN(t2, 4, t1, tt0);
+        SecP256R1Field.multiply(t1, t2, t1, tt0);
 
-        SecP256R1Field.squareN(t1, 8, t2);
-        SecP256R1Field.multiply(t2, t1, t2);
+        SecP256R1Field.squareN(t1, 8, t2, tt0);
+        SecP256R1Field.multiply(t2, t1, t2, tt0);
 
-        SecP256R1Field.squareN(t2, 16, t1);
-        SecP256R1Field.multiply(t1, t2, t1);
+        SecP256R1Field.squareN(t2, 16, t1, tt0);
+        SecP256R1Field.multiply(t1, t2, t1, tt0);
 
-        SecP256R1Field.squareN(t1, 32, t1);
-        SecP256R1Field.multiply(t1, x1, t1);
+        SecP256R1Field.squareN(t1, 32, t1, tt0);
+        SecP256R1Field.multiply(t1, x1, t1, tt0);
 
-        SecP256R1Field.squareN(t1, 96, t1);
-        SecP256R1Field.multiply(t1, x1, t1);
+        SecP256R1Field.squareN(t1, 96, t1, tt0);
+        SecP256R1Field.multiply(t1, x1, t1, tt0);
 
-        SecP256R1Field.squareN(t1, 94, t1);
-        SecP256R1Field.square(t1, t2);
+        SecP256R1Field.squareN(t1, 94, t1, tt0);
+        SecP256R1Field.square(t1, t2, tt0);
 
         return Nat256.eq(x1, t2) ? new SecP256R1FieldElement(t1) : null;
     }
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/math/ec/custom/sec/SecP256R1Point.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/math/ec/custom/sec/SecP256R1Point.java
index 74627ca..70130e2 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/math/ec/custom/sec/SecP256R1Point.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/math/ec/custom/sec/SecP256R1Point.java
@@ -51,6 +51,7 @@
         SecP256R1FieldElement Z2 = (SecP256R1FieldElement)b.getZCoord(0);
 
         int c;
+        int[] tt0 = Nat256.createExt();
         int[] tt1 = Nat256.createExt();
         int[] t2 = Nat256.create();
         int[] t3 = Nat256.create();
@@ -66,13 +67,13 @@
         else
         {
             S2 = t3;
-            SecP256R1Field.square(Z1.x, S2);
+            SecP256R1Field.square(Z1.x, S2, tt0);
 
             U2 = t2;
-            SecP256R1Field.multiply(S2, X2.x, U2);
+            SecP256R1Field.multiply(S2, X2.x, U2, tt0);
 
-            SecP256R1Field.multiply(S2, Z1.x, S2);
-            SecP256R1Field.multiply(S2, Y2.x, S2);
+            SecP256R1Field.multiply(S2, Z1.x, S2, tt0);
+            SecP256R1Field.multiply(S2, Y2.x, S2, tt0);
         }
 
         boolean Z2IsOne = Z2.isOne();
@@ -85,13 +86,13 @@
         else
         {
             S1 = t4;
-            SecP256R1Field.square(Z2.x, S1);
+            SecP256R1Field.square(Z2.x, S1, tt0);
 
             U1 = tt1;
-            SecP256R1Field.multiply(S1, X1.x, U1);
+            SecP256R1Field.multiply(S1, X1.x, U1, tt0);
 
-            SecP256R1Field.multiply(S1, Z2.x, S1);
-            SecP256R1Field.multiply(S1, Y1.x, S1);
+            SecP256R1Field.multiply(S1, Z2.x, S1, tt0);
+            SecP256R1Field.multiply(S1, Y1.x, S1, tt0);
         }
 
         int[] H = Nat256.create();
@@ -114,13 +115,13 @@
         }
 
         int[] HSquared = t3;
-        SecP256R1Field.square(H, HSquared);
+        SecP256R1Field.square(H, HSquared, tt0);
 
         int[] G = Nat256.create();
-        SecP256R1Field.multiply(HSquared, H, G);
+        SecP256R1Field.multiply(HSquared, H, G, tt0);
 
         int[] V = t3;
-        SecP256R1Field.multiply(HSquared, U1, V);
+        SecP256R1Field.multiply(HSquared, U1, V, tt0);
 
         SecP256R1Field.negate(G, G);
         Nat256.mul(S1, G, tt1);
@@ -129,7 +130,7 @@
         SecP256R1Field.reduce32(c, G);
 
         SecP256R1FieldElement X3 = new SecP256R1FieldElement(t4);
-        SecP256R1Field.square(R, X3.x);
+        SecP256R1Field.square(R, X3.x, tt0);
         SecP256R1Field.subtract(X3.x, G, X3.x);
 
         SecP256R1FieldElement Y3 = new SecP256R1FieldElement(G);
@@ -140,11 +141,11 @@
         SecP256R1FieldElement Z3 = new SecP256R1FieldElement(H);
         if (!Z1IsOne)
         {
-            SecP256R1Field.multiply(Z3.x, Z1.x, Z3.x);
+            SecP256R1Field.multiply(Z3.x, Z1.x, Z3.x, tt0);
         }
         if (!Z2IsOne)
         {
-            SecP256R1Field.multiply(Z3.x, Z2.x, Z3.x);
+            SecP256R1Field.multiply(Z3.x, Z2.x, Z3.x, tt0);
         }
 
         ECFieldElement[] zs = new ECFieldElement[]{ Z3 };
@@ -170,14 +171,15 @@
         SecP256R1FieldElement X1 = (SecP256R1FieldElement)this.x, Z1 = (SecP256R1FieldElement)this.zs[0];
 
         int c;
+        int[] tt0 = Nat256.createExt();
         int[] t1 = Nat256.create();
         int[] t2 = Nat256.create();
 
         int[] Y1Squared = Nat256.create();
-        SecP256R1Field.square(Y1.x, Y1Squared);
+        SecP256R1Field.square(Y1.x, Y1Squared, tt0);
 
         int[] T = Nat256.create();
-        SecP256R1Field.square(Y1Squared, T);
+        SecP256R1Field.square(Y1Squared, T, tt0);
 
         boolean Z1IsOne = Z1.isOne();
 
@@ -185,19 +187,19 @@
         if (!Z1IsOne)
         {
             Z1Squared = t2;
-            SecP256R1Field.square(Z1.x, Z1Squared);
+            SecP256R1Field.square(Z1.x, Z1Squared, tt0);
         }
 
         SecP256R1Field.subtract(X1.x, Z1Squared, t1);
 
         int[] M = t2;
         SecP256R1Field.add(X1.x, Z1Squared, M);
-        SecP256R1Field.multiply(M, t1, M);
+        SecP256R1Field.multiply(M, t1, M, tt0);
         c = Nat256.addBothTo(M, M, M);
         SecP256R1Field.reduce32(c, M);
 
         int[] S = Y1Squared;
-        SecP256R1Field.multiply(Y1Squared, X1.x, S);
+        SecP256R1Field.multiply(Y1Squared, X1.x, S, tt0);
         c = Nat.shiftUpBits(8, S, 2, 0);
         SecP256R1Field.reduce32(c, S);
 
@@ -205,20 +207,20 @@
         SecP256R1Field.reduce32(c, t1);
 
         SecP256R1FieldElement X3 = new SecP256R1FieldElement(T);
-        SecP256R1Field.square(M, X3.x);
+        SecP256R1Field.square(M, X3.x, tt0);
         SecP256R1Field.subtract(X3.x, S, X3.x);
         SecP256R1Field.subtract(X3.x, S, X3.x);
 
         SecP256R1FieldElement Y3 = new SecP256R1FieldElement(S);
         SecP256R1Field.subtract(S, X3.x, Y3.x);
-        SecP256R1Field.multiply(Y3.x, M, Y3.x);
+        SecP256R1Field.multiply(Y3.x, M, Y3.x, tt0);
         SecP256R1Field.subtract(Y3.x, t1, Y3.x);
 
         SecP256R1FieldElement Z3 = new SecP256R1FieldElement(M);
         SecP256R1Field.twice(Y1.x, Z3.x);
         if (!Z1IsOne)
         {
-            SecP256R1Field.multiply(Z3.x, Z1.x, Z3.x);
+            SecP256R1Field.multiply(Z3.x, Z1.x, Z3.x, tt0);
         }
 
         return new SecP256R1Point(curve, X3, Y3, new ECFieldElement[]{ Z3 });
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/math/ec/custom/sec/SecP384R1Field.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/math/ec/custom/sec/SecP384R1Field.java
index 8115b8e..9b30caa 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/math/ec/custom/sec/SecP384R1Field.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/math/ec/custom/sec/SecP384R1Field.java
@@ -104,6 +104,12 @@
         reduce(tt, z);
     }
 
+    public static void multiply(int[] x, int[] y, int[] z, int[] tt)
+    {
+        Nat384.mul(x, y, tt);
+        reduce(tt, z);
+    }
+
     public static void negate(int[] x, int[] z)
     {
         if (0 != isZero(x))
@@ -240,6 +246,12 @@
         reduce(tt, z);
     }
 
+    public static void square(int[] x, int[] z, int[] tt)
+    {
+        Nat384.square(x, tt);
+        reduce(tt, z);
+    }
+
     public static void squareN(int[] x, int n, int[] z)
     {
 //        assert n > 0;
@@ -255,6 +267,20 @@
         }
     }
 
+    public static void squareN(int[] x, int n, int[] z, int[] tt)
+    {
+//        assert n > 0;
+
+        Nat384.square(x, tt);
+        reduce(tt, z);
+
+        while (--n > 0)
+        {
+            Nat384.square(z, tt);
+            reduce(tt, z);
+        }
+    }
+
     public static void subtract(int[] x, int[] y, int[] z)
     {
         int c = Nat.sub(12, x, y, z);
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/math/ec/custom/sec/SecP384R1FieldElement.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/math/ec/custom/sec/SecP384R1FieldElement.java
index 2c7cd14..d5ef6d3 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/math/ec/custom/sec/SecP384R1FieldElement.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/math/ec/custom/sec/SecP384R1FieldElement.java
@@ -141,54 +141,55 @@
             return this;
         }
 
+        int[] tt0 = Nat.create(24);
         int[] t1 = Nat.create(12);
         int[] t2 = Nat.create(12);
         int[] t3 = Nat.create(12);
         int[] t4 = Nat.create(12);
 
-        SecP384R1Field.square(x1, t1);
-        SecP384R1Field.multiply(t1, x1, t1);
+        SecP384R1Field.square(x1, t1, tt0);
+        SecP384R1Field.multiply(t1, x1, t1, tt0);
 
-        SecP384R1Field.squareN(t1, 2, t2);
-        SecP384R1Field.multiply(t2, t1, t2);
+        SecP384R1Field.squareN(t1, 2, t2, tt0);
+        SecP384R1Field.multiply(t2, t1, t2, tt0);
 
-        SecP384R1Field.square(t2, t2);
-        SecP384R1Field.multiply(t2, x1, t2);
+        SecP384R1Field.square(t2, t2, tt0);
+        SecP384R1Field.multiply(t2, x1, t2, tt0);
 
-        SecP384R1Field.squareN(t2, 5, t3);
-        SecP384R1Field.multiply(t3, t2, t3);
+        SecP384R1Field.squareN(t2, 5, t3, tt0);
+        SecP384R1Field.multiply(t3, t2, t3, tt0);
 
-        SecP384R1Field.squareN(t3, 5, t4);
-        SecP384R1Field.multiply(t4, t2, t4);
+        SecP384R1Field.squareN(t3, 5, t4, tt0);
+        SecP384R1Field.multiply(t4, t2, t4, tt0);
 
-        SecP384R1Field.squareN(t4, 15, t2);
-        SecP384R1Field.multiply(t2, t4, t2);
+        SecP384R1Field.squareN(t4, 15, t2, tt0);
+        SecP384R1Field.multiply(t2, t4, t2, tt0);
 
-        SecP384R1Field.squareN(t2, 2, t3);
-        SecP384R1Field.multiply(t1, t3, t1);
+        SecP384R1Field.squareN(t2, 2, t3, tt0);
+        SecP384R1Field.multiply(t1, t3, t1, tt0);
 
-        SecP384R1Field.squareN(t3, 28, t3);
-        SecP384R1Field.multiply(t2, t3, t2);
+        SecP384R1Field.squareN(t3, 28, t3, tt0);
+        SecP384R1Field.multiply(t2, t3, t2, tt0);
 
-        SecP384R1Field.squareN(t2, 60, t3);
-        SecP384R1Field.multiply(t3, t2, t3);
+        SecP384R1Field.squareN(t2, 60, t3, tt0);
+        SecP384R1Field.multiply(t3, t2, t3, tt0);
 
         int[] r = t2;
 
-        SecP384R1Field.squareN(t3, 120, r);
-        SecP384R1Field.multiply(r, t3, r);
+        SecP384R1Field.squareN(t3, 120, r, tt0);
+        SecP384R1Field.multiply(r, t3, r, tt0);
 
-        SecP384R1Field.squareN(r, 15, r);
-        SecP384R1Field.multiply(r, t4, r);
+        SecP384R1Field.squareN(r, 15, r, tt0);
+        SecP384R1Field.multiply(r, t4, r, tt0);
 
-        SecP384R1Field.squareN(r, 33, r);
-        SecP384R1Field.multiply(r, t1, r);
+        SecP384R1Field.squareN(r, 33, r, tt0);
+        SecP384R1Field.multiply(r, t1, r, tt0);
 
-        SecP384R1Field.squareN(r, 64, r);
-        SecP384R1Field.multiply(r, x1, r);
+        SecP384R1Field.squareN(r, 64, r, tt0);
+        SecP384R1Field.multiply(r, x1, r, tt0);
 
-        SecP384R1Field.squareN(r, 30, t1);
-        SecP384R1Field.square(t1, t2);
+        SecP384R1Field.squareN(r, 30, t1, tt0);
+        SecP384R1Field.square(t1, t2, tt0);
 
         return Nat.eq(12, x1, t2) ? new SecP384R1FieldElement(t1) : null;
     }
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/math/ec/custom/sec/SecP384R1Point.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/math/ec/custom/sec/SecP384R1Point.java
index c0ccd53..e0e86b5 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/math/ec/custom/sec/SecP384R1Point.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/math/ec/custom/sec/SecP384R1Point.java
@@ -51,6 +51,7 @@
         SecP384R1FieldElement Z2 = (SecP384R1FieldElement)b.getZCoord(0);
 
         int c;
+        int[] tt0 = Nat.create(24);
         int[] tt1 = Nat.create(24);
         int[] tt2 = Nat.create(24);
         int[] t3 = Nat.create(12);
@@ -66,13 +67,13 @@
         else
         {
             S2 = t3;
-            SecP384R1Field.square(Z1.x, S2);
+            SecP384R1Field.square(Z1.x, S2, tt0);
 
             U2 = tt2;
-            SecP384R1Field.multiply(S2, X2.x, U2);
+            SecP384R1Field.multiply(S2, X2.x, U2, tt0);
 
-            SecP384R1Field.multiply(S2, Z1.x, S2);
-            SecP384R1Field.multiply(S2, Y2.x, S2);
+            SecP384R1Field.multiply(S2, Z1.x, S2, tt0);
+            SecP384R1Field.multiply(S2, Y2.x, S2, tt0);
         }
 
         boolean Z2IsOne = Z2.isOne();
@@ -85,13 +86,13 @@
         else
         {
             S1 = t4;
-            SecP384R1Field.square(Z2.x, S1);
+            SecP384R1Field.square(Z2.x, S1, tt0);
 
             U1 = tt1;
-            SecP384R1Field.multiply(S1, X1.x, U1);
+            SecP384R1Field.multiply(S1, X1.x, U1, tt0);
 
-            SecP384R1Field.multiply(S1, Z2.x, S1);
-            SecP384R1Field.multiply(S1, Y1.x, S1);
+            SecP384R1Field.multiply(S1, Z2.x, S1, tt0);
+            SecP384R1Field.multiply(S1, Y1.x, S1, tt0);
         }
 
         int[] H = Nat.create(12);
@@ -114,13 +115,13 @@
         }
 
         int[] HSquared = t3;
-        SecP384R1Field.square(H, HSquared);
+        SecP384R1Field.square(H, HSquared, tt0);
 
         int[] G = Nat.create(12);
-        SecP384R1Field.multiply(HSquared, H, G);
+        SecP384R1Field.multiply(HSquared, H, G, tt0);
 
         int[] V = t3;
-        SecP384R1Field.multiply(HSquared, U1, V);
+        SecP384R1Field.multiply(HSquared, U1, V, tt0);
 
         SecP384R1Field.negate(G, G);
         Nat384.mul(S1, G, tt1);
@@ -129,7 +130,7 @@
         SecP384R1Field.reduce32(c, G);
 
         SecP384R1FieldElement X3 = new SecP384R1FieldElement(t4);
-        SecP384R1Field.square(R, X3.x);
+        SecP384R1Field.square(R, X3.x, tt0);
         SecP384R1Field.subtract(X3.x, G, X3.x);
 
         SecP384R1FieldElement Y3 = new SecP384R1FieldElement(G);
@@ -141,11 +142,11 @@
         SecP384R1FieldElement Z3 = new SecP384R1FieldElement(H);
         if (!Z1IsOne)
         {
-            SecP384R1Field.multiply(Z3.x, Z1.x, Z3.x);
+            SecP384R1Field.multiply(Z3.x, Z1.x, Z3.x, tt0);
         }
         if (!Z2IsOne)
         {
-            SecP384R1Field.multiply(Z3.x, Z2.x, Z3.x);
+            SecP384R1Field.multiply(Z3.x, Z2.x, Z3.x, tt0);
         }
 
         ECFieldElement[] zs = new ECFieldElement[]{ Z3 };
@@ -171,14 +172,15 @@
         SecP384R1FieldElement X1 = (SecP384R1FieldElement)this.x, Z1 = (SecP384R1FieldElement)this.zs[0];
 
         int c;
+        int[] tt0 = Nat.create(24);
         int[] t1 = Nat.create(12);
         int[] t2 = Nat.create(12);
 
         int[] Y1Squared = Nat.create(12);
-        SecP384R1Field.square(Y1.x, Y1Squared);
+        SecP384R1Field.square(Y1.x, Y1Squared, tt0);
 
         int[] T = Nat.create(12);
-        SecP384R1Field.square(Y1Squared, T);
+        SecP384R1Field.square(Y1Squared, T, tt0);
 
         boolean Z1IsOne = Z1.isOne();
 
@@ -186,19 +188,19 @@
         if (!Z1IsOne)
         {
             Z1Squared = t2;
-            SecP384R1Field.square(Z1.x, Z1Squared);
+            SecP384R1Field.square(Z1.x, Z1Squared, tt0);
         }
 
         SecP384R1Field.subtract(X1.x, Z1Squared, t1);
 
         int[] M = t2;
         SecP384R1Field.add(X1.x, Z1Squared, M);
-        SecP384R1Field.multiply(M, t1, M);
+        SecP384R1Field.multiply(M, t1, M, tt0);
         c = Nat.addBothTo(12, M, M, M);
         SecP384R1Field.reduce32(c, M);
 
         int[] S = Y1Squared;
-        SecP384R1Field.multiply(Y1Squared, X1.x, S);
+        SecP384R1Field.multiply(Y1Squared, X1.x, S, tt0);
         c = Nat.shiftUpBits(12, S, 2, 0);
         SecP384R1Field.reduce32(c, S);
 
@@ -206,20 +208,20 @@
         SecP384R1Field.reduce32(c, t1);
 
         SecP384R1FieldElement X3 = new SecP384R1FieldElement(T);
-        SecP384R1Field.square(M, X3.x);
+        SecP384R1Field.square(M, X3.x, tt0);
         SecP384R1Field.subtract(X3.x, S, X3.x);
         SecP384R1Field.subtract(X3.x, S, X3.x);
 
         SecP384R1FieldElement Y3 = new SecP384R1FieldElement(S);
         SecP384R1Field.subtract(S, X3.x, Y3.x);
-        SecP384R1Field.multiply(Y3.x, M, Y3.x);
+        SecP384R1Field.multiply(Y3.x, M, Y3.x, tt0);
         SecP384R1Field.subtract(Y3.x, t1, Y3.x);
 
         SecP384R1FieldElement Z3 = new SecP384R1FieldElement(M);
         SecP384R1Field.twice(Y1.x, Z3.x);
         if (!Z1IsOne)
         {
-            SecP384R1Field.multiply(Z3.x, Z1.x, Z3.x);
+            SecP384R1Field.multiply(Z3.x, Z1.x, Z3.x, tt0);
         }
 
         return new SecP384R1Point(curve, X3, Y3, new ECFieldElement[]{ Z3 });
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/math/ec/custom/sec/SecP521R1Field.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/math/ec/custom/sec/SecP521R1Field.java
index 0e4431d..9d446be 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/math/ec/custom/sec/SecP521R1Field.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/math/ec/custom/sec/SecP521R1Field.java
@@ -81,6 +81,12 @@
         reduce(tt, z);
     }
 
+    public static void multiply(int[] x, int[] y, int[] z, int[] tt)
+    {
+        implMultiply(x, y, tt);
+        reduce(tt, z);
+    }
+
     public static void negate(int[] x, int[] z)
     {
         if (0 != isZero(x))
@@ -149,6 +155,12 @@
         reduce(tt, z);
     }
 
+    public static void square(int[] x, int[] z, int[] tt)
+    {
+        implSquare(x, tt);
+        reduce(tt, z);
+    }
+
     public static void squareN(int[] x, int n, int[] z)
     {
 //        assert n > 0;
@@ -164,6 +176,20 @@
         }
     }
 
+    public static void squareN(int[] x, int n, int[] z, int[] tt)
+    {
+//        assert n > 0;
+
+        implSquare(x, tt);
+        reduce(tt, z);
+
+        while (--n > 0)
+        {
+            implSquare(z, tt);
+            reduce(tt, z);
+        }
+    }
+
     public static void subtract(int[] x, int[] y, int[] z)
     {
         int c = Nat.sub(16, x, y, z) + x[16] - y[16];
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/math/ec/custom/sec/SecP521R1FieldElement.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/math/ec/custom/sec/SecP521R1FieldElement.java
index 8f50293..893ec85 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/math/ec/custom/sec/SecP521R1FieldElement.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/math/ec/custom/sec/SecP521R1FieldElement.java
@@ -142,11 +142,12 @@
             return this;
         }
 
+        int[] tt0 = Nat.create(33);
         int[] t1 = Nat.create(17);
         int[] t2 = Nat.create(17);
 
-        SecP521R1Field.squareN(x1, 519, t1);
-        SecP521R1Field.square(t1, t2);
+        SecP521R1Field.squareN(x1, 519, t1, tt0);
+        SecP521R1Field.square(t1, t2, tt0);
 
         return Nat.eq(17, x1, t2) ? new SecP521R1FieldElement(t1) : null;
     }
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/math/ec/custom/sec/SecP521R1Point.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/math/ec/custom/sec/SecP521R1Point.java
index 6e91cd4..af635fd 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/math/ec/custom/sec/SecP521R1Point.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/math/ec/custom/sec/SecP521R1Point.java
@@ -49,6 +49,7 @@
         SecP521R1FieldElement Z1 = (SecP521R1FieldElement)this.zs[0];
         SecP521R1FieldElement Z2 = (SecP521R1FieldElement)b.getZCoord(0);
 
+        int[] tt0 = Nat.create(33);
         int[] t1 = Nat.create(17);
         int[] t2 = Nat.create(17);
         int[] t3 = Nat.create(17);
@@ -64,13 +65,13 @@
         else
         {
             S2 = t3;
-            SecP521R1Field.square(Z1.x, S2);
+            SecP521R1Field.square(Z1.x, S2, tt0);
 
             U2 = t2;
-            SecP521R1Field.multiply(S2, X2.x, U2);
+            SecP521R1Field.multiply(S2, X2.x, U2, tt0);
 
-            SecP521R1Field.multiply(S2, Z1.x, S2);
-            SecP521R1Field.multiply(S2, Y2.x, S2);
+            SecP521R1Field.multiply(S2, Z1.x, S2, tt0);
+            SecP521R1Field.multiply(S2, Y2.x, S2, tt0);
         }
 
         boolean Z2IsOne = Z2.isOne();
@@ -83,13 +84,13 @@
         else
         {
             S1 = t4;
-            SecP521R1Field.square(Z2.x, S1);
+            SecP521R1Field.square(Z2.x, S1, tt0);
 
             U1 = t1;
-            SecP521R1Field.multiply(S1, X1.x, U1);
+            SecP521R1Field.multiply(S1, X1.x, U1, tt0);
 
-            SecP521R1Field.multiply(S1, Z2.x, S1);
-            SecP521R1Field.multiply(S1, Y1.x, S1);
+            SecP521R1Field.multiply(S1, Z2.x, S1, tt0);
+            SecP521R1Field.multiply(S1, Y1.x, S1, tt0);
         }
 
         int[] H = Nat.create(17);
@@ -112,35 +113,35 @@
         }
 
         int[] HSquared = t3;
-        SecP521R1Field.square(H, HSquared);
+        SecP521R1Field.square(H, HSquared, tt0);
 
         int[] G = Nat.create(17);
-        SecP521R1Field.multiply(HSquared, H, G);
+        SecP521R1Field.multiply(HSquared, H, G, tt0);
 
         int[] V = t3;
-        SecP521R1Field.multiply(HSquared, U1, V);
+        SecP521R1Field.multiply(HSquared, U1, V, tt0);
 
-        SecP521R1Field.multiply(S1, G, t1);
+        SecP521R1Field.multiply(S1, G, t1, tt0);
 
         SecP521R1FieldElement X3 = new SecP521R1FieldElement(t4);
-        SecP521R1Field.square(R, X3.x);
+        SecP521R1Field.square(R, X3.x, tt0);
         SecP521R1Field.add(X3.x, G, X3.x);
         SecP521R1Field.subtract(X3.x, V, X3.x);
         SecP521R1Field.subtract(X3.x, V, X3.x);
 
         SecP521R1FieldElement Y3 = new SecP521R1FieldElement(G);
         SecP521R1Field.subtract(V, X3.x, Y3.x);
-        SecP521R1Field.multiply(Y3.x, R, t2);
+        SecP521R1Field.multiply(Y3.x, R, t2, tt0);
         SecP521R1Field.subtract(t2, t1, Y3.x);
 
         SecP521R1FieldElement Z3 = new SecP521R1FieldElement(H);
         if (!Z1IsOne)
         {
-            SecP521R1Field.multiply(Z3.x, Z1.x, Z3.x);
+            SecP521R1Field.multiply(Z3.x, Z1.x, Z3.x, tt0);
         }
         if (!Z2IsOne)
         {
-            SecP521R1Field.multiply(Z3.x, Z2.x, Z3.x);
+            SecP521R1Field.multiply(Z3.x, Z2.x, Z3.x, tt0);
         }
 
         ECFieldElement[] zs = new ECFieldElement[]{ Z3 };
@@ -165,14 +166,15 @@
 
         SecP521R1FieldElement X1 = (SecP521R1FieldElement)this.x, Z1 = (SecP521R1FieldElement)this.zs[0];
 
+        int[] tt0 = Nat.create(33);
         int[] t1 = Nat.create(17);
         int[] t2 = Nat.create(17);
 
         int[] Y1Squared = Nat.create(17);
-        SecP521R1Field.square(Y1.x, Y1Squared);
+        SecP521R1Field.square(Y1.x, Y1Squared, tt0);
 
         int[] T = Nat.create(17);
-        SecP521R1Field.square(Y1Squared, T);
+        SecP521R1Field.square(Y1Squared, T, tt0);
 
         boolean Z1IsOne = Z1.isOne();
 
@@ -180,19 +182,19 @@
         if (!Z1IsOne)
         {
             Z1Squared = t2;
-            SecP521R1Field.square(Z1.x, Z1Squared);
+            SecP521R1Field.square(Z1.x, Z1Squared, tt0);
         }
 
         SecP521R1Field.subtract(X1.x, Z1Squared, t1);
 
         int[] M = t2;
         SecP521R1Field.add(X1.x, Z1Squared, M);
-        SecP521R1Field.multiply(M, t1, M);
+        SecP521R1Field.multiply(M, t1, M, tt0);
         Nat.addBothTo(17, M, M, M);
         SecP521R1Field.reduce23(M);
 
         int[] S = Y1Squared;
-        SecP521R1Field.multiply(Y1Squared, X1.x, S);
+        SecP521R1Field.multiply(Y1Squared, X1.x, S, tt0);
         Nat.shiftUpBits(17, S, 2, 0);
         SecP521R1Field.reduce23(S);
 
@@ -200,20 +202,20 @@
         SecP521R1Field.reduce23(t1);
 
         SecP521R1FieldElement X3 = new SecP521R1FieldElement(T);
-        SecP521R1Field.square(M, X3.x);
+        SecP521R1Field.square(M, X3.x, tt0);
         SecP521R1Field.subtract(X3.x, S, X3.x);
         SecP521R1Field.subtract(X3.x, S, X3.x);
 
         SecP521R1FieldElement Y3 = new SecP521R1FieldElement(S);
         SecP521R1Field.subtract(S, X3.x, Y3.x);
-        SecP521R1Field.multiply(Y3.x, M, Y3.x);
+        SecP521R1Field.multiply(Y3.x, M, Y3.x, tt0);
         SecP521R1Field.subtract(Y3.x, t1, Y3.x);
 
         SecP521R1FieldElement Z3 = new SecP521R1FieldElement(M);
         SecP521R1Field.twice(Y1.x, Z3.x);
         if (!Z1IsOne)
         {
-            SecP521R1Field.multiply(Z3.x, Z1.x, Z3.x);
+            SecP521R1Field.multiply(Z3.x, Z1.x, Z3.x, tt0);
         }
 
         return new SecP521R1Point(curve, X3, Y3, new ECFieldElement[]{ Z3 });
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/math/ec/endo/GLVTypeBParameters.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/math/ec/endo/GLVTypeBParameters.java
index e7f3fe0..e26c5ef 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/math/ec/endo/GLVTypeBParameters.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/math/ec/endo/GLVTypeBParameters.java
@@ -11,17 +11,6 @@
     protected final BigInteger beta, lambda;
     protected final ScalarSplitParameters splitParams;
 
-    /**
-     * @deprecated Use constructor taking a {@link ScalarSplitParameters} instead.
-     */
-    public GLVTypeBParameters(BigInteger beta, BigInteger lambda, BigInteger[] v1, BigInteger[] v2, BigInteger g1,
-        BigInteger g2, int bits)
-    {
-        this.beta = beta;
-        this.lambda = lambda;
-        this.splitParams = new ScalarSplitParameters(v1, v2, g1, g2, bits);
-    }
-
     public GLVTypeBParameters(BigInteger beta, BigInteger lambda, ScalarSplitParameters splitParams)
     {
         this.beta = beta;
@@ -43,60 +32,4 @@
     {
         return splitParams;
     }
-
-    /**
-     * @deprecated Access via {@link #getSplitParams()} instead.
-     */
-    public BigInteger getV1A()
-    {
-        return getSplitParams().getV1A();
-    }
-
-    /**
-     * @deprecated Access via {@link #getSplitParams()} instead.
-     */
-    public BigInteger getV1B()
-    {
-        return getSplitParams().getV1B();
-    }
-
-    /**
-     * @deprecated Access via {@link #getSplitParams()} instead.
-     */
-    public BigInteger getV2A()
-    {
-        return getSplitParams().getV2A();
-    }
-
-    /**
-     * @deprecated Access via {@link #getSplitParams()} instead.
-     */
-    public BigInteger getV2B()
-    {
-        return getSplitParams().getV2B();
-    }
-
-    /**
-     * @deprecated Access via {@link #getSplitParams()} instead.
-     */
-    public BigInteger getG1()
-    {
-        return getSplitParams().getG1();
-    }
-
-    /**
-     * @deprecated Access via {@link #getSplitParams()} instead.
-     */
-    public BigInteger getG2()
-    {
-        return getSplitParams().getG2();
-    }
-    
-    /**
-     * @deprecated Access via {@link #getSplitParams()} instead.
-     */
-    public int getBits()
-    {
-        return getSplitParams().getBits();
-    }
 }
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/math/field/FiniteFields.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/math/field/FiniteFields.java
index 3b2f617..4f1ae40 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/math/field/FiniteFields.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/math/field/FiniteFields.java
@@ -3,6 +3,8 @@
 
 import java.math.BigInteger;
 
+import com.android.org.bouncycastle.util.BigIntegers;
+
 /**
  * @hide This class is not part of the Android public SDK API
  */
@@ -43,7 +45,7 @@
 
         if (bitLength < 3)
         {
-            switch (characteristic.intValue())
+            switch (BigIntegers.intValueExact(characteristic))
             {
             case 2:
                 return GF_2;
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/math/raw/Mod.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/math/raw/Mod.java
index 80e45ca..e775375 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/math/raw/Mod.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/math/raw/Mod.java
@@ -18,17 +18,6 @@
     private static final int M30 = 0x3FFFFFFF;
     private static final long M32L = 0xFFFFFFFFL;
 
-    /** @deprecated Will be removed. */
-    public static void add(int[] p, int[] x, int[] y, int[] z)
-    {
-        int len = p.length;
-        int c = Nat.add(len, x, y, z);
-        if (c != 0)
-        {
-            Nat.subFrom(len, p, z);
-        }
-    }
-
     public static void checkedModOddInverse(int[] m, int[] x, int[] z)
     {
         if (0 == modOddInverse(m, x, z))
@@ -58,12 +47,6 @@
         return  x;
     }
 
-    /** @deprecated Use {@link #checkedModOddInverseVar(int[], int[], int[])} instead. */
-    public static void invert(int[] m, int[] x, int[] z)
-    {
-        checkedModOddInverseVar(m,  x,  z);
-    }
-
     public static int modOddInverse(int[] m, int[] x, int[] z)
     {
         int len32 = m.length;
@@ -86,13 +69,13 @@
         encode30(bits, m, 0, M, 0);
         System.arraycopy(M, 0, F, 0, len30);
 
-        int eta = -1;
+        int delta = 0;
         int m0Inv32 = inverse32(M[0]);
         int maxDivsteps = getMaximumDivsteps(bits);
 
         for (int divSteps = 0; divSteps < maxDivsteps; divSteps += 30)
         {
-            eta = divsteps30(eta, F[0], G[0], t);
+            delta = divsteps30(delta, F[0], G[0], t);
             updateDE30(len30, D, E, t, m0Inv32, M);
             updateFG30(len30, F, G, t);
         }
@@ -232,17 +215,6 @@
         return s;
     }
 
-    /** @deprecated Will be removed. */
-    public static void subtract(int[] p, int[] x, int[] y, int[] z)
-    {
-        int len = p.length;
-        int c = Nat.sub(len, x, y, z);
-        if (c != 0)
-        {
-            Nat.addTo(len, p, z);
-        }
-    }
-
     private static int add30(int len30, int[] D, int[] M)
     {
 //        assert len30 > 0;
@@ -280,7 +252,6 @@
 //        assert len30 > 0;
 //        assert D.length >= len30;
 //        assert M.length >= len30;
-
         int last = len30 - 1;
 
         {
@@ -335,38 +306,38 @@
         }
     }
 
-    private static int divsteps30(int eta, int f0, int g0, int[] t)
+    private static int divsteps30(int delta, int f0, int g0, int[] t)
     {
-        int u = 1, v = 0, q = 0, r = 1;
+        int u = 1 << 30, v = 0, q = 0, r = 1 << 30;
         int f = f0, g = g0;
 
         for (int i = 0; i < 30; ++i)
         {
 //            assert (f & 1) == 1;
-//            assert (u * f0 + v * g0) == f << i;
-//            assert (q * f0 + r * g0) == g << i;
+//            assert ((u >> (30 - i)) * f0 + (v >> (30 - i)) * g0) == f << i;
+//            assert ((q >> (30 - i)) * f0 + (r >> (30 - i)) * g0) == g << i;
 
-            int c1 = eta >> 31;
+            int c1 = delta >> 31;
             int c2 = -(g & 1);
 
-            int x = (f ^ c1) - c1;
-            int y = (u ^ c1) - c1;
-            int z = (v ^ c1) - c1;
+            int x = f ^ c1;
+            int y = u ^ c1;
+            int z = v ^ c1;
 
-            g += x & c2;
-            q += y & c2;
-            r += z & c2;
+            g -= x & c2;
+            q -= y & c2;
+            r -= z & c2;
 
-            c1 &= c2;
-            eta = (eta ^ c1) - (c1 + 1);
+            c2 &= ~c1;
+            delta = (delta ^ c2) - (c2 - 1);
 
-            f += g & c1;
-            u += q & c1;
-            v += r & c1;
+            f += g & c2;
+            u += q & c2;
+            v += r & c2;
 
             g >>= 1;
-            u <<= 1;
-            v <<= 1;
+            q >>= 1;
+            r >>= 1;
         }
 
         t[0] = u;
@@ -374,7 +345,7 @@
         t[2] = q;
         t[3] = r;
 
-        return eta;
+        return delta;
     }
 
     private static int divsteps30Var(int eta, int f0, int g0, int[] t)
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/math/raw/Nat.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/math/raw/Nat.java
index cb15aea..0787776 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/math/raw/Nat.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/math/raw/Nat.java
@@ -236,6 +236,19 @@
         return (int)c;
     }
 
+    public static int caddTo(int len, int mask, int[] x, int[] z)
+    {
+        long MASK = -(mask & 1) & M;
+        long c = 0;
+        for (int i = 0; i < len; ++i)
+        {
+            c += (z[i] & M) + (x[i] & MASK);
+            z[i] = (int)c;
+            c >>>= 32;
+        }
+        return (int)c;
+    }
+
     public static void cmov(int len, int mask, int[] x, int xOff, int[] z, int zOff)
     {
         mask = -(mask & 1);
@@ -1129,41 +1142,6 @@
         shiftUpBit(extLen, zz, zzOff, x[xOff] << 31);
     }
 
-    /**
-     * @deprecated Use {@link #squareWordAddTo(int[], int, int[])} instead.
-     */
-    public static int squareWordAdd(int[] x, int xPos, int[] z)
-    {
-        long c = 0, xVal = x[xPos] & M;
-        int i = 0;
-        do
-        {
-            c += xVal * (x[i] & M) + (z[xPos + i] & M);
-            z[xPos + i] = (int)c;
-            c >>>= 32;
-        }
-        while (++i < xPos);
-        return (int)c;
-    }
-
-    /**
-     * @deprecated Use {@link #squareWordAddTo(int[], int, int, int[], int)} instead.
-     */
-    public static int squareWordAdd(int[] x, int xOff, int xPos, int[] z, int zOff)
-    {
-        long c = 0, xVal = x[xOff + xPos] & M;
-        int i = 0;
-        do
-        {
-            c += xVal * (x[xOff + i] & M) + (z[xPos + zOff] & M);
-            z[xPos + zOff] = (int)c;
-            c >>>= 32;
-            ++zOff;
-        }
-        while (++i < xPos);
-        return (int)c;
-    }
-
     public static int squareWordAddTo(int[] x, int xPos, int[] z)
     {
         long c = 0, xVal = x[xPos] & M;
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/math/raw/Nat224.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/math/raw/Nat224.java
index 9f5b623..986bac2 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/math/raw/Nat224.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/math/raw/Nat224.java
@@ -147,6 +147,33 @@
         return (int)c;
     }
 
+    public static int addTo(int[] x, int[] z, int cIn)
+    {
+        long c = cIn & M;
+        c += (x[0] & M) + (z[0] & M);
+        z[0] = (int)c;
+        c >>>= 32;
+        c += (x[1] & M) + (z[1] & M);
+        z[1] = (int)c;
+        c >>>= 32;
+        c += (x[2] & M) + (z[2] & M);
+        z[2] = (int)c;
+        c >>>= 32;
+        c += (x[3] & M) + (z[3] & M);
+        z[3] = (int)c;
+        c >>>= 32;
+        c += (x[4] & M) + (z[4] & M);
+        z[4] = (int)c;
+        c >>>= 32;
+        c += (x[5] & M) + (z[5] & M);
+        z[5] = (int)c;
+        c >>>= 32;
+        c += (x[6] & M) + (z[6] & M);
+        z[6] = (int)c;
+        c >>>= 32;
+        return (int)c;
+    }
+
     public static int addTo(int[] x, int xOff, int[] z, int zOff, int cIn)
     {
         long c = cIn & M;
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/math/raw/Nat256.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/math/raw/Nat256.java
index b38e430..dfa2284 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/math/raw/Nat256.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/math/raw/Nat256.java
@@ -162,6 +162,36 @@
         return (int)c;
     }
 
+    public static int addTo(int[] x, int[] z, int cIn)
+    {
+        long c = cIn & M;
+        c += (x[0] & M) + (z[0] & M);
+        z[0] = (int)c;
+        c >>>= 32;
+        c += (x[1] & M) + (z[1] & M);
+        z[1] = (int)c;
+        c >>>= 32;
+        c += (x[2] & M) + (z[2] & M);
+        z[2] = (int)c;
+        c >>>= 32;
+        c += (x[3] & M) + (z[3] & M);
+        z[3] = (int)c;
+        c >>>= 32;
+        c += (x[4] & M) + (z[4] & M);
+        z[4] = (int)c;
+        c >>>= 32;
+        c += (x[5] & M) + (z[5] & M);
+        z[5] = (int)c;
+        c >>>= 32;
+        c += (x[6] & M) + (z[6] & M);
+        z[6] = (int)c;
+        c >>>= 32;
+        c += (x[7] & M) + (z[7] & M);
+        z[7] = (int)c;
+        c >>>= 32;
+        return (int)c;
+    }
+
     public static int addTo(int[] x, int xOff, int[] z, int zOff, int cIn)
     {
         long c = cIn & M;
@@ -606,6 +636,77 @@
         }
     }
 
+    public static void mul128(int[] x, int[] y128, int[] zz)
+    {
+        long x_0 = x[0] & M;
+        long x_1 = x[1] & M;
+        long x_2 = x[2] & M;
+        long x_3 = x[3] & M;
+        long x_4 = x[4] & M;
+        long x_5 = x[5] & M;
+        long x_6 = x[6] & M;
+        long x_7 = x[7] & M;
+
+        {
+            long c = 0, y_0 = y128[0] & M;
+            c += y_0 * x_0;
+            zz[0] = (int)c;
+            c >>>= 32;
+            c += y_0 * x_1;
+            zz[1] = (int)c;
+            c >>>= 32;
+            c += y_0 * x_2;
+            zz[2] = (int)c;
+            c >>>= 32;
+            c += y_0 * x_3;
+            zz[3] = (int)c;
+            c >>>= 32;
+            c += y_0 * x_4;
+            zz[4] = (int)c;
+            c >>>= 32;
+            c += y_0 * x_5;
+            zz[5] = (int)c;
+            c >>>= 32;
+            c += y_0 * x_6;
+            zz[6] = (int)c;
+            c >>>= 32;
+            c += y_0 * x_7;
+            zz[7] = (int)c;
+            c >>>= 32;
+            zz[8] = (int)c;
+        }
+
+        for (int i = 1; i < 4; ++i)
+        {
+            long c = 0, y_i = y128[i] & M;
+            c += y_i * x_0 + (zz[i + 0] & M);
+            zz[i + 0] = (int)c;
+            c >>>= 32;
+            c += y_i * x_1 + (zz[i + 1] & M);
+            zz[i + 1] = (int)c;
+            c >>>= 32;
+            c += y_i * x_2 + (zz[i + 2] & M);
+            zz[i + 2] = (int)c;
+            c >>>= 32;
+            c += y_i * x_3 + (zz[i + 3] & M);
+            zz[i + 3] = (int)c;
+            c >>>= 32;
+            c += y_i * x_4 + (zz[i + 4] & M);
+            zz[i + 4] = (int)c;
+            c >>>= 32;
+            c += y_i * x_5 + (zz[i + 5] & M);
+            zz[i + 5] = (int)c;
+            c >>>= 32;
+            c += y_i * x_6 + (zz[i + 6] & M);
+            zz[i + 6] = (int)c;
+            c >>>= 32;
+            c += y_i * x_7 + (zz[i + 7] & M);
+            zz[i + 7] = (int)c;
+            c >>>= 32;
+            zz[i + 8] = (int)c;
+        }
+    }
+
     public static int mulAddTo(int[] x, int[] y, int[] zz)
     {
         long y_0 = y[0] & M;
@@ -1351,6 +1452,36 @@
         return (int)c;
     }
 
+    public static int subFrom(int[] x, int[] z, int cIn)
+    {
+        long c = cIn & M;
+        c += (z[0] & M) - (x[0] & M);
+        z[0] = (int)c;
+        c >>= 32;
+        c += (z[1] & M) - (x[1] & M);
+        z[1] = (int)c;
+        c >>= 32;
+        c += (z[2] & M) - (x[2] & M);
+        z[2] = (int)c;
+        c >>= 32;
+        c += (z[3] & M) - (x[3] & M);
+        z[3] = (int)c;
+        c >>= 32;
+        c += (z[4] & M) - (x[4] & M);
+        z[4] = (int)c;
+        c >>= 32;
+        c += (z[5] & M) - (x[5] & M);
+        z[5] = (int)c;
+        c >>= 32;
+        c += (z[6] & M) - (x[6] & M);
+        z[6] = (int)c;
+        c >>= 32;
+        c += (z[7] & M) - (x[7] & M);
+        z[7] = (int)c;
+        c >>= 32;
+        return (int)c;
+    }
+
     public static int subFrom(int[] x, int xOff, int[] z, int zOff)
     {
         long c = 0;
@@ -1381,6 +1512,36 @@
         return (int)c;
     }
 
+    public static int subFrom(int[] x, int xOff, int[] z, int zOff, int cIn)
+    {
+        long c = cIn & M;
+        c += (z[zOff + 0] & M) - (x[xOff + 0] & M);
+        z[zOff + 0] = (int)c;
+        c >>= 32;
+        c += (z[zOff + 1] & M) - (x[xOff + 1] & M);
+        z[zOff + 1] = (int)c;
+        c >>= 32;
+        c += (z[zOff + 2] & M) - (x[xOff + 2] & M);
+        z[zOff + 2] = (int)c;
+        c >>= 32;
+        c += (z[zOff + 3] & M) - (x[xOff + 3] & M);
+        z[zOff + 3] = (int)c;
+        c >>= 32;
+        c += (z[zOff + 4] & M) - (x[xOff + 4] & M);
+        z[zOff + 4] = (int)c;
+        c >>= 32;
+        c += (z[zOff + 5] & M) - (x[xOff + 5] & M);
+        z[zOff + 5] = (int)c;
+        c >>= 32;
+        c += (z[zOff + 6] & M) - (x[xOff + 6] & M);
+        z[zOff + 6] = (int)c;
+        c >>= 32;
+        c += (z[zOff + 7] & M) - (x[xOff + 7] & M);
+        z[zOff + 7] = (int)c;
+        c >>= 32;
+        return (int)c;
+    }
+
     public static BigInteger toBigInteger(int[] x)
     {
         byte[] bs = new byte[32];
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/util/Arrays.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/util/Arrays.java
index 5c66305..dd06d04 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/util/Arrays.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/util/Arrays.java
@@ -151,6 +151,47 @@
         return 0 == d;
     }
 
+    /**
+     * A constant time equals comparison - does not terminate early if
+     * comparison fails. For best results always pass the expected value
+     * as the first parameter.
+     *
+     * @param expected first array
+     * @param supplied second array
+     * @return true if arrays equal, false otherwise.
+     */
+    public static boolean constantTimeAreEqual(
+        char[] expected,
+        char[] supplied)
+    {
+        if (expected == null || supplied == null)
+        {
+            return false;
+        }
+
+        if (expected == supplied)
+        {
+            return true;
+        }
+
+        int len = Math.min(expected.length, supplied.length);
+
+        int nonEqual = expected.length ^ supplied.length;
+
+        // do the char-wise comparison
+        for (int i = 0; i != len; i++)
+        {
+            nonEqual |= (expected[i] ^ supplied[i]);
+        }
+        // If supplied is longer than expected, iterate over rest of supplied with NOPs
+        for (int i = len; i < supplied.length; i++)
+        {
+            nonEqual |= ((byte)supplied[i] ^ (byte)~supplied[i]);
+        }
+
+        return nonEqual == 0;
+    }
+
     public static int compareUnsigned(byte[] a, byte[] b)
     {
         if (a == b)
@@ -276,14 +317,6 @@
         java.util.Arrays.fill(a, val);
     }
 
-    /**
-     * @deprecated Use {@link #fill(byte[], int, int, byte)} instead.
-     */
-    public static void fill(byte[] a, int fromIndex, byte val)
-    {
-        fill(a, fromIndex, a.length, val);
-    }
-
     public static void fill(byte[] a, int fromIndex, int toIndex, byte val)
     {
         java.util.Arrays.fill(a, fromIndex, toIndex, val);
@@ -304,14 +337,6 @@
         java.util.Arrays.fill(a, val);
     }
 
-    /**
-     * @deprecated Use {@link #fill(int[], int, int, int)} instead.
-     */
-    public static void fill(int[] a, int fromIndex, int val)
-    {
-        java.util.Arrays.fill(a, fromIndex, a.length, val);
-    }
-
     public static void fill(int[] a, int fromIndex, int toIndex, int val)
     {
         java.util.Arrays.fill(a, fromIndex, toIndex, val);
@@ -322,14 +347,6 @@
         java.util.Arrays.fill(a, val);
     }
 
-    /**
-     * @deprecated Use {@link #fill(long[], int, int, long)} instead.
-     */
-    public static void fill(long[] a, int fromIndex, long val)
-    {
-        java.util.Arrays.fill(a, fromIndex, a.length, val);
-    }
-
     public static void fill(long[] a, int fromIndex, int toIndex, long val)
     {
         java.util.Arrays.fill(a, fromIndex, toIndex, val);
@@ -350,14 +367,6 @@
         java.util.Arrays.fill(a, val);
     }
 
-    /**
-     * @deprecated Use {@link #fill(short[], int, int, short)} instead.
-     */
-    public static void fill(short[] a, int fromIndex, short val)
-    {
-        java.util.Arrays.fill(a, fromIndex, a.length, val);
-    }
-
     public static void fill(short[] a, int fromIndex, int toIndex, short val)
     {
         java.util.Arrays.fill(a, fromIndex, toIndex, val);
@@ -796,9 +805,7 @@
         int newLength = to - from;
         if (newLength < 0)
         {
-            StringBuffer sb = new StringBuffer(from);
-            sb.append(" > ").append(to);
-            throw new IllegalArgumentException(sb.toString());
+            throw new IllegalArgumentException(from + " > " + to);
         }
         return newLength;
     }
@@ -1065,6 +1072,80 @@
         return result;
     }
 
+    public static void reverse(byte[] input, byte[] output)
+    {
+        int last = input.length - 1;
+        for (int i = 0; i <= last; ++i)
+        {
+            output[i] = input[last - i];
+        }
+    }
+
+    public static byte[] reverseInPlace(byte[] a)
+    {
+        if (null == a)
+        {
+            return null;
+        }
+
+        int p1 = 0, p2 = a.length - 1;
+        while (p1 < p2)
+        {
+            byte t1 = a[p1], t2 = a[p2];
+            a[p1++] = t2;
+            a[p2--] = t1;
+        }
+
+        return a;
+    }
+
+    public static void reverseInPlace(byte[] a, int aOff, int aLen)
+    {
+        int p1 = aOff, p2 = aOff + aLen - 1;
+        while (p1 < p2)
+        {
+            byte t1 = a[p1], t2 = a[p2];
+            a[p1++] = t2;
+            a[p2--] = t1;
+        }
+    }
+
+    public static short[] reverseInPlace(short[] a)
+    {
+        if (null == a)
+        {
+            return null;
+        }
+
+        int p1 = 0, p2 = a.length - 1;
+        while (p1 < p2)
+        {
+            short t1 = a[p1], t2 = a[p2];
+            a[p1++] = t2;
+            a[p2--] = t1;
+        }
+
+        return a;
+    }
+
+    public static int[] reverseInPlace(int[] a)
+    {
+        if (null == a)
+        {
+            return null;
+        }
+
+        int p1 = 0, p2 = a.length - 1;
+        while (p1 < p2)
+        {
+            int t1 = a[p1], t2 = a[p2];
+            a[p1++] = t2;
+            a[p2--] = t1;
+        }
+
+        return a;
+    }
+
     /**
      * Iterator backed by a specific array.
      * @hide This class is not part of the Android public SDK API
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/util/BigIntegers.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/util/BigIntegers.java
index 8b06237..2a05b22 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/util/BigIntegers.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/util/BigIntegers.java
@@ -3,6 +3,11 @@
 
 import java.math.BigInteger;
 import java.security.SecureRandom;
+import java.util.Map;
+import java.util.WeakHashMap;
+
+import com.android.org.bouncycastle.math.raw.Mod;
+import com.android.org.bouncycastle.math.raw.Nat;
 
 import com.android.org.bouncycastle.math.raw.Mod;
 import com.android.org.bouncycastle.math.raw.Nat;
@@ -23,7 +28,7 @@
 
     /**
      * Return the passed in value as an unsigned byte array.
-     * 
+     *
      * @param value the value to be converted.
      * @return a byte array without a leading zero byte if present in the signed encoding.
      */
@@ -31,16 +36,15 @@
         BigInteger value)
     {
         byte[] bytes = value.toByteArray();
-        
         if (bytes[0] == 0 && bytes.length != 1)
         {
             byte[] tmp = new byte[bytes.length - 1];
-            
+
             System.arraycopy(bytes, 1, tmp, 0, tmp.length);
-            
+
             return tmp;
         }
-        
+
         return bytes;
     }
 
@@ -48,10 +52,8 @@
      * Return the passed in value as an unsigned byte array of the specified length, padded with
      * leading zeros as necessary..
      *
-     * @param length
-     *            the fixed length of the result
-     * @param value
-     *            the value to be converted.
+     * @param length the fixed length of the result
+     * @param value  the value to be converted.
      * @return a byte array padded to a fixed length with leading zeros.
      */
     public static byte[] asUnsignedByteArray(int length, BigInteger value)
@@ -79,14 +81,10 @@
      * Write the passed in value as unsigned bytes to the specified buffer range, padded with
      * leading zeros as necessary.
      *
-     * @param value
-     *            the value to be converted.
-     * @param buf
-     *            the buffer to which the value is written.
-     * @param off
-     *            the start offset in array <code>buf</code> at which the data is written.
-     * @param len
-     *            the fixed length of data written (possibly padded with leading zeros).
+     * @param value the value to be converted.
+     * @param buf   the buffer to which the value is written.
+     * @param off   the start offset in array <code>buf</code> at which the data is written.
+     * @param len   the fixed length of data written (possibly padded with leading zeros).
      */
     public static void asUnsignedByteArray(BigInteger value, byte[] buf, int off, int len)
     {
@@ -106,22 +104,23 @@
         }
 
         int padLen = len - count;
-        Arrays.fill(buf,  off, off + padLen, (byte)0x00);
+        Arrays.fill(buf, off, off + padLen, (byte)0x00);
         System.arraycopy(bytes, start, buf, off + padLen, count);
     }
 
+
     /**
      * Return a random BigInteger not less than 'min' and not greater than 'max'
-     * 
-     * @param min the least value that may be generated
-     * @param max the greatest value that may be generated
+     *
+     * @param min    the least value that may be generated
+     * @param max    the greatest value that may be generated
      * @param random the source of randomness
      * @return a random BigInteger value in the range [min,max]
      */
     public static BigInteger createRandomInRange(
-        BigInteger      min,
-        BigInteger      max,
-        SecureRandom    random)
+        BigInteger min,
+        BigInteger max,
+        SecureRandom random)
     {
         int cmp = min.compareTo(max);
         if (cmp >= 0)
@@ -152,6 +151,7 @@
         return createRandomBigInteger(max.subtract(min).bitLength() - 1, random).add(min);
     }
 
+
     public static BigInteger fromUnsignedByteArray(byte[] buf)
     {
         return new BigInteger(1, buf);
@@ -168,6 +168,28 @@
         return new BigInteger(1, mag);
     }
 
+    public static byte byteValueExact(BigInteger x)
+    {
+        // Since Java 1.8 could use BigInteger.byteValueExact instead
+        if (x.bitLength() > 7)
+        {
+            throw new ArithmeticException("BigInteger out of int range");
+        }
+
+        return x.byteValue();
+    }
+
+    public static short shortValueExact(BigInteger x)
+    {
+        // Since Java 1.8 could use BigInteger.shortValueExact instead
+        if (x.bitLength() > 15)
+        {
+            throw new ArithmeticException("BigInteger out of int range");
+        }
+
+        return x.shortValue();
+    }
+
     public static int intValueExact(BigInteger x)
     {
         // Since Java 1.8 could use BigInteger.intValueExact instead
@@ -176,7 +198,7 @@
             throw new ArithmeticException("BigInteger out of int range");
         }
 
-        return x.intValue(); 
+        return x.intValue();
     }
 
     public static long longValueExact(BigInteger x)
@@ -187,7 +209,7 @@
             throw new ArithmeticException("BigInteger out of long range");
         }
 
-        return x.longValue(); 
+        return x.longValue();
     }
 
     public static BigInteger modOddInverse(BigInteger M, BigInteger X)
@@ -266,7 +288,7 @@
      * Return a positive BigInteger in the range of 0 to 2**bitLength - 1.
      *
      * @param bitLength maximum bit length for the generated BigInteger.
-     * @param random a source of randomness.
+     * @param random    a source of randomness.
      * @return a positive BigInteger
      */
     public static BigInteger createRandomBigInteger(int bitLength, SecureRandom random)
@@ -276,7 +298,7 @@
 
     // Hexadecimal value of the product of the 131 smallest odd primes from 3 to 743
     private static final BigInteger SMALL_PRIMES_PRODUCT = new BigInteger(
-              "8138e8a0fcf3a4e84a771d40fd305d7f4aa59306d7251de54d98af8fe95729a1f"
+        "8138e8a0fcf3a4e84a771d40fd305d7f4aa59306d7251de54d98af8fe95729a1f"
             + "73d893fa424cd2edc8636a6c3285e022b0e3866a565ae8108eed8591cd4fe8d2"
             + "ce86165a978d719ebf647f362d33fca29cd179fb42401cbaf3df0c614056f9c8"
             + "f3cfd51e474afb6bc6974f78db8aba8e9e517fded658591ab7502bd41849462f",
@@ -287,7 +309,7 @@
      * Return a prime number candidate of the specified bit length.
      *
      * @param bitLength bit length for the generated BigInteger.
-     * @param random a source of randomness.
+     * @param random    a source of randomness.
      * @return a positive BigInteger of numBits length
      */
     public static BigInteger createRandomPrime(int bitLength, int certainty, SecureRandom random)
@@ -349,4 +371,41 @@
 
         return rv;
     }
+
+    /**
+     * @hide This class is not part of the Android public SDK API
+     */
+    public static class Cache
+    {
+        private final Map<BigInteger, Boolean> values = new WeakHashMap<BigInteger, Boolean>();
+        private final BigInteger[] preserve = new BigInteger[8];
+
+        private int preserveCounter = 0;
+
+        public synchronized void add(BigInteger value)
+        {
+            values.put(value, Boolean.TRUE);
+            preserve[preserveCounter] = value;
+            preserveCounter = (preserveCounter + 1) % preserve.length;
+        }
+
+        public synchronized boolean contains(BigInteger value)
+        {
+            return values.containsKey(value);
+        }
+
+        public synchronized int size()
+        {
+            return values.size();
+        }
+
+        public synchronized void clear()
+        {
+            values.clear();
+            for (int i = 0; i != preserve.length; i++)
+            {
+                preserve[i] = null;
+            }
+        }
+    }
 }
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/util/Bytes.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/util/Bytes.java
new file mode 100644
index 0000000..2862502
--- /dev/null
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/util/Bytes.java
@@ -0,0 +1,44 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.org.bouncycastle.util;
+
+/**
+ * Utility methods and constants for bytes.
+ * @hide This class is not part of the Android public SDK API
+ */
+public class Bytes
+{
+    public static final int BYTES = 1;
+    public static final int SIZE = Byte.SIZE;
+
+    public static void xor(int len, byte[] x, byte[] y, byte[] z)
+    {
+        for (int i = 0; i < len; ++i)
+        {
+            z[i] = (byte)(x[i] ^ y[i]);
+        }
+    }
+
+    public static void xor(int len, byte[] x, int xOff, byte[] y, int yOff, byte[] z, int zOff)
+    {
+        for (int i = 0; i < len; ++i)
+        {
+            z[zOff + i] = (byte)(x[xOff + i] ^ y[yOff + i]);
+        }
+    }
+
+    public static void xorTo(int len, byte[] x, byte[] z)
+    {
+        for (int i = 0; i < len; ++i)
+        {
+            z[i] ^= x[i];
+        }
+    }
+
+    public static void xorTo(int len, byte[] x, int xOff, byte[] z, int zOff)
+    {
+        for (int i = 0; i < len; ++i)
+        {
+            z[zOff + i] ^= x[xOff + i];
+        }
+    }
+}
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/util/Characters.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/util/Characters.java
new file mode 100644
index 0000000..7e16b78
--- /dev/null
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/util/Characters.java
@@ -0,0 +1,13 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.org.bouncycastle.util;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+public class Characters
+{
+    public static Character valueOf(char c)
+    {
+        return Character.valueOf(c);
+    }
+}
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/util/Exceptions.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/util/Exceptions.java
new file mode 100644
index 0000000..3bf6646
--- /dev/null
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/util/Exceptions.java
@@ -0,0 +1,26 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.org.bouncycastle.util;
+
+import java.io.IOException;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+public class Exceptions
+{
+    public static IllegalArgumentException illegalArgumentException(String message, Throwable cause)
+    {
+        return new IllegalArgumentException(message, cause);
+    }
+
+    public static IllegalStateException illegalStateException(String message, Throwable cause)
+    {
+        return new IllegalStateException(message, cause);
+    }
+
+    public static IOException ioException(String message, Throwable cause)
+    {
+        return new IOException(message, cause);
+    }
+
+}
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/util/IPAddress.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/util/IPAddress.java
index b5f960c..f8f791b 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/util/IPAddress.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/util/IPAddress.java
@@ -14,8 +14,7 @@
      *
      * @return true if a valid address, false otherwise
      */
-    public static boolean isValid(
-        String address)
+    public static boolean isValid(String address)
     {
         return isValidIPv4(address) || isValidIPv6(address);
     }
@@ -27,8 +26,7 @@
      *
      * @return true if a valid address with netmask, false otherwise
      */
-    public static boolean isValidWithNetMask(
-        String address)
+    public static boolean isValidWithNetMask(String address)
     {
         return isValidIPv4WithNetmask(address) || isValidIPv6WithNetmask(address);
     }
@@ -40,79 +38,42 @@
      *
      * @return true if a valid IPv4 address, false otherwise
      */
-    public static boolean isValidIPv4(
-        String address)
+    public static boolean isValidIPv4(String address)
     {
-        if (address.length() == 0)
+        int length = address.length();
+        if (length < 7 || length > 15)
         {
             return false;
         }
 
-        int octet;
-        int octets = 0;
-        
-        String temp = address+".";
-
-        int pos;
-        int start = 0;
-        while (start < temp.length()
-            && (pos = temp.indexOf('.', start)) > start)
+        int pos = 0;
+        for (int octetIndex = 0; octetIndex < 3; ++octetIndex)
         {
-            if (octets == 4)
+            int end = address.indexOf('.', pos);
+
+            if (!isParseableIPv4Octet(address, pos, end))
             {
                 return false;
             }
-            try
-            {
-                octet = Integer.parseInt(temp.substring(start, pos));
-            }
-            catch (NumberFormatException ex)
-            {
-                return false;
-            }
-            if (octet < 0 || octet > 255)
-            {
-                return false;
-            }
-            start = pos + 1;
-            octets++;
+
+            pos = end + 1;
         }
 
-        return octets == 4;
+        return isParseableIPv4Octet(address, pos, length);
     }
 
-    public static boolean isValidIPv4WithNetmask(
-        String address)
+    public static boolean isValidIPv4WithNetmask(String address)
     {
         int index = address.indexOf("/");
-        String mask = address.substring(index + 1);
-
-        return (index > 0) && isValidIPv4(address.substring(0, index))
-                           && (isValidIPv4(mask) || isMaskValue(mask, 32));
-    }
-
-    public static boolean isValidIPv6WithNetmask(
-        String address)
-    {
-        int index = address.indexOf("/");
-        String mask = address.substring(index + 1);
-
-        return (index > 0) && (isValidIPv6(address.substring(0, index))
-                           && (isValidIPv6(mask) || isMaskValue(mask, 128)));
-    }
-
-    private static boolean isMaskValue(String component, int size)
-    {
-        try
-        {
-            int value = Integer.parseInt(component);
-
-            return value >= 0 && value <= size;
-        }
-        catch (NumberFormatException e)
+        if (index < 1)
         {
             return false;
         }
+
+        String before = address.substring(0, index);
+        String after = address.substring(index + 1);
+
+        return isValidIPv4(before) && (isValidIPv4(after) || isParseableIPv4Mask(after));
     }
 
     /**
@@ -122,72 +83,131 @@
      *
      * @return true if a valid IPv6 address, false otherwise
      */
-    public static boolean isValidIPv6(
-        String address)
+    public static boolean isValidIPv6(String address)
     {
         if (address.length() == 0)
         {
             return false;
         }
 
-        int octet;
-        int octets = 0;
+        char firstChar = address.charAt(0);
+        if (firstChar != ':' && Character.digit(firstChar, 16) < 0)
+        {
+            return false;
+        }        
 
+        int segmentCount = 0;
         String temp = address + ":";
         boolean doubleColonFound = false;
-        int pos;
-        int start = 0;
-        while (start < temp.length()
-            && (pos = temp.indexOf(':', start)) >= start)
+
+        int pos = 0, end;
+        while (pos < temp.length() && (end = temp.indexOf(':', pos)) >= pos)
         {
-            if (octets == 8)
+            if (segmentCount == 8)
             {
                 return false;
             }
 
-            if (start != pos)
+            if (pos != end)
             {
-                String value = temp.substring(start, pos);
+                String value = temp.substring(pos, end);
 
-                if (pos == (temp.length() - 1) && value.indexOf('.') > 0)
+                if (end == temp.length() - 1 && value.indexOf('.') > 0)
                 {
+                    // add an extra one as address covers 2 words.
+                    if (++segmentCount == 8)
+                    {
+                        return false;
+                    }
                     if (!isValidIPv4(value))
                     {
                         return false;
                     }
-
-                    octets++; // add an extra one as address covers 2 words.
                 }
-                else
+                else if (!isParseableIPv6Segment(temp, pos, end))
                 {
-                    try
-                    {
-                        octet = Integer.parseInt(temp.substring(start, pos), 16);
-                    }
-                    catch (NumberFormatException ex)
-                    {
-                        return false;
-                    }
-                    if (octet < 0 || octet > 0xffff)
-                    {
-                        return false;
-                    }
+                    return false;
                 }
             }
             else
             {
-                if (pos != 1 && pos != temp.length() - 1 && doubleColonFound)
+                if (end != 1 && end != temp.length() - 1 && doubleColonFound)
                 {
                     return false;
                 }
                 doubleColonFound = true;
             }
-            start = pos + 1;
-            octets++;
+
+            pos = end + 1;
+            ++segmentCount;
         }
 
-        return octets == 8 || doubleColonFound;
+        return segmentCount == 8 || doubleColonFound;
+    }
+
+    public static boolean isValidIPv6WithNetmask(String address)
+    {
+        int index = address.indexOf("/");
+        if (index < 1)
+        {
+            return false;
+        }
+
+        String before = address.substring(0, index);
+        String after = address.substring(index + 1);
+
+        return isValidIPv6(before) && (isValidIPv6(after) || isParseableIPv6Mask(after));
+    }
+
+    private static boolean isParseableIPv4Mask(String s)
+    {
+        return isParseable(s, 0, s.length(), 10, 2, false, 0, 32);
+    }
+
+    private static boolean isParseableIPv4Octet(String s, int pos, int end)
+    {
+        return isParseable(s, pos, end, 10, 3, true, 0, 255);
+    }
+
+    private static boolean isParseableIPv6Mask(String s)
+    {
+        return isParseable(s, 0, s.length(), 10, 3, false, 1, 128);
+    }
+
+    private static boolean isParseableIPv6Segment(String s, int pos, int end)
+    {
+        return isParseable(s, pos, end, 16, 4, true, 0x0000, 0xFFFF);
+    }
+
+    private static boolean isParseable(String s, int pos, int end, int radix, int maxLength, boolean allowLeadingZero,
+        int minValue, int maxValue)
+    {
+        int length = end - pos;
+        if (length < 1 | length > maxLength)
+        {
+            return false;
+        }
+
+        boolean checkLeadingZero = length > 1 & !allowLeadingZero; 
+        if (checkLeadingZero && Character.digit(s.charAt(pos), radix) <= 0)
+        {
+            return false;
+        }
+
+        int value = 0;
+        while (pos < end)
+        {
+            char c = s.charAt(pos++);
+            int d = Character.digit(c, radix);
+            if (d < 0)
+            {
+                return false;
+            }
+
+            value *= radix;
+            value += d;
+        }
+
+        return value >= minValue & value <= maxValue;
     }
 }
-
-
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/util/Integers.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/util/Integers.java
index b6627eb..afd4b9d 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/util/Integers.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/util/Integers.java
@@ -2,11 +2,29 @@
 package com.android.org.bouncycastle.util;
 
 /**
- * Utility methods for ints.
+ * Utility methods and constants for ints.
  * @hide This class is not part of the Android public SDK API
  */
 public class Integers
 {
+    public static final int BYTES = 4;
+    public static final int SIZE = Integer.SIZE;
+
+    public static int bitCount(int i)
+    {
+        return Integer.bitCount(i);
+    }
+
+    public static int highestOneBit(int i)
+    {
+        return Integer.highestOneBit(i);
+    }
+
+    public static int lowestOneBit(int i)
+    {
+        return Integer.lowestOneBit(i);
+    }
+
     public static int numberOfLeadingZeros(int i)
     {
         return Integer.numberOfLeadingZeros(i);
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/util/Longs.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/util/Longs.java
index 2a812f4..915fa8b 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/util/Longs.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/util/Longs.java
@@ -2,10 +2,34 @@
 package com.android.org.bouncycastle.util;
 
 /**
+ * Utility methods and constants for longs.
  * @hide This class is not part of the Android public SDK API
  */
 public class Longs
 {
+    public static final int BYTES = 8;
+    public static final int SIZE = Long.SIZE;
+
+    public static long highestOneBit(long i)
+    {
+        return Long.highestOneBit(i);
+    }
+
+    public static long lowestOneBit(long i)
+    {
+        return Long.lowestOneBit(i);
+    }
+
+    public static int numberOfLeadingZeros(long i)
+    {
+        return Long.numberOfLeadingZeros(i);
+    }
+
+    public static int numberOfTrailingZeros(long i)
+    {
+        return Long.numberOfTrailingZeros(i);
+    }
+
     public static long reverse(long i)
     {
         return Long.reverse(i);
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/util/Memoable.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/util/Memoable.java
index 4c71b23..d9a332d 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/util/Memoable.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/util/Memoable.java
@@ -3,7 +3,7 @@
 
 /**
  * Interface for Memoable objects. Memoable objects allow the taking of a snapshot of their internal state
- * via the copy() method and then reseting the object back to that state later using the reset() method.
+ * via the copy() method and then resetting the object back to that state later using the reset() method.
  * @hide This class is not part of the Android public SDK API
  */
 public interface Memoable
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/util/Pack.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/util/Pack.java
index 7297e85..d043555 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/util/Pack.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/util/Pack.java
@@ -149,7 +149,6 @@
      * @param bs    The target.
      * @param off   Position in target to start.
      * @param bytes number of bytes to write.
-     * 
      * @deprecated Will be removed
      */
     public static void longToBigEndian(long value, byte[] bs, int off, int bytes)
@@ -177,6 +176,25 @@
         return n;
     }
 
+    public static int littleEndianToInt_High(byte[] bs, int off, int len)
+    {
+        return littleEndianToInt_Low(bs, off, len) << ((4 - len) << 3);
+    }
+
+    public static int littleEndianToInt_Low(byte[] bs, int off, int len)
+    {
+//        assert 1 <= len && len <= 4;
+
+        int result = bs[off] & 0xff;
+        int pos = 0;
+        for (int i = 1; i < len; ++i)
+        {
+            pos += 8;
+            result |= (bs[off + i] & 0xff) << pos;
+        }
+        return result;
+    }
+
     public static void littleEndianToInt(byte[] bs, int off, int[] ns)
     {
         for (int i = 0; i < ns.length; ++i)
@@ -299,6 +317,40 @@
         }
     }
 
+    public static void longToLittleEndian_High(long n, byte[] bs, int off, int len)
+    {
+        //Debug.Assert(1 <= len && len <= 8);
+        int pos = 56;
+        bs[off] = (byte)(n >>> pos);
+        for (int i = 1; i < len; ++i)
+        {
+            pos -= 8;
+            bs[off + i] = (byte)(n >>> pos);
+        }
+    }
+
+//    public static void longToLittleEndian_Low(long n, byte[] bs, int off, int len)
+//    {
+//        longToLittleEndian_High(n << ((8 - len) << 3), bs, off, len);
+//    }
+
+    public static long littleEndianToLong_High(byte[] bs, int off, int len)
+    {
+        return littleEndianToLong_Low(bs, off, len) << ((8 - len) << 3);
+    }
+
+    public static long littleEndianToLong_Low(byte[] bs, int off, int len)
+    {
+        //Debug.Assert(1 <= len && len <= 8);
+        long result = bs[off] & 0xFF;
+        for (int i = 1; i < len; ++i)
+        {
+            result <<= 8;
+            result |= bs[off + i] & 0xFF;
+        }
+        return result;
+    }
+
     public static byte[] longToLittleEndian(long n)
     {
         byte[] bs = new byte[8];
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/util/Properties.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/util/Properties.java
index 87f5275..f28ffbf 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/util/Properties.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/util/Properties.java
@@ -14,11 +14,18 @@
 import java.util.StringTokenizer;
 
 /**
- * Utility method for accessing system properties.
+ * Utility method for accessing properties values - properties can be set in java.security,
+ * thread local, and system properties. They are checked for in the same order with
+ * checking stopped as soon as a value is found.
  * @hide This class is not part of the Android public SDK API
  */
 public class Properties
 {
+    /**
+     * If set the provider will attempt, where possible, to behave the same way as the oracle one.
+     */
+    public static final String EMULATE_ORACLE = "com.android.org.bouncycastle.emulate.oracle";
+
     private Properties()
     {
     }
@@ -117,6 +124,31 @@
         return false;
     }
 
+    /**
+     * Return propertyName as an integer, defaultValue used if not defined.
+     *
+     * @param propertyName name of property.
+     * @param defaultValue integer to return if property not defined.
+     * @return value of property, or default if not found, as an int.
+     */
+    public static int asInteger(String propertyName, int defaultValue)
+    {
+        String p = getPropertyValue(propertyName);
+
+        if (p != null)
+        {
+            return Integer.parseInt(p);
+        }
+
+        return defaultValue;
+    }
+
+    /**
+     * Return propertyName as a BigInteger.
+     *
+     * @param propertyName name of property.
+     * @return value of property as a BigInteger, null if not defined.
+     */
     public static BigInteger asBigInteger(String propertyName)
     {
         String p = getPropertyValue(propertyName);
@@ -147,6 +179,13 @@
         return Collections.unmodifiableSet(set);
     }
 
+    /**
+     * Return the String value of the property propertyName. Property valuation
+     * starts with java.security, then thread local, then system properties.
+     *
+     * @param propertyName name of property.
+     * @return value of property as a String, null if not defined.
+     */
     public static String getPropertyValue(final String propertyName)
     {
         String val = (String)AccessController.doPrivileged(new PrivilegedAction()
@@ -180,6 +219,18 @@
         });
     }
 
+    public static String getPropertyValue(final String propertyName,  String defValue)
+    {
+        String rv = getPropertyValue(propertyName);
+
+        if (rv == null)
+        {
+            return defValue;
+        }
+
+        return rv;
+    }
+
     private static boolean isSetFalse(String p)
     {
         if (p == null || p.length() != 5)
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/util/Strings.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/util/Strings.java
index cf3b206..2a5f512 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/util/Strings.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/util/Strings.java
@@ -58,6 +58,17 @@
         return new String(chars, 0, len);
     }
 
+    public static String fromUTF8ByteArray(byte[] bytes, int off, int length)
+    {
+        char[] chars = new char[length];
+        int len = UTF8.transcodeToUTF16(bytes, off, length, chars);
+        if (len < 0)
+        {
+            throw new IllegalArgumentException("Invalid UTF-8 input");
+        }
+        return new String(chars, 0, len);
+    }
+
     public static byte[] toUTF8ByteArray(String string)
     {
         return toUTF8ByteArray(string.toCharArray());
@@ -230,6 +241,37 @@
     }
 
     /**
+     * Constant time string comparison.
+     *
+     * @param a a string.
+     * @param b another string to compare to a.
+     *
+     * @return true if a and b represent the same string, false otherwise.
+     */
+    public static boolean constantTimeAreEqual(String a, String b)
+    {
+        boolean isEqual = a.length() == b.length();
+        int     len = a.length();
+
+        if (isEqual)
+        {
+            for (int i = 0; i != len; i++)
+            {
+                isEqual &= (a.charAt(i) == b.charAt(i));
+            }
+        }
+        else
+        {
+            for (int i = 0; i != len; i++)
+            {
+                isEqual &= (a.charAt(i) == ' ');
+            }
+        }
+
+        return isEqual;
+    }
+
+    /**
      * Convert an array of 8 bit characters into a string.
      *
      * @param bytes 8 bit characters.
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/util/encoders/Base32.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/util/encoders/Base32.java
new file mode 100644
index 0000000..2e384d7
--- /dev/null
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/util/encoders/Base32.java
@@ -0,0 +1,176 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.org.bouncycastle.util.encoders;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+import com.android.org.bouncycastle.util.Strings;
+
+/**
+ * Utility class for converting Base32 data to bytes and back again.
+ * @hide This class is not part of the Android public SDK API
+ */
+public class Base32
+{
+    private static final Encoder encoder = new Base32Encoder();
+
+    public static String toBase32String(
+        byte[] data)
+    {
+        return toBase32String(data, 0, data.length);
+    }
+
+    public static String toBase32String(
+        byte[] data,
+        int off,
+        int length)
+    {
+        byte[] encoded = encode(data, off, length);
+        return Strings.fromByteArray(encoded);
+    }
+
+    /**
+     * encode the input data producing a base 32 encoded byte array.
+     *
+     * @return a byte array containing the base 32 encoded data.
+     */
+    public static byte[] encode(
+        byte[] data)
+    {
+        return encode(data, 0, data.length);
+    }
+
+    /**
+     * encode the input data producing a base 32 encoded byte array.
+     *
+     * @return a byte array containing the base 32 encoded data.
+     */
+    public static byte[] encode(
+        byte[] data,
+        int off,
+        int length)
+    {
+        int len = encoder.getEncodedLength(length);
+        ByteArrayOutputStream bOut = new ByteArrayOutputStream(len);
+
+        try
+        {
+            encoder.encode(data, off, length, bOut);
+        }
+        catch (Exception e)
+        {
+            throw new EncoderException("exception encoding base32 string: " + e.getMessage(), e);
+        }
+
+        return bOut.toByteArray();
+    }
+
+    /**
+     * Encode the byte data to base 32 writing it to the given output stream.
+     *
+     * @return the number of bytes produced.
+     */
+    public static int encode(
+        byte[] data,
+        OutputStream out)
+        throws IOException
+    {
+        return encoder.encode(data, 0, data.length, out);
+    }
+
+    /**
+     * Encode the byte data to base 32 writing it to the given output stream.
+     *
+     * @return the number of bytes produced.
+     */
+    public static int encode(
+        byte[] data,
+        int off,
+        int length,
+        OutputStream out)
+        throws IOException
+    {
+        return encoder.encode(data, off, length, out);
+    }
+
+    /**
+     * decode the base 32 encoded input data. It is assumed the input data is valid.
+     *
+     * @return a byte array representing the decoded data.
+     */
+    public static byte[] decode(
+        byte[] data)
+    {
+        int len = data.length / 8 * 5;
+        ByteArrayOutputStream bOut = new ByteArrayOutputStream(len);
+
+        try
+        {
+            encoder.decode(data, 0, data.length, bOut);
+        }
+        catch (Exception e)
+        {
+            throw new DecoderException("unable to decode base32 data: " + e.getMessage(), e);
+        }
+
+        return bOut.toByteArray();
+    }
+
+    /**
+     * decode the base 32 encoded String data - whitespace will be ignored.
+     *
+     * @return a byte array representing the decoded data.
+     */
+    public static byte[] decode(
+        String data)
+    {
+        int len = data.length() / 8 * 5;
+        ByteArrayOutputStream bOut = new ByteArrayOutputStream(len);
+
+        try
+        {
+            encoder.decode(data, bOut);
+        }
+        catch (Exception e)
+        {
+            throw new DecoderException("unable to decode base32 string: " + e.getMessage(), e);
+        }
+
+        return bOut.toByteArray();
+    }
+
+    /**
+     * decode the base 32 encoded String data writing it to the given output stream,
+     * whitespace characters will be ignored.
+     *
+     * @return the number of bytes produced.
+     */
+    public static int decode(
+        String data,
+        OutputStream out)
+        throws IOException
+    {
+        return encoder.decode(data, out);
+    }
+
+    /**
+     * Decode to an output stream;
+     *
+     * @param base32Data       The source data.
+     * @param start            Start position.
+     * @param length           the length.
+     * @param out The output stream to write to.
+     */
+    public static int decode(byte[] base32Data, int start, int length, OutputStream out)
+    {
+        try
+        {
+           return encoder.decode(base32Data, start, length, out);
+        }
+        catch (Exception e)
+        {
+            throw new DecoderException("unable to decode base32 data: " + e.getMessage(), e);
+        }
+    }
+}
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/util/encoders/Base32Encoder.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/util/encoders/Base32Encoder.java
new file mode 100644
index 0000000..8b913dd
--- /dev/null
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/util/encoders/Base32Encoder.java
@@ -0,0 +1,455 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.org.bouncycastle.util.encoders;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+import com.android.org.bouncycastle.util.Arrays;
+import com.android.org.bouncycastle.util.Strings;
+
+/**
+ * A streaming Base32 encoder.
+ * @hide This class is not part of the Android public SDK API
+ */
+public class Base32Encoder
+    implements Encoder
+{
+    private static final byte[] DEAULT_ENCODING_TABLE =
+    {
+        (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F', (byte)'G',
+        (byte)'H', (byte)'I', (byte)'J', (byte)'K', (byte)'L', (byte)'M', (byte)'N',
+        (byte)'O', (byte)'P', (byte)'Q', (byte)'R', (byte)'S', (byte)'T', (byte)'U',
+        (byte)'V', (byte)'W', (byte)'X', (byte)'Y', (byte)'Z',
+        (byte)'2', (byte)'3', (byte)'4', (byte)'5', (byte)'6', (byte)'7'
+    };
+
+    private static final byte DEFAULT_PADDING = (byte)'=';
+
+    /*
+     * set up the decoding table.
+     */
+    private final byte[] encodingTable;
+    private final byte   padding;
+    private final byte[] decodingTable = new byte[128];
+
+    protected void initialiseDecodingTable()
+    {
+        for (int i = 0; i < decodingTable.length; i++)
+        {
+            decodingTable[i] = (byte)0xff;
+        }
+
+        for (int i = 0; i < encodingTable.length; i++)
+        {
+            decodingTable[encodingTable[i]] = (byte)i;
+        }
+    }
+
+    /**
+     * Base constructor for RFC 4648, Section 6.
+     */
+    public Base32Encoder()
+    {
+        this.encodingTable = DEAULT_ENCODING_TABLE;
+        this.padding = DEFAULT_PADDING;
+
+        initialiseDecodingTable();
+    }
+
+    /**
+     * Constructor allowing the setting of an alternative alphabet.
+     *
+     * @param encodingTable a 32 entry encoding table to do the mapping.
+     * @param padding the padding value to use.
+     */
+    public Base32Encoder(byte[] encodingTable, byte padding)
+    {
+        if (encodingTable.length != 32)
+        {
+            throw new IllegalArgumentException("encoding table needs to be length 32");
+        }
+
+        this.encodingTable = Arrays.clone(encodingTable);
+        this.padding = padding;
+        
+        initialiseDecodingTable();
+    }
+
+    public int encode(byte[] inBuf, int inOff, int inLen, byte[] outBuf, int outOff) throws IOException
+    {
+        int inPos = inOff;
+        int inEnd = inOff + inLen - 4;
+        int outPos = outOff;
+
+        while (inPos < inEnd)
+        {
+             encodeBlock(inBuf, inPos, outBuf, outPos);
+             inPos += 5;
+             outPos += 8;
+        }
+
+        int extra = inLen - (inPos - inOff);
+        if (extra > 0)
+        {
+            byte[] in = new byte[5];
+            System.arraycopy(inBuf, inPos, in, 0, extra);
+            encodeBlock(in, 0, outBuf, outPos);
+            switch (extra)
+            {
+            case 1:
+                outBuf[outPos + 2] = padding;
+                outBuf[outPos + 3] = padding;
+                outBuf[outPos + 4] = padding;
+                outBuf[outPos + 5] = padding;
+                outBuf[outPos + 6] = padding;
+                outBuf[outPos + 7] = padding;
+                break;
+            case 2:
+                outBuf[outPos + 4] = padding;
+                outBuf[outPos + 5] = padding;
+                outBuf[outPos + 6] = padding;
+                outBuf[outPos + 7] = padding;
+                break;
+            case 3:
+                outBuf[outPos + 5] = padding;
+                outBuf[outPos + 6] = padding;
+                outBuf[outPos + 7] = padding;
+                break;
+            case 4:
+                outBuf[outPos + 7] = padding;
+                break;
+            }
+
+            outPos += 8;
+        }
+
+        return outPos - outOff;
+    }
+
+    private void encodeBlock(byte[] inBuf, int inPos, byte[] outBuf, int outPos)
+    {
+        int a1 = inBuf[inPos++];
+        int a2 = inBuf[inPos++] & 0xFF;
+        int a3 = inBuf[inPos++] & 0xFF;
+        int a4 = inBuf[inPos++] & 0xFF;
+        int a5 = inBuf[inPos] & 0xFF;
+
+        outBuf[outPos++] = encodingTable[(a1 >>> 3) & 0x1F];
+        outBuf[outPos++] = encodingTable[((a1 << 2) | (a2 >>> 6)) & 0x1F];
+        outBuf[outPos++] = encodingTable[(a2 >>> 1) & 0x1F];
+        outBuf[outPos++] = encodingTable[((a2 << 4) | (a3 >>> 4)) & 0x1F];
+        outBuf[outPos++] = encodingTable[((a3 << 1) | (a4 >>> 7)) & 0x1F];
+        outBuf[outPos++] = encodingTable[(a4 >>> 2) & 0x1F];
+        outBuf[outPos++] = encodingTable[((a4 << 3) | (a5 >>> 5)) & 0x1F];
+        outBuf[outPos] = encodingTable[a5 & 0x1F];
+    }
+
+    public int getEncodedLength(int inputLength)
+    {
+        return (inputLength + 4) / 5 * 8;
+    }
+
+    public int getMaxDecodedLength(int inputLength)
+    {
+        return inputLength / 8 * 5;
+    }
+
+    /**
+     * encode the input data producing a base 32 output stream.
+     *
+     * @return the number of bytes produced.
+     */
+    public int encode(byte[] buf, int off, int len, OutputStream out) 
+        throws IOException
+    {
+        if (len < 0)
+        {
+            return 0;
+        }
+
+        byte[] tmp = new byte[72];
+        int remaining = len;
+        while (remaining > 0)
+        {
+            int inLen = Math.min(45, remaining);
+            int outLen = encode(buf, off, inLen, tmp, 0);
+            out.write(tmp, 0, outLen);
+            off += inLen;
+            remaining -= inLen;
+        }
+        return (len + 2) / 3 * 4;
+    }
+
+    private boolean ignore(
+        char    c)
+    {
+        return (c == '\n' || c =='\r' || c == '\t' || c == ' ');
+    }
+    
+    /**
+     * decode the base 32 encoded byte data writing it to the given output stream,
+     * whitespace characters will be ignored.
+     *
+     * @return the number of bytes produced.
+     */
+    public int decode(
+        byte[]          data,
+        int             off,
+        int             length,
+        OutputStream    out)
+        throws IOException
+    {
+        byte    b1, b2, b3, b4, b5, b6, b7, b8;
+        byte[]  outBuffer = new byte[55];
+        int     bufOff = 0;
+        int     outLen = 0;
+        
+        int     end = off + length;
+        
+        while (end > off)
+        {
+            if (!ignore((char)data[end - 1]))
+            {
+                break;
+            }
+            
+            end--;
+        }
+
+        // empty data!
+        if (end == 0)
+        {
+            return 0;
+        }
+        
+        int  i = 0;
+        int  finish = end;
+
+        while (finish > off && i != 8)
+        {
+            if (!ignore((char)data[finish - 1]))
+            {
+                i++;
+            }
+
+            finish--;
+        }
+
+        i = nextI(data, off, finish);
+
+        while (i < finish)
+        {
+            b1 = decodingTable[data[i++]];
+            
+            i = nextI(data, i, finish);
+            
+            b2 = decodingTable[data[i++]];
+            
+            i = nextI(data, i, finish);
+            
+            b3 = decodingTable[data[i++]];
+            
+            i = nextI(data, i, finish);
+            
+            b4 = decodingTable[data[i++]];
+
+            i = nextI(data, i, finish);
+
+            b5 = decodingTable[data[i++]];
+
+            i = nextI(data, i, finish);
+
+            b6 = decodingTable[data[i++]];
+
+            i = nextI(data, i, finish);
+
+            b7 = decodingTable[data[i++]];
+
+            i = nextI(data, i, finish);
+
+            b8 = decodingTable[data[i++]];
+
+            if ((b1 | b2 | b3 | b4 | b5 | b6 | b7 | b8) < 0)
+            {
+                throw new IOException("invalid characters encountered in base32 data");
+            }
+
+            outBuffer[bufOff++] = (byte)((b1 << 3) | (b2 >> 2));
+            outBuffer[bufOff++] = (byte)((b2 << 6) | (b3 << 1) | (b4 >> 4));
+            outBuffer[bufOff++] = (byte)((b4 << 4) | (b5 >> 1));
+            outBuffer[bufOff++] = (byte)((b5 << 7) | (b6 << 2) | (b7 >> 3));
+            outBuffer[bufOff++] = (byte)((b7 << 5) | b8);
+
+            if (bufOff == outBuffer.length)
+            {
+                out.write(outBuffer);
+                bufOff = 0;
+            }
+            
+            outLen += 5;
+            
+            i = nextI(data, i, finish);
+        }
+
+        if (bufOff > 0)
+        {
+            out.write(outBuffer, 0, bufOff);
+        }
+
+        int e0 = nextI(data, i, end);
+        int e1 = nextI(data, e0 + 1, end);
+        int e2 = nextI(data, e1 + 1, end);
+        int e3 = nextI(data, e2 + 1, end);
+        int e4 = nextI(data, e3 + 1, end);
+        int e5 = nextI(data, e4 + 1, end);
+        int e6 = nextI(data, e5 + 1, end);
+        int e7 = nextI(data, e6 + 1, end);
+
+        outLen += decodeLastBlock(out,
+            (char)data[e0], (char)data[e1], (char)data[e2], (char)data[e3],
+            (char)data[e4], (char)data[e5], (char)data[e6], (char)data[e7]);
+
+        return outLen;
+    }
+
+    private int nextI(byte[] data, int i, int finish)
+    {
+        while ((i < finish) && ignore((char)data[i]))
+        {
+            i++;
+        }
+        return i;
+    }
+    
+    /**
+     * decode the base 32 encoded String data writing it to the given output stream,
+     * whitespace characters will be ignored.
+     *
+     * @return the number of bytes produced.
+     */
+    public int decode(
+        String          data,
+        OutputStream    out)
+        throws IOException
+    {
+        byte[] bytes = Strings.toByteArray(data);
+        return decode(bytes, 0, bytes.length, out);
+    }
+
+    private int decodeLastBlock(OutputStream out,
+                                char c1, char c2, char c3, char c4,
+                                char c5, char c6, char c7, char c8)
+        throws IOException
+    {
+        byte    b1, b2, b3, b4, b5, b6, b7, b8;
+        
+        if (c8 == padding)
+        {
+            if (c7 != padding)
+            {
+                b1 = decodingTable[c1];
+                b2 = decodingTable[c2];
+                b3 = decodingTable[c3];
+                b4 = decodingTable[c4];
+                b5 = decodingTable[c5];
+                b6 = decodingTable[c6];
+                b7 = decodingTable[c7];
+
+                if ((b1 | b2 | b3 | b4 | b5 | b6 | b7) < 0)
+                {
+                    throw new IOException("invalid characters encountered at end of base32 data");
+                }
+
+                out.write((b1 << 3) | (b2 >> 2));
+                out.write((b2 << 6) | (b3 << 1) | (b4 >> 4));
+                out.write((b4 << 4) | (b5 >> 1));
+                out.write((b5 << 7) | (b6 << 2) | (b7 >> 3));
+
+                return 4;
+            }
+            if (c6 != padding)
+            {
+                throw new IOException("invalid characters encountered at end of base32 data");
+            }
+
+            if (c5 != padding)
+            {
+                b1 = decodingTable[c1];
+                b2 = decodingTable[c2];
+                b3 = decodingTable[c3];
+                b4 = decodingTable[c4];
+                b5 = decodingTable[c5];
+
+                if ((b1 | b2 | b3 | b4 | b5) < 0)
+                {
+                    throw new IOException("invalid characters encountered at end of base32 data");
+                }
+
+                out.write((b1 << 3) | (b2 >> 2));
+                out.write((b2 << 6) | (b3 << 1) | (b4 >> 4));
+                out.write((b4 << 4) | (b5 >> 1));
+
+                return 3;
+            }
+
+            if (c4 != padding)
+            {
+                b1 = decodingTable[c1];
+                b2 = decodingTable[c2];
+                b3 = decodingTable[c3];
+                b4 = decodingTable[c4];
+
+                if ((b1 | b2 | b3 | b4) < 0)
+                {
+                    throw new IOException("invalid characters encountered at end of base32 data");
+                }
+
+                out.write((b1 << 3) | (b2 >> 2));
+                out.write((b2 << 6) | (b3 << 1) | (b4 >> 4));
+
+                return 2;
+            }
+            
+            if (c3 != padding)
+            {
+                throw new IOException("invalid characters encountered at end of base32 data");
+            }
+
+            b1 = decodingTable[c1];
+            b2 = decodingTable[c2];
+
+            if ((b1 | b2) < 0)
+            {
+                throw new IOException("invalid characters encountered at end of base32 data");
+            }
+
+            out.write((b1 << 3) | (b2 >> 2));
+            
+            return 1;
+        }
+        else
+        {
+            b1 = decodingTable[c1];
+            b2 = decodingTable[c2];
+            b3 = decodingTable[c3];
+            b4 = decodingTable[c4];
+            b5 = decodingTable[c5];
+            b6 = decodingTable[c6];
+            b7 = decodingTable[c7];
+            b8 = decodingTable[c8];
+
+            if ((b1 | b2 | b3 | b4 | b5 | b6 | b7 | b8) < 0)
+            {
+                throw new IOException("invalid characters encountered at end of base32 data");
+            }
+            
+            out.write((b1 << 3) | (b2 >> 2));
+            out.write((b2 << 6) | (b3 << 1) | (b4 >> 4));
+            out.write((b4 << 4) | (b5 >> 1));
+            out.write((b5 << 7) | (b6 << 2) | (b7 >> 3));
+            out.write((b7 << 5) | b8);
+            
+            return 5;
+        } 
+    }
+}
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/util/encoders/Base64.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/util/encoders/Base64.java
index a685400..3a03470 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/util/encoders/Base64.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/util/encoders/Base64.java
@@ -51,7 +51,7 @@
         int off,
         int length)
     {
-        int len = (length + 2) / 3 * 4;
+        int len = encoder.getEncodedLength(length);
         ByteArrayOutputStream bOut = new ByteArrayOutputStream(len);
 
         try
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/util/encoders/Base64Encoder.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/util/encoders/Base64Encoder.java
index 1fee863..0fba03a 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/util/encoders/Base64Encoder.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/util/encoders/Base64Encoder.java
@@ -98,24 +98,40 @@
         return outPos - outOff;
     }
 
+    public int getEncodedLength(int inputLength)
+    {
+        return (inputLength + 2) / 3 * 4;
+    }
+
+    public int getMaxDecodedLength(int inputLength)
+    {
+        return inputLength / 4 * 3;
+    }
+
     /**
      * encode the input data producing a base 64 output stream.
      *
      * @return the number of bytes produced.
      */
-    public int encode(byte[] buf, int off, int len, OutputStream out) 
+    public int encode(byte[] buf, int off, int len, OutputStream out)
         throws IOException
     {
-        byte[] tmp = new byte[72];
-        while (len > 0)
+        if (len < 0)
         {
-            int inLen = Math.min(54, len);
+            return 0;
+        }
+
+        byte[] tmp = new byte[72];
+        int remaining = len;
+        while (remaining > 0)
+        {
+            int inLen = Math.min(54, remaining);
             int outLen = encode(buf, off, inLen, tmp, 0);
             out.write(tmp, 0, outLen);
             off += inLen;
-            len -= inLen;
+            remaining -= inLen;
         }
-        return ((len + 2) / 3) * 4;
+        return (len + 2) / 3 * 4;
     }
 
     private boolean ignore(
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/util/encoders/Encoder.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/util/encoders/Encoder.java
index 5c5dc1b..38c16d0 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/util/encoders/Encoder.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/util/encoders/Encoder.java
@@ -11,6 +11,23 @@
  */
 public interface Encoder
 {
+    /**
+     * Return the expected output length of the encoding.
+     *
+     * @param inputLength the input length of the data.
+     * @return the output length of an encoding.
+     */
+    int getEncodedLength(int inputLength);
+
+    /**
+     * Return the maximum expected output length of a decoding. If padding
+     * is present the value returned will be greater than the decoded data length.
+     *
+     * @param inputLength the input length of the encoded data.
+     * @return the upper bound of the output length of a decoding.
+     */
+    int getMaxDecodedLength(int inputLength);
+
     int encode(byte[] data, int off, int length, OutputStream out) throws IOException;
     
     int decode(byte[] data, int off, int length, OutputStream out) throws IOException;
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/util/encoders/HexEncoder.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/util/encoders/HexEncoder.java
index 2a37631..c234813 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/util/encoders/HexEncoder.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/util/encoders/HexEncoder.java
@@ -64,6 +64,16 @@
         return outPos - outOff;
     }
 
+    public int getEncodedLength(int inputLength)
+    {
+        return inputLength * 2;
+    }
+
+    public int getMaxDecodedLength(int inputLength)
+    {
+        return inputLength / 2;
+    }
+
     /**
      * encode the input data producing a Hex output stream.
      *
@@ -72,14 +82,20 @@
     public int encode(byte[] buf, int off, int len, OutputStream out) 
         throws IOException
     {
-        byte[] tmp = new byte[72];
-        while (len > 0)
+        if (len < 0)
         {
-            int inLen = Math.min(36, len);
+            return 0;
+        }
+
+        byte[] tmp = new byte[72];
+        int remaining = len;
+        while (remaining > 0)
+        {
+            int inLen = Math.min(36, remaining);
             int outLen = encode(buf, off, inLen, tmp, 0);
             out.write(tmp, 0, outLen);
             off += inLen;
-            len -= inLen;
+            remaining -= inLen;
         }
         return len * 2;
     }
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/util/encoders/UTF8.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/util/encoders/UTF8.java
index 78ced9c..7dff001 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/util/encoders/UTF8.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/util/encoders/UTF8.java
@@ -3,7 +3,7 @@
 
 /**
  * Utilities for working with UTF-8 encodings.
- * 
+ * <p>
  * Decoding of UTF-8 is based on a presentation by Bob Steagall at CppCon2018 (see
  * https://github.com/BobSteagall/CppCon2018). It uses a Deterministic Finite Automaton (DFA) to
  * recognize and decode multi-byte code points.
@@ -73,8 +73,8 @@
         fill(transitionTable, S_P4A + 0x9, S_P4A + 0xB, S_CS2);
         fill(transitionTable, S_P4B + 0x8, S_P4B + 0x8, S_CS2);
 
-        byte[] firstUnitMasks = { 0x00, 0x00, 0x00, 0x00, 0x1F, 0x0F, 0x0F, 0x0F, 0x07, 0x07, 0x07 };
-        byte[] firstUnitTransitions = { S_ERR, S_ERR, S_ERR, S_ERR, S_CS1, S_P3A, S_CS2, S_P3B, S_P4A, S_CS3, S_P4B };
+        byte[] firstUnitMasks = {0x00, 0x00, 0x00, 0x00, 0x1F, 0x0F, 0x0F, 0x0F, 0x07, 0x07, 0x07};
+        byte[] firstUnitTransitions = {S_ERR, S_ERR, S_ERR, S_ERR, S_CS1, S_P3A, S_CS2, S_P3B, S_P4A, S_CS3, S_P4B};
 
         for (int i = 0x00; i < 0x80; ++i)
         {
@@ -96,26 +96,52 @@
      * "overlong" encodings, and unmappable code points. In particular, no unmatched surrogates will
      * be produced. An error will also result if {@code utf16} is found to be too small to store the
      * complete output.
-     * 
-     * @param utf8
-     *            A non-null array containing a well-formed UTF-8 encoding.
-     * @param utf16
-     *            A non-null array, at least as long as the {@code utf8} array in order to ensure
-     *            the output will fit.
+     *
+     * @param utf8  A non-null array containing a well-formed UTF-8 encoding.
+     * @param utf16 A non-null array, at least as long as the {@code utf8} array in order to ensure
+     *              the output will fit.
      * @return The number of UTF-16 code units written to {@code utf16} (beginning from index 0), or
-     *         else -1 if the input was either malformed or encoded any unmappable characters, or if
-     *         the {@code utf16} is too small.
+     * else -1 if the input was either malformed or encoded any unmappable characters, or if
+     * the {@code utf16} is too small.
      */
     public static int transcodeToUTF16(byte[] utf8, char[] utf16)
     {
-        int i = 0, j = 0;
+        return transcodeToUTF16(utf8, 0, utf8.length, utf16);
+    }
 
-        while (i < utf8.length)
+    /**
+     * Transcode a UTF-8 encoding into a UTF-16 representation. In the general case the output
+     * {@code utf16} array should be at least as long as the input length from {@code utf8} to handle
+     * arbitrary inputs. The number of output UTF-16 code units is returned, or -1 if any errors are
+     * encountered (in which case an arbitrary amount of data may have been written into the output
+     * array). Errors that will be detected are malformed UTF-8, including incomplete, truncated or
+     * "overlong" encodings, and unmappable code points. In particular, no unmatched surrogates will
+     * be produced. An error will also result if {@code utf16} is found to be too small to store the
+     * complete output.
+     *
+     * @param utf8  A non-null array containing a well-formed UTF-8 encoding.
+     * @param utf8Off start position in the array for the well-formed encoding.
+     * @param utf8Length length in bytes of the well-formed encoding.
+     * @param utf16 A non-null array, at least as long as the {@code utf8} array in order to ensure
+     *              the output will fit.
+     * @return The number of UTF-16 code units written to {@code utf16} (beginning from index 0), or
+     * else -1 if the input was either malformed or encoded any unmappable characters, or if
+     * the {@code utf16} is too small.
+     */
+    public static int transcodeToUTF16(byte[] utf8, int utf8Off, int utf8Length, char[] utf16)
+    {
+        int i = utf8Off, j = 0;
+        int maxI = utf8Off + utf8Length;
+
+        while (i < maxI)
         {
             byte codeUnit = utf8[i++];
             if (codeUnit >= 0)
             {
-                if (j >= utf16.length) { return -1; }
+                if (j >= utf16.length)
+                {
+                    return -1;
+                }
 
                 utf16[j++] = (char)codeUnit;
                 continue;
@@ -127,25 +153,37 @@
 
             while (state >= 0)
             {
-                if (i >= utf8.length) { return -1; }
+                if (i >= maxI)
+                {
+                    return -1;
+                }
 
                 codeUnit = utf8[i++];
                 codePoint = (codePoint << 6) | (codeUnit & 0x3F);
                 state = transitionTable[state + ((codeUnit & 0xFF) >>> 4)];
             }
 
-            if (state == S_ERR) { return -1; }
+            if (state == S_ERR)
+            {
+                return -1;
+            }
 
             if (codePoint <= 0xFFFF)
             {
-                if (j >= utf16.length) { return -1; }
+                if (j >= utf16.length)
+                {
+                    return -1;
+                }
 
                 // Code points from U+D800 to U+DFFF are caught by the DFA
                 utf16[j++] = (char)codePoint;
             }
             else
             {
-                if (j >= utf16.length - 1) { return -1; }
+                if (j >= utf16.length - 1)
+                {
+                    return -1;
+                }
 
                 // Code points above U+10FFFF are caught by the DFA
                 utf16[j++] = (char)(0xD7C0 + (codePoint >>> 10));
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/util/io/Streams.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/util/io/Streams.java
index 5ab96d9..95e376f 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/util/io/Streams.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/util/io/Streams.java
@@ -30,6 +30,64 @@
     }
 
     /**
+     * Write the full contents of inStr to the destination stream outStr.
+     *
+     * @param inStr source input stream.
+     * @param outStr destination output stream.
+     * @throws IOException in case of underlying IOException.
+     */
+    public static void pipeAll(InputStream inStr, OutputStream outStr)
+        throws IOException
+    {
+        pipeAll(inStr, outStr, BUFFER_SIZE);
+    }
+
+    /**
+     * Write the full contents of inStr to the destination stream outStr.
+     *
+     * @param inStr source input stream.
+     * @param outStr destination output stream.
+     * @param bufferSize the size of temporary buffer to use.
+     * @throws IOException in case of underlying IOException.
+     */
+    public static void pipeAll(InputStream inStr, OutputStream outStr, int bufferSize)
+        throws IOException
+    {
+        byte[] bs = new byte[bufferSize];
+        int numRead;
+        while ((numRead = inStr.read(bs, 0, bs.length)) >= 0)
+        {
+            outStr.write(bs, 0, numRead);
+        }
+    }
+
+    /**
+     * Write up to limit bytes of data from inStr to the destination stream outStr.
+     *
+     * @param inStr source input stream.
+     * @param limit the maximum number of bytes allowed to be read.
+     * @param outStr destination output stream.
+     * @throws IOException in case of underlying IOException, or if limit is reached on inStr still has data in it.
+     */
+    public static long pipeAllLimited(InputStream inStr, long limit, OutputStream outStr)
+        throws IOException
+    {
+        long total = 0;
+        byte[] bs = new byte[BUFFER_SIZE];
+        int numRead;
+        while ((numRead = inStr.read(bs, 0, bs.length)) >= 0)
+        {
+            if ((limit - total) < numRead)
+            {
+                throw new StreamOverflowException("Data Overflow");
+            }
+            total += numRead;
+            outStr.write(bs, 0, numRead);
+        }
+        return total;
+    }
+
+    /**
      * Read stream fully, returning contents in a byte array.
      *
      * @param inStr stream to be read.
@@ -98,51 +156,22 @@
             }
             totalRead += numRead;
         }
+        
         return totalRead;
     }
 
-    /**
-     * Write the full contents of inStr to the destination stream outStr.
-     *
-     * @param inStr source input stream.
-     * @param outStr destination output stream.
-     * @throws IOException in case of underlying IOException.
-     */
-    public static void pipeAll(InputStream inStr, OutputStream outStr)
-        throws IOException
+    public static void validateBufferArguments(byte[] buf, int off, int len)
     {
-        byte[] bs = new byte[BUFFER_SIZE];
-        int numRead;
-        while ((numRead = inStr.read(bs, 0, bs.length)) >= 0)
+        if (buf == null)
         {
-            outStr.write(bs, 0, numRead);
+            throw new NullPointerException();
         }
-    }
-
-    /**
-     * Write up to limit bytes of data from inStr to the destination stream outStr.
-     *
-     * @param inStr source input stream.
-     * @param limit the maximum number of bytes allowed to be read.
-     * @param outStr destination output stream.
-     * @throws IOException in case of underlying IOException, or if limit is reached on inStr still has data in it.
-     */
-    public static long pipeAllLimited(InputStream inStr, long limit, OutputStream outStr)
-        throws IOException
-    {
-        long total = 0;
-        byte[] bs = new byte[BUFFER_SIZE];
-        int numRead;
-        while ((numRead = inStr.read(bs, 0, bs.length)) >= 0)
+        int available = buf.length - off;
+        int remaining = available - len;
+        if ((off | len | available | remaining) < 0)
         {
-            if ((limit - total) < numRead)
-            {
-                throw new StreamOverflowException("Data Overflow");
-            }
-            total += numRead;
-            outStr.write(bs, 0, numRead);
+            throw new IndexOutOfBoundsException();
         }
-        return total;
     }
 
     public static void writeBufTo(ByteArrayOutputStream buf, OutputStream output)
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/util/io/pem/PemReader.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/util/io/pem/PemReader.java
index 5a9ebb2..9846cef 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/util/io/pem/PemReader.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/util/io/pem/PemReader.java
@@ -66,9 +66,9 @@
 
         while ((line = readLine()) != null)
         {
-            if (line.indexOf(":") >= 0)
+            int index = line.indexOf(':');
+            if (index >= 0)
             {
-                int index = line.indexOf(':');
                 String hdr = line.substring(0, index);
                 String value = line.substring(index + 1).trim();
 
diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/x509/X509V2AttributeCertificate.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/x509/X509V2AttributeCertificate.java
index 5eac49a..2c09768 100644
--- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/x509/X509V2AttributeCertificate.java
+++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/x509/X509V2AttributeCertificate.java
@@ -22,12 +22,12 @@
 import java.util.List;
 import java.util.Set;
 
+import com.android.org.bouncycastle.asn1.ASN1BitString;
 import com.android.org.bouncycastle.asn1.ASN1Encodable;
 import com.android.org.bouncycastle.asn1.ASN1Encoding;
 import com.android.org.bouncycastle.asn1.ASN1InputStream;
 import com.android.org.bouncycastle.asn1.ASN1ObjectIdentifier;
 import com.android.org.bouncycastle.asn1.ASN1Sequence;
-import com.android.org.bouncycastle.asn1.DERBitString;
 import com.android.org.bouncycastle.asn1.x509.AttributeCertificate;
 import com.android.org.bouncycastle.asn1.x509.Extension;
 import com.android.org.bouncycastle.asn1.x509.Extensions;
@@ -125,7 +125,7 @@
     
     public boolean[] getIssuerUniqueID()
     {
-        DERBitString    id = cert.getAcinfo().getIssuerUniqueID();
+        ASN1BitString id = cert.getAcinfo().getIssuerUniqueID();
 
         if (id != null)
         {
diff --git a/repackaged/bcprov/src/test/java/com/android/org/bouncycastle/math/ec/test/ECPointTest.java b/repackaged/bcprov/src/test/java/com/android/org/bouncycastle/math/ec/test/ECPointTest.java
index 0978d2e..ca93afc 100644
--- a/repackaged/bcprov/src/test/java/com/android/org/bouncycastle/math/ec/test/ECPointTest.java
+++ b/repackaged/bcprov/src/test/java/com/android/org/bouncycastle/math/ec/test/ECPointTest.java
@@ -1,6 +1,6 @@
 /* GENERATED SOURCE. DO NOT MODIFY. */
-package com.android.org.bouncycastle.math.ec.test;
 
+package com.android.org.bouncycastle.math.ec.test;
 import java.math.BigInteger;
 import java.security.SecureRandom;
 import java.util.ArrayList;
@@ -10,7 +10,6 @@
 import java.util.List;
 import java.util.Random;
 import java.util.Set;
-
 import junit.framework.Test;
 import junit.framework.TestCase;
 import junit.framework.TestSuite;
@@ -28,9 +27,7 @@
 import com.android.org.bouncycastle.util.BigIntegers;
 import com.android.org.bouncycastle.util.Integers;
 import com.android.org.bouncycastle.util.encoders.Hex;
-
 import android.platform.test.annotations.LargeTest;
-
 /**
  * Test class for {@link com.android.org.bouncycastle.math.ec.ECPoint ECPoint}. All
  * literature values are taken from "Guide to elliptic curve cryptography",
@@ -44,11 +41,8 @@
      * Random source used to generate random points
      */
     private SecureRandom secRand = new SecureRandom();
-
     private ECPointTest.Fp fp = null;
-
     private ECPointTest.F2m f2m = null;
-
     /**
      * Nested class containing sample literature values for <code>Fp</code>.
      * @hide This class is not part of the Android public SDK API
@@ -56,23 +50,14 @@
     public static class Fp
     {
         private final BigInteger q = new BigInteger("1063");
-
         private final BigInteger a = new BigInteger("4");
-
         private final BigInteger b = new BigInteger("20");
-
         private final BigInteger n = new BigInteger("38");
-
         private final BigInteger h = new BigInteger("1");
-
         private final ECCurve curve = new ECCurve.Fp(q, a, b, n, h);
-
         private final ECPoint infinity = curve.getInfinity();
-
         private final int[] pointSource = { 1, 5, 4, 10, 234, 1024, 817, 912 };
-
         private ECPoint[] p = new ECPoint[pointSource.length / 2];
-
         /**
          * Creates the points on the curve with literature values.
          */
@@ -86,7 +71,6 @@
             }
         }
     }
-
     /**
      * Nested class containing sample literature values for <code>F2m</code>.
      * @hide This class is not part of the Android public SDK API
@@ -95,28 +79,18 @@
     {
         // Irreducible polynomial for TPB z^4 + z + 1
         private final int m = 4;
-
         private final int k1 = 1;
-
         // a = z^3
         private final BigInteger aTpb = new BigInteger("1000", 2);
-
         // b = z^3 + 1
         private final BigInteger bTpb = new BigInteger("1001", 2);
-
         private final BigInteger n = new BigInteger("23");
-
         private final BigInteger h = new BigInteger("1");
-
         private final ECCurve.F2m curve = new ECCurve.F2m(m, k1, aTpb, bTpb, n, h);
-
         private final ECPoint.F2m infinity = (ECPoint.F2m) curve.getInfinity();
-
         private final String[] pointSource = { "0010", "1111", "1100", "1100",
                 "0001", "0001", "1011", "0010" };
-
         private ECPoint[] p = new ECPoint[pointSource.length / 2];
-
         /**
          * Creates the points on the curve with literature values.
          */
@@ -130,16 +104,13 @@
             }
         }
     }
-
     public void setUp()
     {
         fp = new ECPointTest.Fp();
         fp.createPoints();
-
         f2m = new ECPointTest.F2m();
         f2m.createPoints();
     }
-
     /**
      * Tests, if inconsistent points can be created, i.e. points with exactly
      * one null coordinate (not permitted).
@@ -154,7 +125,6 @@
         catch (IllegalArgumentException expected)
         {
         }
-
         try
         {
             ECPoint bad = fp.curve.createPoint(null, new BigInteger("12"));
@@ -163,7 +133,6 @@
         catch (IllegalArgumentException expected)
         {
         }
-
         try
         {
             ECPoint bad = f2m.curve.createPoint(new BigInteger("1011"), null);
@@ -172,7 +141,6 @@
         catch (IllegalArgumentException expected)
         {
         }
-
         try
         {
             ECPoint bad = f2m.curve.createPoint(null, new BigInteger("1011"));
@@ -182,7 +150,6 @@
         {
         }
     }
-
     /**
      * Tests <code>ECPoint.add()</code> against literature values.
      * 
@@ -201,7 +168,6 @@
             assertPointsEqual("Adding to infinity failed", p[i], infinity.add(p[i]));
         }
     }
-
     /**
      * Calls <code>implTestAdd()</code> for <code>Fp</code> and
      * <code>F2m</code>.
@@ -211,7 +177,6 @@
         implTestAdd(fp.p, fp.infinity);
         implTestAdd(f2m.p, f2m.infinity);
     }
-
     /**
      * Tests <code>ECPoint.twice()</code> against literature values.
      * 
@@ -223,7 +188,6 @@
         assertPointsEqual("Twice incorrect", p[3], p[0].twice());
         assertPointsEqual("Add same point incorrect", p[3], p[0].add(p[0]));
     }
-
     /**
      * Calls <code>implTestTwice()</code> for <code>Fp</code> and
      * <code>F2m</code>.
@@ -233,7 +197,6 @@
         implTestTwice(fp.p);
         implTestTwice(f2m.p);
     }
-
     private void implTestThreeTimes(ECPoint[] p)
     {
         ECPoint P = p[0];
@@ -241,7 +204,6 @@
         assertPointsEqual("ThreeTimes incorrect", _3P, P.threeTimes());
         assertPointsEqual("TwicePlus incorrect", _3P, P.twicePlus(P));
     }
-
     /**
      * Calls <code>implTestThreeTimes()</code> for <code>Fp</code> and
      * <code>F2m</code>.
@@ -251,7 +213,6 @@
         implTestThreeTimes(fp.p);
         implTestThreeTimes(f2m.p);
     }
-
     /**
      * Goes through all points on an elliptic curve and checks, if adding a
      * point <code>k</code>-times is the same as multiplying the point by
@@ -267,7 +228,6 @@
     {
         ECPoint adder = infinity;
         ECPoint multiplier = infinity;
-
         BigInteger i = BigInteger.valueOf(1);
         do
         {
@@ -279,7 +239,6 @@
         }
         while (!(adder.equals(infinity)));
     }
-
     /**
      * Calls <code>implTestAllPoints()</code> for the small literature curves,
      * both for <code>Fp</code> and <code>F2m</code>.
@@ -290,13 +249,11 @@
         {
             implTestAllPoints(fp.p[i], fp.infinity);
         }
-
         for (int i = 0; i < f2m.p.length; i++)
         {
             implTestAllPoints(f2m.p[i], f2m.infinity);
         }
     }
-
     /**
      * Checks, if the point multiplication algorithm of the given point yields
      * the same result as point multiplication done by the reference
@@ -316,7 +273,6 @@
         ECPoint q = p.multiply(k);
         assertPointsEqual("ECPoint.multiply is incorrect", ref, q);
     }
-
     /**
      * Checks, if the point multiplication algorithm of the given point yields
      * the same result as point multiplication done by the reference
@@ -333,7 +289,6 @@
     {
         BigInteger bound = BigInteger.ONE.shiftLeft(numBits);
         BigInteger k = BigInteger.ZERO;
-
         do
         {
             ECPoint ref = ECAlgorithms.referenceMultiply(p, k);
@@ -343,7 +298,6 @@
         }
         while (k.compareTo(bound) < 0);
     }
-
     /**
      * Tests <code>ECPoint.add()</code> and <code>ECPoint.subtract()</code>
      * for the given point and the given point at infinity.
@@ -364,7 +318,6 @@
         assertPointsEqual("infinity plus infinity is not infinity ", infinity, infinity.add(infinity));
         assertPointsEqual("Twice infinity is not infinity ", infinity, infinity.twice());
     }
-
     /**
      * Calls <code>implTestAddSubtract()</code> for literature values, both
      * for <code>Fp</code> and <code>F2m</code>.
@@ -375,21 +328,17 @@
         for (int iFp = 0; iFp < fp.pointSource.length / 2; iFp++)
         {
             implTestAddSubtract(fp.p[iFp], fp.infinity);
-
             implTestMultiplyAll(fp.p[iFp], fpBits);
             implTestMultiplyAll(fp.infinity, fpBits);
         }
-
         int f2mBits = f2m.curve.getOrder().bitLength();
         for (int iF2m = 0; iF2m < f2m.pointSource.length / 2; iF2m++)
         {
             implTestAddSubtract(f2m.p[iF2m], f2m.infinity);
-
             implTestMultiplyAll(f2m.p[iF2m], f2mBits);
             implTestMultiplyAll(f2m.infinity, f2mBits);
         }
     }
-
     /**
      * Test encoding with and without point compression.
      * 
@@ -402,25 +351,20 @@
         byte[] unCompBarr = p.getEncoded(false);
         ECPoint decUnComp = p.getCurve().decodePoint(unCompBarr);
         assertPointsEqual("Error decoding uncompressed point", p, decUnComp);
-
         // Point compression
         byte[] compBarr = p.getEncoded(true);
         ECPoint decComp = p.getCurve().decodePoint(compBarr);
         assertPointsEqual("Error decoding compressed point", p, decComp);
     }
-
     private void implAddSubtractMultiplyTwiceEncodingTest(ECCurve curve, ECPoint q, BigInteger n)
     {
         // Get point at infinity on the curve
         ECPoint infinity = curve.getInfinity();
-
         implTestAddSubtract(q, infinity);
         implTestMultiply(q, n.bitLength());
         implTestMultiply(infinity, n.bitLength());
-
         int logSize = 32 - Integers.numberOfLeadingZeros(curve.getFieldSize() - 1);
         int rounds = Math.max(2, Math.min(10, 32 - 3 * logSize));
-
         ECPoint p = q;
         for (int i = 0; i < rounds; ++i)
         {
@@ -428,7 +372,6 @@
             p = p.twice();
         }
     }
-
     private void implSqrtTest(ECCurve c)
     {
         if (ECAlgorithms.isFpCurve(c))
@@ -436,19 +379,15 @@
             BigInteger p = c.getField().getCharacteristic();
             BigInteger pMinusOne = p.subtract(ECConstants.ONE);
             BigInteger legendreExponent = p.shiftRight(1);
-
             ECFieldElement zero = c.fromBigInteger(BigInteger.ZERO);
             assertEquals(zero, zero.sqrt());
-
             ECFieldElement one = c.fromBigInteger(BigInteger.ONE);
             assertEquals(one, one.sqrt());
-
             for (int i = 0; i < 20; ++i)
             {
                 BigInteger x = BigIntegers.createRandomInRange(ECConstants.TWO, pMinusOne, secRand);
                 ECFieldElement fe = c.fromBigInteger(x);
                 ECFieldElement root = fe.sqrt();
-
                 if (root == null)
                 {
                     assertEquals(pMinusOne, x.modPow(legendreExponent, p));
@@ -473,11 +412,9 @@
             }
         }
     }
-
     private void implValidityTest(ECCurve c, ECPoint g)
     {
         assertTrue(g.isValid());
-
         if (ECAlgorithms.isF2mCurve(c))
         {
             BigInteger h = c.getCofactor();
@@ -493,7 +430,6 @@
                     assertFalse(bad2.isValid());
                     ECPoint good2 = bad2.add(order2);
                     assertTrue(good2.isValid());
-
                     if (!h.testBit(1))
                     {
                         ECFieldElement L = solveQuadraticEquation(c, c.getA());
@@ -517,13 +453,11 @@
             }
         }
     }
-
     private void implAddSubtractMultiplyTwiceEncodingTestAllCoords(X9ECParameters x9ECParameters)
     {
         BigInteger n = x9ECParameters.getN();
         ECPoint G = x9ECParameters.getG();
         ECCurve C = x9ECParameters.getCurve();
-
         int[] coords = ECCurve.getAllCoordinateSystems();
         for (int i = 0; i < coords.length; ++i)
         {
@@ -532,26 +466,20 @@
             {
                 ECCurve c = C;
                 ECPoint g = G;
-
                 if (c.getCoordinateSystem() != coord)
                 {
                     c = C.configure().setCoordinateSystem(coord).create();
                     g = c.importPoint(G);
                 }
-
                 // The generator is multiplied by random b to get random q
                 BigInteger b = new BigInteger(n.bitLength(), secRand);
                 ECPoint q = g.multiply(b).normalize();
-
                 implAddSubtractMultiplyTwiceEncodingTest(c, q, n);
-
                 implSqrtTest(c);
-
                 implValidityTest(c, g);
             }
         }
     }
-
     /**
      * Calls <code>implTestAddSubtract()</code>,
      * <code>implTestMultiply</code> and <code>implTestEncoding</code> for
@@ -562,15 +490,12 @@
     {
         Set names = new HashSet(enumToList(ECNamedCurveTable.getNames()));
         names.addAll(enumToList(CustomNamedCurves.getNames()));
-
         Iterator it = names.iterator();
         while (it.hasNext())
         {
             String name = (String)it.next();
-
             X9ECParameters x9A = ECNamedCurveTable.getByName(name);
             X9ECParameters x9B = CustomNamedCurves.getByName(name);
-
             if (x9A != null && x9B != null)
             {
                 assertEquals(x9A.getCurve().getField(), x9B.getCurve().getField());
@@ -578,31 +503,25 @@
                 assertEquals(x9A.getCurve().getB().toBigInteger(), x9B.getCurve().getB().toBigInteger());
                 assertOptionalValuesAgree(x9A.getCurve().getCofactor(), x9B.getCurve().getCofactor());
                 assertOptionalValuesAgree(x9A.getCurve().getOrder(), x9B.getCurve().getOrder());
-
                 assertPointsEqual("Custom curve base-point inconsistency", x9A.getG(), x9B.getG());
-
                 assertEquals(x9A.getH(), x9B.getH());
                 assertEquals(x9A.getN(), x9B.getN());
                 assertOptionalValuesAgree(x9A.getSeed(), x9B.getSeed());
-
                 BigInteger k = new BigInteger(x9A.getN().bitLength(), secRand);
                 ECPoint pA = x9A.getG().multiply(k);
                 ECPoint pB = x9B.getG().multiply(k);
                 assertPointsEqual("Custom curve multiplication inconsistency", pA, pB);
             }
-
             if (x9A != null)
             {
                 implAddSubtractMultiplyTwiceEncodingTestAllCoords(x9A);
             }
-
             if (x9B != null)
             {
                 implAddSubtractMultiplyTwiceEncodingTestAllCoords(x9B);
             }
         }
     }
-
     public void testExampleFpB0() throws Exception
     {
         /*
@@ -623,9 +542,7 @@
         byte[] S = null;
         BigInteger n = p.add(BigInteger.valueOf(1)).shiftRight(2);
         BigInteger h = BigInteger.valueOf(4);
-
         ECCurve curve = configureCurve(new ECCurve.Fp(p, a, b, n, h));
-
         X9ECPoint G = configureBasepoint(curve, "04"
             // Px
             + "53FC09EE332C29AD0A7990053ED9B52A"
@@ -645,19 +562,15 @@
             + "AC6F1E80164AA989492D979FC5A4D5F2"
             + "13515AD7E9CB99A980BDAD5AD5BB4636"
             + "ADB9B5706A67DCDE75573FD71BEF16D7");
-
         X9ECParameters x9 = new X9ECParameters(curve, G, n, h, S);
-
         implAddSubtractMultiplyTwiceEncodingTestAllCoords(x9);
     }
-
     private void assertPointsEqual(String message, ECPoint a, ECPoint b)
     {
         // NOTE: We intentionally test points for equality in both directions
         assertEquals(message, a, b);
         assertEquals(message, b, a);
     }
-
     private void assertOptionalValuesAgree(Object a, Object b)
     {
         if (a != null && b != null)
@@ -665,7 +578,6 @@
             assertEquals(a, b);
         }
     }
-
     private void assertOptionalValuesAgree(byte[] a, byte[] b)
     {
         if (a != null && b != null)
@@ -673,46 +585,37 @@
             assertTrue(Arrays.areEqual(a, b));
         }
     }
-
     private static X9ECPoint configureBasepoint(ECCurve curve, String encoding)
     {
         X9ECPoint G = new X9ECPoint(curve, Hex.decode(encoding));
         WNafUtil.configureBasepoint(G.getPoint());
         return G;
     }
-
     private static ECCurve configureCurve(ECCurve curve)
     {
         return curve;
     }
-
     private List enumToList(Enumeration en)
     {
         List rv = new ArrayList();
-
         while (en.hasMoreElements())
         {
             rv.add(en.nextElement());
         }
-
         return rv;
     }
-
     private static BigInteger fromHex(
         String hex)
     {
         return new BigInteger(1, Hex.decode(hex));
     }
-
     private static ECFieldElement solveQuadraticEquation(ECCurve c, ECFieldElement rhs)
     {
         if (rhs.isZero())
         {
             return rhs;
         }
-
         ECFieldElement gamma, z, zeroElement = c.fromBigInteger(ECConstants.ZERO);
-
         int m = c.getFieldSize();
         Random rand = new Random();
         do
@@ -733,12 +636,11 @@
             gamma = z.square().add(z);
         }
         while (gamma.isZero());
-
         return z;
     }
-
     public static Test suite()
     {
         return new TestSuite(ECPointTest.class);
     }
 }
+
diff --git a/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cert/AttributeCertificateHolder.java b/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cert/AttributeCertificateHolder.java
index 58541a8..e70381c 100644
--- a/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cert/AttributeCertificateHolder.java
+++ b/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cert/AttributeCertificateHolder.java
@@ -325,9 +325,9 @@
 
                 digOut.close();
 
-                if (!Arrays.areEqual(digCalc.getDigest(), getObjectDigest()))
+                if (Arrays.areEqual(digCalc.getDigest(), getObjectDigest()))
                 {
-                    return false;
+                    return true;
                 }
             }
             catch (Exception e)
diff --git a/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cert/CertUtils.java b/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cert/CertUtils.java
index ff8383e..c7e6408 100644
--- a/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cert/CertUtils.java
+++ b/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cert/CertUtils.java
@@ -13,6 +13,7 @@
 import java.util.List;
 import java.util.Set;
 
+import com.android.internal.org.bouncycastle.asn1.ASN1BitString;
 import com.android.internal.org.bouncycastle.asn1.ASN1Encodable;
 import com.android.internal.org.bouncycastle.asn1.ASN1EncodableVector;
 import com.android.internal.org.bouncycastle.asn1.ASN1Encoding;
@@ -20,9 +21,12 @@
 import com.android.internal.org.bouncycastle.asn1.ASN1Object;
 import com.android.internal.org.bouncycastle.asn1.ASN1ObjectIdentifier;
 import com.android.internal.org.bouncycastle.asn1.ASN1Primitive;
+import com.android.internal.org.bouncycastle.asn1.ASN1Sequence;
+import com.android.internal.org.bouncycastle.asn1.ASN1TaggedObject;
 import com.android.internal.org.bouncycastle.asn1.DERBitString;
 import com.android.internal.org.bouncycastle.asn1.DERNull;
 import com.android.internal.org.bouncycastle.asn1.DERSequence;
+import com.android.internal.org.bouncycastle.asn1.DERTaggedObject;
 import com.android.internal.org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 import com.android.internal.org.bouncycastle.asn1.x509.AttributeCertificate;
 import com.android.internal.org.bouncycastle.asn1.x509.AttributeCertificateInfo;
@@ -78,28 +82,6 @@
         }
     }
 
-    static X509CRLHolder generateFullCRL(ContentSigner signer, TBSCertList tbsCertList)
-    {
-        try
-        {
-            return new X509CRLHolder(generateCRLStructure(tbsCertList, signer.getAlgorithmIdentifier(), generateSig(signer, tbsCertList)));
-        }
-        catch (IOException e)
-        {
-            throw new IllegalStateException("cannot produce certificate signature");
-        }
-    }
-
-    private static byte[] generateSig(ContentSigner signer, ASN1Object tbsObj)
-        throws IOException
-    {
-        OutputStream sOut = signer.getOutputStream();
-        tbsObj.encodeTo(sOut, ASN1Encoding.DER);
-        sOut.close();
-
-        return signer.getSignature();
-    }
-
     private static Certificate generateStructure(TBSCertificate tbsCert, AlgorithmIdentifier sigAlgId, byte[] signature)
     {
         ASN1EncodableVector v = new ASN1EncodableVector();
@@ -198,12 +180,12 @@
         }
     }
 
-    static boolean[] bitStringToBoolean(DERBitString bitString)
+    static boolean[] bitStringToBoolean(ASN1BitString bitString)
     {
         if (bitString != null)
         {
-            byte[]          bytes = bitString.getBytes();
-            boolean[]       boolId = new boolean[bytes.length * 8 - bitString.getPadBits()];
+            byte[] bytes = bitString.getBytes();
+            boolean[] boolId = new boolean[bytes.length * 8 - bitString.getPadBits()];
 
             for (int i = 0; i != boolId.length; i++)
             {
@@ -235,40 +217,40 @@
              return false;
          }
 
-         if (Properties.isOverrideSet("com.android.internal.org.bouncycastle.x509.allow_absent_equiv_NULL"))
-         {
-             if (id1.getParameters() == null)
-             {
-                 if (id2.getParameters() != null && !id2.getParameters().equals(DERNull.INSTANCE))
-                 {
-                     return false;
-                 }
+        if (Properties.isOverrideSet("com.android.internal.org.bouncycastle.x509.allow_absent_equiv_NULL"))
+        {
+            if (id1.getParameters() == null)
+            {
+                if (id2.getParameters() != null && !id2.getParameters().equals(DERNull.INSTANCE))
+                {
+                    return false;
+                }
 
-                 return true;
-             }
+                return true;
+            }
 
-             if (id2.getParameters() == null)
-             {
-                 if (id1.getParameters() != null && !id1.getParameters().equals(DERNull.INSTANCE))
-                 {
-                     return false;
-                 }
+            if (id2.getParameters() == null)
+            {
+                if (id1.getParameters() != null && !id1.getParameters().equals(DERNull.INSTANCE))
+                {
+                    return false;
+                }
 
-                 return true;
-             }
-         }
+                return true;
+            }
+        }
 
-         if (id1.getParameters() != null)
-         {
-             return id1.getParameters().equals(id2.getParameters());
-         }
+        if (id1.getParameters() != null)
+        {
+            return id1.getParameters().equals(id2.getParameters());
+        }
 
-         if (id2.getParameters() != null)
-         {
-             return id2.getParameters().equals(id1.getParameters());
-         }
+        if (id2.getParameters() != null)
+        {
+            return id2.getParameters().equals(id1.getParameters());
+        }
 
-         return true;
+        return true;
     }
 
     static ExtensionsGenerator doReplaceExtension(ExtensionsGenerator extGenerator, Extension ext)
@@ -277,7 +259,7 @@
         Extensions exts = extGenerator.generate();
         extGenerator = new ExtensionsGenerator();
 
-        for (Enumeration en = exts.oids(); en.hasMoreElements();)
+        for (Enumeration en = exts.oids(); en.hasMoreElements(); )
         {
             ASN1ObjectIdentifier extOid = (ASN1ObjectIdentifier)en.nextElement();
 
@@ -300,13 +282,13 @@
         return extGenerator;
     }
 
-    static ExtensionsGenerator doRemoveExtension(ExtensionsGenerator extGenerator, ASN1ObjectIdentifier  oid)
+    static ExtensionsGenerator doRemoveExtension(ExtensionsGenerator extGenerator, ASN1ObjectIdentifier oid)
     {
         boolean isRemoved = false;
         Extensions exts = extGenerator.generate();
         extGenerator = new ExtensionsGenerator();
 
-        for (Enumeration en = exts.oids(); en.hasMoreElements();)
+        for (Enumeration en = exts.oids(); en.hasMoreElements(); )
         {
             ASN1ObjectIdentifier extOid = (ASN1ObjectIdentifier)en.nextElement();
 
@@ -327,4 +309,31 @@
 
         return extGenerator;
     }
+
+    private static byte[] generateSig(ContentSigner signer, ASN1Object tbsObj)
+        throws IOException
+    {
+        OutputStream sOut = signer.getOutputStream();
+        tbsObj.encodeTo(sOut, ASN1Encoding.DER);
+        sOut.close();
+
+        return signer.getSignature();
+    }
+
+    static ASN1TaggedObject trimExtensions(int tagNo, Extensions exts)
+    {
+        ASN1Sequence extSeq = ASN1Sequence.getInstance(exts.toASN1Primitive());
+        ASN1EncodableVector extV = new ASN1EncodableVector();
+        for (int i = 0; i != extSeq.size(); i++)
+        {
+            ASN1Sequence ext = ASN1Sequence.getInstance(extSeq.getObjectAt(i));
+
+            if (!Extension.altSignatureValue.equals(ext.getObjectAt(0)))
+            {
+                extV.add(ext);
+            }
+        }
+
+        return new DERTaggedObject(true, tagNo, new DERSequence(extV));
+    }
 }
diff --git a/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cert/DeltaCertificateTool.java b/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cert/DeltaCertificateTool.java
new file mode 100644
index 0000000..f2fd22f
--- /dev/null
+++ b/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cert/DeltaCertificateTool.java
@@ -0,0 +1,187 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.cert;
+
+import java.io.IOException;
+
+import com.android.internal.org.bouncycastle.asn1.ASN1BitString;
+import com.android.internal.org.bouncycastle.asn1.ASN1Encodable;
+import com.android.internal.org.bouncycastle.asn1.ASN1EncodableVector;
+import com.android.internal.org.bouncycastle.asn1.ASN1Encoding;
+import com.android.internal.org.bouncycastle.asn1.ASN1Integer;
+import com.android.internal.org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import com.android.internal.org.bouncycastle.asn1.ASN1Sequence;
+import com.android.internal.org.bouncycastle.asn1.ASN1TaggedObject;
+import com.android.internal.org.bouncycastle.asn1.DERBitString;
+import com.android.internal.org.bouncycastle.asn1.DERSequence;
+import com.android.internal.org.bouncycastle.asn1.DERTaggedObject;
+import com.android.internal.org.bouncycastle.asn1.x509.Certificate;
+import com.android.internal.org.bouncycastle.asn1.x509.Extension;
+import com.android.internal.org.bouncycastle.asn1.x509.ExtensionsGenerator;
+
+/**
+ * General tool for handling the extension described in: https://datatracker.ietf.org/doc/draft-bonnell-lamps-chameleon-certs/
+ * @hide This class is not part of the Android public SDK API
+ */
+public class DeltaCertificateTool
+{
+    public static Extension makeDeltaCertificateExtension(boolean isCritical, X509CertificateHolder deltaCert)
+        throws IOException
+    {
+        ASN1EncodableVector deltaV = new ASN1EncodableVector();
+
+        deltaV.add(new ASN1Integer(deltaCert.getSerialNumber()));
+        deltaV.add(new DERTaggedObject(false, 0, deltaCert.getSignatureAlgorithm()));
+        deltaV.add(new DERTaggedObject(false, 1, deltaCert.getIssuer()));
+
+        ASN1EncodableVector validity = new ASN1EncodableVector(2);
+        validity.add(deltaCert.toASN1Structure().getStartDate());
+        validity.add(deltaCert.toASN1Structure().getEndDate());
+
+        deltaV.add(new DERTaggedObject(false, 2, new DERSequence(validity)));
+        deltaV.add(new DERTaggedObject(false, 3, deltaCert.getSubject()));
+        deltaV.add(deltaCert.getSubjectPublicKeyInfo());
+        if (deltaCert.getExtensions() != null)
+        {
+            deltaV.add(new DERTaggedObject(false, 4, deltaCert.getExtensions()));
+        }
+        deltaV.add(new DERBitString(deltaCert.getSignature()));
+
+        return new Extension(Extension.deltaCertificateDescriptor, isCritical, new DERSequence(deltaV).getEncoded(ASN1Encoding.DER));
+    }
+
+    public static X509CertificateHolder extractDeltaCertificate(X509CertificateHolder originCert)
+    {
+        ASN1ObjectIdentifier deltaExtOid = Extension.deltaCertificateDescriptor;
+        Extension deltaExt = originCert.getExtension(deltaExtOid);
+
+        ASN1Sequence seq = ASN1Sequence.getInstance(deltaExt.getParsedValue());
+//        *      version          [ 0 ]  Version DEFAULT v1(0),
+//        *      serialNumber            CertificateSerialNumber,
+//        *      signature               AlgorithmIdentifier,
+//        *      issuer                  Name,
+//        *      validity                Validity,
+//        *      subject                 Name,
+//        *      subjectPublicKeyInfo    SubjectPublicKeyInfo,
+//        *      issuerUniqueID    [ 1 ] IMPLICIT UniqueIdentifier OPTIONAL,
+//        *      subjectUniqueID   [ 2 ] IMPLICIT UniqueIdentifier OPTIONAL,
+//        *      extensions        [ 3 ] Extensions OPTIONAL
+        ASN1Sequence originTbs = ASN1Sequence.getInstance(originCert.toASN1Structure().getTBSCertificate().toASN1Primitive());
+        int idx = 0;
+        ASN1Encodable[] extracted = originTbs.toArray();
+
+        extracted[0] = originTbs.getObjectAt(0);
+        extracted[1] = ASN1Integer.getInstance(seq.getObjectAt(idx++));
+
+        ASN1Encodable next = seq.getObjectAt(idx++);
+        while (next instanceof ASN1TaggedObject)
+        {
+            ASN1TaggedObject tagged = ASN1TaggedObject.getInstance(next);
+            switch (tagged.getTagNo())
+            {
+            case 0:
+                extracted[2] = ASN1Sequence.getInstance(tagged, false);
+                break;
+            case 1:
+                extracted[3] = ASN1Sequence.getInstance(tagged, true);   // issuer
+                break;
+            case 2:
+                extracted[4] = ASN1Sequence.getInstance(tagged, false);
+                break;
+            case 3:
+                extracted[5] = ASN1Sequence.getInstance((ASN1TaggedObject)next, true);   // subject
+                break;
+            }
+            next = seq.getObjectAt(idx++);
+        }
+
+        extracted[6] = next;  // subjectPublicKey
+
+        if (extracted[2] == null)
+        {
+            extracted[2] = originTbs.getObjectAt(2);
+        }
+
+        if (extracted[3] == null)
+        {
+            extracted[3] = originTbs.getObjectAt(3);
+        }
+
+        if (extracted[4] == null)
+        {
+            extracted[4] = originTbs.getObjectAt(4);
+        }
+
+        if (extracted[5] == null)
+        {
+            extracted[5] = originTbs.getObjectAt(5);
+        }
+
+        ExtensionsGenerator extGen = extractExtensions(originTbs);
+
+        if (idx < (seq.size() - 1))  // last element is the signature
+        {
+            next = seq.getObjectAt(idx++);
+            ASN1TaggedObject tagged = ASN1TaggedObject.getInstance(next);
+            if (tagged.getTagNo() != 4)
+            {
+                throw new IllegalArgumentException("malformed delta extension");
+            }
+
+            ASN1Sequence deltaExts = ASN1Sequence.getInstance(tagged, false);
+
+            for (int i = 0; i != deltaExts.size(); i++)
+            {
+                Extension ext = Extension.getInstance(deltaExts.getObjectAt(i));
+
+                extGen.replaceExtension(ext);
+            }
+
+            extracted[7] = new DERTaggedObject(3, extGen.generate());
+        }
+        else
+        {
+            if (!extGen.isEmpty())
+            {
+                extracted[7] = new DERTaggedObject(3, extGen.generate());
+            }
+            else
+            {
+                extracted[7] = null;
+            }
+        }
+
+        ASN1EncodableVector tbsDeltaCertV = new ASN1EncodableVector(7);
+        for (int i = 0; i != extracted.length; i++)
+        {
+            if (extracted[i] != null)
+            {
+                tbsDeltaCertV.add(extracted[i]);
+            }
+        }
+
+        ASN1EncodableVector certV = new ASN1EncodableVector();
+        certV.add(new DERSequence(tbsDeltaCertV));
+        certV.add(ASN1Sequence.getInstance(extracted[2]));
+        certV.add(ASN1BitString.getInstance(seq.getObjectAt(seq.size() - 1)));
+
+        return new X509CertificateHolder(Certificate.getInstance(new DERSequence(certV)));
+    }
+
+    private static ExtensionsGenerator extractExtensions(ASN1Sequence originTbs)
+    {
+        ASN1ObjectIdentifier deltaExtOid = Extension.deltaCertificateDescriptor;
+        ASN1Sequence originExt = ASN1Sequence.getInstance(ASN1TaggedObject.getInstance(originTbs.getObjectAt(originTbs.size() - 1)), true);
+        ExtensionsGenerator extGen = new ExtensionsGenerator();
+
+        for (int i = 0; i != originExt.size(); i++)
+        {
+            Extension ext = Extension.getInstance(originExt.getObjectAt(i));
+            if (!deltaExtOid.equals(ext.getExtnId()))
+            {
+                extGen.addExtension(ext);
+            }
+        }
+
+        return extGen;
+    }
+}
diff --git a/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cert/X509CRLHolder.java b/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cert/X509CRLHolder.java
index ef13899..b8a718d 100644
--- a/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cert/X509CRLHolder.java
+++ b/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cert/X509CRLHolder.java
@@ -16,11 +16,18 @@
 import java.util.List;
 import java.util.Set;
 
+import com.android.internal.org.bouncycastle.asn1.ASN1EncodableVector;
 import com.android.internal.org.bouncycastle.asn1.ASN1Encoding;
 import com.android.internal.org.bouncycastle.asn1.ASN1InputStream;
+import com.android.internal.org.bouncycastle.asn1.ASN1Integer;
 import com.android.internal.org.bouncycastle.asn1.ASN1ObjectIdentifier;
 import com.android.internal.org.bouncycastle.asn1.ASN1Primitive;
+import com.android.internal.org.bouncycastle.asn1.ASN1Sequence;
+import com.android.internal.org.bouncycastle.asn1.DERSequence;
 import com.android.internal.org.bouncycastle.asn1.x500.X500Name;
+import com.android.internal.org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import com.android.internal.org.bouncycastle.asn1.x509.AltSignatureAlgorithm;
+import com.android.internal.org.bouncycastle.asn1.x509.AltSignatureValue;
 import com.android.internal.org.bouncycastle.asn1.x509.CertificateList;
 import com.android.internal.org.bouncycastle.asn1.x509.Extension;
 import com.android.internal.org.bouncycastle.asn1.x509.Extensions;
@@ -41,7 +48,7 @@
     implements Encodable, Serializable
 {
     private static final long serialVersionUID = 20170722001L;
-    
+
     private transient CertificateList x509CRL;
     private transient boolean isIndirect;
     private transient Extensions extensions;
@@ -164,7 +171,7 @@
     public X509CRLEntryHolder getRevokedCertificate(BigInteger serialNumber)
     {
         GeneralNames currentCA = issuerName;
-        for (Enumeration en = x509CRL.getRevokedCertificateEnumeration(); en.hasMoreElements();)
+        for (Enumeration en = x509CRL.getRevokedCertificateEnumeration(); en.hasMoreElements(); )
         {
             TBSCertList.CRLEntry entry = (TBSCertList.CRLEntry)en.nextElement();
 
@@ -199,7 +206,7 @@
         List l = new ArrayList(entries.length);
         GeneralNames currentCA = issuerName;
 
-        for (Enumeration en = x509CRL.getRevokedCertificateEnumeration(); en.hasMoreElements();)
+        for (Enumeration en = x509CRL.getRevokedCertificateEnumeration(); en.hasMoreElements(); )
         {
             TBSCertList.CRLEntry entry = (TBSCertList.CRLEntry)en.nextElement();
             X509CRLEntryHolder crlEntry = new X509CRLEntryHolder(entry, isIndirect, currentCA);
@@ -211,7 +218,7 @@
 
         return l;
     }
-    
+
     /**
      * Return whether or not the holder's CRL contains extensions.
      *
@@ -226,7 +233,6 @@
      * Look up the extension associated with the passed in OID.
      *
      * @param oid the OID of the extension of interest.
-     *
      * @return the extension if present, null otherwise.
      */
     public Extension getExtension(ASN1ObjectIdentifier oid)
@@ -327,6 +333,51 @@
         return verifier.verify(x509CRL.getSignature().getOctets());
     }
 
+    public boolean isAlternativeSignatureValid(ContentVerifierProvider verifierProvider)
+        throws CertException
+    {
+        TBSCertList tbsCrList = x509CRL.getTBSCertList();
+        AltSignatureAlgorithm altSigAlg = AltSignatureAlgorithm.fromExtensions(tbsCrList.getExtensions());
+        AltSignatureValue altSigValue = AltSignatureValue.fromExtensions(tbsCrList.getExtensions());
+
+        ContentVerifier verifier;
+
+        try
+        {
+            verifier = verifierProvider.get(AlgorithmIdentifier.getInstance(altSigAlg.toASN1Primitive()));
+
+            OutputStream sOut = verifier.getOutputStream();
+
+            ASN1Sequence tbsSeq = ASN1Sequence.getInstance(tbsCrList.toASN1Primitive());
+            ASN1EncodableVector v = new ASN1EncodableVector();
+
+            int start = 1;    //  want to skip signature field
+            if (tbsSeq.getObjectAt(0) instanceof ASN1Integer)
+            {
+                v.add(tbsSeq.getObjectAt(0));
+                start++;
+            }
+
+            for (int i = start; i != tbsSeq.size() - 1; i++)
+            {
+                v.add(tbsSeq.getObjectAt(i));
+            }
+            
+            v.add(CertUtils.trimExtensions(0, tbsCrList.getExtensions()));
+
+            new DERSequence(v).encodeTo(sOut, ASN1Encoding.DER);
+
+            sOut.close();
+        }
+        catch (Exception e)
+        {
+            throw new CertException("unable to process signature: " + e.getMessage(), e);
+        }
+
+        return verifier.verify(altSigValue.getSignature().getOctets());
+    }
+
+
     public boolean equals(
         Object o)
     {
diff --git a/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cert/X509CertificateHolder.java b/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cert/X509CertificateHolder.java
index 9ca4a28..f302f89 100644
--- a/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cert/X509CertificateHolder.java
+++ b/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cert/X509CertificateHolder.java
@@ -11,10 +11,15 @@
 import java.util.List;
 import java.util.Set;
 
+import com.android.internal.org.bouncycastle.asn1.ASN1EncodableVector;
 import com.android.internal.org.bouncycastle.asn1.ASN1Encoding;
 import com.android.internal.org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import com.android.internal.org.bouncycastle.asn1.ASN1Sequence;
+import com.android.internal.org.bouncycastle.asn1.DERSequence;
 import com.android.internal.org.bouncycastle.asn1.x500.X500Name;
 import com.android.internal.org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import com.android.internal.org.bouncycastle.asn1.x509.AltSignatureAlgorithm;
+import com.android.internal.org.bouncycastle.asn1.x509.AltSignatureValue;
 import com.android.internal.org.bouncycastle.asn1.x509.Certificate;
 import com.android.internal.org.bouncycastle.asn1.x509.Extension;
 import com.android.internal.org.bouncycastle.asn1.x509.Extensions;
@@ -245,9 +250,9 @@
     }
 
     /**
-     * Return the bytes making up the signature associated with this attribute certificate.
+     * Return the bytes making up the signature associated with this certificate.
      *
-     * @return the attribute certificate signature bytes.
+     * @return the certificate signature bytes.
      */
     public byte[] getSignature()
     {
@@ -300,6 +305,53 @@
         return verifier.verify(this.getSignature());
     }
 
+    /**
+     * Validate the signature on the certificate in this holder.
+     *
+     * @param verifierProvider a ContentVerifierProvider that can generate a verifier for the signature.
+     * @return true if the signature is valid, false otherwise.
+     * @throws CertException if the signature cannot be processed or is inappropriate.
+     */
+    public boolean isAlternativeSignatureValid(ContentVerifierProvider verifierProvider)
+        throws CertException
+    {
+        TBSCertificate tbsCert = x509Certificate.getTBSCertificate();
+        AltSignatureAlgorithm altSigAlg = AltSignatureAlgorithm.fromExtensions(tbsCert.getExtensions());
+        AltSignatureValue altSigValue = AltSignatureValue.fromExtensions(tbsCert.getExtensions());
+
+        ContentVerifier verifier;
+
+        try
+        {
+            verifier = verifierProvider.get(AlgorithmIdentifier.getInstance(altSigAlg.toASN1Primitive()));
+
+            OutputStream sOut = verifier.getOutputStream();
+
+            ASN1Sequence tbsSeq = ASN1Sequence.getInstance(tbsCert.toASN1Primitive());
+            ASN1EncodableVector v = new ASN1EncodableVector();
+
+            for (int i = 0; i != tbsSeq.size() - 1; i++)
+            {
+                if (i != 2) // signature field - must be ver 3 so version always present
+                {
+                    v.add(tbsSeq.getObjectAt(i));
+                }
+            }
+
+            v.add(CertUtils.trimExtensions(3, tbsCert.getExtensions()));
+
+            new DERSequence(v).encodeTo(sOut, ASN1Encoding.DER);
+
+            sOut.close();
+        }
+        catch (Exception e)
+        {
+            throw new CertException("unable to process signature: " + e.getMessage(), e);
+        }
+
+        return verifier.verify(altSigValue.getSignature().getOctets());
+    }
+
     public boolean equals(
         Object o)
     {
diff --git a/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cert/X509v3CertificateBuilder.java b/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cert/X509v3CertificateBuilder.java
index cd446d0..0faeadf 100644
--- a/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cert/X509v3CertificateBuilder.java
+++ b/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cert/X509v3CertificateBuilder.java
@@ -15,10 +15,12 @@
 import com.android.internal.org.bouncycastle.asn1.ASN1Object;
 import com.android.internal.org.bouncycastle.asn1.ASN1ObjectIdentifier;
 import com.android.internal.org.bouncycastle.asn1.DERBitString;
+import com.android.internal.org.bouncycastle.asn1.DERNull;
 import com.android.internal.org.bouncycastle.asn1.DERSequence;
 import com.android.internal.org.bouncycastle.asn1.x500.X500Name;
 import com.android.internal.org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 import com.android.internal.org.bouncycastle.asn1.x509.Certificate;
+import com.android.internal.org.bouncycastle.asn1.x509.DeltaCertificateDescriptor;
 import com.android.internal.org.bouncycastle.asn1.x509.Extension;
 import com.android.internal.org.bouncycastle.asn1.x509.Extensions;
 import com.android.internal.org.bouncycastle.asn1.x509.ExtensionsGenerator;
@@ -27,6 +29,7 @@
 import com.android.internal.org.bouncycastle.asn1.x509.Time;
 import com.android.internal.org.bouncycastle.asn1.x509.V3TBSCertificateGenerator;
 import com.android.internal.org.bouncycastle.operator.ContentSigner;
+import com.android.internal.org.bouncycastle.util.Exceptions;
 
 
 /**
@@ -114,7 +117,16 @@
 
         for (Enumeration en = exts.oids(); en.hasMoreElements();)
         {
-            extGenerator.addExtension(exts.getExtension((ASN1ObjectIdentifier)en.nextElement()));
+            ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier)en.nextElement();
+            // we remove the altSignatureAlgorithm, altSignatureValue, and subjectAltPublicKeyInfo
+            // extensions as they probably need to be regenerated.
+            if (Extension.subjectAltPublicKeyInfo.equals(oid)
+                || Extension.altSignatureAlgorithm.equals(oid)
+                || Extension.altSignatureValue.equals(oid))
+            {
+                continue;
+            }
+            extGenerator.addExtension(exts.getExtension(oid));
         }
     }
 
@@ -142,6 +154,11 @@
 
     private Extension doGetExtension(ASN1ObjectIdentifier oid)
     {
+        if (extGenerator.isEmpty())
+        {
+            return null;
+        }
+        
         Extensions exts = extGenerator.generate();
 
         return exts.getExtension(oid);
@@ -365,6 +382,21 @@
 
         if (!extGenerator.isEmpty())
         {
+            if (extGenerator.hasExtension(Extension.deltaCertificateDescriptor))
+            {
+                Extension deltaExt = extGenerator.getExtension(Extension.deltaCertificateDescriptor);
+                DeltaCertificateDescriptor deltaDesc = DeltaCertificateDescriptor.getInstance(deltaExt.getParsedValue());
+
+                try
+                {
+                    extGenerator.replaceExtension(Extension.deltaCertificateDescriptor, deltaExt.isCritical(),
+                        deltaDesc.trimTo(tbsGen.generateTBSCertificate(), extGenerator.generate()));
+                }
+                catch (IOException e)
+                {
+                    throw new IllegalStateException("unable to replace deltaCertificateDescriptor: " + e.getMessage()) ;
+                }
+            }
             tbsGen.setExtensions(extGenerator.generate());
         }
 
@@ -375,7 +407,75 @@
         }
         catch (IOException e)
         {
-            throw new IllegalArgumentException("cannot produce certificate signature");
+            throw Exceptions.illegalArgumentException("cannot produce certificate signature", e);
+        }
+    }
+
+    /**
+     * Generate an X.509 certificate, based on the current issuer and subject
+     * using the passed in signer and containing altSignatureAlgorithm and altSignatureValue extensions
+     * based on the passed altSigner.
+     *
+     * @param signer the content signer to be used to generate the signature validating the certificate.
+     * @param altSigner the content signer used to create the altSignatureAlgorithm and altSignatureValue extension.
+     * @return a holder containing the resulting signed certificate.
+     */
+    public X509CertificateHolder build(
+        ContentSigner signer,
+        boolean isCritical,
+        ContentSigner altSigner)
+    {
+        try
+        {
+            extGenerator.addExtension(Extension.altSignatureAlgorithm, isCritical, altSigner.getAlgorithmIdentifier());
+        }
+        catch (IOException e)
+        {
+            throw Exceptions.illegalStateException("cannot add altSignatureAlgorithm extension", e);
+        }
+
+        if (extGenerator.hasExtension(Extension.deltaCertificateDescriptor))
+        {
+            tbsGen.setSignature(signer.getAlgorithmIdentifier());
+            
+            Extension deltaExt = extGenerator.getExtension(Extension.deltaCertificateDescriptor);
+            DeltaCertificateDescriptor deltaDesc = DeltaCertificateDescriptor.getInstance(deltaExt.getParsedValue());
+
+            try
+            {
+                // the altSignatureValue is not present yet, but it must be in the deltaCertificate and
+                // it must be different (by definition!). We add a dummy one to trigger inclusion.
+                ExtensionsGenerator tmpExtGen = new ExtensionsGenerator();
+                tmpExtGen.addExtension(extGenerator.generate());
+                tmpExtGen.addExtension(Extension.altSignatureValue, false, DERNull.INSTANCE);
+
+                extGenerator.replaceExtension(Extension.deltaCertificateDescriptor, deltaExt.isCritical(),
+                    deltaDesc.trimTo(tbsGen.generateTBSCertificate(), tmpExtGen.generate()));
+            }
+            catch (IOException e)
+            {
+                throw new IllegalStateException("unable to replace deltaCertificateDescriptor: " + e.getMessage());
+            }
+        }
+
+        tbsGen.setSignature(null);
+
+        tbsGen.setExtensions(extGenerator.generate());
+
+        try
+        {
+            extGenerator.addExtension(Extension.altSignatureValue, isCritical, new DERBitString(generateSig(altSigner, tbsGen.generatePreTBSCertificate())));
+
+            tbsGen.setSignature(signer.getAlgorithmIdentifier());
+
+            tbsGen.setExtensions(extGenerator.generate());
+            
+            TBSCertificate tbsCert = tbsGen.generateTBSCertificate();
+            return new X509CertificateHolder(generateStructure(tbsCert, signer.getAlgorithmIdentifier(), generateSig(signer, tbsCert)));
+        }
+        catch (IOException e)
+        {
+            throw Exceptions.illegalArgumentException("cannot produce certificate signature", e);
         }
     }
 
diff --git a/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cert/ocsp/BasicOCSPResp.java b/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cert/ocsp/BasicOCSPResp.java
new file mode 100644
index 0000000..ce6beb8
--- /dev/null
+++ b/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cert/ocsp/BasicOCSPResp.java
@@ -0,0 +1,228 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.cert.ocsp;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.Date;
+import java.util.List;
+import java.util.Set;
+
+import com.android.internal.org.bouncycastle.asn1.ASN1Encoding;
+import com.android.internal.org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import com.android.internal.org.bouncycastle.asn1.ASN1Sequence;
+import com.android.internal.org.bouncycastle.asn1.ocsp.BasicOCSPResponse;
+import com.android.internal.org.bouncycastle.asn1.ocsp.ResponseData;
+import com.android.internal.org.bouncycastle.asn1.ocsp.SingleResponse;
+import com.android.internal.org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import com.android.internal.org.bouncycastle.asn1.x509.Certificate;
+import com.android.internal.org.bouncycastle.asn1.x509.Extension;
+import com.android.internal.org.bouncycastle.asn1.x509.Extensions;
+import com.android.internal.org.bouncycastle.cert.X509CertificateHolder;
+import com.android.internal.org.bouncycastle.operator.ContentVerifier;
+import com.android.internal.org.bouncycastle.operator.ContentVerifierProvider;
+import com.android.internal.org.bouncycastle.util.Encodable;
+
+/**
+ * OCSP RFC 2560, RFC 6960
+ * <pre>
+ * BasicOCSPResponse       ::= SEQUENCE {
+ *    tbsResponseData      ResponseData,
+ *    signatureAlgorithm   AlgorithmIdentifier,
+ *    signature            BIT STRING,
+ *    certs                [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL }
+ * </pre>
+ * @hide This class is not part of the Android public SDK API
+ */
+public class BasicOCSPResp
+    implements Encodable
+{
+    private BasicOCSPResponse   resp;
+    private ResponseData        data;
+    private Extensions extensions;
+
+    public BasicOCSPResp(
+        BasicOCSPResponse   resp)
+    {
+        this.resp = resp;
+        this.data = resp.getTbsResponseData();
+        this.extensions = Extensions.getInstance(resp.getTbsResponseData().getResponseExtensions());
+    }
+
+    /**
+     * Return the DER encoding of the tbsResponseData field.
+     * @return DER encoding of tbsResponseData
+     */
+    public byte[] getTBSResponseData()
+    {
+        try
+        {
+            return resp.getTbsResponseData().getEncoded(ASN1Encoding.DER);
+        }
+        catch (IOException e)
+        {
+            return null;
+        }
+    }
+
+    /**
+     * Return the algorithm identifier describing the signature used in the response.
+     *
+     * @return an AlgorithmIdentifier
+     */
+    public AlgorithmIdentifier getSignatureAlgorithmID()
+    {
+        return resp.getSignatureAlgorithm();
+    }
+
+    public int getVersion()
+    {
+        return data.getVersion().intValueExact() + 1;
+    }
+
+    public RespID getResponderId()
+    {
+        return new RespID(data.getResponderID());
+    }
+
+    public Date getProducedAt()
+    {
+        return OCSPUtils.extractDate(data.getProducedAt());
+    }
+
+    public SingleResp[] getResponses()
+    {
+        ASN1Sequence    s = data.getResponses();
+        SingleResp[]    rs = new SingleResp[s.size()];
+
+        for (int i = 0; i != rs.length; i++)
+        {
+            rs[i] = new SingleResp(SingleResponse.getInstance(s.getObjectAt(i)));
+        }
+
+        return rs;
+    }
+
+    public boolean hasExtensions()
+   {
+       return extensions != null;
+   }
+
+   public Extension getExtension(ASN1ObjectIdentifier oid)
+   {
+       if (extensions != null)
+       {
+           return extensions.getExtension(oid);
+       }
+
+       return null;
+   }
+
+   public List getExtensionOIDs()
+   {
+       return OCSPUtils.getExtensionOIDs(extensions);
+   }
+
+   public Set getCriticalExtensionOIDs()
+   {
+       return OCSPUtils.getCriticalExtensionOIDs(extensions);
+   }
+
+   public Set getNonCriticalExtensionOIDs()
+   {
+       return OCSPUtils.getNonCriticalExtensionOIDs(extensions);
+   }
+
+
+    public ASN1ObjectIdentifier getSignatureAlgOID()
+    {
+        return resp.getSignatureAlgorithm().getAlgorithm();
+    }
+
+    public byte[] getSignature()
+    {
+        return resp.getSignature().getOctets();
+    }
+
+    public X509CertificateHolder[] getCerts()
+    {
+        //
+        // load the certificates if we have any
+        //
+        if (resp.getCerts() != null)
+        {
+            ASN1Sequence s = resp.getCerts();
+
+            if (s != null)
+            {
+                X509CertificateHolder[] certs = new X509CertificateHolder[s.size()];
+
+                for (int i = 0; i != certs.length; i++)
+                {
+                    certs[i] = new X509CertificateHolder(Certificate.getInstance(s.getObjectAt(i)));
+                }
+
+                return certs;
+            }
+
+            return OCSPUtils.EMPTY_CERTS;
+        }
+        else
+        {
+            return OCSPUtils.EMPTY_CERTS;
+        }
+    }
+
+    /**
+     * verify the signature against the tbsResponseData object we contain.
+     */
+    public boolean isSignatureValid(
+        ContentVerifierProvider verifierProvider)
+        throws OCSPException
+    {
+        try
+        {
+            ContentVerifier verifier = verifierProvider.get(resp.getSignatureAlgorithm());
+            OutputStream vOut = verifier.getOutputStream();
+
+            vOut.write(resp.getTbsResponseData().getEncoded(ASN1Encoding.DER));
+            vOut.close();
+
+            return verifier.verify(this.getSignature());
+        }
+        catch (Exception e)
+        {
+            throw new OCSPException("exception processing sig: " + e, e);
+        }
+    }
+
+    /**
+     * return the ASN.1 encoded representation of this object.
+     */
+    public byte[] getEncoded()
+        throws IOException
+    {
+        return resp.getEncoded();
+    }
+    
+    public boolean equals(Object o)
+    {
+        if (o == this)
+        {
+            return true;
+        }
+        
+        if (!(o instanceof BasicOCSPResp))
+        {
+            return false;
+        }
+        
+        BasicOCSPResp r = (BasicOCSPResp)o;
+        
+        return resp.equals(r.resp);
+    }
+    
+    public int hashCode()
+    {
+        return resp.hashCode();
+    }
+}
diff --git a/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cert/ocsp/BasicOCSPRespBuilder.java b/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cert/ocsp/BasicOCSPRespBuilder.java
new file mode 100644
index 0000000..988b852
--- /dev/null
+++ b/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cert/ocsp/BasicOCSPRespBuilder.java
@@ -0,0 +1,285 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.cert.ocsp;
+
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.List;
+
+import com.android.internal.org.bouncycastle.asn1.ASN1EncodableVector;
+import com.android.internal.org.bouncycastle.asn1.ASN1Encoding;
+import com.android.internal.org.bouncycastle.asn1.ASN1GeneralizedTime;
+import com.android.internal.org.bouncycastle.asn1.DERBitString;
+import com.android.internal.org.bouncycastle.asn1.DERGeneralizedTime;
+import com.android.internal.org.bouncycastle.asn1.DERNull;
+import com.android.internal.org.bouncycastle.asn1.DERSequence;
+import com.android.internal.org.bouncycastle.asn1.ocsp.BasicOCSPResponse;
+import com.android.internal.org.bouncycastle.asn1.ocsp.CertStatus;
+import com.android.internal.org.bouncycastle.asn1.ocsp.ResponseData;
+import com.android.internal.org.bouncycastle.asn1.ocsp.RevokedInfo;
+import com.android.internal.org.bouncycastle.asn1.ocsp.SingleResponse;
+import com.android.internal.org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import com.android.internal.org.bouncycastle.asn1.x509.CRLReason;
+import com.android.internal.org.bouncycastle.asn1.x509.Extensions;
+import com.android.internal.org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import com.android.internal.org.bouncycastle.cert.X509CertificateHolder;
+import com.android.internal.org.bouncycastle.operator.ContentSigner;
+import com.android.internal.org.bouncycastle.operator.DigestCalculator;
+
+/**
+ * Generator for basic OCSP response objects.
+ * @hide This class is not part of the Android public SDK API
+ */
+public class BasicOCSPRespBuilder
+{
+    private List            list = new ArrayList();
+    private Extensions  responseExtensions = null;
+    private RespID          responderID;
+
+    private static class ResponseObject
+    {
+        CertificateID         certId;
+        CertStatus            certStatus;
+        ASN1GeneralizedTime   thisUpdate;
+        ASN1GeneralizedTime   nextUpdate;
+        Extensions        extensions;
+
+        public ResponseObject(
+            CertificateID     certId,
+            CertificateStatus certStatus,
+            Date              thisUpdate,
+            Date              nextUpdate,
+            Extensions    extensions)
+        {
+            this.certId = certId;
+
+            if (certStatus == null)
+            {
+                this.certStatus = new CertStatus();
+            }
+            else if (certStatus instanceof UnknownStatus)
+            {
+                this.certStatus = new CertStatus(2, DERNull.INSTANCE);
+            }
+            else
+            {
+                RevokedStatus rs = (RevokedStatus)certStatus;
+
+                if (rs.hasRevocationReason())
+                {
+                    this.certStatus = new CertStatus(
+                                            new RevokedInfo(new ASN1GeneralizedTime(rs.getRevocationTime()), CRLReason.lookup(rs.getRevocationReason())));
+                }
+                else
+                {
+                    this.certStatus = new CertStatus(
+                                            new RevokedInfo(new ASN1GeneralizedTime(rs.getRevocationTime()), null));
+                }
+            }
+
+            this.thisUpdate = new DERGeneralizedTime(thisUpdate);
+
+            if (nextUpdate != null)
+            {
+                this.nextUpdate = new DERGeneralizedTime(nextUpdate);
+            }
+            else
+            {
+                this.nextUpdate = null;
+            }
+
+            this.extensions = extensions;
+        }
+
+        public SingleResponse toResponse()
+            throws Exception
+        {
+            return new SingleResponse(certId.toASN1Primitive(), certStatus, thisUpdate, nextUpdate, extensions);
+        }
+    }
+
+    /**
+     * basic constructor
+     */
+    public BasicOCSPRespBuilder(
+        RespID  responderID)
+    {
+        this.responderID = responderID;
+    }
+
+    /**
+     * construct with the responderID to be the SHA-1 keyHash of the passed in public key.
+     *
+     * @param key the key info of the responder public key.
+     * @param digCalc  a SHA-1 digest calculator
+     */
+    public BasicOCSPRespBuilder(
+        SubjectPublicKeyInfo key,
+        DigestCalculator     digCalc)
+        throws OCSPException
+    {
+        this.responderID = new RespID(key, digCalc);
+    }
+
+    /**
+     * Add a response for a particular Certificate ID.
+     * 
+     * @param certID certificate ID details
+     * @param certStatus status of the certificate - null if okay
+     */
+    public BasicOCSPRespBuilder addResponse(
+        CertificateID       certID,
+        CertificateStatus   certStatus)
+    {
+        this.addResponse(certID, certStatus, new Date(), null, null);
+
+        return this;
+    }
+
+    /**
+     * Add a response for a particular Certificate ID.
+     * 
+     * @param certID certificate ID details
+     * @param certStatus status of the certificate - null if okay
+     * @param singleExtensions optional extensions
+     */
+    public BasicOCSPRespBuilder addResponse(
+        CertificateID       certID,
+        CertificateStatus   certStatus,
+        Extensions          singleExtensions)
+    {
+        this.addResponse(certID, certStatus, new Date(), null, singleExtensions);
+
+        return this;
+    }
+    
+    /**
+     * Add a response for a particular Certificate ID.
+     * 
+     * @param certID certificate ID details
+     * @param nextUpdate date when next update should be requested
+     * @param certStatus status of the certificate - null if okay
+     * @param singleExtensions optional extensions
+     */
+    public BasicOCSPRespBuilder addResponse(
+        CertificateID       certID,
+        CertificateStatus   certStatus,
+        Date                nextUpdate,
+        Extensions          singleExtensions)
+    {
+        this.addResponse(certID, certStatus, new Date(), nextUpdate, singleExtensions);
+
+        return this;
+    }
+
+    /**
+     * Add a response for a particular Certificate ID.
+     *
+     * @param certID certificate ID details
+     * @param thisUpdate date this response was valid on
+     * @param nextUpdate date when next update should be requested
+     * @param certStatus status of the certificate - null if okay
+     */
+    public BasicOCSPRespBuilder addResponse(
+        CertificateID       certID,
+        CertificateStatus   certStatus,
+        Date                thisUpdate,
+        Date                nextUpdate)
+    {
+        this.addResponse(certID, certStatus, thisUpdate, nextUpdate, null);
+
+        return this;
+    }
+
+    /**
+     * Add a response for a particular Certificate ID.
+     * 
+     * @param certID certificate ID details
+     * @param thisUpdate date this response was valid on
+     * @param nextUpdate date when next update should be requested
+     * @param certStatus status of the certificate - null if okay
+     * @param singleExtensions optional extensions
+     */
+    public BasicOCSPRespBuilder addResponse(
+        CertificateID       certID,
+        CertificateStatus   certStatus,
+        Date                thisUpdate,
+        Date                nextUpdate,
+        Extensions          singleExtensions)
+    {
+        list.add(new ResponseObject(certID, certStatus, thisUpdate, nextUpdate, singleExtensions));
+
+        return this;
+    }
+    
+    /**
+     * Set the extensions for the response.
+     * 
+     * @param responseExtensions the extension object to carry.
+     */
+    public BasicOCSPRespBuilder setResponseExtensions(
+        Extensions  responseExtensions)
+    {
+        this.responseExtensions = responseExtensions;
+
+        return this;
+    }
+
+    public BasicOCSPResp build(
+        ContentSigner signer,
+        X509CertificateHolder[]   chain,
+        Date                producedAt)
+        throws OCSPException
+    {
+        Iterator    it = list.iterator();
+
+        ASN1EncodableVector responses = new ASN1EncodableVector();
+
+        while (it.hasNext())
+        {
+            try
+            {
+                responses.add(((ResponseObject)it.next()).toResponse());
+            }
+            catch (Exception e)
+            {
+                throw new OCSPException("exception creating Request", e);
+            }
+        }
+
+        ResponseData  tbsResp = new ResponseData(responderID.toASN1Primitive(), new ASN1GeneralizedTime(producedAt), new DERSequence(responses), responseExtensions);
+        DERBitString    bitSig;
+
+        try
+        {
+            OutputStream sigOut = signer.getOutputStream();
+
+            sigOut.write(tbsResp.getEncoded(ASN1Encoding.DER));
+            sigOut.close();
+
+            bitSig = new DERBitString(signer.getSignature());
+        }
+        catch (Exception e)
+        {
+            throw new OCSPException("exception processing TBSRequest: " + e.getMessage(), e);
+        }
+
+        AlgorithmIdentifier sigAlgId = signer.getAlgorithmIdentifier();
+
+        DERSequence chainSeq = null;
+        if (chain != null && chain.length > 0)
+        {
+            ASN1EncodableVector v = new ASN1EncodableVector();
+
+            for (int i = 0; i != chain.length; i++)
+            {
+                v.add(chain[i].toASN1Structure());
+            }
+
+            chainSeq = new DERSequence(v);
+        }
+
+        return new BasicOCSPResp(new BasicOCSPResponse(tbsResp, sigAlgId, bitSig, chainSeq));
+    }
+}
diff --git a/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cert/ocsp/CertificateID.java b/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cert/ocsp/CertificateID.java
new file mode 100644
index 0000000..611aae6
--- /dev/null
+++ b/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cert/ocsp/CertificateID.java
@@ -0,0 +1,160 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.cert.ocsp;
+
+import java.io.OutputStream;
+import java.math.BigInteger;
+
+import com.android.internal.org.bouncycastle.asn1.ASN1Encoding;
+import com.android.internal.org.bouncycastle.asn1.ASN1Integer;
+import com.android.internal.org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import com.android.internal.org.bouncycastle.asn1.ASN1OctetString;
+import com.android.internal.org.bouncycastle.asn1.DERNull;
+import com.android.internal.org.bouncycastle.asn1.DEROctetString;
+import com.android.internal.org.bouncycastle.asn1.ocsp.CertID;
+import com.android.internal.org.bouncycastle.asn1.oiw.OIWObjectIdentifiers;
+import com.android.internal.org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import com.android.internal.org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import com.android.internal.org.bouncycastle.cert.X509CertificateHolder;
+import com.android.internal.org.bouncycastle.operator.DigestCalculator;
+import com.android.internal.org.bouncycastle.operator.DigestCalculatorProvider;
+import com.android.internal.org.bouncycastle.operator.OperatorCreationException;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+public class CertificateID
+{
+    public static final AlgorithmIdentifier HASH_SHA1 = new AlgorithmIdentifier(OIWObjectIdentifiers.idSHA1, DERNull.INSTANCE);
+
+    private final CertID id;
+
+    public CertificateID(
+        CertID id)
+    {
+        if (id == null)
+        {
+            throw new IllegalArgumentException("'id' cannot be null");
+        }
+        this.id = id;
+    }
+
+    /**
+     * create from an issuer certificate and the serial number of the
+     * certificate it signed.
+     *
+     * @param issuerCert issuing certificate
+     * @param number serial number
+     *
+     * @exception OCSPException if any problems occur creating the id fields.
+     */
+    public CertificateID(
+        DigestCalculator digestCalculator, X509CertificateHolder issuerCert,
+        BigInteger number)
+        throws OCSPException
+    {
+        this.id = createCertID(digestCalculator, issuerCert, new ASN1Integer(number));
+    }
+
+    public ASN1ObjectIdentifier getHashAlgOID()
+    {
+        return id.getHashAlgorithm().getAlgorithm();
+    }
+
+    public byte[] getIssuerNameHash()
+    {
+        return id.getIssuerNameHash().getOctets();
+    }
+
+    public byte[] getIssuerKeyHash()
+    {
+        return id.getIssuerKeyHash().getOctets();
+    }
+
+    /**
+     * return the serial number for the certificate associated
+     * with this request.
+     */
+    public BigInteger getSerialNumber()
+    {
+        return id.getSerialNumber().getValue();
+    }
+
+    public boolean matchesIssuer(X509CertificateHolder issuerCert, DigestCalculatorProvider digCalcProvider)
+        throws OCSPException
+    {
+        try
+        {
+            return createCertID(digCalcProvider.get(id.getHashAlgorithm()), issuerCert, id.getSerialNumber()).equals(id);
+        }
+        catch (OperatorCreationException e)
+        {
+            throw new OCSPException("unable to create digest calculator: " + e.getMessage(), e);
+        }
+    }
+
+    public CertID toASN1Primitive()
+    {
+        return id;
+    }
+
+    public boolean equals(
+        Object  o)
+    {
+        if (!(o instanceof CertificateID))
+        {
+            return false;
+        }
+
+        CertificateID obj = (CertificateID)o;
+
+        return id.toASN1Primitive().equals(obj.id.toASN1Primitive());
+    }
+
+    public int hashCode()
+    {
+        return id.toASN1Primitive().hashCode();
+    }
+
+    /**
+     * Create a new CertificateID for a new serial number derived from a previous one
+     * calculated for the same CA certificate.
+     *
+     * @param original the previously calculated CertificateID for the CA.
+     * @param newSerialNumber the serial number for the new certificate of interest.
+     *
+     * @return a new CertificateID for newSerialNumber
+     */
+    public static CertificateID deriveCertificateID(CertificateID original, BigInteger newSerialNumber)
+    {
+        return new CertificateID(new CertID(original.id.getHashAlgorithm(), original.id.getIssuerNameHash(), original.id.getIssuerKeyHash(), new ASN1Integer(newSerialNumber)));
+    }
+
+    private static CertID createCertID(DigestCalculator digCalc, X509CertificateHolder issuerCert, ASN1Integer serialNumber)
+        throws OCSPException
+    {
+        try
+        {
+            OutputStream dgOut = digCalc.getOutputStream();
+
+            dgOut.write(issuerCert.toASN1Structure().getSubject().getEncoded(ASN1Encoding.DER));
+            dgOut.close();
+
+            ASN1OctetString issuerNameHash = new DEROctetString(digCalc.getDigest());
+
+            SubjectPublicKeyInfo info = issuerCert.getSubjectPublicKeyInfo();
+
+            dgOut = digCalc.getOutputStream();
+
+            dgOut.write(info.getPublicKeyData().getBytes());
+            dgOut.close();
+
+            ASN1OctetString issuerKeyHash = new DEROctetString(digCalc.getDigest());
+
+            return new CertID(digCalc.getAlgorithmIdentifier(), issuerNameHash, issuerKeyHash, serialNumber);
+        }
+        catch (Exception e)
+        {
+            throw new OCSPException("problem creating ID: " + e, e);
+        }
+    }
+}
diff --git a/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cert/ocsp/CertificateStatus.java b/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cert/ocsp/CertificateStatus.java
new file mode 100644
index 0000000..84321c8
--- /dev/null
+++ b/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cert/ocsp/CertificateStatus.java
@@ -0,0 +1,10 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.cert.ocsp;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+public interface CertificateStatus
+{
+    public static final CertificateStatus GOOD = null;
+}
diff --git a/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cert/ocsp/OCSPException.java b/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cert/ocsp/OCSPException.java
new file mode 100644
index 0000000..c7cee8d
--- /dev/null
+++ b/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cert/ocsp/OCSPException.java
@@ -0,0 +1,31 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.cert.ocsp;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+public class OCSPException
+    extends Exception
+{
+    private Throwable   cause;
+
+    public OCSPException(
+        String name)
+    {
+        super(name);
+    }
+
+    public OCSPException(
+        String name,
+        Throwable cause)
+    {
+        super(name);
+
+        this.cause = cause;
+    }
+
+    public Throwable getCause()
+    {
+        return cause;
+    }
+}
diff --git a/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cert/ocsp/OCSPReq.java b/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cert/ocsp/OCSPReq.java
new file mode 100644
index 0000000..14f1eab
--- /dev/null
+++ b/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cert/ocsp/OCSPReq.java
@@ -0,0 +1,255 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.cert.ocsp;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.List;
+import java.util.Set;
+
+import com.android.internal.org.bouncycastle.asn1.ASN1Encoding;
+import com.android.internal.org.bouncycastle.asn1.ASN1Exception;
+import com.android.internal.org.bouncycastle.asn1.ASN1InputStream;
+import com.android.internal.org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import com.android.internal.org.bouncycastle.asn1.ASN1OutputStream;
+import com.android.internal.org.bouncycastle.asn1.ASN1Sequence;
+import com.android.internal.org.bouncycastle.asn1.ocsp.OCSPRequest;
+import com.android.internal.org.bouncycastle.asn1.ocsp.Request;
+import com.android.internal.org.bouncycastle.asn1.x509.Certificate;
+import com.android.internal.org.bouncycastle.asn1.x509.Extension;
+import com.android.internal.org.bouncycastle.asn1.x509.Extensions;
+import com.android.internal.org.bouncycastle.asn1.x509.GeneralName;
+import com.android.internal.org.bouncycastle.cert.CertIOException;
+import com.android.internal.org.bouncycastle.cert.X509CertificateHolder;
+import com.android.internal.org.bouncycastle.operator.ContentVerifier;
+import com.android.internal.org.bouncycastle.operator.ContentVerifierProvider;
+
+/**
+ * <pre>
+ * OCSPRequest     ::=     SEQUENCE {
+ *       tbsRequest                  TBSRequest,
+ *       optionalSignature   [0]     EXPLICIT Signature OPTIONAL }
+ *
+ *   TBSRequest      ::=     SEQUENCE {
+ *       version             [0]     EXPLICIT Version DEFAULT v1,
+ *       requestorName       [1]     EXPLICIT GeneralName OPTIONAL,
+ *       requestList                 SEQUENCE OF Request,
+ *       requestExtensions   [2]     EXPLICIT Extensions OPTIONAL }
+ *
+ *   Signature       ::=     SEQUENCE {
+ *       signatureAlgorithm      AlgorithmIdentifier,
+ *       signature               BIT STRING,
+ *       certs               [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL}
+ *
+ *   Version         ::=             INTEGER  {  v1(0) }
+ *
+ *   Request         ::=     SEQUENCE {
+ *       reqCert                     CertID,
+ *       singleRequestExtensions     [0] EXPLICIT Extensions OPTIONAL }
+ *
+ *   CertID          ::=     SEQUENCE {
+ *       hashAlgorithm       AlgorithmIdentifier,
+ *       issuerNameHash      OCTET STRING, -- Hash of Issuer's DN
+ *       issuerKeyHash       OCTET STRING, -- Hash of Issuers public key
+ *       serialNumber        CertificateSerialNumber }
+ * </pre>
+ * @hide This class is not part of the Android public SDK API
+ */
+public class OCSPReq
+{
+    private static final X509CertificateHolder[] EMPTY_CERTS = new X509CertificateHolder[0];
+
+    private OCSPRequest    req;
+    private Extensions extensions;
+
+    public OCSPReq(
+        OCSPRequest req)
+    {
+        this.req = req;
+        this.extensions = req.getTbsRequest().getRequestExtensions();
+    }
+    
+    public OCSPReq(
+        byte[]          req)
+        throws IOException
+    {
+        this(new ASN1InputStream(req));
+    }
+
+    private OCSPReq(
+        ASN1InputStream aIn)
+        throws IOException
+    {
+        try
+        {
+            this.req = OCSPRequest.getInstance(aIn.readObject());
+            if (req == null)
+            {
+                throw new CertIOException("malformed request: no request data found");
+            }
+            this.extensions = req.getTbsRequest().getRequestExtensions();
+        }
+        catch (IllegalArgumentException e)
+        {
+            throw new CertIOException("malformed request: " + e.getMessage(), e);
+        }
+        catch (ClassCastException e)
+        {
+            throw new CertIOException("malformed request: " + e.getMessage(), e);
+        }
+        catch (ASN1Exception e)
+        {
+            throw new CertIOException("malformed request: " + e.getMessage(), e);
+        }
+    }
+
+    public int getVersionNumber()
+    {
+        return req.getTbsRequest().getVersion().intValueExact() + 1;
+    }
+
+    public GeneralName getRequestorName()
+    {
+        return GeneralName.getInstance(req.getTbsRequest().getRequestorName());
+    }
+
+    public Req[] getRequestList()
+    {
+        ASN1Sequence    seq = req.getTbsRequest().getRequestList();
+        Req[]           requests = new Req[seq.size()];
+
+        for (int i = 0; i != requests.length; i++)
+        {
+            requests[i] = new Req(Request.getInstance(seq.getObjectAt(i)));
+        }
+
+        return requests;
+    }
+
+    public boolean hasExtensions()
+    {
+        return extensions != null;
+    }
+
+    public Extension getExtension(ASN1ObjectIdentifier oid)
+    {
+        if (extensions != null)
+        {
+            return extensions.getExtension(oid);
+        }
+
+        return null;
+    }
+
+    public List getExtensionOIDs()
+    {
+        return OCSPUtils.getExtensionOIDs(extensions);
+    }
+
+    public Set getCriticalExtensionOIDs()
+    {
+        return OCSPUtils.getCriticalExtensionOIDs(extensions);
+    }
+
+    public Set getNonCriticalExtensionOIDs()
+    {
+        return OCSPUtils.getNonCriticalExtensionOIDs(extensions);
+    }
+
+    /**
+     * return the object identifier representing the signature algorithm
+     */
+    public ASN1ObjectIdentifier getSignatureAlgOID()
+    {
+        if (!this.isSigned())
+        {
+            return null;
+        }
+
+        return req.getOptionalSignature().getSignatureAlgorithm().getAlgorithm();
+    }
+
+    public byte[] getSignature()
+    {
+        if (!this.isSigned())
+        {
+            return null;
+        }
+
+        return req.getOptionalSignature().getSignature().getOctets();
+    }
+
+    public X509CertificateHolder[] getCerts()
+    {
+        //
+        // load the certificates if we have any
+        //
+        if (req.getOptionalSignature() != null)
+        {
+            ASN1Sequence s = req.getOptionalSignature().getCerts();
+
+            if (s != null)
+            {
+                X509CertificateHolder[] certs = new X509CertificateHolder[s.size()];
+
+                for (int i = 0; i != certs.length; i++)
+                {
+                    certs[i] = new X509CertificateHolder(Certificate.getInstance(s.getObjectAt(i)));
+                }
+
+                return certs;
+            }
+
+            return EMPTY_CERTS;
+        }
+        else
+        {
+            return EMPTY_CERTS;
+        }
+    }
+    
+    /**
+     * Return whether or not this request is signed.
+     * 
+     * @return true if signed false otherwise.
+     */
+    public boolean isSigned()
+    {
+        return req.getOptionalSignature() != null;
+    }
+
+    /**
+     * verify the signature against the TBSRequest object we contain.
+     */
+    public boolean isSignatureValid(
+        ContentVerifierProvider verifierProvider)
+        throws OCSPException
+    {
+        if (!this.isSigned())
+        {
+            throw new OCSPException("attempt to verify signature on unsigned object");
+        }
+
+        try
+        {
+            ContentVerifier verifier = verifierProvider.get(req.getOptionalSignature().getSignatureAlgorithm());
+            OutputStream sOut = verifier.getOutputStream();
+
+            sOut.write(req.getTbsRequest().getEncoded(ASN1Encoding.DER));
+
+            return verifier.verify(this.getSignature());
+        }
+        catch (Exception e)
+        {
+            throw new OCSPException("exception processing signature: " + e, e);
+        }
+    }
+
+    /**
+     * return the ASN.1 encoded representation of this object.
+     */
+    public byte[] getEncoded() throws IOException
+    {
+        return req.getEncoded();
+    }
+}
diff --git a/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cert/ocsp/OCSPReqBuilder.java b/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cert/ocsp/OCSPReqBuilder.java
new file mode 100644
index 0000000..428ccb7
--- /dev/null
+++ b/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cert/ocsp/OCSPReqBuilder.java
@@ -0,0 +1,203 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.cert.ocsp;
+
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import com.android.internal.org.bouncycastle.asn1.ASN1EncodableVector;
+import com.android.internal.org.bouncycastle.asn1.ASN1Encoding;
+import com.android.internal.org.bouncycastle.asn1.DERBitString;
+import com.android.internal.org.bouncycastle.asn1.DERSequence;
+import com.android.internal.org.bouncycastle.asn1.ocsp.OCSPRequest;
+import com.android.internal.org.bouncycastle.asn1.ocsp.Request;
+import com.android.internal.org.bouncycastle.asn1.ocsp.Signature;
+import com.android.internal.org.bouncycastle.asn1.ocsp.TBSRequest;
+import com.android.internal.org.bouncycastle.asn1.x500.X500Name;
+import com.android.internal.org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import com.android.internal.org.bouncycastle.asn1.x509.Extensions;
+import com.android.internal.org.bouncycastle.asn1.x509.GeneralName;
+import com.android.internal.org.bouncycastle.cert.X509CertificateHolder;
+import com.android.internal.org.bouncycastle.operator.ContentSigner;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+public class OCSPReqBuilder
+{
+    private List            list = new ArrayList();
+    private GeneralName     requestorName = null;
+    private Extensions  requestExtensions = null;
+    
+    private static class RequestObject
+    {
+        CertificateID   certId;
+        Extensions  extensions;
+
+        public RequestObject(
+            CertificateID   certId,
+            Extensions  extensions)
+        {
+            this.certId = certId;
+            this.extensions = extensions;
+        }
+
+        public Request toRequest()
+            throws Exception
+        {
+            return new Request(certId.toASN1Primitive(), extensions);
+        }
+    }
+
+    /**
+     * Add a request for the given CertificateID.
+     * 
+     * @param certId certificate ID of interest
+     */
+    public OCSPReqBuilder addRequest(
+        CertificateID   certId)
+    {
+        list.add(new RequestObject(certId, null));
+
+        return this;
+    }
+
+    /**
+     * Add a request with extensions
+     * 
+     * @param certId certificate ID of interest
+     * @param singleRequestExtensions the extensions to attach to the request
+     */
+    public OCSPReqBuilder addRequest(
+        CertificateID   certId,
+        Extensions singleRequestExtensions)
+    {
+        list.add(new RequestObject(certId, singleRequestExtensions));
+
+        return this;
+    }
+
+    /**
+     * Set the requestor name to the passed in X500Name
+     * 
+     * @param requestorName an X500Name representing the requestor name.
+     */
+    public OCSPReqBuilder setRequestorName(
+        X500Name requestorName)
+    {
+        this.requestorName = new GeneralName(GeneralName.directoryName, requestorName);
+
+        return this;
+    }
+
+    public OCSPReqBuilder setRequestorName(
+        GeneralName         requestorName)
+    {
+        this.requestorName = requestorName;
+
+        return this;
+    }
+    
+    public OCSPReqBuilder setRequestExtensions(
+        Extensions      requestExtensions)
+    {
+        this.requestExtensions = requestExtensions;
+
+        return this;
+    }
+
+    private OCSPReq generateRequest(
+        ContentSigner           contentSigner,
+        X509CertificateHolder[] chain)
+        throws OCSPException
+    {
+        Iterator    it = list.iterator();
+
+        ASN1EncodableVector requests = new ASN1EncodableVector();
+
+        while (it.hasNext())
+        {
+            try
+            {
+                requests.add(((RequestObject)it.next()).toRequest());
+            }
+            catch (Exception e)
+            {
+                throw new OCSPException("exception creating Request", e);
+            }
+        }
+
+        TBSRequest  tbsReq = new TBSRequest(requestorName, new DERSequence(requests), requestExtensions);
+
+        Signature               signature = null;
+
+        if (contentSigner != null)
+        {
+            if (requestorName == null)
+            {
+                throw new OCSPException("requestorName must be specified if request is signed.");
+            }
+
+            try
+            {
+                OutputStream sOut = contentSigner.getOutputStream();
+
+                sOut.write(tbsReq.getEncoded(ASN1Encoding.DER));
+
+                sOut.close();
+            }
+            catch (Exception e)
+            {
+                throw new OCSPException("exception processing TBSRequest: " + e, e);
+            }
+
+            DERBitString    bitSig = new DERBitString(contentSigner.getSignature());
+
+            AlgorithmIdentifier sigAlgId = contentSigner.getAlgorithmIdentifier();
+
+            if (chain != null && chain.length > 0)
+            {
+                ASN1EncodableVector v = new ASN1EncodableVector();
+
+                for (int i = 0; i != chain.length; i++)
+                {
+                    v.add(chain[i].toASN1Structure());
+                }
+
+                signature = new Signature(sigAlgId, bitSig, new DERSequence(v));
+            }
+            else
+            {
+                signature = new Signature(sigAlgId, bitSig);
+            }
+        }
+
+        return new OCSPReq(new OCSPRequest(tbsReq, signature));
+    }
+    
+    /**
+     * Generate an unsigned request
+     * 
+     * @return the OCSPReq
+     * @throws com.android.internal.org.bouncycastle.cert.ocsp.OCSPException
+     */
+    public OCSPReq build()
+        throws OCSPException
+    {
+        return generateRequest(null, null);
+    }
+
+    public OCSPReq build(
+        ContentSigner             signer,
+        X509CertificateHolder[]   chain)
+        throws OCSPException, IllegalArgumentException
+    {
+        if (signer == null)
+        {
+            throw new IllegalArgumentException("no signer specified");
+        }
+
+        return generateRequest(signer, chain);
+    }
+}
diff --git a/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cert/ocsp/OCSPResp.java b/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cert/ocsp/OCSPResp.java
new file mode 100644
index 0000000..7293e66
--- /dev/null
+++ b/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cert/ocsp/OCSPResp.java
@@ -0,0 +1,145 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.cert.ocsp;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+import com.android.internal.org.bouncycastle.asn1.ASN1Exception;
+import com.android.internal.org.bouncycastle.asn1.ASN1InputStream;
+import com.android.internal.org.bouncycastle.asn1.ASN1Primitive;
+import com.android.internal.org.bouncycastle.asn1.ocsp.BasicOCSPResponse;
+import com.android.internal.org.bouncycastle.asn1.ocsp.OCSPObjectIdentifiers;
+import com.android.internal.org.bouncycastle.asn1.ocsp.OCSPResponse;
+import com.android.internal.org.bouncycastle.asn1.ocsp.ResponseBytes;
+import com.android.internal.org.bouncycastle.cert.CertIOException;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+public class OCSPResp
+{
+    public static final int SUCCESSFUL = 0;  // Response has valid confirmations
+    public static final int MALFORMED_REQUEST = 1;  // Illegal confirmation request
+    public static final int INTERNAL_ERROR = 2;  // Internal error in issuer
+    public static final int TRY_LATER = 3;  // Try again later
+    // (4) is not used
+    public static final int SIG_REQUIRED = 5;  // Must sign the request
+    public static final int UNAUTHORIZED = 6;  // Request unauthorized
+
+    private OCSPResponse    resp;
+
+    public OCSPResp(
+        OCSPResponse    resp)
+    {
+        this.resp = resp;
+    }
+
+    public OCSPResp(
+        byte[]          resp)
+        throws IOException
+    {
+        this(new ByteArrayInputStream(resp));
+    }
+
+    public OCSPResp(
+        InputStream resp)
+        throws IOException
+    {
+        this(new ASN1InputStream(resp));
+    }
+
+    private OCSPResp(
+        ASN1InputStream aIn)
+        throws IOException
+    {
+        try
+        {
+            this.resp = OCSPResponse.getInstance(aIn.readObject());
+        }
+        catch (IllegalArgumentException e)
+        {
+            throw new CertIOException("malformed response: " + e.getMessage(), e);
+        }
+        catch (ClassCastException e)
+        {
+            throw new CertIOException("malformed response: " + e.getMessage(), e);
+        }
+        catch (ASN1Exception e)
+        {
+            throw new CertIOException("malformed response: " + e.getMessage(), e);
+        }
+
+        if (resp == null)
+        {
+            throw new CertIOException("malformed response: no response data found");
+        }
+    }
+
+    public int getStatus()
+    {
+        return this.resp.getResponseStatus().getIntValue();
+    }
+
+    public Object getResponseObject()
+        throws OCSPException
+    {
+        ResponseBytes   rb = this.resp.getResponseBytes();
+
+        if (rb == null)
+        {
+            return null;
+        }
+
+        if (rb.getResponseType().equals(OCSPObjectIdentifiers.id_pkix_ocsp_basic))
+        {
+            try
+            {
+                ASN1Primitive obj = ASN1Primitive.fromByteArray(rb.getResponse().getOctets());
+                return new BasicOCSPResp(BasicOCSPResponse.getInstance(obj));
+            }
+            catch (Exception e)
+            {
+                throw new OCSPException("problem decoding object: " + e, e);
+            }
+        }
+
+        return rb.getResponse();
+    }
+
+    /**
+     * return the ASN.1 encoded representation of this object.
+     */
+    public byte[] getEncoded()
+        throws IOException
+    {
+        return resp.getEncoded();
+    }
+    
+    public boolean equals(Object o)
+    {
+        if (o == this)
+        {
+            return true;
+        }
+        
+        if (!(o instanceof OCSPResp))
+        {
+            return false;
+        }
+        
+        OCSPResp r = (OCSPResp)o;
+        
+        return resp.equals(r.resp);
+    }
+    
+    public int hashCode()
+    {
+        return resp.hashCode();
+    }
+
+    public OCSPResponse toASN1Structure()
+    {
+        return resp;
+    }
+}
diff --git a/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cert/ocsp/OCSPRespBuilder.java b/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cert/ocsp/OCSPRespBuilder.java
new file mode 100644
index 0000000..9ba31fa
--- /dev/null
+++ b/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cert/ocsp/OCSPRespBuilder.java
@@ -0,0 +1,61 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.cert.ocsp;
+
+import java.io.IOException;
+
+import com.android.internal.org.bouncycastle.asn1.ASN1OctetString;
+import com.android.internal.org.bouncycastle.asn1.DEROctetString;
+import com.android.internal.org.bouncycastle.asn1.ocsp.OCSPObjectIdentifiers;
+import com.android.internal.org.bouncycastle.asn1.ocsp.OCSPResponse;
+import com.android.internal.org.bouncycastle.asn1.ocsp.OCSPResponseStatus;
+import com.android.internal.org.bouncycastle.asn1.ocsp.ResponseBytes;
+
+/**
+ * base generator for an OCSP response - at the moment this only supports the
+ * generation of responses containing BasicOCSP responses.
+ * @hide This class is not part of the Android public SDK API
+ */
+public class OCSPRespBuilder
+{
+    public static final int SUCCESSFUL = 0;  // Response has valid confirmations
+    public static final int MALFORMED_REQUEST = 1;  // Illegal confirmation request
+    public static final int INTERNAL_ERROR = 2;  // Internal error in issuer
+    public static final int TRY_LATER = 3;  // Try again later
+    // (4) is not used
+    public static final int SIG_REQUIRED = 5;  // Must sign the request
+    public static final int UNAUTHORIZED = 6;  // Request unauthorized
+
+    public OCSPResp build(
+        int status,
+        Object response)
+        throws OCSPException
+    {
+        if (response == null)
+        {
+            return new OCSPResp(new OCSPResponse(new OCSPResponseStatus(status), null));
+        }
+
+        if (response instanceof BasicOCSPResp)
+        {
+            BasicOCSPResp r = (BasicOCSPResp)response;
+            ASN1OctetString octs;
+
+            try
+            {
+                octs = new DEROctetString(r.getEncoded());
+            }
+            catch (IOException e)
+            {
+                throw new OCSPException("can't encode object.", e);
+            }
+
+            ResponseBytes rb = new ResponseBytes(
+                OCSPObjectIdentifiers.id_pkix_ocsp_basic, octs);
+
+            return new OCSPResp(new OCSPResponse(
+                new OCSPResponseStatus(status), rb));
+        }
+
+        throw new OCSPException("unknown response object");
+    }
+}
diff --git a/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cert/ocsp/OCSPUtils.java b/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cert/ocsp/OCSPUtils.java
new file mode 100644
index 0000000..320ec85
--- /dev/null
+++ b/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cert/ocsp/OCSPUtils.java
@@ -0,0 +1,65 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.cert.ocsp;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import com.android.internal.org.bouncycastle.asn1.ASN1GeneralizedTime;
+import com.android.internal.org.bouncycastle.asn1.x509.Extensions;
+import com.android.internal.org.bouncycastle.cert.X509CertificateHolder;
+
+class OCSPUtils
+{
+    static final X509CertificateHolder[] EMPTY_CERTS = new X509CertificateHolder[0];
+
+    static Set EMPTY_SET = Collections.unmodifiableSet(new HashSet());
+    static List EMPTY_LIST = Collections.unmodifiableList(new ArrayList());
+
+    static Date extractDate(ASN1GeneralizedTime time)
+    {
+        try
+        {
+            return time.getDate();
+        }
+        catch (Exception e)
+        {
+            throw new IllegalStateException("exception processing GeneralizedTime: " + e.getMessage());
+        }
+    }
+
+    static Set getCriticalExtensionOIDs(Extensions extensions)
+    {
+        if (extensions == null)
+        {
+            return EMPTY_SET;
+        }
+
+        return Collections.unmodifiableSet(new HashSet(Arrays.asList(extensions.getCriticalExtensionOIDs())));
+    }
+
+    static Set getNonCriticalExtensionOIDs(Extensions extensions)
+    {
+        if (extensions == null)
+        {
+            return EMPTY_SET;
+        }
+
+        // TODO: should probably produce a set that imposes correct ordering
+        return Collections.unmodifiableSet(new HashSet(Arrays.asList(extensions.getNonCriticalExtensionOIDs())));
+    }
+
+    static List getExtensionOIDs(Extensions extensions)
+    {
+        if (extensions == null)
+        {
+            return EMPTY_LIST;
+        }
+
+        return Collections.unmodifiableList(Arrays.asList(extensions.getExtensionOIDs()));
+    }
+}
diff --git a/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cert/ocsp/Req.java b/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cert/ocsp/Req.java
new file mode 100644
index 0000000..df884fe
--- /dev/null
+++ b/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cert/ocsp/Req.java
@@ -0,0 +1,29 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.cert.ocsp;
+
+import com.android.internal.org.bouncycastle.asn1.ocsp.Request;
+import com.android.internal.org.bouncycastle.asn1.x509.Extensions;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+public class Req
+{
+    private Request req;
+
+    public Req(
+        Request req)
+    {
+        this.req = req;
+    }
+
+    public CertificateID getCertID()
+    {
+        return new CertificateID(req.getReqCert());
+    }
+
+    public Extensions getSingleRequestExtensions()
+    {
+        return req.getSingleRequestExtensions();
+    }
+}
diff --git a/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cert/ocsp/RespData.java b/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cert/ocsp/RespData.java
new file mode 100644
index 0000000..1e39a39
--- /dev/null
+++ b/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cert/ocsp/RespData.java
@@ -0,0 +1,65 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.cert.ocsp;
+
+import java.util.Date;
+
+import com.android.internal.org.bouncycastle.asn1.ASN1Sequence;
+import com.android.internal.org.bouncycastle.asn1.ocsp.ResponseData;
+import com.android.internal.org.bouncycastle.asn1.ocsp.SingleResponse;
+import com.android.internal.org.bouncycastle.asn1.x509.Extensions;
+
+/**
+ * OCSP RFC 2560, RFC 6960
+ * <pre>
+ * ResponseData ::= SEQUENCE {
+ *     version              [0] EXPLICIT Version DEFAULT v1,
+ *     responderID              ResponderID,
+ *     producedAt               GeneralizedTime,
+ *     responses                SEQUENCE OF SingleResponse,
+ *     responseExtensions   [1] EXPLICIT Extensions OPTIONAL }
+ * </pre>
+ * @hide This class is not part of the Android public SDK API
+ */
+public class RespData
+{
+    private ResponseData    data;
+
+    public RespData(
+        ResponseData    data)
+    {
+        this.data = data;
+    }
+
+    public int getVersion()
+    {
+        return data.getVersion().intValueExact() + 1;
+    }
+
+    public RespID getResponderId()
+    {
+        return new RespID(data.getResponderID());
+    }
+
+    public Date getProducedAt()
+    {
+        return OCSPUtils.extractDate(data.getProducedAt());
+    }
+
+    public SingleResp[] getResponses()
+    {
+        ASN1Sequence    s = data.getResponses();
+        SingleResp[]    rs = new SingleResp[s.size()];
+
+        for (int i = 0; i != rs.length; i++)
+        {
+            rs[i] = new SingleResp(SingleResponse.getInstance(s.getObjectAt(i)));
+        }
+
+        return rs;
+    }
+
+    public Extensions getResponseExtensions()
+    {
+        return data.getResponseExtensions();
+    }
+}
diff --git a/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cert/ocsp/RespID.java b/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cert/ocsp/RespID.java
new file mode 100644
index 0000000..82d0ebb
--- /dev/null
+++ b/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cert/ocsp/RespID.java
@@ -0,0 +1,91 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.cert.ocsp;
+
+import java.io.OutputStream;
+
+import com.android.internal.org.bouncycastle.asn1.DERNull;
+import com.android.internal.org.bouncycastle.asn1.DEROctetString;
+import com.android.internal.org.bouncycastle.asn1.ocsp.ResponderID;
+import com.android.internal.org.bouncycastle.asn1.oiw.OIWObjectIdentifiers;
+import com.android.internal.org.bouncycastle.asn1.x500.X500Name;
+import com.android.internal.org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import com.android.internal.org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import com.android.internal.org.bouncycastle.operator.DigestCalculator;
+
+/**
+ * Carrier for a ResponderID.
+ * @hide This class is not part of the Android public SDK API
+ */
+public class RespID
+{
+    public static final AlgorithmIdentifier HASH_SHA1 = new AlgorithmIdentifier(OIWObjectIdentifiers.idSHA1, DERNull.INSTANCE);
+
+    ResponderID id;
+
+    public RespID(
+        ResponderID id)
+    {
+        this.id = id;
+    }
+
+    public RespID(
+        X500Name name)
+    {
+        this.id = new ResponderID(name);
+    }
+
+    /**
+     * Calculate a RespID based on the public key of the responder.
+     *
+     * @param subjectPublicKeyInfo the info structure for the responder public key.
+     * @param digCalc a SHA-1 digest calculator.
+     * @throws OCSPException on exception creating ID.
+     */
+    public RespID(
+        SubjectPublicKeyInfo     subjectPublicKeyInfo,
+        DigestCalculator         digCalc)
+        throws OCSPException
+    {
+        try
+        {
+            if (!digCalc.getAlgorithmIdentifier().equals(HASH_SHA1))
+            {
+                throw new IllegalArgumentException("only SHA-1 can be used with RespID - found: " + digCalc.getAlgorithmIdentifier().getAlgorithm());
+            }
+
+            OutputStream     digOut = digCalc.getOutputStream();
+
+            digOut.write(subjectPublicKeyInfo.getPublicKeyData().getBytes());
+            digOut.close();
+
+            this.id = new ResponderID(new DEROctetString(digCalc.getDigest()));
+        }
+        catch (Exception e)
+        {
+            throw new OCSPException("problem creating ID: " + e, e);
+        }
+    }
+
+    public ResponderID toASN1Primitive()
+    {
+        return id;
+    }
+
+    public boolean equals(
+        Object  o)
+    {
+        if (!(o instanceof RespID))
+        {
+            return false;
+        }
+
+        RespID obj = (RespID)o;
+
+        return id.equals(obj.id);
+    }
+
+    public int hashCode()
+    {
+        return id.hashCode();
+    }
+}
diff --git a/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cert/ocsp/RevokedStatus.java b/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cert/ocsp/RevokedStatus.java
new file mode 100644
index 0000000..b383892
--- /dev/null
+++ b/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cert/ocsp/RevokedStatus.java
@@ -0,0 +1,62 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.cert.ocsp;
+
+import java.util.Date;
+
+import com.android.internal.org.bouncycastle.asn1.ASN1GeneralizedTime;
+import com.android.internal.org.bouncycastle.asn1.ocsp.RevokedInfo;
+import com.android.internal.org.bouncycastle.asn1.x509.CRLReason;
+
+/**
+ * wrapper for the RevokedInfo object
+ * @hide This class is not part of the Android public SDK API
+ */
+public class RevokedStatus
+    implements CertificateStatus
+{
+    RevokedInfo info;
+
+    public RevokedStatus(
+        RevokedInfo info)
+    {
+        this.info = info;
+    }
+
+    public RevokedStatus(Date revocationDate)
+    {
+        this.info = new RevokedInfo(new ASN1GeneralizedTime(revocationDate));
+    }
+
+    public RevokedStatus(
+        Date        revocationDate,
+        int         reason)
+    {
+        this.info = new RevokedInfo(new ASN1GeneralizedTime(revocationDate), CRLReason.lookup(reason));
+    }
+
+    public Date getRevocationTime()
+    {
+        return OCSPUtils.extractDate(info.getRevocationTime());
+    }
+
+    public boolean hasRevocationReason()
+    {
+        return (info.getRevocationReason() != null);
+    }
+
+    /**
+     * return the revocation reason. Note: this field is optional, test for it
+     * with hasRevocationReason() first.
+     * @return the revocation reason value.
+     * @exception IllegalStateException if a reason is asked for and none is avaliable
+     */
+    public int getRevocationReason()
+    {
+        if (info.getRevocationReason() == null)
+        {
+            throw new IllegalStateException("attempt to get a reason where none is available");
+        }
+
+        return info.getRevocationReason().getValue().intValue();
+    }
+}
diff --git a/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cert/ocsp/SingleResp.java b/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cert/ocsp/SingleResp.java
new file mode 100644
index 0000000..a328c1b
--- /dev/null
+++ b/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cert/ocsp/SingleResp.java
@@ -0,0 +1,106 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.cert.ocsp;
+
+import java.util.Date;
+import java.util.List;
+import java.util.Set;
+
+import com.android.internal.org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import com.android.internal.org.bouncycastle.asn1.ocsp.CertStatus;
+import com.android.internal.org.bouncycastle.asn1.ocsp.RevokedInfo;
+import com.android.internal.org.bouncycastle.asn1.ocsp.SingleResponse;
+import com.android.internal.org.bouncycastle.asn1.x509.Extension;
+import com.android.internal.org.bouncycastle.asn1.x509.Extensions;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+public class SingleResp
+{
+    private SingleResponse  resp;
+    private Extensions extensions;
+
+    public SingleResp(
+        SingleResponse  resp)
+    {
+        this.resp = resp;
+        this.extensions = resp.getSingleExtensions();
+    }
+
+    public CertificateID getCertID()
+    {
+        return new CertificateID(resp.getCertID());
+    }
+
+    /**
+     * Return the status object for the response - null indicates good.
+     * 
+     * @return the status object for the response, null if it is good.
+     */
+    public CertificateStatus getCertStatus()
+    {
+        CertStatus  s = resp.getCertStatus();
+
+        if (s.getTagNo() == 0)
+        {
+            return null;            // good
+        }
+        else if (s.getTagNo() == 1)
+        {
+            return new RevokedStatus(RevokedInfo.getInstance(s.getStatus()));
+        }
+
+        return new UnknownStatus();
+    }
+
+    public Date getThisUpdate()
+    {
+        return OCSPUtils.extractDate(resp.getThisUpdate());
+    }
+
+    /**
+     * return the NextUpdate value - note: this is an optional field so may
+     * be returned as null.
+     *
+     * @return nextUpdate, or null if not present.
+     */
+    public Date getNextUpdate()
+    {
+        if (resp.getNextUpdate() == null)
+        {
+            return null;
+        }
+
+        return OCSPUtils.extractDate(resp.getNextUpdate());
+    }
+
+    public boolean hasExtensions()
+    {
+        return extensions != null;
+    }
+
+    public Extension getExtension(ASN1ObjectIdentifier oid)
+    {
+        if (extensions != null)
+        {
+            return extensions.getExtension(oid);
+        }
+
+        return null;
+    }
+
+    public List getExtensionOIDs()
+    {
+        return OCSPUtils.getExtensionOIDs(extensions);
+    }
+
+    public Set getCriticalExtensionOIDs()
+    {
+        return OCSPUtils.getCriticalExtensionOIDs(extensions);
+    }
+
+    public Set getNonCriticalExtensionOIDs()
+    {
+        return OCSPUtils.getNonCriticalExtensionOIDs(extensions);
+    }
+}
diff --git a/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cert/ocsp/UnknownStatus.java b/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cert/ocsp/UnknownStatus.java
new file mode 100644
index 0000000..229d191
--- /dev/null
+++ b/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cert/ocsp/UnknownStatus.java
@@ -0,0 +1,14 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.cert.ocsp;
+
+/**
+ * wrapper for the UnknownInfo object
+ * @hide This class is not part of the Android public SDK API
+ */
+public class UnknownStatus
+    implements CertificateStatus
+{
+    public UnknownStatus()
+    {
+    }
+}
diff --git a/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cert/ocsp/jcajce/JcaBasicOCSPRespBuilder.java b/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cert/ocsp/jcajce/JcaBasicOCSPRespBuilder.java
new file mode 100644
index 0000000..30d769b
--- /dev/null
+++ b/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cert/ocsp/jcajce/JcaBasicOCSPRespBuilder.java
@@ -0,0 +1,29 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.cert.ocsp.jcajce;
+
+import java.security.PublicKey;
+
+import javax.security.auth.x500.X500Principal;
+
+import com.android.internal.org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import com.android.internal.org.bouncycastle.cert.ocsp.BasicOCSPRespBuilder;
+import com.android.internal.org.bouncycastle.cert.ocsp.OCSPException;
+import com.android.internal.org.bouncycastle.operator.DigestCalculator;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+public class JcaBasicOCSPRespBuilder
+    extends BasicOCSPRespBuilder
+{
+    public JcaBasicOCSPRespBuilder(X500Principal principal)
+    {
+        super(new JcaRespID(principal));
+    }
+
+    public JcaBasicOCSPRespBuilder(PublicKey key, DigestCalculator digCalc)
+        throws OCSPException
+    {
+        super(SubjectPublicKeyInfo.getInstance(key.getEncoded()), digCalc);
+    }
+}
diff --git a/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cert/ocsp/jcajce/JcaCertificateID.java b/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cert/ocsp/jcajce/JcaCertificateID.java
new file mode 100644
index 0000000..1497d7f
--- /dev/null
+++ b/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cert/ocsp/jcajce/JcaCertificateID.java
@@ -0,0 +1,24 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.cert.ocsp.jcajce;
+
+import java.math.BigInteger;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.X509Certificate;
+
+import com.android.internal.org.bouncycastle.cert.jcajce.JcaX509CertificateHolder;
+import com.android.internal.org.bouncycastle.cert.ocsp.CertificateID;
+import com.android.internal.org.bouncycastle.cert.ocsp.OCSPException;
+import com.android.internal.org.bouncycastle.operator.DigestCalculator;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+public class JcaCertificateID
+    extends CertificateID
+{
+    public JcaCertificateID(DigestCalculator digestCalculator, X509Certificate issuerCert, BigInteger number)
+        throws OCSPException, CertificateEncodingException
+    {
+        super(digestCalculator, new JcaX509CertificateHolder(issuerCert), number);
+    }
+}
diff --git a/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cert/ocsp/jcajce/JcaRespID.java b/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cert/ocsp/jcajce/JcaRespID.java
new file mode 100644
index 0000000..aabbac4
--- /dev/null
+++ b/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cert/ocsp/jcajce/JcaRespID.java
@@ -0,0 +1,30 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.cert.ocsp.jcajce;
+
+import java.security.PublicKey;
+
+import javax.security.auth.x500.X500Principal;
+
+import com.android.internal.org.bouncycastle.asn1.x500.X500Name;
+import com.android.internal.org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import com.android.internal.org.bouncycastle.cert.ocsp.OCSPException;
+import com.android.internal.org.bouncycastle.cert.ocsp.RespID;
+import com.android.internal.org.bouncycastle.operator.DigestCalculator;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+public class JcaRespID
+    extends RespID
+{
+    public JcaRespID(X500Principal name)
+    {
+        super(X500Name.getInstance(name.getEncoded()));
+    }
+
+    public JcaRespID(PublicKey pubKey, DigestCalculator digCalc)
+        throws OCSPException
+    {
+        super(SubjectPublicKeyInfo.getInstance(pubKey.getEncoded()), digCalc);
+    }
+}
diff --git a/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cmc/CMCException.java b/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cmc/CMCException.java
new file mode 100644
index 0000000..173c809
--- /dev/null
+++ b/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cmc/CMCException.java
@@ -0,0 +1,27 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.cmc;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+public class CMCException
+    extends Exception
+{
+    private final Throwable cause;
+
+    public CMCException(String msg)
+    {
+        this(msg, null);
+    }
+
+    public CMCException(String msg, Throwable cause)
+    {
+        super(msg);
+        this.cause = cause;
+    }
+
+    public Throwable getCause()
+    {
+        return cause;
+    }
+}
diff --git a/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cmc/SimplePKIResponse.java b/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cmc/SimplePKIResponse.java
new file mode 100644
index 0000000..a6a2fb2
--- /dev/null
+++ b/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cmc/SimplePKIResponse.java
@@ -0,0 +1,108 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.cmc;
+
+import java.io.IOException;
+
+import com.android.internal.org.bouncycastle.asn1.ASN1Primitive;
+import com.android.internal.org.bouncycastle.asn1.cms.ContentInfo;
+import com.android.internal.org.bouncycastle.cert.X509CRLHolder;
+import com.android.internal.org.bouncycastle.cert.X509CertificateHolder;
+import com.android.internal.org.bouncycastle.cms.CMSException;
+import com.android.internal.org.bouncycastle.cms.CMSSignedData;
+import com.android.internal.org.bouncycastle.util.Encodable;
+import com.android.internal.org.bouncycastle.util.Store;
+
+/**
+ * Carrier for a Simple PKI Response.
+ * <p>
+ * A Simple PKI Response is defined in RFC 5272 as a CMS SignedData object with no EncapsulatedContentInfo
+ * and no SignerInfos attached.
+ * </p>
+ * @hide This class is not part of the Android public SDK API
+ */
+public class SimplePKIResponse
+    implements Encodable
+{
+    private final CMSSignedData certificateResponse;
+
+    private static ContentInfo parseBytes(byte[] responseEncoding)
+        throws CMCException
+    {
+        try
+        {
+            return ContentInfo.getInstance(ASN1Primitive.fromByteArray(responseEncoding));
+        }
+        catch (Exception e)
+        {
+            throw new CMCException("malformed data: " + e.getMessage(), e);
+        }
+    }
+
+    /**
+     * Create a SimplePKIResponse from the passed in bytes.
+     *
+     * @param responseEncoding BER/DER encoding of the certificate.
+     * @throws CMCException in the event of corrupted data, or an incorrect structure.
+     */
+    public SimplePKIResponse(byte[] responseEncoding)
+        throws CMCException
+    {
+        this(parseBytes(responseEncoding));
+    }
+
+    /**
+     * Create a SimplePKIResponse from the passed in ASN.1 structure.
+     *
+     * @param signedData a ContentInfo containing a SignedData.
+     */
+    public SimplePKIResponse(ContentInfo signedData)
+        throws CMCException
+    {
+        try
+        {
+            this.certificateResponse = new CMSSignedData(signedData);
+        }
+        catch (CMSException e)
+        {
+            throw new CMCException("malformed response: " + e.getMessage(), e);
+        }
+
+        if (certificateResponse.getSignerInfos().size() != 0)
+        {
+            throw new CMCException("malformed response: SignerInfo structures found");
+        }
+        if (certificateResponse.getSignedContent() != null)
+        {
+            throw new CMCException("malformed response: Signed Content found");
+        }
+    }
+
+    /**
+     * Return any X.509 certificate objects in this SimplePKIResponse structure as a Store of X509CertificateHolder objects.
+     *
+     * @return a Store of X509CertificateHolder objects.
+     */
+    public Store<X509CertificateHolder> getCertificates()
+    {
+        return certificateResponse.getCertificates();
+    }
+
+    /**
+     * Return any X.509 CRL objects in this SimplePKIResponse structure as a Store of X509CRLHolder objects.
+     *
+     * @return a Store of X509CRLHolder objects.
+     */
+    public Store<X509CRLHolder> getCRLs()
+    {
+        return certificateResponse.getCRLs();
+    }
+
+    /**
+     * return the ASN.1 encoded representation of this object.
+     */
+    public byte[] getEncoded()
+        throws IOException
+    {
+        return certificateResponse.getEncoded();
+    }
+}
diff --git a/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cms/CMSPatchKit.java b/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cms/CMSPatchKit.java
new file mode 100644
index 0000000..883a68f
--- /dev/null
+++ b/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cms/CMSPatchKit.java
@@ -0,0 +1,73 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.cms;
+
+import java.io.IOException;
+
+import com.android.internal.org.bouncycastle.asn1.ASN1Encoding;
+import com.android.internal.org.bouncycastle.asn1.cms.SignerInfo;
+import com.android.internal.org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+
+/**
+ * Toolkit methods for dealing with common errors in CMS
+ * classes.
+ * @hide This class is not part of the Android public SDK API
+ */
+public class CMSPatchKit
+{
+    /**
+     * Create a SignerInformation based on original which uses definite-length
+     * rather than DER encoding for verifying the signature on the signed attributes.
+     *
+     * @param original the source SignerInformation
+     */
+    public static SignerInformation createNonDERSignerInfo(
+        SignerInformation original)
+    {
+        return new DLSignerInformation(original);
+    }
+
+    /**
+     * Create a SignerInformation based on original has it's signatureAlgorithm replaced
+     * with the passed in AlgorithmIdentifier.
+     *
+     * @param original the source SignerInformation
+     */
+    public static SignerInformation createWithSignatureAlgorithm(
+        SignerInformation original,
+        AlgorithmIdentifier signatureAlgorithm)
+    {
+         return new ModEncAlgSignerInformation(original, signatureAlgorithm);
+    }
+
+    private static class DLSignerInformation
+        extends SignerInformation
+    {
+        protected DLSignerInformation(SignerInformation baseInfo)
+        {
+            super(baseInfo);
+        }
+
+        public byte[] getEncodedSignedAttributes()
+            throws IOException
+        {
+            return signedAttributeSet.getEncoded(ASN1Encoding.DL);
+        }
+    }
+
+    private static class ModEncAlgSignerInformation
+        extends SignerInformation
+    {
+        protected ModEncAlgSignerInformation(
+            SignerInformation baseInfo,
+            AlgorithmIdentifier signatureAlgorithm)
+        {
+            super(baseInfo, editEncAlg(baseInfo.info, signatureAlgorithm));
+        }
+
+        private static SignerInfo editEncAlg(SignerInfo info, AlgorithmIdentifier signatureAlgorithm)
+        {
+            return new SignerInfo(info.getSID(), info.getDigestAlgorithm(), info.getAuthenticatedAttributes(),
+                signatureAlgorithm, info.getEncryptedDigest(), info.getUnauthenticatedAttributes());
+        }
+    }
+}
diff --git a/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cms/CMSSignedData.java b/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cms/CMSSignedData.java
index f42ea21..2cb9d8a 100644
--- a/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cms/CMSSignedData.java
+++ b/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cms/CMSSignedData.java
@@ -22,7 +22,6 @@
 import com.android.internal.org.bouncycastle.asn1.ASN1Sequence;
 import com.android.internal.org.bouncycastle.asn1.ASN1Set;
 import com.android.internal.org.bouncycastle.asn1.BERSequence;
-import com.android.internal.org.bouncycastle.asn1.DERSet;
 import com.android.internal.org.bouncycastle.asn1.DLSet;
 import com.android.internal.org.bouncycastle.asn1.cms.ContentInfo;
 import com.android.internal.org.bouncycastle.asn1.cms.SignedData;
@@ -31,15 +30,17 @@
 import com.android.internal.org.bouncycastle.cert.X509AttributeCertificateHolder;
 import com.android.internal.org.bouncycastle.cert.X509CRLHolder;
 import com.android.internal.org.bouncycastle.cert.X509CertificateHolder;
+import com.android.internal.org.bouncycastle.operator.DefaultDigestAlgorithmIdentifierFinder;
+import com.android.internal.org.bouncycastle.operator.DigestAlgorithmIdentifierFinder;
 import com.android.internal.org.bouncycastle.operator.OperatorCreationException;
 import com.android.internal.org.bouncycastle.util.Encodable;
 import com.android.internal.org.bouncycastle.util.Store;
 
 /**
  * general class for handling a pkcs7-signature message.
- *
+ * <p>
  * A simple example of usage - note, in the example below the validity of
- * the certificate isn't verified, just the fact that one of the certs 
+ * the certificate isn't verified, just the fact that one of the certs
  * matches the given signer...
  *
  * <pre>
@@ -47,7 +48,7 @@
  *  SignerInformationStore  signers = s.getSignerInfos();
  *  Collection              c = signers.getSigners();
  *  Iterator                it = c.iterator();
- *  
+ *
  *  while (it.hasNext())
  *  {
  *      SignerInformation   signer = (SignerInformation)it.next();
@@ -55,11 +56,11 @@
  *
  *      Iterator              certIt = certCollection.iterator();
  *      X509CertificateHolder cert = (X509CertificateHolder)certIt.next();
- *  
+ *
  *      if (signer.verify(new JcaSimpleSignerInfoVerifierBuilder().setProvider("BC").build(cert)))
  *      {
  *          verified++;
- *      }   
+ *      }
  *  }
  * </pre>
  * @hide This class is not part of the Android public SDK API
@@ -68,16 +69,18 @@
     implements Encodable
 {
     private static final CMSSignedHelper HELPER = CMSSignedHelper.INSTANCE;
-    
-    SignedData              signedData;
-    ContentInfo             contentInfo;
-    CMSTypedData            signedContent;
-    SignerInformationStore  signerInfoStore;
+    private static final DefaultDigestAlgorithmIdentifierFinder DIGEST_ALG_ID_FINDER =
+        new DefaultDigestAlgorithmIdentifierFinder();
 
-    private Map             hashes;
+    SignedData signedData;
+    ContentInfo contentInfo;
+    CMSTypedData signedContent;
+    SignerInformationStore signerInfoStore;
+
+    private Map hashes;
 
     private CMSSignedData(
-        CMSSignedData   c)
+        CMSSignedData c)
     {
         this.signedData = c.signedData;
         this.contentInfo = c.contentInfo;
@@ -86,15 +89,15 @@
     }
 
     public CMSSignedData(
-        byte[]      sigBlock)
+        byte[] sigBlock)
         throws CMSException
     {
         this(CMSUtils.readContentInfo(sigBlock));
     }
 
     public CMSSignedData(
-        CMSProcessable  signedContent,
-        byte[]          sigBlock)
+        CMSProcessable signedContent,
+        byte[] sigBlock)
         throws CMSException
     {
         this(signedContent, CMSUtils.readContentInfo(sigBlock));
@@ -103,12 +106,12 @@
     /**
      * Content with detached signature, digests precomputed
      *
-     * @param hashes a map of precomputed digests for content indexed by name of hash.
+     * @param hashes   a map of precomputed digests for content indexed by name of hash.
      * @param sigBlock the signature object.
      */
     public CMSSignedData(
-        Map     hashes,
-        byte[]  sigBlock)
+        Map hashes,
+        byte[] sigBlock)
         throws CMSException
     {
         this(hashes, CMSUtils.readContentInfo(sigBlock));
@@ -118,11 +121,11 @@
      * base constructor - content with detached signature.
      *
      * @param signedContent the content that was signed.
-     * @param sigData the signature object.
+     * @param sigData       the signature object.
      */
     public CMSSignedData(
-        CMSProcessable  signedContent,
-        InputStream     sigData)
+        CMSProcessable signedContent,
+        InputStream sigData)
         throws CMSException
     {
         this(signedContent, CMSUtils.readContentInfo(new ASN1InputStream(sigData)));
@@ -139,8 +142,8 @@
     }
 
     public CMSSignedData(
-        final CMSProcessable  signedContent,
-        ContentInfo     sigData)
+        final CMSProcessable signedContent,
+        ContentInfo sigData)
         throws CMSException
     {
         if (signedContent instanceof CMSTypedData)
@@ -174,8 +177,8 @@
     }
 
     public CMSSignedData(
-        Map             hashes,
-        ContentInfo     sigData)
+        Map hashes,
+        ContentInfo sigData)
         throws CMSException
     {
         this.hashes = hashes;
@@ -246,8 +249,8 @@
     {
         if (signerInfoStore == null)
         {
-            ASN1Set         s = signedData.getSignerInfos();
-            List            signerInfos = new ArrayList();
+            ASN1Set s = signedData.getSignerInfos();
+            List signerInfos = new ArrayList();
 
             for (int i = 0; i != s.size(); i++)
             {
@@ -330,7 +333,6 @@
      * this SignedData structure.
      *
      * @param otherRevocationInfoFormat OID of the format type been looked for.
-     *
      * @return a Store of ASN1Encodable objects representing any objects of otherRevocationInfoFormat found.
      *
     public Store getOtherRevocationInfo(ASN1ObjectIdentifier otherRevocationInfoFormat)
@@ -347,9 +349,9 @@
      */
     public Set<AlgorithmIdentifier> getDigestAlgorithmIDs()
     {
-        Set<AlgorithmIdentifier> digests = new HashSet<AlgorithmIdentifier>(signedData.getDigestAlgorithms().size());
+        Set<AlgorithmIdentifier> digests = new HashSet<AlgorithmIdentifier>();
 
-        for (Enumeration en = signedData.getDigestAlgorithms().getObjects(); en.hasMoreElements();)
+        for (Enumeration en = signedData.getDigestAlgorithms().getObjects(); en.hasMoreElements(); )
         {
             digests.add(AlgorithmIdentifier.getInstance(en.nextElement()));
         }
@@ -360,14 +362,14 @@
     /**
      * Return the a string representation of the OID associated with the
      * encapsulated content info structure carried in the signed data.
-     * 
+     *
      * @return the OID for the content type.
      */
     public String getSignedContentTypeOID()
     {
         return signedData.getEncapContentInfo().getContentType().getId();
     }
-    
+
     public CMSTypedData getSignedContent()
     {
         return signedContent;
@@ -396,7 +398,18 @@
      * return the ASN.1 encoded representation of this object using the specified encoding.
      *
      * @param encoding the ASN.1 encoding format to use ("BER", "DL", or "DER").
-     */
+     *
+    public byte[] getEncoded(String encoding)
+        throws IOException
+    {
+        return contentInfo.getEncoded(encoding);
+    }
+
+    /**
+     * return the ASN.1 encoded representation of this object using the specified encoding.
+     *
+     * @param encoding the ASN.1 encoding format to use ("BER", "DL", or "DER").
+     *
     public byte[] getEncoded(String encoding)
         throws IOException
     {
@@ -407,9 +420,9 @@
      * Verify all the SignerInformation objects and their associated counter signatures attached
      * to this CMS SignedData object.
      *
-     * @param verifierProvider  a provider of SignerInformationVerifier objects.
+     * @param verifierProvider a provider of SignerInformationVerifier objects.
      * @return true if all verify, false otherwise.
-     * @throws CMSException  if an exception occurs during the verification process.
+     * @throws CMSException if an exception occurs during the verification process.
      *
     public boolean verifySignatures(SignerInformationVerifierProvider verifierProvider)
         throws CMSException
@@ -421,17 +434,17 @@
      * Verify all the SignerInformation objects and optionally their associated counter signatures attached
      * to this CMS SignedData object.
      *
-     * @param verifierProvider  a provider of SignerInformationVerifier objects.
+     * @param verifierProvider        a provider of SignerInformationVerifier objects.
      * @param ignoreCounterSignatures if true don't check counter signatures. If false check counter signatures as well.
      * @return true if all verify, false otherwise.
-     * @throws CMSException  if an exception occurs during the verification process.
+     * @throws CMSException if an exception occurs during the verification process.
      *
     public boolean verifySignatures(SignerInformationVerifierProvider verifierProvider, boolean ignoreCounterSignatures)
         throws CMSException
     {
         Collection signers = this.getSignerInfos().getSigners();
 
-        for (Iterator it = signers.iterator(); it.hasNext();)
+        for (Iterator it = signers.iterator(); it.hasNext(); )
         {
             SignerInformation signer = (SignerInformation)it.next();
 
@@ -448,7 +461,7 @@
                 {
                     Collection counterSigners = signer.getCounterSignatures().getSigners();
 
-                    for  (Iterator cIt = counterSigners.iterator(); cIt.hasNext();)
+                    for (Iterator cIt = counterSigners.iterator(); cIt.hasNext(); )
                     {
                         if (!verifyCounterSignature((SignerInformation)cIt.next(), verifierProvider))
                         {
@@ -477,7 +490,7 @@
         }
 
         Collection counterSigners = counterSigner.getCounterSignatures().getSigners();
-        for  (Iterator cIt = counterSigners.iterator(); cIt.hasNext();)
+        for (Iterator cIt = counterSigners.iterator(); cIt.hasNext(); )
         {
             if (!verifyCounterSignature((SignerInformation)cIt.next(), verifierProvider))
             {
@@ -491,24 +504,119 @@
     // END Android-removed: Unknown reason
 
     /**
+     * Return a new CMSSignedData which guarantees to have the passed in digestAlgorithm
+     * in it. Uses the current DigestAlgorithmIdentifierFinder for creating the digest sets.
+     *
+     * @param signedData      the signed data object to be used as a base.
+     * @param digestAlgorithm the digest algorithm to be added to the signed data.
+     * @return a new signed data object.
+     */
+    public static CMSSignedData addDigestAlgorithm(CMSSignedData signedData, AlgorithmIdentifier digestAlgorithm)
+    {
+        return addDigestAlgorithm(signedData, digestAlgorithm, DIGEST_ALG_ID_FINDER);
+    }
+
+    /**
+     * Return a new CMSSignedData which guarantees to have the passed in digestAlgorithm
+     * in it. Uses the passed in DigestAlgorithmIdentifierFinder for creating the digest sets.
+     *
+     * @param signedData      the signed data object to be used as a base.
+     * @param digestAlgorithm the digest algorithm to be added to the signed data.
+     * @param digestAlgIdFinder      the digest algorithmID map to generate the digest set with.
+     * @return a new signed data object.
+     */
+    public static CMSSignedData addDigestAlgorithm(CMSSignedData signedData, AlgorithmIdentifier digestAlgorithm,
+        DigestAlgorithmIdentifierFinder digestAlgIdFinder)
+    {
+        Set<AlgorithmIdentifier> digestAlgorithms = signedData.getDigestAlgorithmIDs();
+        AlgorithmIdentifier digestAlg = HELPER.fixDigestAlgID(digestAlgorithm, digestAlgIdFinder);
+
+        //
+        // if the algorithm is already present there is no need to add it.
+        //
+        if (digestAlgorithms.contains(digestAlg))
+        {
+            return signedData;
+        }
+
+        //
+        // copy
+        //
+        CMSSignedData cms = new CMSSignedData(signedData);
+
+        //
+        // build up the new set
+        //
+        Set<AlgorithmIdentifier> digestAlgs = new HashSet<AlgorithmIdentifier>();
+
+        Iterator it = digestAlgorithms.iterator();
+        while (it.hasNext())
+        {
+            digestAlgs.add(HELPER.fixDigestAlgID((AlgorithmIdentifier)it.next(), digestAlgIdFinder));
+        }
+        digestAlgs.add(digestAlg);
+
+        ASN1Set digestSet = CMSUtils.convertToDlSet(digestAlgs);
+        ASN1Sequence sD = (ASN1Sequence)signedData.signedData.toASN1Primitive();
+
+        //
+        // signers are the last item in the sequence.
+        //
+        ASN1EncodableVector vec = new ASN1EncodableVector(sD.size());
+        vec.add(sD.getObjectAt(0)); // version
+        vec.add(digestSet);
+
+        for (int i = 2; i != sD.size(); i++)
+        {
+            vec.add(sD.getObjectAt(i));
+        }
+
+        cms.signedData = SignedData.getInstance(new BERSequence(vec));
+
+        //
+        // replace the contentInfo with the new one
+        //
+        cms.contentInfo = new ContentInfo(cms.contentInfo.getContentType(), cms.signedData);
+
+        return cms;
+    }
+
+    /**
      * Replace the SignerInformation store associated with this
-     * CMSSignedData object with the new one passed in. You would
-     * probably only want to do this if you wanted to change the unsigned 
+     * CMSSignedData object with the new one passed in using the current
+     * DigestAlgorithmIdentifierFinder for creating the digest sets. You would
+     * probably only want to do this if you wanted to change the unsigned
      * attributes associated with a signer, or perhaps delete one.
-     * 
-     * @param signedData the signed data object to be used as a base.
+     *
+     * @param signedData             the signed data object to be used as a base.
      * @param signerInformationStore the new signer information store to use.
      * @return a new signed data object.
      */
-    public static CMSSignedData replaceSigners(
-        CMSSignedData           signedData,
-        SignerInformationStore  signerInformationStore)
+    public static CMSSignedData replaceSigners(CMSSignedData signedData, SignerInformationStore signerInformationStore)
+    {
+        return replaceSigners(signedData, signerInformationStore, DIGEST_ALG_ID_FINDER);
+    }
+
+    /**
+     * Replace the SignerInformation store associated with this
+     * CMSSignedData object with the new one passed in using the passed in
+     * DigestAlgorithmIdentifierFinder for creating the digest sets. You would
+     * probably only want to do this if you wanted to change the unsigned
+     * attributes associated with a signer, or perhaps delete one.
+     *
+     * @param signedData             the signed data object to be used as a base.
+     * @param signerInformationStore the new signer information store to use.
+     * @param digestAlgIdFinder      the digest algorithmID map to generate the digest set with.
+     * @return a new signed data object.
+     */
+    public static CMSSignedData replaceSigners(CMSSignedData signedData, SignerInformationStore signerInformationStore,
+        DigestAlgorithmIdentifierFinder digestAlgIdFinder)
     {
         //
         // copy
         //
-        CMSSignedData   cms = new CMSSignedData(signedData);
-        
+        CMSSignedData cms = new CMSSignedData(signedData);
+
         //
         // replace the store
         //
@@ -517,43 +625,44 @@
         //
         // replace the signers in the SignedData object
         //
-        ASN1EncodableVector digestAlgs = new ASN1EncodableVector();
-        ASN1EncodableVector vec = new ASN1EncodableVector();
-        
-        Iterator    it = signerInformationStore.getSigners().iterator();
+        Set<AlgorithmIdentifier> digestAlgs = new HashSet<AlgorithmIdentifier>();
+
+        Collection<SignerInformation> signers = signerInformationStore.getSigners();
+        ASN1EncodableVector vec = new ASN1EncodableVector(signers.size());
+
+        Iterator it = signers.iterator();
         while (it.hasNext())
         {
             SignerInformation signer = (SignerInformation)it.next();
-            digestAlgs.add(CMSSignedHelper.INSTANCE.fixAlgID(signer.getDigestAlgorithmID()));
+            CMSUtils.addDigestAlgs(digestAlgs, signer, digestAlgIdFinder);
             vec.add(signer.toASN1Structure());
         }
 
-        ASN1Set             digests = new DERSet(digestAlgs);
-        ASN1Set             signers = new DLSet(vec);
-        ASN1Sequence        sD = (ASN1Sequence)signedData.signedData.toASN1Primitive();
+        ASN1Set digestSet = CMSUtils.convertToDlSet(digestAlgs);
+        ASN1Set signerSet = new DLSet(vec);
+        ASN1Sequence sD = (ASN1Sequence)signedData.signedData.toASN1Primitive();
 
-        vec = new ASN1EncodableVector();
-        
         //
         // signers are the last item in the sequence.
         //
+        vec = new ASN1EncodableVector(sD.size());
         vec.add(sD.getObjectAt(0)); // version
-        vec.add(digests);
+        vec.add(digestSet);
 
         for (int i = 2; i != sD.size() - 1; i++)
         {
             vec.add(sD.getObjectAt(i));
         }
-        
-        vec.add(signers);
-        
+
+        vec.add(signerSet);
+
         cms.signedData = SignedData.getInstance(new BERSequence(vec));
-        
+
         //
         // replace the contentInfo with the new one
         //
         cms.contentInfo = new ContentInfo(cms.contentInfo.getContentType(), cms.signedData);
-        
+
         return cms;
     }
 
@@ -561,24 +670,24 @@
      * Replace the certificate and CRL information associated with this
      * CMSSignedData object with the new one passed in.
      *
-     * @param signedData the signed data object to be used as a base.
+     * @param signedData   the signed data object to be used as a base.
      * @param certificates the new certificates to be used.
-     * @param attrCerts the new attribute certificates to be used.
-     * @param revocations the new CRLs to be used - a collection of X509CRLHolder objects, OtherRevocationInfoFormat, or both.
+     * @param attrCerts    the new attribute certificates to be used.
+     * @param revocations  the new CRLs to be used - a collection of X509CRLHolder objects, OtherRevocationInfoFormat, or both.
      * @return a new signed data object.
-     * @exception CMSException if there is an error processing the CertStore
+     * @throws CMSException if there is an error processing the CertStore
      */
     public static CMSSignedData replaceCertificatesAndCRLs(
-        CMSSignedData   signedData,
-        Store           certificates,
-        Store           attrCerts,
-        Store           revocations)
+        CMSSignedData signedData,
+        Store certificates,
+        Store attrCerts,
+        Store revocations)
         throws CMSException
     {
         //
         // copy
         //
-        CMSSignedData   cms = new CMSSignedData(signedData);
+        CMSSignedData cms = new CMSSignedData(signedData);
 
         //
         // replace the certs and revocations in the SignedData object
@@ -596,7 +705,7 @@
             }
             if (attrCerts != null)
             {
-                certs.addAll(CMSUtils.getAttributeCertificatesFromStore(attrCerts));   
+                certs.addAll(CMSUtils.getAttributeCertificatesFromStore(attrCerts));
             }
 
             ASN1Set set = CMSUtils.createBerSetFromList(certs);
@@ -621,10 +730,10 @@
         // replace the CMS structure.
         //
         cms.signedData = new SignedData(signedData.signedData.getDigestAlgorithms(),
-                                   signedData.signedData.getEncapContentInfo(),
-                                   certSet,
-                                   crlSet,
-                                   signedData.signedData.getSignerInfos());
+            signedData.signedData.getEncapContentInfo(),
+            certSet,
+            crlSet,
+            signedData.signedData.getSignerInfos());
 
         //
         // replace the contentInfo with the new one
diff --git a/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cms/CMSSignedDataGenerator.java b/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cms/CMSSignedDataGenerator.java
index 1fdaaa9..6474962 100644
--- a/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cms/CMSSignedDataGenerator.java
+++ b/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cms/CMSSignedDataGenerator.java
@@ -6,18 +6,23 @@
 import java.io.OutputStream;
 import java.util.ArrayList;
 import java.util.Iterator;
+import java.util.LinkedHashSet;
 import java.util.List;
+import java.util.Set;
 
 import com.android.internal.org.bouncycastle.asn1.ASN1EncodableVector;
 import com.android.internal.org.bouncycastle.asn1.ASN1ObjectIdentifier;
 import com.android.internal.org.bouncycastle.asn1.ASN1OctetString;
 import com.android.internal.org.bouncycastle.asn1.ASN1Set;
 import com.android.internal.org.bouncycastle.asn1.BEROctetString;
+import com.android.internal.org.bouncycastle.asn1.DEROctetString;
 import com.android.internal.org.bouncycastle.asn1.DERSet;
 import com.android.internal.org.bouncycastle.asn1.cms.CMSObjectIdentifiers;
 import com.android.internal.org.bouncycastle.asn1.cms.ContentInfo;
 import com.android.internal.org.bouncycastle.asn1.cms.SignedData;
 import com.android.internal.org.bouncycastle.asn1.cms.SignerInfo;
+import com.android.internal.org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import com.android.internal.org.bouncycastle.operator.DigestAlgorithmIdentifierFinder;
 
 /**
  * general class for generating a pkcs7-signature message.
@@ -50,6 +55,7 @@
     extends CMSSignedGenerator
 {
     private List signerInfs = new ArrayList();
+    private boolean isDefiniteLength = false;
 
     /**
      * base constructor
@@ -59,6 +65,24 @@
     }
 
     /**
+     * base constructor with a custom DigestAlgorithmIdentifierFinder
+     */
+    public CMSSignedDataGenerator(DigestAlgorithmIdentifierFinder digestAlgIdFinder)
+    {
+        super(digestAlgIdFinder);
+    }
+
+    /**
+     * Specify use of definite length rather than indefinite length encoding.
+     *
+     * @param isDefiniteLength true use definite length, false use indefinite (default false).
+     */
+    public void setDefiniteLengthEncoding(boolean isDefiniteLength)
+    {
+        this.isDefiniteLength = isDefiniteLength;
+    }
+
+    /**
      * Generate a CMS Signed Data object carrying a detached CMS signature.
      *
      * @param content the content to be signed.
@@ -118,7 +142,7 @@
 //            // TODO signedAttrs must be present for all signers
 //        }
 
-        ASN1EncodableVector  digestAlgs = new ASN1EncodableVector();
+        Set<AlgorithmIdentifier> digestAlgs = new LinkedHashSet<AlgorithmIdentifier>();
         ASN1EncodableVector  signerInfos = new ASN1EncodableVector();
 
         digests.clear();  // clear the current preserved digest state
@@ -129,8 +153,7 @@
         for (Iterator it = _signers.iterator(); it.hasNext();)
         {
             SignerInformation signer = (SignerInformation)it.next();
-            digestAlgs.add(CMSSignedHelper.INSTANCE.fixAlgID(signer.getDigestAlgorithmID()));
-
+            CMSUtils.addDigestAlgs(digestAlgs, signer, digestAlgIdFinder);
             // TODO Verify the content type and calculated digest match the precalculated SignerInfo
             signerInfos.add(signer.toASN1Structure());
         }
@@ -169,7 +192,14 @@
 
             if (encapsulate)
             {
-                octs = new BEROctetString(bOut.toByteArray());
+                if (isDefiniteLength)
+                {
+                    octs = new DEROctetString(bOut.toByteArray());
+                }
+                else
+                {
+                    octs = new BEROctetString(bOut.toByteArray());
+                }
             }
         }
 
@@ -193,20 +223,34 @@
 
         if (certs.size() != 0)
         {
-            certificates = CMSUtils.createBerSetFromList(certs);
+            if (isDefiniteLength)
+            {
+                certificates = CMSUtils.createDlSetFromList(certs);
+            }
+            else
+            {
+                certificates = CMSUtils.createBerSetFromList(certs);
+            }
         }
 
         ASN1Set certrevlist = null;
 
         if (crls.size() != 0)
         {
-            certrevlist = CMSUtils.createBerSetFromList(crls);
+            if (isDefiniteLength)
+            {
+                certrevlist = CMSUtils.createDlSetFromList(crls);
+            }
+            else
+            {
+                certrevlist = CMSUtils.createBerSetFromList(crls);
+            }
         }
 
         ContentInfo encInfo = new ContentInfo(contentTypeOID, octs);
 
         SignedData  sd = new SignedData(
-                                 new DERSet(digestAlgs),
+                                 CMSUtils.convertToDlSet(digestAlgs),
                                  encInfo,
                                  certificates,
                                  certrevlist,
diff --git a/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cms/CMSSignedGenerator.java b/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cms/CMSSignedGenerator.java
index eae4ff4..14e573a 100644
--- a/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cms/CMSSignedGenerator.java
+++ b/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cms/CMSSignedGenerator.java
@@ -12,8 +12,8 @@
 import com.android.internal.org.bouncycastle.asn1.ASN1Encodable;
 import com.android.internal.org.bouncycastle.asn1.ASN1ObjectIdentifier;
 import com.android.internal.org.bouncycastle.asn1.DERTaggedObject;
-import com.android.internal.org.bouncycastle.asn1.cms.CMSObjectIdentifiers;
 // Android-removed: Unsupported algorithms
+// import org.bouncycastle.asn1.cms.CMSObjectIdentifiers;
 // import org.bouncycastle.asn1.cms.OtherRevocationInfoFormat;
 // import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers;
 import com.android.internal.org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
@@ -27,6 +27,8 @@
 import com.android.internal.org.bouncycastle.cert.X509AttributeCertificateHolder;
 import com.android.internal.org.bouncycastle.cert.X509CRLHolder;
 import com.android.internal.org.bouncycastle.cert.X509CertificateHolder;
+import com.android.internal.org.bouncycastle.operator.DefaultDigestAlgorithmIdentifierFinder;
+import com.android.internal.org.bouncycastle.operator.DigestAlgorithmIdentifierFinder;
 import com.android.internal.org.bouncycastle.util.Arrays;
 import com.android.internal.org.bouncycastle.util.Store;
 
@@ -38,7 +40,6 @@
     /**
      * Default type for the signed data.
      */
-    public static final String  DATA = CMSObjectIdentifiers.data.getId();
     
     public static final String  DIGEST_SHA1 = OIWObjectIdentifiers.idSHA1.getId();
     public static final String  DIGEST_SHA224 = NISTObjectIdentifiers.id_sha224.getId();
@@ -47,6 +48,7 @@
     public static final String  DIGEST_SHA512 = NISTObjectIdentifiers.id_sha512.getId();
     public static final String  DIGEST_MD5 = PKCSObjectIdentifiers.md5.getId();
     // BEGIN Android-removed: Unsupported algorithms
+    // public static final String  DATA = CMSObjectIdentifiers.data.getId();
     // public static final String  DIGEST_GOST3411 = CryptoProObjectIdentifiers.gostR3411.getId();
     // public static final String  DIGEST_RIPEMD128 = TeleTrusTObjectIdentifiers.ripemd128.getId();
     // public static final String  DIGEST_RIPEMD160 = TeleTrusTObjectIdentifiers.ripemd160.getId();
@@ -96,11 +98,19 @@
     protected List signerGens = new ArrayList();
     protected Map digests = new HashMap();
 
+    protected DigestAlgorithmIdentifierFinder digestAlgIdFinder;
+
     /**
      * base constructor
      */
     protected CMSSignedGenerator()
     {
+        this(new DefaultDigestAlgorithmIdentifierFinder());
+    }
+
+    protected CMSSignedGenerator(DigestAlgorithmIdentifierFinder digestAlgIdFinder)
+    {
+        this.digestAlgIdFinder = digestAlgIdFinder;
     }
 
     protected Map getBaseParameters(ASN1ObjectIdentifier contentType, AlgorithmIdentifier digAlgId, byte[] hash)
@@ -199,7 +209,9 @@
         ASN1ObjectIdentifier   otherRevocationInfoFormat,
         ASN1Encodable          otherRevocationInfo)
     {
-        crls.add(new DERTaggedObject(false, 1, new OtherRevocationInfoFormat(otherRevocationInfoFormat, otherRevocationInfo)));
+        OtherRevocationInfoFormat infoFormat = new OtherRevocationInfoFormat(otherRevocationInfoFormat, otherRevocationInfo);
+        CMSUtils.validateInfoFormat(infoFormat);
+        crls.add(new DERTaggedObject(false, 1, infoFormat));
     }
 
     /**
diff --git a/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cms/CMSSignedHelper.java b/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cms/CMSSignedHelper.java
index bd528bd..f547805 100644
--- a/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cms/CMSSignedHelper.java
+++ b/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cms/CMSSignedHelper.java
@@ -13,11 +13,12 @@
 import com.android.internal.org.bouncycastle.asn1.ASN1Sequence;
 import com.android.internal.org.bouncycastle.asn1.ASN1Set;
 import com.android.internal.org.bouncycastle.asn1.ASN1TaggedObject;
+import com.android.internal.org.bouncycastle.asn1.BERTags;
 import com.android.internal.org.bouncycastle.asn1.DERNull;
 // Android-removed: Unsupported algorithms
 // import org.bouncycastle.asn1.cms.OtherRevocationInfoFormat;
 // import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers;
-import com.android.internal.org.bouncycastle.asn1.eac.EACObjectIdentifiers;
+// import org.bouncycastle.asn1.eac.EACObjectIdentifiers;
 import com.android.internal.org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
 import com.android.internal.org.bouncycastle.asn1.oiw.OIWObjectIdentifiers;
 import com.android.internal.org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
@@ -32,6 +33,7 @@
 import com.android.internal.org.bouncycastle.cert.X509AttributeCertificateHolder;
 import com.android.internal.org.bouncycastle.cert.X509CRLHolder;
 import com.android.internal.org.bouncycastle.cert.X509CertificateHolder;
+import com.android.internal.org.bouncycastle.operator.DigestAlgorithmIdentifierFinder;
 import com.android.internal.org.bouncycastle.util.CollectionStore;
 import com.android.internal.org.bouncycastle.util.Store;
 
@@ -100,9 +102,6 @@
         addEntries(NISTObjectIdentifiers.id_ecdsa_with_sha3_256,  "ECDSA");
         addEntries(NISTObjectIdentifiers.id_ecdsa_with_sha3_384, "ECDSA");
         addEntries(NISTObjectIdentifiers.id_ecdsa_with_sha3_512,  "ECDSA");
-        */
-        // END Android-removed: Unsupported algorithms
-        addEntries(X9ObjectIdentifiers.id_dsa_with_sha1,  "DSA");
         addEntries(EACObjectIdentifiers.id_TA_ECDSA_SHA_1,  "ECDSA");
         addEntries(EACObjectIdentifiers.id_TA_ECDSA_SHA_224,  "ECDSA");
         addEntries(EACObjectIdentifiers.id_TA_ECDSA_SHA_256,  "ECDSA");
@@ -112,6 +111,9 @@
         addEntries(EACObjectIdentifiers.id_TA_RSA_v1_5_SHA_256, "RSA");
         addEntries(EACObjectIdentifiers.id_TA_RSA_PSS_SHA_1,  "RSAandMGF1");
         addEntries(EACObjectIdentifiers.id_TA_RSA_PSS_SHA_256, "RSAandMGF1");
+        */
+        // END Android-removed: Unsupported algorithms
+        addEntries(X9ObjectIdentifiers.id_dsa_with_sha1,  "DSA");
 
         addEntries(X9ObjectIdentifiers.id_dsa, "DSA");
         addEntries(PKCSObjectIdentifiers.rsaEncryption, "RSA");
@@ -153,14 +155,17 @@
         return encryptionAlgOID;
     }
 
-    AlgorithmIdentifier fixAlgID(AlgorithmIdentifier algId)
+    AlgorithmIdentifier fixDigestAlgID(AlgorithmIdentifier algId, DigestAlgorithmIdentifierFinder dgstAlgFinder)
     {
-        if (algId.getParameters() == null)
+        ASN1Encodable params = algId.getParameters();
+        if (params == null || DERNull.INSTANCE.equals(params))
         {
-            return new AlgorithmIdentifier(algId.getAlgorithm(), DERNull.INSTANCE);
+            return dgstAlgFinder.find(algId.getAlgorithm());
         }
-
-        return algId;
+        else
+        {
+            return algId;
+        }
     }
 
     void setSigningEncryptionAlgorithmMapping(ASN1ObjectIdentifier oid, String algorithmName)
@@ -202,7 +207,18 @@
 
                 if (obj instanceof ASN1TaggedObject)
                 {
-                    certList.add(new X509AttributeCertificateHolder(AttributeCertificate.getInstance(((ASN1TaggedObject)obj).getObject())));
+                    ASN1TaggedObject tObj = (ASN1TaggedObject)obj;
+
+                    // CertificateChoices ::= CHOICE {
+                    //     certificate Certificate,
+                    //     extendedCertificate [0] IMPLICIT ExtendedCertificate,  -- Obsolete
+                    //     v1AttrCert [1] IMPLICIT AttributeCertificateV1,        -- Obsolete
+                    //     v2AttrCert [2] IMPLICIT AttributeCertificateV2,
+                    //     other [3] IMPLICIT OtherCertificateFormat }
+                    if (tObj.getTagNo() == 1 || tObj.getTagNo() == 2)
+                    {
+                        certList.add(new X509AttributeCertificateHolder(AttributeCertificate.getInstance(tObj.getBaseUniversal(false, BERTags.SEQUENCE))));
+                    }
                 }
             }
 
@@ -250,7 +266,7 @@
                 {
                     ASN1TaggedObject tObj = ASN1TaggedObject.getInstance(obj);
 
-                    if (tObj.getTagNo() == 1)
+                    if (tObj.hasContextTag(1))
                     {
                         OtherRevocationInfoFormat other = OtherRevocationInfoFormat.getInstance(tObj, false);
 
diff --git a/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cms/CMSUtils.java b/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cms/CMSUtils.java
index a803260..3b9933a 100644
--- a/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cms/CMSUtils.java
+++ b/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cms/CMSUtils.java
@@ -22,6 +22,7 @@
 import com.android.internal.org.bouncycastle.asn1.DERNull;
 import com.android.internal.org.bouncycastle.asn1.DERSet;
 import com.android.internal.org.bouncycastle.asn1.DERTaggedObject;
+import com.android.internal.org.bouncycastle.asn1.DLSet;
 import com.android.internal.org.bouncycastle.asn1.cms.CMSObjectIdentifiers;
 import com.android.internal.org.bouncycastle.asn1.cms.ContentInfo;
 // Android-removed: Unsupported algorithms
@@ -38,6 +39,7 @@
 import com.android.internal.org.bouncycastle.cert.X509AttributeCertificateHolder;
 import com.android.internal.org.bouncycastle.cert.X509CRLHolder;
 import com.android.internal.org.bouncycastle.cert.X509CertificateHolder;
+import com.android.internal.org.bouncycastle.operator.DigestAlgorithmIdentifierFinder;
 import com.android.internal.org.bouncycastle.operator.DigestCalculator;
 import com.android.internal.org.bouncycastle.util.Store;
 import com.android.internal.org.bouncycastle.util.Strings;
@@ -60,7 +62,6 @@
         /*
         des.add(OIWObjectIdentifiers.desCBC.getId());
         des.add(PKCSObjectIdentifiers.des_EDE3_CBC.getId());
-        des.add(PKCSObjectIdentifiers.des_EDE3_CBC.getId());
         des.add(PKCSObjectIdentifiers.id_alg_CMS3DESwrap.getId());
 
         mqvAlgs.add(X9ObjectIdentifiers.mqvSinglePass_sha1kdf_scheme);
@@ -156,6 +157,23 @@
         return readContentInfo(new ASN1InputStream(input));
     }
 
+    static ASN1Set convertToDlSet(Set<AlgorithmIdentifier> digestAlgs)
+    {
+        return new DLSet((AlgorithmIdentifier[])digestAlgs.toArray(new AlgorithmIdentifier[digestAlgs.size()]));
+    }
+
+    static void addDigestAlgs(Set<AlgorithmIdentifier> digestAlgs, SignerInformation signer, DigestAlgorithmIdentifierFinder dgstAlgFinder)
+    {
+        digestAlgs.add(CMSSignedHelper.INSTANCE.fixDigestAlgID(signer.getDigestAlgorithmID(), dgstAlgFinder));
+        SignerInformationStore counterSignaturesStore = signer.getCounterSignatures();
+        Iterator<SignerInformation> counterSignatureIt = counterSignaturesStore.iterator();
+        while (counterSignatureIt.hasNext())
+        {
+            SignerInformation counterSigner = (SignerInformation)counterSignatureIt.next();
+            digestAlgs.add(CMSSignedHelper.INSTANCE.fixDigestAlgID(counterSigner.getDigestAlgorithmID(), dgstAlgFinder));
+        }
+    }
+
     static List getCertificatesFromStore(Store certStore)
         throws CMSException
     {
@@ -244,7 +262,7 @@
 
     // BEGIN Android-removed: OtherRevocationInfoFormat isn't supported
     /*
-    private static void validateInfoFormat(OtherRevocationInfoFormat infoFormat)
+    static void validateInfoFormat(OtherRevocationInfoFormat infoFormat)
     {
         if (CMSObjectIdentifiers.id_ri_ocsp_response.equals(infoFormat.getInfoFormat()))
         {
@@ -288,6 +306,18 @@
         return new BERSet(v);
     }
 
+    static ASN1Set createDlSetFromList(List derObjects)
+    {
+        ASN1EncodableVector v = new ASN1EncodableVector();
+
+        for (Iterator it = derObjects.iterator(); it.hasNext(); )
+        {
+            v.add((ASN1Encodable)it.next());
+        }
+
+        return new DLSet(v);
+    }
+
     static ASN1Set createDerSetFromList(List derObjects)
     {
         ASN1EncodableVector v = new ASN1EncodableVector();
diff --git a/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cms/DefaultCMSSignatureAlgorithmNameGenerator.java b/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cms/DefaultCMSSignatureAlgorithmNameGenerator.java
index 45a401c..51ef394 100644
--- a/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cms/DefaultCMSSignatureAlgorithmNameGenerator.java
+++ b/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cms/DefaultCMSSignatureAlgorithmNameGenerator.java
@@ -6,11 +6,14 @@
 
 import com.android.internal.org.bouncycastle.asn1.ASN1ObjectIdentifier;
 // Android-removed: Unsupported algorithms
+// import org.bouncycastle.asn1.bc.BCObjectIdentifiers;
 // import org.bouncycastle.asn1.bsi.BSIObjectIdentifiers;
+// import org.bouncycastle.asn1.cms.CMSObjectIdentifiers;
 // import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers;
-import com.android.internal.org.bouncycastle.asn1.eac.EACObjectIdentifiers;
+// import org.bouncycastle.asn1.eac.EACObjectIdentifiers;
 // import org.bouncycastle.asn1.gm.GMObjectIdentifiers;
 // import org.bouncycastle.asn1.edec.EdECObjectIdentifiers;
+// import org.bouncycastle.asn1.misc.MiscObjectIdentifiers;
 import com.android.internal.org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
 import com.android.internal.org.bouncycastle.asn1.oiw.OIWObjectIdentifiers;
 import com.android.internal.org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
@@ -28,6 +31,7 @@
 {
     private final Map encryptionAlgs = new HashMap();
     private final Map     digestAlgs = new HashMap();
+    private final Map     simpleAlgs = new HashMap();
 
     private void addEntries(ASN1ObjectIdentifier alias, String digest, String encryption)
     {
@@ -74,12 +78,22 @@
         addEntries(PKCSObjectIdentifiers.sha256WithRSAEncryption, "SHA256", "RSA");
         addEntries(PKCSObjectIdentifiers.sha384WithRSAEncryption, "SHA384", "RSA");
         addEntries(PKCSObjectIdentifiers.sha512WithRSAEncryption, "SHA512", "RSA");
+        addEntries(PKCSObjectIdentifiers.sha512_224WithRSAEncryption, "SHA512(224)", "RSA");
+        addEntries(PKCSObjectIdentifiers.sha512_256WithRSAEncryption, "SHA512(256)", "RSA");
+        addEntries(NISTObjectIdentifiers.id_rsassa_pkcs1_v1_5_with_sha3_224, "SHA3-224", "RSA");
+        addEntries(NISTObjectIdentifiers.id_rsassa_pkcs1_v1_5_with_sha3_256, "SHA3-256", "RSA");
+        addEntries(NISTObjectIdentifiers.id_rsassa_pkcs1_v1_5_with_sha3_384, "SHA3-384", "RSA");
+        addEntries(NISTObjectIdentifiers.id_rsassa_pkcs1_v1_5_with_sha3_512, "SHA3-512", "RSA");
 
         // BEGIN Android-removed: Unsupported algorithms
         /*
+        addEntries(CMSObjectIdentifiers.id_RSASSA_PSS_SHAKE128, "SHAKE128", "RSAPSS");
+        addEntries(CMSObjectIdentifiers.id_RSASSA_PSS_SHAKE256, "SHAKE256", "RSAPSS");
         addEntries(TeleTrusTObjectIdentifiers.rsaSignatureWithripemd128, "RIPEMD128", "RSA");
         addEntries(TeleTrusTObjectIdentifiers.rsaSignatureWithripemd160, "RIPEMD160", "RSA");
         addEntries(TeleTrusTObjectIdentifiers.rsaSignatureWithripemd256, "RIPEMD256", "RSA");
+        addEntries(CMSObjectIdentifiers.id_ecdsa_with_shake128, "SHAKE128", "ECDSA");
+        addEntries(CMSObjectIdentifiers.id_ecdsa_with_shake256, "SHAKE256", "ECDSA");
         */
         // END Android-removed: Unsupported algorithms
 
@@ -89,6 +103,8 @@
         addEntries(X9ObjectIdentifiers.ecdsa_with_SHA384, "SHA384", "ECDSA");
         addEntries(X9ObjectIdentifiers.ecdsa_with_SHA512, "SHA512", "ECDSA");
         addEntries(X9ObjectIdentifiers.id_dsa_with_sha1, "SHA1", "DSA");
+        // BEGIN Android-removed: Unsupported algorithms
+        /*
         addEntries(EACObjectIdentifiers.id_TA_ECDSA_SHA_1, "SHA1", "ECDSA");
         addEntries(EACObjectIdentifiers.id_TA_ECDSA_SHA_224, "SHA224", "ECDSA");
         addEntries(EACObjectIdentifiers.id_TA_ECDSA_SHA_256, "SHA256", "ECDSA");
@@ -98,14 +114,31 @@
         addEntries(EACObjectIdentifiers.id_TA_RSA_v1_5_SHA_256, "SHA256", "RSA");
         addEntries(EACObjectIdentifiers.id_TA_RSA_PSS_SHA_1, "SHA1", "RSAandMGF1");
         addEntries(EACObjectIdentifiers.id_TA_RSA_PSS_SHA_256, "SHA256", "RSAandMGF1");
-        // BEGIN Android-removed: Unsupported algorithms
-        /*
         addEntries(BSIObjectIdentifiers.ecdsa_plain_SHA1, "SHA1", "PLAIN-ECDSA");
         addEntries(BSIObjectIdentifiers.ecdsa_plain_SHA224, "SHA224", "PLAIN-ECDSA");
         addEntries(BSIObjectIdentifiers.ecdsa_plain_SHA256, "SHA256", "PLAIN-ECDSA");
         addEntries(BSIObjectIdentifiers.ecdsa_plain_SHA384, "SHA384", "PLAIN-ECDSA");
         addEntries(BSIObjectIdentifiers.ecdsa_plain_SHA512, "SHA512", "PLAIN-ECDSA");
         addEntries(BSIObjectIdentifiers.ecdsa_plain_RIPEMD160, "RIPEMD160", "PLAIN-ECDSA");
+        addEntries(BSIObjectIdentifiers.ecdsa_plain_SHA3_224, "SHA3-224", "PLAIN-ECDSA");
+        addEntries(BSIObjectIdentifiers.ecdsa_plain_SHA3_256, "SHA3-256", "PLAIN-ECDSA");
+        addEntries(BSIObjectIdentifiers.ecdsa_plain_SHA3_384, "SHA3-384", "PLAIN-ECDSA");
+        addEntries(BSIObjectIdentifiers.ecdsa_plain_SHA3_512, "SHA3-512", "PLAIN-ECDSA");
+
+//        addEntries(GMObjectIdentifiers.sm2sign_with_rmd160, "RIPEMD160", "SM2");
+//        addEntries(GMObjectIdentifiers.sm2sign_with_sha1, "SHA1", "SM2");
+//        addEntries(GMObjectIdentifiers.sm2sign_with_sha224, "SHA224", "SM2");
+        addEntries(GMObjectIdentifiers.sm2sign_with_sha256, "SHA256", "SM2");
+//        addEntries(GMObjectIdentifiers.sm2sign_with_sha384, "SHA384", "SM2");
+//        addEntries(GMObjectIdentifiers.sm2sign_with_sha512, "SHA512", "SM2");
+        addEntries(GMObjectIdentifiers.sm2sign_with_sm3, "SM3", "SM2");
+
+        addEntries(BCObjectIdentifiers.sphincs256_with_SHA512, "SHA512", "SPHINCS256");
+        addEntries(BCObjectIdentifiers.sphincs256_with_SHA3_512, "SHA3-512", "SPHINCS256");
+
+        addEntries(BCObjectIdentifiers.picnic_with_shake256, "SHAKE256", "Picnic");
+        addEntries(BCObjectIdentifiers.picnic_with_sha512, "SHA512", "Picnic");
+        addEntries(BCObjectIdentifiers.picnic_with_sha3_512, "SHA3-512", "Picnic");
 
         addEntries(GMObjectIdentifiers.sm2sign_with_rmd160, "RIPEMD160", "SM2");
         addEntries(GMObjectIdentifiers.sm2sign_with_sha1, "SHA1", "SM2");
@@ -134,6 +167,7 @@
         encryptionAlgs.put(CryptoProObjectIdentifiers.gostR3411_94_with_gostR3410_94, "GOST3410");
         encryptionAlgs.put(RosstandartObjectIdentifiers.id_tc26_signwithdigest_gost_3410_12_256, "ECGOST3410-2012-256");
         encryptionAlgs.put(RosstandartObjectIdentifiers.id_tc26_signwithdigest_gost_3410_12_512, "ECGOST3410-2012-512");
+        encryptionAlgs.put(X9ObjectIdentifiers.id_ecPublicKey, "ECDSA");
 
         digestAlgs.put(PKCSObjectIdentifiers.md2, "MD2");
         digestAlgs.put(PKCSObjectIdentifiers.md4, "MD4");
@@ -147,6 +181,10 @@
         digestAlgs.put(NISTObjectIdentifiers.id_sha512, "SHA512");
         // BEGIN Android-removed: Unsupported algorithms
         /*
+        digestAlgs.put(NISTObjectIdentifiers.id_sha512_224, "SHA512(224)");
+        digestAlgs.put(NISTObjectIdentifiers.id_sha512_256, "SHA512(256)");
+        digestAlgs.put(NISTObjectIdentifiers.id_shake128, "SHAKE128");
+        digestAlgs.put(NISTObjectIdentifiers.id_shake256, "SHAKE256");
         digestAlgs.put(NISTObjectIdentifiers.id_sha3_224, "SHA3-224");
         digestAlgs.put(NISTObjectIdentifiers.id_sha3_256, "SHA3-256");
         digestAlgs.put(NISTObjectIdentifiers.id_sha3_384, "SHA3-384");
@@ -159,6 +197,34 @@
         digestAlgs.put(RosstandartObjectIdentifiers.id_tc26_gost_3411_12_256,  "GOST3411-2012-256");
         digestAlgs.put(RosstandartObjectIdentifiers.id_tc26_gost_3411_12_512,  "GOST3411-2012-512");
         digestAlgs.put(GMObjectIdentifiers.sm3,  "SM3");
+
+        simpleAlgs.put(EdECObjectIdentifiers.id_Ed25519, "Ed25519");
+        simpleAlgs.put(EdECObjectIdentifiers.id_Ed448, "Ed448");
+        simpleAlgs.put(PKCSObjectIdentifiers.id_alg_hss_lms_hashsig, "LMS");
+
+        simpleAlgs.put(MiscObjectIdentifiers.id_alg_composite, "COMPOSITE");
+        simpleAlgs.put(BCObjectIdentifiers.falcon_512, "Falcon-512");
+        simpleAlgs.put(BCObjectIdentifiers.falcon_1024, "Falcon-1024");
+        simpleAlgs.put(BCObjectIdentifiers.dilithium2, "Dilithium2");
+        simpleAlgs.put(BCObjectIdentifiers.dilithium3, "Dilithium3");
+        simpleAlgs.put(BCObjectIdentifiers.dilithium5, "Dilithium5");
+        simpleAlgs.put(BCObjectIdentifiers.sphincsPlus_sha2_128s, "SPHINCS+-SHA2-128s");
+        simpleAlgs.put(BCObjectIdentifiers.sphincsPlus_sha2_128f, "SPHINCS+-SHA2-128f");
+        simpleAlgs.put(BCObjectIdentifiers.sphincsPlus_sha2_192s, "SPHINCS+-SHA2-192s");
+        simpleAlgs.put(BCObjectIdentifiers.sphincsPlus_sha2_192f, "SPHINCS+-SHA2-192f");
+        simpleAlgs.put(BCObjectIdentifiers.sphincsPlus_sha2_256s, "SPHINCS+-SHA2-256s");
+        simpleAlgs.put(BCObjectIdentifiers.sphincsPlus_sha2_256f, "SPHINCS+-SHA2-256f");
+        simpleAlgs.put(BCObjectIdentifiers.sphincsPlus_shake_128s, "SPHINCS+-SHAKE-128s");
+        simpleAlgs.put(BCObjectIdentifiers.sphincsPlus_shake_128f, "SPHINCS+-SHAKE-128f");
+        simpleAlgs.put(BCObjectIdentifiers.sphincsPlus_shake_192s, "SPHINCS+-SHAKE-192s");
+        simpleAlgs.put(BCObjectIdentifiers.sphincsPlus_shake_192f, "SPHINCS+-SHAKE-192f");
+        simpleAlgs.put(BCObjectIdentifiers.sphincsPlus_shake_256s, "SPHINCS+-SHAKE-256s");
+        simpleAlgs.put(BCObjectIdentifiers.sphincsPlus_shake_256f, "SPHINCS+-SHAKE-256f");
+        simpleAlgs.put(BCObjectIdentifiers.dilithium2, "Dilithium2");
+        simpleAlgs.put(BCObjectIdentifiers.dilithium3, "Dilithium3");
+        simpleAlgs.put(BCObjectIdentifiers.dilithium5, "Dilithium5");
+
+        simpleAlgs.put(BCObjectIdentifiers.picnic_signature, "Picnic");
         */
         // END Android-removed: Unsupported algorithms
     }
@@ -234,16 +300,29 @@
         {
             return "Ed448";
         }
+
+        // if (encryptionAlgOID.on(BCObjectIdentifiers.sphincsPlus))
+        // {
+        //     return "SPHINCSPlus";
+        // }
         */
         // END Android-removed: unsupported algorithms
 
-        String digestName = getDigestAlgName(encryptionAlg.getAlgorithm());
+        ASN1ObjectIdentifier encryptionAlgOID = encryptionAlg.getAlgorithm();
 
-        if (!digestName.equals(encryptionAlg.getAlgorithm().getId()))
+        String simpleAlgName = (String)simpleAlgs.get(encryptionAlgOID);
+        if (simpleAlgName != null)
         {
-            return digestName + "with" + getEncryptionAlgName(encryptionAlg.getAlgorithm());
+            return simpleAlgName;
         }
 
-        return getDigestAlgName(digestAlg.getAlgorithm()) + "with" + getEncryptionAlgName(encryptionAlg.getAlgorithm());
+        String digestName = getDigestAlgName(encryptionAlgOID);
+
+        if (!digestName.equals(encryptionAlgOID.getId()))
+        {
+            return digestName + "with" + getEncryptionAlgName(encryptionAlgOID);
+        }
+
+        return getDigestAlgName(digestAlg.getAlgorithm()) + "with" + getEncryptionAlgName(encryptionAlgOID);
     }
 }
diff --git a/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cms/DefaultCMSSignatureEncryptionAlgorithmFinder.java b/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cms/DefaultCMSSignatureEncryptionAlgorithmFinder.java
index f82906b..0c34339 100644
--- a/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cms/DefaultCMSSignatureEncryptionAlgorithmFinder.java
+++ b/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cms/DefaultCMSSignatureEncryptionAlgorithmFinder.java
@@ -20,8 +20,8 @@
 public class DefaultCMSSignatureEncryptionAlgorithmFinder
     implements CMSSignatureEncryptionAlgorithmFinder
 {
-    private static final Set RSA_PKCS1d5 = new HashSet();
-    private static final Map GOST_ENC = new HashMap();
+    protected static final Set RSA_PKCS1d5 = new HashSet();
+    protected static final Map GOST_ENC = new HashMap();
 
     static
     {
diff --git a/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cms/KEMRecipient.java b/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cms/KEMRecipient.java
new file mode 100644
index 0000000..b754f97
--- /dev/null
+++ b/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cms/KEMRecipient.java
@@ -0,0 +1,14 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.cms;
+
+import com.android.internal.org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+public interface KEMRecipient
+    extends Recipient
+{
+    RecipientOperator getRecipientOperator(AlgorithmIdentifier keyEncAlg, AlgorithmIdentifier contentEncryptionAlgorithm, byte[] encryptedContentKey)
+        throws CMSException;
+}
diff --git a/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cms/SignerInfoGenerator.java b/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cms/SignerInfoGenerator.java
index b6d006a..c08fcd0 100644
--- a/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cms/SignerInfoGenerator.java
+++ b/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cms/SignerInfoGenerator.java
@@ -20,11 +20,7 @@
 import com.android.internal.org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 import com.android.internal.org.bouncycastle.cert.X509CertificateHolder;
 import com.android.internal.org.bouncycastle.operator.ContentSigner;
-import com.android.internal.org.bouncycastle.operator.DefaultDigestAlgorithmIdentifierFinder;
-import com.android.internal.org.bouncycastle.operator.DigestAlgorithmIdentifierFinder;
 import com.android.internal.org.bouncycastle.operator.DigestCalculator;
-import com.android.internal.org.bouncycastle.operator.DigestCalculatorProvider;
-import com.android.internal.org.bouncycastle.operator.OperatorCreationException;
 import com.android.internal.org.bouncycastle.util.Arrays;
 import com.android.internal.org.bouncycastle.util.io.TeeOutputStream;
 
@@ -38,7 +34,7 @@
     private final CMSAttributeTableGenerator unsAttrGen;
     private final ContentSigner signer;
     private final DigestCalculator digester;
-    private final DigestAlgorithmIdentifierFinder digAlgFinder = new DefaultDigestAlgorithmIdentifierFinder();
+    private final AlgorithmIdentifier digestAlgorithm;
     private final CMSSignatureEncryptionAlgorithmFinder sigEncAlgFinder;
 
     private byte[] calculatedDigest = null;
@@ -47,44 +43,32 @@
     SignerInfoGenerator(
         SignerIdentifier signerIdentifier,
         ContentSigner signer,
-        DigestCalculatorProvider digesterProvider,
+        AlgorithmIdentifier digesterAlgorithm,
         CMSSignatureEncryptionAlgorithmFinder sigEncAlgFinder)
-        throws OperatorCreationException
     {
-        this(signerIdentifier, signer, digesterProvider, sigEncAlgFinder, false);
+        this.signerIdentifier = signerIdentifier;
+        this.signer = signer;
+        this.digestAlgorithm = digesterAlgorithm;
+        this.digester = null;
+        this.sAttrGen = null;
+        this.unsAttrGen = null;
+        this.sigEncAlgFinder = sigEncAlgFinder;
     }
 
     SignerInfoGenerator(
         SignerIdentifier signerIdentifier,
         ContentSigner signer,
-        DigestCalculatorProvider digesterProvider,
+        DigestCalculator digester,
         CMSSignatureEncryptionAlgorithmFinder sigEncAlgFinder,
-        boolean isDirectSignature)
-        throws OperatorCreationException
+        CMSAttributeTableGenerator sAttrGen,
+        CMSAttributeTableGenerator unsAttrGen)
     {
         this.signerIdentifier = signerIdentifier;
         this.signer = signer;
-
-        if (digesterProvider != null)
-        {
-            this.digester = digesterProvider.get(digAlgFinder.find(signer.getAlgorithmIdentifier()));
-        }
-        else
-        {
-            this.digester = null;
-        }
-
-        if (isDirectSignature)
-        {
-            this.sAttrGen = null;
-            this.unsAttrGen = null;
-        }
-        else
-        {
-            this.sAttrGen = new DefaultSignedAttributeTableGenerator();
-            this.unsAttrGen = null;
-        }
-
+        this.digestAlgorithm = digester.getAlgorithmIdentifier();
+        this.digester = digester;
+        this.sAttrGen = sAttrGen;
+        this.unsAttrGen = unsAttrGen;
         this.sigEncAlgFinder = sigEncAlgFinder;
     }
 
@@ -95,38 +79,13 @@
     {
         this.signerIdentifier = original.signerIdentifier;
         this.signer = original.signer;
+        this.digestAlgorithm = original.digestAlgorithm;
         this.digester = original.digester;
         this.sigEncAlgFinder = original.sigEncAlgFinder;
         this.sAttrGen = sAttrGen;
         this.unsAttrGen = unsAttrGen;
     }
 
-    SignerInfoGenerator(
-        SignerIdentifier signerIdentifier,
-        ContentSigner signer,
-        DigestCalculatorProvider digesterProvider,
-        CMSSignatureEncryptionAlgorithmFinder sigEncAlgFinder,
-        CMSAttributeTableGenerator sAttrGen,
-        CMSAttributeTableGenerator unsAttrGen)
-        throws OperatorCreationException
-    {
-        this.signerIdentifier = signerIdentifier;
-        this.signer = signer;
-
-        if (digesterProvider != null)
-        {
-            this.digester = digesterProvider.get(digAlgFinder.find(signer.getAlgorithmIdentifier()));
-        }
-        else
-        {
-            this.digester = null;
-        }
-
-        this.sAttrGen = sAttrGen;
-        this.unsAttrGen = unsAttrGen;
-        this.sigEncAlgFinder = sigEncAlgFinder;
-    }
-
     public SignerIdentifier getSID()
     {
         return signerIdentifier;
@@ -149,12 +108,7 @@
     
     public AlgorithmIdentifier getDigestAlgorithm()
     {
-        if (digester != null)
-        {
-            return digester.getAlgorithmIdentifier();
-        }
-
-        return digAlgFinder.find(signer.getAlgorithmIdentifier());
+        return digestAlgorithm;
     }
     
     public OutputStream getCalculatingOutputStream()
@@ -211,14 +165,13 @@
             }
             else
             {
+                digestAlg = digestAlgorithm;
                 if (digester != null)
                 {
-                    digestAlg = digester.getAlgorithmIdentifier();
                     calculatedDigest = digester.getDigest();
                 }
                 else
                 {
-                    digestAlg = digAlgFinder.find(signer.getAlgorithmIdentifier());
                     calculatedDigest = null;
                 }
             }
diff --git a/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cms/SignerInfoGeneratorBuilder.java b/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cms/SignerInfoGeneratorBuilder.java
index e6aea68..b818671 100644
--- a/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cms/SignerInfoGeneratorBuilder.java
+++ b/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cms/SignerInfoGeneratorBuilder.java
@@ -4,8 +4,12 @@
 import com.android.internal.org.bouncycastle.asn1.DEROctetString;
 import com.android.internal.org.bouncycastle.asn1.cms.IssuerAndSerialNumber;
 import com.android.internal.org.bouncycastle.asn1.cms.SignerIdentifier;
+import com.android.internal.org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 import com.android.internal.org.bouncycastle.cert.X509CertificateHolder;
 import com.android.internal.org.bouncycastle.operator.ContentSigner;
+import com.android.internal.org.bouncycastle.operator.DefaultDigestAlgorithmIdentifierFinder;
+import com.android.internal.org.bouncycastle.operator.DigestAlgorithmIdentifierFinder;
+import com.android.internal.org.bouncycastle.operator.DigestCalculator;
 import com.android.internal.org.bouncycastle.operator.DigestCalculatorProvider;
 import com.android.internal.org.bouncycastle.operator.OperatorCreationException;
 
@@ -15,11 +19,14 @@
  */
 public class SignerInfoGeneratorBuilder
 {
+    private final DigestAlgorithmIdentifierFinder digAlgFinder = new DefaultDigestAlgorithmIdentifierFinder();
+
     private DigestCalculatorProvider digestProvider;
     private boolean directSignature;
     private CMSAttributeTableGenerator signedGen;
     private CMSAttributeTableGenerator unsignedGen;
     private CMSSignatureEncryptionAlgorithmFinder sigEncAlgFinder;
+    private AlgorithmIdentifier contentDigest;
 
     /**
      *  Base constructor.
@@ -57,6 +64,18 @@
     }
 
     /**
+     * Set the algorithm identifier for the contentDigest to be used for processing the data.
+     *
+     * @return the builder object
+     */
+    public SignerInfoGeneratorBuilder setContentDigest(AlgorithmIdentifier contentDigest)
+    {
+        this.contentDigest = contentDigest;
+
+        return this;
+    }
+
+    /**
      *  Provide a custom signed attribute generator.
      *
      * @param signedGen a generator of signed attributes.
@@ -122,9 +141,19 @@
     private SignerInfoGenerator createGenerator(ContentSigner contentSigner, SignerIdentifier sigId)
         throws OperatorCreationException
     {
+        DigestCalculator digester;
+        if (contentDigest != null)
+        {
+            digester = digestProvider.get(contentDigest);
+        }
+        else
+        {
+            digester = digestProvider.get(digAlgFinder.find(contentSigner.getAlgorithmIdentifier()));
+        }
+
         if (directSignature)
         {
-            return new SignerInfoGenerator(sigId, contentSigner, digestProvider, sigEncAlgFinder, true);
+            return new SignerInfoGenerator(sigId, contentSigner, digester.getAlgorithmIdentifier(), sigEncAlgFinder);
         }
 
         if (signedGen != null || unsignedGen != null)
@@ -134,9 +163,9 @@
                 signedGen = new DefaultSignedAttributeTableGenerator();
             }
 
-            return new SignerInfoGenerator(sigId, contentSigner, digestProvider, sigEncAlgFinder, signedGen, unsignedGen);
+            return new SignerInfoGenerator(sigId, contentSigner, digester, sigEncAlgFinder, signedGen, unsignedGen);
         }
         
-        return new SignerInfoGenerator(sigId, contentSigner, digestProvider, sigEncAlgFinder);
+        return new SignerInfoGenerator(sigId, contentSigner, digester, sigEncAlgFinder, new DefaultSignedAttributeTableGenerator(), null);
     }
 }
diff --git a/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cms/SignerInformation.java b/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cms/SignerInformation.java
index 1f70626..9393e25 100644
--- a/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cms/SignerInformation.java
+++ b/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cms/SignerInformation.java
@@ -25,6 +25,8 @@
 import com.android.internal.org.bouncycastle.asn1.cms.SignerIdentifier;
 import com.android.internal.org.bouncycastle.asn1.cms.SignerInfo;
 import com.android.internal.org.bouncycastle.asn1.cms.Time;
+import com.android.internal.org.bouncycastle.asn1.oiw.OIWObjectIdentifiers;
+import com.android.internal.org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
 import com.android.internal.org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 import com.android.internal.org.bouncycastle.asn1.x509.DigestInfo;
 import com.android.internal.org.bouncycastle.cert.X509CertificateHolder;
@@ -41,34 +43,34 @@
  */
 public class SignerInformation
 {
-    private final SignerId                sid;
-    private final CMSProcessable          content;
-    private final byte[]                  signature;
-    private final ASN1ObjectIdentifier    contentType;
-    private final boolean                 isCounterSignature;
+    private final SignerId sid;
+    private final CMSProcessable content;
+    private final byte[] signature;
+    private final ASN1ObjectIdentifier contentType;
+    private final boolean isCounterSignature;
 
     // Derived
-    private AttributeTable                signedAttributeValues;
-    private AttributeTable                unsignedAttributeValues;
-    private byte[]                        resultDigest;
+    private AttributeTable signedAttributeValues;
+    private AttributeTable unsignedAttributeValues;
+    private byte[] resultDigest;
 
-    protected final SignerInfo            info;
-    protected final AlgorithmIdentifier   digestAlgorithm;
-    protected final AlgorithmIdentifier   encryptionAlgorithm;
-    protected final ASN1Set               signedAttributeSet;
-    protected final ASN1Set               unsignedAttributeSet;
+    protected final SignerInfo info;
+    protected final AlgorithmIdentifier digestAlgorithm;
+    protected final AlgorithmIdentifier encryptionAlgorithm;
+    protected final ASN1Set signedAttributeSet;
+    protected final ASN1Set unsignedAttributeSet;
 
     SignerInformation(
-        SignerInfo          info,
+        SignerInfo info,
         ASN1ObjectIdentifier contentType,
-        CMSProcessable      content,
-        byte[]              resultDigest)
+        CMSProcessable content,
+        byte[] resultDigest)
     {
         this.info = info;
         this.contentType = contentType;
         this.isCounterSignature = contentType == null;
 
-        SignerIdentifier   s = info.getSID();
+        SignerIdentifier s = info.getSID();
 
         if (s.isTagged())
         {
@@ -78,7 +80,7 @@
         }
         else
         {
-            IssuerAndSerialNumber   iAnds = IssuerAndSerialNumber.getInstance(s.getId());
+            IssuerAndSerialNumber iAnds = IssuerAndSerialNumber.getInstance(s.getId());
 
             sid = new SignerId(iAnds.getName(), iAnds.getSerialNumber().getValue());
         }
@@ -111,7 +113,7 @@
      * that by also tweaking the SignerInfo so that these issues can be dealt with.
      *
      * @param baseInfo the SignerInformation to base this one on.
-     * @param info the SignerInfo to associate with the existing baseInfo data.
+     * @param info     the SignerInfo to associate with the existing baseInfo data.
      */
     protected SignerInformation(SignerInformation baseInfo, SignerInfo info)
     {
@@ -126,8 +128,8 @@
         this.signature = info.getEncryptedDigest().getOctets();
         this.content = baseInfo.content;
         this.resultDigest = baseInfo.resultDigest;
-        this.signedAttributeValues = baseInfo.signedAttributeValues;
-        this.unsignedAttributeValues = baseInfo.unsignedAttributeValues;
+        this.signedAttributeValues = getSignedAttributes();
+        this.unsignedAttributeValues = getUnsignedAttributes();
     }
 
     public boolean isCounterSignature()
@@ -141,7 +143,7 @@
     }
 
     private byte[] encodeObj(
-        ASN1Encodable    obj)
+        ASN1Encodable obj)
         throws IOException
     {
         if (obj != null)
@@ -202,10 +204,10 @@
         {
             throw new IllegalStateException("method can only be called after verify.");
         }
-        
+
         return Arrays.clone(resultDigest);
     }
-    
+
     /**
      * return the object identifier for the signature.
      */
@@ -228,7 +230,7 @@
         {
             throw new RuntimeException("exception getting encryption parameters " + e);
         }
-    }  
+    }
 
     /**
      * return a table of the signed attributes - indexed by
@@ -278,7 +280,7 @@
         The countersignature attribute MUST be an unsigned attribute; it MUST
         NOT be a signed attribute, an authenticated attribute, an
         unauthenticated attribute, or an unprotected attribute.
-        */        
+        */
         AttributeTable unsignedAttributeTable = getUnsignedAttributes();
         if (unsignedAttributeTable == null)
         {
@@ -309,7 +311,7 @@
                 // TODO Throw an appropriate exception?
             }
 
-            for (Enumeration en = values.getObjects(); en.hasMoreElements();)
+            for (Enumeration en = values.getObjects(); en.hasMoreElements(); )
             {
                 /*
                 Countersignature values have the same meaning as SignerInfo values
@@ -333,9 +335,10 @@
 
         return new SignerInformationStore(counterSignatures);
     }
-    
+
     /**
      * return the DER encoding of the signed attributes.
+     *
      * @throws IOException if an encoding error occurs.
      */
     public byte[] getEncodedSignedAttributes()
@@ -353,12 +356,14 @@
         SignerInformationVerifier verifier)
         throws CMSException
     {
-        String          encName = CMSSignedHelper.INSTANCE.getEncryptionAlgName(this.getEncryptionAlgOID());
+        String encName = CMSSignedHelper.INSTANCE.getEncryptionAlgName(this.getEncryptionAlgOID());
+        AlgorithmIdentifier realDigestAlgorithm = signedAttributeSet != null ?
+            info.getDigestAlgorithm() : translateBrokenRSAPkcs7(encryptionAlgorithm, info.getDigestAlgorithm());
         ContentVerifier contentVerifier;
 
         try
         {
-            contentVerifier = verifier.getContentVerifier(encryptionAlgorithm, info.getDigestAlgorithm());
+            contentVerifier = verifier.getContentVerifier(encryptionAlgorithm, realDigestAlgorithm);
         }
         catch (OperatorCreationException e)
         {
@@ -371,10 +376,10 @@
 
             if (resultDigest == null)
             {
-                DigestCalculator calc = verifier.getDigestCalculator(this.getDigestAlgorithmID());
+                DigestCalculator calc = verifier.getDigestCalculator(realDigestAlgorithm);
                 if (content != null)
                 {
-                    OutputStream      digOut = calc.getOutputStream();
+                    OutputStream digOut = calc.getOutputStream();
 
                     if (signedAttributeSet == null)
                     {
@@ -438,128 +443,18 @@
         }
 
         // RFC 3852 11.1 Check the content-type attribute is correct
-        {
-            ASN1Primitive validContentType = getSingleValuedSignedAttribute(
-                CMSAttributes.contentType, "content-type");
-            if (validContentType == null)
-            {
-                if (!isCounterSignature && signedAttributeSet != null)
-                {
-                    throw new CMSException("The content-type attribute type MUST be present whenever signed attributes are present in signed-data");
-                }
-            }
-            else
-            {
-                if (isCounterSignature)
-                {
-                    throw new CMSException("[For counter signatures,] the signedAttributes field MUST NOT contain a content-type attribute");
-                }
-
-                if (!(validContentType instanceof ASN1ObjectIdentifier))
-                {
-                    throw new CMSException("content-type attribute value not of ASN.1 type 'OBJECT IDENTIFIER'");
-                }
-
-                ASN1ObjectIdentifier signedContentType = (ASN1ObjectIdentifier)validContentType;
-
-                if (!signedContentType.equals(contentType))
-                {
-                    throw new CMSException("content-type attribute value does not match eContentType");
-                }
-            }
-        }
+        verifyContentTypeAttributeValue();
 
         AttributeTable signedAttrTable = this.getSignedAttributes();
 
         // RFC 6211 Validate Algorithm Identifier protection attribute if present
-        {
-            AttributeTable unsignedAttrTable = this.getUnsignedAttributes();
-            if (unsignedAttrTable != null && unsignedAttrTable.getAll(CMSAttributes.cmsAlgorithmProtect).size() > 0)
-            {
-                throw new CMSException("A cmsAlgorithmProtect attribute MUST be a signed attribute");
-            }
-            if (signedAttrTable != null)
-            {
-                ASN1EncodableVector protectionAttributes = signedAttrTable.getAll(CMSAttributes.cmsAlgorithmProtect);
-                if (protectionAttributes.size() > 1)
-                {
-                    throw new CMSException("Only one instance of a cmsAlgorithmProtect attribute can be present");
-                }
-
-                if (protectionAttributes.size() > 0)
-                {
-                    Attribute attr = Attribute.getInstance(protectionAttributes.get(0));
-                    if (attr.getAttrValues().size() != 1)
-                    {
-                        throw new CMSException("A cmsAlgorithmProtect attribute MUST contain exactly one value");
-                    }
-
-                    CMSAlgorithmProtection algorithmProtection = CMSAlgorithmProtection.getInstance(attr.getAttributeValues()[0]);
-
-                    if (!CMSUtils.isEquivalent(algorithmProtection.getDigestAlgorithm(), info.getDigestAlgorithm()))
-                    {
-                        throw new CMSException("CMS Algorithm Identifier Protection check failed for digestAlgorithm");
-                    }
-
-                    if (!CMSUtils.isEquivalent(algorithmProtection.getSignatureAlgorithm(), info.getDigestEncryptionAlgorithm()))
-                    {
-                        throw new CMSException("CMS Algorithm Identifier Protection check failed for signatureAlgorithm");
-                    }
-                }
-            }
-        }
+        verifyAlgorithmIdentifierProtectionAttribute(signedAttrTable);
 
         // RFC 3852 11.2 Check the message-digest attribute is correct
-        {
-            ASN1Primitive validMessageDigest = getSingleValuedSignedAttribute(
-                CMSAttributes.messageDigest, "message-digest");
-            if (validMessageDigest == null)
-            {
-                if (signedAttributeSet != null)
-                {
-                    throw new CMSException("the message-digest signed attribute type MUST be present when there are any signed attributes present");
-                }
-            }
-            else
-            {
-                if (!(validMessageDigest instanceof ASN1OctetString))
-                {
-                    throw new CMSException("message-digest attribute value not of ASN.1 type 'OCTET STRING'");
-                }
-
-                ASN1OctetString signedMessageDigest = (ASN1OctetString)validMessageDigest;
-
-                if (!Arrays.constantTimeAreEqual(resultDigest, signedMessageDigest.getOctets()))
-                {
-                    throw new CMSSignerDigestMismatchException("message-digest attribute value does not match calculated value");
-                }
-            }
-        }
+        verifyMessageDigestAttribute();
 
         // RFC 3852 11.4 Validate countersignature attribute(s)
-        {
-            if (signedAttrTable != null
-                && signedAttrTable.getAll(CMSAttributes.counterSignature).size() > 0)
-            {
-                throw new CMSException("A countersignature attribute MUST NOT be a signed attribute");
-            }
-
-            AttributeTable unsignedAttrTable = this.getUnsignedAttributes();
-            if (unsignedAttrTable != null)
-            {
-                ASN1EncodableVector csAttrs = unsignedAttrTable.getAll(CMSAttributes.counterSignature);
-                for (int i = 0; i < csAttrs.size(); ++i)
-                {
-                    Attribute csAttr = Attribute.getInstance(csAttrs.get(i));
-                    if (csAttr.getAttrValues().size() < 1)
-                    {
-                        throw new CMSException("A countersignature attribute MUST contain at least one AttributeValue");
-                    }
-
-                    // Note: We don't recursively validate the countersignature value
-                }
-            }
-        }
+        verifyCounterSignatureAttribute(signedAttrTable);
 
         try
         {
@@ -571,7 +466,7 @@
 
                     if (encName.equals("RSA"))
                     {
-                        DigestInfo digInfo = new DigestInfo(new AlgorithmIdentifier(digestAlgorithm.getAlgorithm(), DERNull.INSTANCE), resultDigest);
+                        DigestInfo digInfo = new DigestInfo(new AlgorithmIdentifier(realDigestAlgorithm.getAlgorithm(), DERNull.INSTANCE), resultDigest);
 
                         return rawVerifier.verify(digInfo.getEncoded(ASN1Encoding.DER), this.getSignature());
                     }
@@ -589,6 +484,154 @@
     }
 
     /**
+     * RFC 3852 11.1 Check the content-type attribute is correct
+     *
+     * @throws CMSException when content-type was invalid.
+     */
+    private void verifyContentTypeAttributeValue()
+        throws CMSException
+    {
+        ASN1Primitive validContentType = getSingleValuedSignedAttribute(
+            CMSAttributes.contentType, "content-type");
+        if (validContentType == null)
+        {
+            if (!isCounterSignature && signedAttributeSet != null)
+            {
+                throw new CMSException("The content-type attribute type MUST be present whenever signed attributes are present in signed-data");
+            }
+        }
+        else
+        {
+            if (isCounterSignature)
+            {
+                throw new CMSException("[For counter signatures,] the signedAttributes field MUST NOT contain a content-type attribute");
+            }
+
+            if (!(validContentType instanceof ASN1ObjectIdentifier))
+            {
+                throw new CMSException("content-type attribute value not of ASN.1 type 'OBJECT IDENTIFIER'");
+            }
+
+            ASN1ObjectIdentifier signedContentType = (ASN1ObjectIdentifier)validContentType;
+
+            if (!signedContentType.equals(contentType))
+            {
+                throw new CMSException("content-type attribute value does not match eContentType");
+            }
+        }
+    }
+
+    /**
+     * RFC 3852 11.2 Check the message-digest attribute is correct
+     *
+     * @throws CMSException when message-digest attribute was rejected
+     */
+    private void verifyMessageDigestAttribute()
+        throws CMSException
+    {
+        ASN1Primitive validMessageDigest = getSingleValuedSignedAttribute(
+            CMSAttributes.messageDigest, "message-digest");
+        if (validMessageDigest == null)
+        {
+            if (signedAttributeSet != null)
+            {
+                throw new CMSException("the message-digest signed attribute type MUST be present when there are any signed attributes present");
+            }
+        }
+        else
+        {
+            if (!(validMessageDigest instanceof ASN1OctetString))
+            {
+                throw new CMSException("message-digest attribute value not of ASN.1 type 'OCTET STRING'");
+            }
+
+            ASN1OctetString signedMessageDigest = (ASN1OctetString)validMessageDigest;
+
+            if (!Arrays.constantTimeAreEqual(resultDigest, signedMessageDigest.getOctets()))
+            {
+                throw new CMSSignerDigestMismatchException("message-digest attribute value does not match calculated value");
+            }
+        }
+    }
+
+    /**
+     * RFC 6211 Validate Algorithm Identifier protection attribute if present
+     *
+     * @param signedAttrTable signed attributes
+     * @throws CMSException when cmsAlgorihmProtect attribute was rejected
+     */
+    private void verifyAlgorithmIdentifierProtectionAttribute(AttributeTable signedAttrTable)
+        throws CMSException
+    {
+        AttributeTable unsignedAttrTable = this.getUnsignedAttributes();
+        if (unsignedAttrTable != null && unsignedAttrTable.getAll(CMSAttributes.cmsAlgorithmProtect).size() > 0)
+        {
+            throw new CMSException("A cmsAlgorithmProtect attribute MUST be a signed attribute");
+        }
+        if (signedAttrTable != null)
+        {
+            ASN1EncodableVector protectionAttributes = signedAttrTable.getAll(CMSAttributes.cmsAlgorithmProtect);
+            if (protectionAttributes.size() > 1)
+            {
+                throw new CMSException("Only one instance of a cmsAlgorithmProtect attribute can be present");
+            }
+
+            if (protectionAttributes.size() > 0)
+            {
+                Attribute attr = Attribute.getInstance(protectionAttributes.get(0));
+                if (attr.getAttrValues().size() != 1)
+                {
+                    throw new CMSException("A cmsAlgorithmProtect attribute MUST contain exactly one value");
+                }
+
+                CMSAlgorithmProtection algorithmProtection = CMSAlgorithmProtection.getInstance(attr.getAttributeValues()[0]);
+
+                if (!CMSUtils.isEquivalent(algorithmProtection.getDigestAlgorithm(), info.getDigestAlgorithm()))
+                {
+                    throw new CMSException("CMS Algorithm Identifier Protection check failed for digestAlgorithm");
+                }
+
+                if (!CMSUtils.isEquivalent(algorithmProtection.getSignatureAlgorithm(), info.getDigestEncryptionAlgorithm()))
+                {
+                    throw new CMSException("CMS Algorithm Identifier Protection check failed for signatureAlgorithm");
+                }
+            }
+        }
+    }
+
+    /**
+     * RFC 3852 11.4 Validate countersignature attribute(s)
+     *
+     * @param signedAttrTable signed attributes
+     * @throws CMSException when countersignature attribute was rejected
+     */
+    private void verifyCounterSignatureAttribute(AttributeTable signedAttrTable)
+        throws CMSException
+    {
+        if (signedAttrTable != null
+            && signedAttrTable.getAll(CMSAttributes.counterSignature).size() > 0)
+        {
+            throw new CMSException("A countersignature attribute MUST NOT be a signed attribute");
+        }
+
+        AttributeTable unsignedAttrTable = this.getUnsignedAttributes();
+        if (unsignedAttrTable != null)
+        {
+            ASN1EncodableVector csAttrs = unsignedAttrTable.getAll(CMSAttributes.counterSignature);
+            for (int i = 0; i < csAttrs.size(); ++i)
+            {
+                Attribute csAttr = Attribute.getInstance(csAttrs.get(i));
+                if (csAttr.getAttrValues().size() < 1)
+                {
+                    throw new CMSException("A countersignature attribute MUST contain at least one AttributeValue");
+                }
+
+                // Note: We don't recursively validate the countersignature value
+            }
+        }
+    }
+
+    /**
      * Verify that the given verifier can successfully verify the signature on
      * this SignerInformation object.
      *
@@ -649,27 +692,28 @@
         ASN1EncodableVector v = signedAttrTable.getAll(attrOID);
         switch (v.size())
         {
-            case 0:
-                return null;
-            case 1:
+        case 0:
+            return null;
+        case 1:
+        {
+            Attribute t = (Attribute)v.get(0);
+            ASN1Set attrValues = t.getAttrValues();
+            if (attrValues.size() != 1)
             {
-                Attribute t = (Attribute)v.get(0);
-                ASN1Set attrValues = t.getAttrValues();
-                if (attrValues.size() != 1)
-                {
-                    throw new CMSException("A " + printableName
-                        + " attribute MUST have a single attribute value");
-                }
-
-                return attrValues.getObjectAt(0).toASN1Primitive();
+                throw new CMSException("A " + printableName
+                    + " attribute MUST have a single attribute value");
             }
-            default:
-                throw new CMSException("The SignedAttributes in a signerInfo MUST NOT include multiple instances of the "
-                    + printableName + " attribute");
+
+            return attrValues.getObjectAt(0).toASN1Primitive();
+        }
+        default:
+            throw new CMSException("The SignedAttributes in a signerInfo MUST NOT include multiple instances of the "
+                + printableName + " attribute");
         }
     }
 
-    private Time getSigningTime() throws CMSException
+    private Time getSigningTime()
+        throws CMSException
     {
         ASN1Primitive validSigningTime = getSingleValuedSignedAttribute(
             CMSAttributes.signingTime, "signing-time");
@@ -693,27 +737,27 @@
      * Return a signer information object with the passed in unsigned
      * attributes replacing the ones that are current associated with
      * the object passed in.
-     * 
-     * @param signerInformation the signerInfo to be used as the basis.
+     *
+     * @param signerInformation  the signerInfo to be used as the basis.
      * @param unsignedAttributes the unsigned attributes to add.
      * @return a copy of the original SignerInformationObject with the changed attributes.
      */
     public static SignerInformation replaceUnsignedAttributes(
-        SignerInformation   signerInformation,
-        AttributeTable      unsignedAttributes)
+        SignerInformation signerInformation,
+        AttributeTable unsignedAttributes)
     {
-        SignerInfo  sInfo = signerInformation.info;
-        ASN1Set     unsignedAttr = null;
-        
+        SignerInfo sInfo = signerInformation.info;
+        ASN1Set unsignedAttr = null;
+
         if (unsignedAttributes != null)
         {
             unsignedAttr = new DERSet(unsignedAttributes.toASN1EncodableVector());
         }
-        
+
         return new SignerInformation(
-                new SignerInfo(sInfo.getSID(), sInfo.getDigestAlgorithm(),
-                    sInfo.getAuthenticatedAttributes(), sInfo.getDigestEncryptionAlgorithm(), sInfo.getEncryptedDigest(), unsignedAttr),
-                    signerInformation.contentType, signerInformation.content, null);
+            new SignerInfo(sInfo.getSID(), sInfo.getDigestAlgorithm(),
+                sInfo.getAuthenticatedAttributes(), sInfo.getDigestEncryptionAlgorithm(), sInfo.getEncryptedDigest(), unsignedAttr),
+            signerInformation.contentType, signerInformation.content, null);
     }
 
     /**
@@ -721,17 +765,17 @@
      * signatures attached as an unsigned attribute.
      *
      * @param signerInformation the signerInfo to be used as the basis.
-     * @param counterSigners signer info objects carrying counter signature.
+     * @param counterSigners    signer info objects carrying counter signature.
      * @return a copy of the original SignerInformationObject with the changed attributes.
      */
     public static SignerInformation addCounterSigners(
-        SignerInformation        signerInformation,
-        SignerInformationStore   counterSigners)
+        SignerInformation signerInformation,
+        SignerInformationStore counterSigners)
     {
         // TODO Perform checks from RFC 3852 11.4
 
-        SignerInfo          sInfo = signerInformation.info;
-        AttributeTable      unsignedAttr = signerInformation.getUnsignedAttributes();
+        SignerInfo sInfo = signerInformation.info;
+        AttributeTable unsignedAttr = signerInformation.getUnsignedAttributes();
         ASN1EncodableVector v;
 
         if (unsignedAttr != null)
@@ -745,7 +789,7 @@
 
         ASN1EncodableVector sigs = new ASN1EncodableVector();
 
-        for (Iterator it = counterSigners.getSigners().iterator(); it.hasNext();)
+        for (Iterator it = counterSigners.getSigners().iterator(); it.hasNext(); )
         {
             sigs.add(((SignerInformation)it.next()).toASN1Structure());
         }
@@ -753,8 +797,23 @@
         v.add(new Attribute(CMSAttributes.counterSignature, new DERSet(sigs)));
 
         return new SignerInformation(
-                new SignerInfo(sInfo.getSID(), sInfo.getDigestAlgorithm(),
-                    sInfo.getAuthenticatedAttributes(), sInfo.getDigestEncryptionAlgorithm(), sInfo.getEncryptedDigest(), new DERSet(v)),
-                    signerInformation.contentType, signerInformation.content, null);
+            new SignerInfo(sInfo.getSID(), sInfo.getDigestAlgorithm(),
+                sInfo.getAuthenticatedAttributes(), sInfo.getDigestEncryptionAlgorithm(), sInfo.getEncryptedDigest(), new DERSet(v)),
+            signerInformation.contentType, signerInformation.content, null);
+    }
+
+    private static AlgorithmIdentifier translateBrokenRSAPkcs7(AlgorithmIdentifier encryptionAlgorithm, AlgorithmIdentifier digestAlgorithm)
+    {
+        if (PKCSObjectIdentifiers.rsaEncryption.equals(encryptionAlgorithm.getAlgorithm()))
+        {
+            // Yes, some people really did this.
+            if (OIWObjectIdentifiers.sha1WithRSA.equals(digestAlgorithm.getAlgorithm())
+                || PKCSObjectIdentifiers.sha1WithRSAEncryption.equals(digestAlgorithm.getAlgorithm()))
+            {
+                return new AlgorithmIdentifier(OIWObjectIdentifiers.idSHA1, DERNull.INSTANCE);
+            }
+        }
+
+        return digestAlgorithm;
     }
 }
diff --git a/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cms/jcajce/JcaSignerInfoGeneratorBuilder.java b/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cms/jcajce/JcaSignerInfoGeneratorBuilder.java
index e1b4fa5..082f3f0 100644
--- a/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cms/jcajce/JcaSignerInfoGeneratorBuilder.java
+++ b/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cms/jcajce/JcaSignerInfoGeneratorBuilder.java
@@ -4,6 +4,7 @@
 import java.security.cert.CertificateEncodingException;
 import java.security.cert.X509Certificate;
 
+import com.android.internal.org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 import com.android.internal.org.bouncycastle.cert.X509CertificateHolder;
 import com.android.internal.org.bouncycastle.cert.jcajce.JcaX509CertificateHolder;
 import com.android.internal.org.bouncycastle.cms.CMSAttributeTableGenerator;
@@ -56,6 +57,13 @@
         return this;
     }
 
+    public JcaSignerInfoGeneratorBuilder setContentDigest(AlgorithmIdentifier contentDigest)
+    {
+        builder.setContentDigest(contentDigest);
+
+        return this;
+    }
+
     public JcaSignerInfoGeneratorBuilder setSignedAttributeGenerator(CMSAttributeTableGenerator signedGen)
     {
         builder.setSignedAttributeGenerator(signedGen);
diff --git a/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cms/jcajce/JcaSignerInfoVerifierBuilder.java b/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cms/jcajce/JcaSignerInfoVerifierBuilder.java
index 13512d3..b587f40 100644
--- a/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cms/jcajce/JcaSignerInfoVerifierBuilder.java
+++ b/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cms/jcajce/JcaSignerInfoVerifierBuilder.java
@@ -85,7 +85,7 @@
         return new SignerInformationVerifier(sigAlgNameGen, sigAlgIDFinder, helper.createContentVerifierProvider(pubKey), digestProvider);
     }
 
-    private class Helper
+    private static class Helper
     {
         ContentVerifierProvider createContentVerifierProvider(PublicKey publicKey)
             throws OperatorCreationException
@@ -112,7 +112,7 @@
         }
     }
 
-    private class NamedHelper
+    private static class NamedHelper
         extends Helper
     {
         private final String providerName;
@@ -147,7 +147,7 @@
         }
     }
 
-    private class ProviderHelper
+    private static class ProviderHelper
         extends Helper
     {
         private final Provider provider;
diff --git a/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cms/jcajce/JcaSimpleSignerInfoVerifierBuilder.java b/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cms/jcajce/JcaSimpleSignerInfoVerifierBuilder.java
index b1e22fb..e0e58d3 100644
--- a/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cms/jcajce/JcaSimpleSignerInfoVerifierBuilder.java
+++ b/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cms/jcajce/JcaSimpleSignerInfoVerifierBuilder.java
@@ -55,7 +55,7 @@
         return new SignerInformationVerifier(new DefaultCMSSignatureAlgorithmNameGenerator(), new DefaultSignatureAlgorithmIdentifierFinder(), helper.createContentVerifierProvider(pubKey), helper.createDigestCalculatorProvider());
     }
 
-    private class Helper
+    private static class Helper
     {
         ContentVerifierProvider createContentVerifierProvider(PublicKey publicKey)
             throws OperatorCreationException
@@ -82,7 +82,7 @@
         }
     }
 
-    private class NamedHelper
+    private static class NamedHelper
         extends Helper
     {
         private final String providerName;
@@ -117,7 +117,7 @@
         }
     }
 
-    private class ProviderHelper
+    private static class ProviderHelper
         extends Helper
     {
         private final Provider provider;
diff --git a/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cms/jcajce/JceAADStream.java b/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cms/jcajce/JceAADStream.java
new file mode 100644
index 0000000..53a1084
--- /dev/null
+++ b/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/cms/jcajce/JceAADStream.java
@@ -0,0 +1,32 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.cms.jcajce;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+import javax.crypto.Cipher;
+
+class JceAADStream
+    extends OutputStream
+{
+     private static final byte[] SINGLE_BYTE = new byte[1];
+     private Cipher cipher;
+
+     JceAADStream(Cipher cipher)
+     {
+         this.cipher = cipher;
+     }
+
+     public void write(byte[] buf, int off, int len)
+         throws IOException
+     {
+         cipher.updateAAD(buf, off, len);
+     }
+
+     public void write(int b)
+         throws IOException
+     {
+         SINGLE_BYTE[0] = (byte)b;
+         cipher.updateAAD(SINGLE_BYTE, 0, 1);
+     }
+ }
diff --git a/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/openssl/CertificateTrustBlock.java b/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/openssl/CertificateTrustBlock.java
new file mode 100644
index 0000000..8c11861
--- /dev/null
+++ b/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/openssl/CertificateTrustBlock.java
@@ -0,0 +1,137 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.openssl;
+
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+
+import com.android.internal.org.bouncycastle.asn1.ASN1Encodable;
+import com.android.internal.org.bouncycastle.asn1.ASN1EncodableVector;
+import com.android.internal.org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import com.android.internal.org.bouncycastle.asn1.ASN1Sequence;
+import com.android.internal.org.bouncycastle.asn1.ASN1TaggedObject;
+import com.android.internal.org.bouncycastle.asn1.ASN1UTF8String;
+import com.android.internal.org.bouncycastle.asn1.DERSequence;
+import com.android.internal.org.bouncycastle.asn1.DERTaggedObject;
+import com.android.internal.org.bouncycastle.asn1.DERUTF8String;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+public class CertificateTrustBlock
+{
+    private ASN1Sequence uses;
+    private ASN1Sequence prohibitions;
+    private String alias;
+
+    public CertificateTrustBlock(Set<ASN1ObjectIdentifier> uses)
+    {
+        this(null, uses, null);
+    }
+
+    public CertificateTrustBlock(String alias, Set<ASN1ObjectIdentifier> uses)
+    {
+        this(alias, uses, null);
+    }
+
+    public CertificateTrustBlock(String alias, Set<ASN1ObjectIdentifier> uses, Set<ASN1ObjectIdentifier> prohibitions)
+    {
+        this.alias = alias;
+        this.uses = toSequence(uses);
+        this.prohibitions = toSequence(prohibitions);
+    }
+
+    CertificateTrustBlock(byte[] encoded)
+    {
+        ASN1Sequence seq = ASN1Sequence.getInstance(encoded);
+
+        for (Enumeration en = seq.getObjects(); en.hasMoreElements();)
+        {
+            ASN1Encodable obj = (ASN1Encodable)en.nextElement();
+
+            if (obj instanceof ASN1Sequence)
+            {
+                this.uses = ASN1Sequence.getInstance(obj);
+            }
+            else if (obj instanceof ASN1TaggedObject)
+            {
+                this.prohibitions = ASN1Sequence.getInstance((ASN1TaggedObject)obj, false);
+            }
+            else if (obj instanceof ASN1UTF8String)
+            {
+                this.alias = ASN1UTF8String.getInstance(obj).getString();
+            }
+        }
+    }
+
+    public String getAlias()
+    {
+        return alias;
+    }
+
+    public Set<ASN1ObjectIdentifier> getUses()
+    {
+        return toSet(uses);
+    }
+
+    public Set<ASN1ObjectIdentifier> getProhibitions()
+    {
+        return toSet(prohibitions);
+    }
+
+    private Set<ASN1ObjectIdentifier> toSet(ASN1Sequence seq)
+    {
+        if (seq != null)
+        {
+            Set<ASN1ObjectIdentifier> oids = new HashSet<ASN1ObjectIdentifier>(seq.size());
+
+            for (Enumeration en = seq.getObjects(); en.hasMoreElements(); )
+            {
+                oids.add(ASN1ObjectIdentifier.getInstance(en.nextElement()));
+            }
+
+            return oids;
+        }
+
+        return Collections.EMPTY_SET;
+    }
+
+    private ASN1Sequence toSequence(Set<ASN1ObjectIdentifier> oids)
+    {
+        if (oids == null || oids.isEmpty())
+        {
+            return null;
+        }
+
+        ASN1EncodableVector v = new ASN1EncodableVector();
+
+        for (Iterator it = oids.iterator(); it.hasNext();)
+        {
+           v.add((ASN1Encodable)it.next());
+        }
+
+        return new DERSequence(v);
+    }
+
+    ASN1Sequence toASN1Sequence()
+    {
+        ASN1EncodableVector v = new ASN1EncodableVector();
+
+        if (uses != null)
+        {
+           v.add(uses);
+        }
+        if (prohibitions != null)
+        {
+            v.add(new DERTaggedObject(false, 0, prohibitions));
+        }
+        if (alias != null)
+        {
+            v.add(new DERUTF8String(alias));
+        }
+
+        return new DERSequence(v);
+    }
+}
diff --git a/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/operator/AADProcessor.java b/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/operator/AADProcessor.java
new file mode 100644
index 0000000..0477f70
--- /dev/null
+++ b/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/operator/AADProcessor.java
@@ -0,0 +1,26 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.operator;
+
+import java.io.OutputStream;
+
+/**
+ * Base interface for extra methods required for handling associated data in AEAD ciphers.
+ * @hide This class is not part of the Android public SDK API
+ */
+public interface AADProcessor
+{
+    /**
+     * Return a stream to write associated data to in order to have it incorporated into the
+     * AEAD cipher's MAC.
+     *
+     * @return a stream for collecting associated data.
+     */
+    OutputStream getAADStream();
+
+    /**
+     * Return the final value of AEAD cipher's MAC.
+     *
+     * @return MAC value for the AEAD cipher.
+     */
+    byte[] getMAC();
+}
diff --git a/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/operator/AlgorithmNameFinder.java b/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/operator/AlgorithmNameFinder.java
new file mode 100644
index 0000000..38776e1
--- /dev/null
+++ b/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/operator/AlgorithmNameFinder.java
@@ -0,0 +1,37 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.operator;
+
+import com.android.internal.org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import com.android.internal.org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+
+/**
+ * General finder for converting OIDs and AlgorithmIdentifiers into strings.
+ * @hide This class is not part of the Android public SDK API
+ */
+public interface AlgorithmNameFinder
+{
+    /**
+     * Return true if the passed in objectIdentifier has a "human friendly" name associated with it.
+     *
+     * @param objectIdentifier the OID of interest.
+     * @return true if a name lookup exists for the OID, false otherwise.
+     */
+    boolean hasAlgorithmName(ASN1ObjectIdentifier objectIdentifier);
+
+    /**
+     * Return a string representation of the passed in objectIdentifier.
+     *
+     * @param objectIdentifier the OID of interest.
+     * @return a "human friendly" representation of the OID, the OID as a string if none available.
+     */
+    String getAlgorithmName(ASN1ObjectIdentifier objectIdentifier);
+
+    /**
+     * Return a string representation of the passed in AlgorithmIdentifier, based on the OID in the AlgorithmField, with the parameters
+     * included where appropriate.
+     *
+     * @param algorithmIdentifier the AlgorithmIdentifier of interest.
+     * @return a "human friendly" representation of the algorithmIdentifier, the identifiers OID as a string if none available.
+     */
+    String getAlgorithmName(AlgorithmIdentifier algorithmIdentifier);
+}
diff --git a/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/operator/DefaultDigestAlgorithmIdentifierFinder.java b/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/operator/DefaultDigestAlgorithmIdentifierFinder.java
index 0e3edd3..e350e8c 100644
--- a/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/operator/DefaultDigestAlgorithmIdentifierFinder.java
+++ b/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/operator/DefaultDigestAlgorithmIdentifierFinder.java
@@ -2,7 +2,9 @@
 package com.android.internal.org.bouncycastle.operator;
 
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Map;
+import java.util.Set;
 
 import com.android.internal.org.bouncycastle.asn1.ASN1Integer;
 import com.android.internal.org.bouncycastle.asn1.ASN1ObjectIdentifier;
@@ -10,6 +12,7 @@
 // Android-removed: Unsupported algorithms
 // import org.bouncycastle.asn1.bc.BCObjectIdentifiers;
 // import org.bouncycastle.asn1.bsi.BSIObjectIdentifiers;
+// import org.bouncycastle.asn1.cms.CMSObjectIdentifiers;
 // import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers;
 // import org.bouncycastle.asn1.eac.EACObjectIdentifiers;
 // import org.bouncycastle.asn1.gm.GMObjectIdentifiers;
@@ -31,6 +34,9 @@
 {
     private static Map digestOids = new HashMap();
     private static Map digestNameToOids = new HashMap();
+    private static Map digestOidToAlgIds = new HashMap();
+
+    private static Set shake256oids = new HashSet();  // signatures that use SHAKE-256
 
     static
     {
@@ -41,14 +47,18 @@
         // digestOids.put(OIWObjectIdentifiers.md4WithRSAEncryption, PKCSObjectIdentifiers.md4);
         // digestOids.put(OIWObjectIdentifiers.md4WithRSA, PKCSObjectIdentifiers.md4);
         // digestOids.put(OIWObjectIdentifiers.dsaWithSHA1, OIWObjectIdentifiers.idSHA1);
+        // digestOids.put(OIWObjectIdentifiers.md5WithRSA, PKCSObjectIdentifiers.md5);
         // END Android-removed: Unsupported algorithms
         digestOids.put(OIWObjectIdentifiers.sha1WithRSA, OIWObjectIdentifiers.idSHA1);
 
+        digestOids.put(PKCSObjectIdentifiers.rsaEncryption, NISTObjectIdentifiers.id_sha256);
         digestOids.put(PKCSObjectIdentifiers.sha224WithRSAEncryption, NISTObjectIdentifiers.id_sha224);
         digestOids.put(PKCSObjectIdentifiers.sha256WithRSAEncryption, NISTObjectIdentifiers.id_sha256);
         digestOids.put(PKCSObjectIdentifiers.sha384WithRSAEncryption, NISTObjectIdentifiers.id_sha384);
         digestOids.put(PKCSObjectIdentifiers.sha512WithRSAEncryption, NISTObjectIdentifiers.id_sha512);
         // BEGIN Android-removed: Unsupported algorithms
+        // digestOids.put(PKCSObjectIdentifiers.sha512_224WithRSAEncryption, NISTObjectIdentifiers.id_sha512_224);
+        // digestOids.put(PKCSObjectIdentifiers.sha512_256WithRSAEncryption, NISTObjectIdentifiers.id_sha512_256);
         // digestOids.put(PKCSObjectIdentifiers.md2WithRSAEncryption, PKCSObjectIdentifiers.md2);
         // digestOids.put(PKCSObjectIdentifiers.md4WithRSAEncryption, PKCSObjectIdentifiers.md4);
         // END Android-removed: Unsupported algorithms
@@ -69,6 +79,10 @@
         digestOids.put(BSIObjectIdentifiers.ecdsa_plain_SHA256, NISTObjectIdentifiers.id_sha256);
         digestOids.put(BSIObjectIdentifiers.ecdsa_plain_SHA384, NISTObjectIdentifiers.id_sha384);
         digestOids.put(BSIObjectIdentifiers.ecdsa_plain_SHA512, NISTObjectIdentifiers.id_sha512);
+        digestOids.put(BSIObjectIdentifiers.ecdsa_plain_SHA3_224, NISTObjectIdentifiers.id_sha3_224);
+        digestOids.put(BSIObjectIdentifiers.ecdsa_plain_SHA3_256, NISTObjectIdentifiers.id_sha3_256);
+        digestOids.put(BSIObjectIdentifiers.ecdsa_plain_SHA3_384, NISTObjectIdentifiers.id_sha3_384);
+        digestOids.put(BSIObjectIdentifiers.ecdsa_plain_SHA3_512, NISTObjectIdentifiers.id_sha3_512);
         digestOids.put(BSIObjectIdentifiers.ecdsa_plain_RIPEMD160, TeleTrusTObjectIdentifiers.ripemd160);
 
         digestOids.put(EACObjectIdentifiers.id_TA_ECDSA_SHA_1, OIWObjectIdentifiers.idSHA1);
@@ -118,6 +132,54 @@
         digestOids.put(GMObjectIdentifiers.sm2sign_with_sha384, NISTObjectIdentifiers.id_sha384);
         digestOids.put(GMObjectIdentifiers.sm2sign_with_sha512, NISTObjectIdentifiers.id_sha512);
         digestOids.put(GMObjectIdentifiers.sm2sign_with_sm3, GMObjectIdentifiers.sm3);
+
+        digestOids.put(BCObjectIdentifiers.sphincsPlus_sha2_128s_r3, NISTObjectIdentifiers.id_sha256);
+        digestOids.put(BCObjectIdentifiers.sphincsPlus_sha2_128f_r3, NISTObjectIdentifiers.id_sha256);
+        digestOids.put(BCObjectIdentifiers.sphincsPlus_shake_128s_r3, NISTObjectIdentifiers.id_shake256);
+        digestOids.put(BCObjectIdentifiers.sphincsPlus_shake_128f_r3, NISTObjectIdentifiers.id_shake256);
+        digestOids.put(BCObjectIdentifiers.sphincsPlus_sha2_192s_r3, NISTObjectIdentifiers.id_sha256);
+        digestOids.put(BCObjectIdentifiers.sphincsPlus_sha2_192f_r3, NISTObjectIdentifiers.id_sha256);
+        digestOids.put(BCObjectIdentifiers.sphincsPlus_shake_192s_r3, NISTObjectIdentifiers.id_shake256);
+        digestOids.put(BCObjectIdentifiers.sphincsPlus_shake_192f_r3, NISTObjectIdentifiers.id_shake256);
+        digestOids.put(BCObjectIdentifiers.sphincsPlus_sha2_256s_r3, NISTObjectIdentifiers.id_sha256);
+        digestOids.put(BCObjectIdentifiers.sphincsPlus_sha2_256f_r3, NISTObjectIdentifiers.id_sha256);
+        digestOids.put(BCObjectIdentifiers.sphincsPlus_shake_256s_r3, NISTObjectIdentifiers.id_shake256);
+        digestOids.put(BCObjectIdentifiers.sphincsPlus_shake_256f_r3, NISTObjectIdentifiers.id_shake256);
+
+        digestOids.put(BCObjectIdentifiers.sphincsPlus_sha2_128s_r3_simple, NISTObjectIdentifiers.id_sha256);
+        digestOids.put(BCObjectIdentifiers.sphincsPlus_sha2_128f_r3_simple, NISTObjectIdentifiers.id_sha256);
+        digestOids.put(BCObjectIdentifiers.sphincsPlus_shake_128s_r3_simple, NISTObjectIdentifiers.id_shake256);
+        digestOids.put(BCObjectIdentifiers.sphincsPlus_shake_128f_r3_simple, NISTObjectIdentifiers.id_shake256);
+        digestOids.put(BCObjectIdentifiers.sphincsPlus_sha2_192s_r3_simple, NISTObjectIdentifiers.id_sha256);
+        digestOids.put(BCObjectIdentifiers.sphincsPlus_sha2_192f_r3_simple, NISTObjectIdentifiers.id_sha256);
+        digestOids.put(BCObjectIdentifiers.sphincsPlus_shake_192s_r3_simple, NISTObjectIdentifiers.id_shake256);
+        digestOids.put(BCObjectIdentifiers.sphincsPlus_shake_192f_r3_simple, NISTObjectIdentifiers.id_shake256);
+        digestOids.put(BCObjectIdentifiers.sphincsPlus_sha2_256s_r3_simple, NISTObjectIdentifiers.id_sha256);
+        digestOids.put(BCObjectIdentifiers.sphincsPlus_sha2_256f_r3_simple, NISTObjectIdentifiers.id_sha256);
+        digestOids.put(BCObjectIdentifiers.sphincsPlus_shake_256s_r3_simple, NISTObjectIdentifiers.id_shake256);
+        digestOids.put(BCObjectIdentifiers.sphincsPlus_shake_256f_r3_simple, NISTObjectIdentifiers.id_shake256);
+
+        digestOids.put(BCObjectIdentifiers.falcon, NISTObjectIdentifiers.id_shake256);
+        digestOids.put(BCObjectIdentifiers.falcon_512, NISTObjectIdentifiers.id_shake256);
+        digestOids.put(BCObjectIdentifiers.falcon_1024, NISTObjectIdentifiers.id_shake256);
+
+        digestOids.put(BCObjectIdentifiers.picnic_signature, NISTObjectIdentifiers.id_shake256);
+        digestOids.put(BCObjectIdentifiers.picnic_with_sha512, NISTObjectIdentifiers.id_sha512);
+        digestOids.put(BCObjectIdentifiers.picnic_with_sha3_512, NISTObjectIdentifiers.id_sha3_512);
+        digestOids.put(BCObjectIdentifiers.picnic_with_shake256, NISTObjectIdentifiers.id_shake256);
+
+//        digestOids.put(GMObjectIdentifiers.sm2sign_with_rmd160, TeleTrusTObjectIdentifiers.ripemd160);
+//        digestOids.put(GMObjectIdentifiers.sm2sign_with_sha1, OIWObjectIdentifiers.idSHA1);
+//        digestOids.put(GMObjectIdentifiers.sm2sign_with_sha224, NISTObjectIdentifiers.id_sha224);
+        digestOids.put(GMObjectIdentifiers.sm2sign_with_sha256, NISTObjectIdentifiers.id_sha256);
+//        digestOids.put(GMObjectIdentifiers.sm2sign_with_sha384, NISTObjectIdentifiers.id_sha384);
+//        digestOids.put(GMObjectIdentifiers.sm2sign_with_sha512, NISTObjectIdentifiers.id_sha512);
+        digestOids.put(GMObjectIdentifiers.sm2sign_with_sm3, GMObjectIdentifiers.sm3);
+
+        digestOids.put(CMSObjectIdentifiers.id_RSASSA_PSS_SHAKE128, NISTObjectIdentifiers.id_shake128);
+        digestOids.put(CMSObjectIdentifiers.id_RSASSA_PSS_SHAKE256, NISTObjectIdentifiers.id_shake256);
+        digestOids.put(CMSObjectIdentifiers.id_ecdsa_with_shake128, NISTObjectIdentifiers.id_shake128);
+        digestOids.put(CMSObjectIdentifiers.id_ecdsa_with_shake256, NISTObjectIdentifiers.id_shake256);
         */
         // END Android-removed: Unsupported algorithms
 
@@ -144,6 +206,8 @@
         digestNameToOids.put("SHA3-384", NISTObjectIdentifiers.id_sha3_384);
         digestNameToOids.put("SHA3-512", NISTObjectIdentifiers.id_sha3_512);
 
+        digestNameToOids.put("SHAKE128", NISTObjectIdentifiers.id_shake128);
+        digestNameToOids.put("SHAKE256", NISTObjectIdentifiers.id_shake256);
         digestNameToOids.put("SHAKE-128", NISTObjectIdentifiers.id_shake128);
         digestNameToOids.put("SHAKE-256", NISTObjectIdentifiers.id_shake256);
 
@@ -164,20 +228,106 @@
         digestNameToOids.put("RIPEMD256", TeleTrusTObjectIdentifiers.ripemd256);
 
         digestNameToOids.put("SM3", GMObjectIdentifiers.sm3);
+
+        // IETF RFC 3370
+        addDigestAlgId(OIWObjectIdentifiers.idSHA1, true);
+        // IETF RFC 5754
+        addDigestAlgId(NISTObjectIdentifiers.id_sha224, false);
+        addDigestAlgId(NISTObjectIdentifiers.id_sha256, false);
+        addDigestAlgId(NISTObjectIdentifiers.id_sha384, false);
+        addDigestAlgId(NISTObjectIdentifiers.id_sha512, false);
+        addDigestAlgId(NISTObjectIdentifiers.id_sha512_224, false);
+        addDigestAlgId(NISTObjectIdentifiers.id_sha512_256, false);
+
+        // NIST CSOR
+        addDigestAlgId(NISTObjectIdentifiers.id_sha3_224, false);
+        addDigestAlgId(NISTObjectIdentifiers.id_sha3_256, false);
+        addDigestAlgId(NISTObjectIdentifiers.id_sha3_384, false);
+        addDigestAlgId(NISTObjectIdentifiers.id_sha3_512, false);
+
+        // RFC 8702
+        addDigestAlgId(NISTObjectIdentifiers.id_shake128, false);
+        addDigestAlgId(NISTObjectIdentifiers.id_shake256, false);
+
+        // RFC 4357
+        addDigestAlgId(CryptoProObjectIdentifiers.gostR3411, true);
+
+        // draft-deremin-rfc4491
+        addDigestAlgId(RosstandartObjectIdentifiers.id_tc26_gost_3411_12_256, false);
+        addDigestAlgId(RosstandartObjectIdentifiers.id_tc26_gost_3411_12_512, false);
+
+        // IETF RFC 1319
+        addDigestAlgId(PKCSObjectIdentifiers.md2, true);
+        // IETF RFC 1320
+        addDigestAlgId(PKCSObjectIdentifiers.md4, true);
+        // IETF RFC 1321
+        addDigestAlgId(PKCSObjectIdentifiers.md5, true);
+
+        // found no standard which specified the handle of AlgorithmIdentifier.parameters,
+        // so let it as before.
+        addDigestAlgId(TeleTrusTObjectIdentifiers.ripemd128, true);
+        addDigestAlgId(TeleTrusTObjectIdentifiers.ripemd160, true);
+        addDigestAlgId(TeleTrusTObjectIdentifiers.ripemd256, true);
+
+        shake256oids.add(EdECObjectIdentifiers.id_Ed448);
+
+        shake256oids.add(BCObjectIdentifiers.dilithium2);
+        shake256oids.add(BCObjectIdentifiers.dilithium3);
+        shake256oids.add(BCObjectIdentifiers.dilithium5);
+        shake256oids.add(BCObjectIdentifiers.dilithium2_aes);
+        shake256oids.add(BCObjectIdentifiers.dilithium3_aes);
+        shake256oids.add(BCObjectIdentifiers.dilithium5_aes);
+
+        shake256oids.add(BCObjectIdentifiers.falcon_512);
+        shake256oids.add(BCObjectIdentifiers.falcon_1024);
         */
         // END Android-removed: Unsupported algorithms
     }
 
+    private static void addDigestAlgId(ASN1ObjectIdentifier oid, boolean withNullParams)
+    {
+        AlgorithmIdentifier algId;
+        if (withNullParams)
+        {
+            algId = new AlgorithmIdentifier(oid, DERNull.INSTANCE);
+        }
+        else
+        {
+            algId = new AlgorithmIdentifier(oid);
+        }
+        digestOidToAlgIds.put(oid, algId);
+    }
+
     public AlgorithmIdentifier find(AlgorithmIdentifier sigAlgId)
     {
-        AlgorithmIdentifier digAlgId;
+        ASN1ObjectIdentifier sigAlgOid = sigAlgId.getAlgorithm();
 
-        if (sigAlgId.getAlgorithm().equals(PKCSObjectIdentifiers.id_RSASSA_PSS))
+        if (shake256oids.contains(sigAlgOid))
         {
-            digAlgId = RSASSAPSSparams.getInstance(sigAlgId.getParameters()).getHashAlgorithm();
+            // TODO: it seems it's very hard to find people accepting SHAKE256-len at the moment...
+            // Android-removed: unsupported
+            // if (!sigAlgOid.equals(EdECObjectIdentifiers.id_Ed448))
+            // {
+            //     return new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha256);
+            // }
+
+            return new AlgorithmIdentifier(NISTObjectIdentifiers.id_shake256_len, new ASN1Integer(512));
+        }
+
+        ASN1ObjectIdentifier digAlgOid;
+        if (sigAlgOid.equals(PKCSObjectIdentifiers.id_RSASSA_PSS))
+        {
+            digAlgOid = RSASSAPSSparams.getInstance(sigAlgId.getParameters()).getHashAlgorithm().getAlgorithm();
+        }
+        else if (sigAlgOid.equals(PKCSObjectIdentifiers.id_alg_hss_lms_hashsig))
+        {
+            digAlgOid = NISTObjectIdentifiers.id_sha256;
         }
         // BEGIN Android-removed: Unsupported algorithms
-        /*
+        /*        // else if (sigAlgOid.equals(EdECObjectIdentifiers.id_Ed25519))
+        {
+            digAlgOid = NISTObjectIdentifiers.id_sha512;
+        }
         else if (sigAlgId.getAlgorithm().equals(EdECObjectIdentifiers.id_Ed25519))
         {
             digAlgId = new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha512);
@@ -190,14 +340,47 @@
         // END Android-removed: Unsupported algorithms
         else
         {
-            digAlgId = new AlgorithmIdentifier((ASN1ObjectIdentifier)digestOids.get(sigAlgId.getAlgorithm()), DERNull.INSTANCE);
+            digAlgOid = (ASN1ObjectIdentifier)digestOids.get(sigAlgOid);
         }
 
-        return digAlgId;
+        return find(digAlgOid);
+    }
+
+    public AlgorithmIdentifier find(ASN1ObjectIdentifier digAlgOid)
+    {
+        if (digAlgOid == null)
+        {
+            throw new NullPointerException("digest OID is null");
+        }
+
+        AlgorithmIdentifier digAlgId = (AlgorithmIdentifier)digestOidToAlgIds.get(digAlgOid);
+        if (digAlgId == null)
+        {
+            return new AlgorithmIdentifier(digAlgOid);
+        }
+        else
+        {
+            return digAlgId;
+        }
     }
 
     public AlgorithmIdentifier find(String digAlgName)
     {
-        return new AlgorithmIdentifier((ASN1ObjectIdentifier)digestNameToOids.get(digAlgName), DERNull.INSTANCE);
+        ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier)digestNameToOids.get(digAlgName);
+        if (oid != null)
+        {
+            return find(oid);
+        }
+
+        try
+        {
+            return find(new ASN1ObjectIdentifier(digAlgName));
+        }
+        catch (RuntimeException e)
+        {
+            // ignore - tried it but it didn't work...
+        }
+
+        return null;
     }
-}
\ No newline at end of file
+}
diff --git a/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/operator/DefaultMacAlgorithmIdentifierFinder.java b/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/operator/DefaultMacAlgorithmIdentifierFinder.java
new file mode 100644
index 0000000..3a2dcff
--- /dev/null
+++ b/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/operator/DefaultMacAlgorithmIdentifierFinder.java
@@ -0,0 +1,42 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.operator;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import com.android.internal.org.bouncycastle.asn1.DERNull;
+import com.android.internal.org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
+import com.android.internal.org.bouncycastle.asn1.oiw.OIWObjectIdentifiers;
+import com.android.internal.org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import com.android.internal.org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import com.android.internal.org.bouncycastle.util.Strings;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+public class DefaultMacAlgorithmIdentifierFinder
+    implements MacAlgorithmIdentifierFinder
+{
+    private static Map macNameToAlgIds = new HashMap();
+
+    static
+    {
+        macNameToAlgIds.put("HMACSHA1", new AlgorithmIdentifier(OIWObjectIdentifiers.idSHA1));
+        macNameToAlgIds.put("HMACSHA224", new AlgorithmIdentifier(PKCSObjectIdentifiers.id_hmacWithSHA224, DERNull.INSTANCE));
+        macNameToAlgIds.put("HMACSHA256", new AlgorithmIdentifier(PKCSObjectIdentifiers.id_hmacWithSHA256, DERNull.INSTANCE));
+        macNameToAlgIds.put("HMACSHA384", new AlgorithmIdentifier(PKCSObjectIdentifiers.id_hmacWithSHA384, DERNull.INSTANCE));
+        macNameToAlgIds.put("HMACSHA512", new AlgorithmIdentifier(PKCSObjectIdentifiers.id_hmacWithSHA512, DERNull.INSTANCE));
+        macNameToAlgIds.put("HMACSHA512-224", new AlgorithmIdentifier(PKCSObjectIdentifiers.id_hmacWithSHA512_224, DERNull.INSTANCE));
+        macNameToAlgIds.put("HMACSHA512-256", new AlgorithmIdentifier(PKCSObjectIdentifiers.id_hmacWithSHA512_256, DERNull.INSTANCE));
+
+        macNameToAlgIds.put("HMACSHA3-224", new AlgorithmIdentifier(NISTObjectIdentifiers.id_hmacWithSHA3_224));
+        macNameToAlgIds.put("HMACSHA3-256", new AlgorithmIdentifier(NISTObjectIdentifiers.id_hmacWithSHA3_256));
+        macNameToAlgIds.put("HMACSHA3-384", new AlgorithmIdentifier(NISTObjectIdentifiers.id_hmacWithSHA3_384));
+        macNameToAlgIds.put("HMACSHA3-512", new AlgorithmIdentifier(NISTObjectIdentifiers.id_hmacWithSHA3_512));
+    }
+
+    public AlgorithmIdentifier find(String macAlgName)
+    {
+        return (AlgorithmIdentifier)macNameToAlgIds.get(Strings.toUpperCase(macAlgName));
+    }
+}
\ No newline at end of file
diff --git a/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/operator/DefaultSignatureAlgorithmIdentifierFinder.java b/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/operator/DefaultSignatureAlgorithmIdentifierFinder.java
index 0ed6507..f7562d8 100644
--- a/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/operator/DefaultSignatureAlgorithmIdentifierFinder.java
+++ b/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/operator/DefaultSignatureAlgorithmIdentifierFinder.java
@@ -13,11 +13,13 @@
 // Android-removed: Unsupported algorithms
 // import org.bouncycastle.asn1.bc.BCObjectIdentifiers;
 // import org.bouncycastle.asn1.bsi.BSIObjectIdentifiers;
+// import org.bouncycastle.asn1.cms.CMSObjectIdentifiers;
 // import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers;
 // import org.bouncycastle.asn1.eac.EACObjectIdentifiers;
 // import org.bouncycastle.asn1.gm.GMObjectIdentifiers;
 // import org.bouncycastle.asn1.edec.EdECObjectIdentifiers;
 // import org.bouncycastle.asn1.isara.IsaraObjectIdentifiers;
+// import org.bouncycastle.asn1.misc.MiscObjectIdentifiers;
 import com.android.internal.org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
 import com.android.internal.org.bouncycastle.asn1.oiw.OIWObjectIdentifiers;
 import com.android.internal.org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
@@ -54,6 +56,7 @@
     static
     {
         // BEGIN Android-removed: Unsupported algorithms
+        // algorithms.put("COMPOSITE", MiscObjectIdentifiers.id_alg_composite);
         // algorithms.put("MD2WITHRSAENCRYPTION", PKCSObjectIdentifiers.md2WithRSAEncryption);
         // algorithms.put("MD2WITHRSA", PKCSObjectIdentifiers.md2WithRSAEncryption);
         // END Android-removed: Unsupported algorithms
@@ -69,6 +72,10 @@
         algorithms.put("SHA384WITHRSA", PKCSObjectIdentifiers.sha384WithRSAEncryption);
         algorithms.put("SHA512WITHRSAENCRYPTION", PKCSObjectIdentifiers.sha512WithRSAEncryption);
         algorithms.put("SHA512WITHRSA", PKCSObjectIdentifiers.sha512WithRSAEncryption);
+        algorithms.put("SHA512(224)WITHRSAENCRYPTION", PKCSObjectIdentifiers.sha512_224WithRSAEncryption);
+        algorithms.put("SHA512(224)WITHRSA", PKCSObjectIdentifiers.sha512_224WithRSAEncryption);
+        algorithms.put("SHA512(256)WITHRSAENCRYPTION", PKCSObjectIdentifiers.sha512_256WithRSAEncryption);
+        algorithms.put("SHA512(256)WITHRSA", PKCSObjectIdentifiers.sha512_256WithRSAEncryption);
         algorithms.put("SHA1WITHRSAANDMGF1", PKCSObjectIdentifiers.id_RSASSA_PSS);
         algorithms.put("SHA224WITHRSAANDMGF1", PKCSObjectIdentifiers.id_RSASSA_PSS);
         algorithms.put("SHA256WITHRSAANDMGF1", PKCSObjectIdentifiers.id_RSASSA_PSS);
@@ -136,12 +143,7 @@
         algorithms.put("GOST3411-2012-512WITHECGOST3410-2012-512", RosstandartObjectIdentifiers.id_tc26_signwithdigest_gost_3410_12_512);
         algorithms.put("GOST3411-2012-256WITHGOST3410-2012-256", RosstandartObjectIdentifiers.id_tc26_signwithdigest_gost_3410_12_256);
         algorithms.put("GOST3411-2012-512WITHGOST3410-2012-512", RosstandartObjectIdentifiers.id_tc26_signwithdigest_gost_3410_12_512);
-        algorithms.put("SHA1WITHPLAIN-ECDSA", BSIObjectIdentifiers.ecdsa_plain_SHA1);
-        algorithms.put("SHA224WITHPLAIN-ECDSA", BSIObjectIdentifiers.ecdsa_plain_SHA224);
-        algorithms.put("SHA256WITHPLAIN-ECDSA", BSIObjectIdentifiers.ecdsa_plain_SHA256);
-        algorithms.put("SHA384WITHPLAIN-ECDSA", BSIObjectIdentifiers.ecdsa_plain_SHA384);
-        algorithms.put("SHA512WITHPLAIN-ECDSA", BSIObjectIdentifiers.ecdsa_plain_SHA512);
-        algorithms.put("RIPEMD160WITHPLAIN-ECDSA", BSIObjectIdentifiers.ecdsa_plain_RIPEMD160);
+
         algorithms.put("SHA1WITHCVC-ECDSA", EACObjectIdentifiers.id_TA_ECDSA_SHA_1);
         algorithms.put("SHA224WITHCVC-ECDSA", EACObjectIdentifiers.id_TA_ECDSA_SHA_224);
         algorithms.put("SHA256WITHCVC-ECDSA", EACObjectIdentifiers.id_TA_ECDSA_SHA_256);
@@ -159,12 +161,33 @@
         algorithms.put("SHA256WITHSM2", GMObjectIdentifiers.sm2sign_with_sha256);
         algorithms.put("SHA384WITHSM2", GMObjectIdentifiers.sm2sign_with_sha384);
         algorithms.put("SHA512WITHSM2", GMObjectIdentifiers.sm2sign_with_sha512);
+        algorithms.put("SHA1WITHPLAIN-ECDSA", BSIObjectIdentifiers.ecdsa_plain_SHA1);
+        algorithms.put("RIPEMD160WITHPLAIN-ECDSA", BSIObjectIdentifiers.ecdsa_plain_RIPEMD160);
+        algorithms.put("SHA224WITHPLAIN-ECDSA", BSIObjectIdentifiers.ecdsa_plain_SHA224);
+        algorithms.put("SHA256WITHPLAIN-ECDSA", BSIObjectIdentifiers.ecdsa_plain_SHA256);
+        algorithms.put("SHA384WITHPLAIN-ECDSA", BSIObjectIdentifiers.ecdsa_plain_SHA384);
+        algorithms.put("SHA512WITHPLAIN-ECDSA", BSIObjectIdentifiers.ecdsa_plain_SHA512);
+        algorithms.put("SHA3-224WITHPLAIN-ECDSA", BSIObjectIdentifiers.ecdsa_plain_SHA3_224);
+        algorithms.put("SHA3-256WITHPLAIN-ECDSA", BSIObjectIdentifiers.ecdsa_plain_SHA3_256);
+        algorithms.put("SHA3-384WITHPLAIN-ECDSA", BSIObjectIdentifiers.ecdsa_plain_SHA3_384);
+        algorithms.put("SHA3-512WITHPLAIN-ECDSA", BSIObjectIdentifiers.ecdsa_plain_SHA3_512);
+
+        // RFC 8702
+        algorithms.put("SHAKE128WITHRSAPSS", CMSObjectIdentifiers.id_RSASSA_PSS_SHAKE128);
+        algorithms.put("SHAKE256WITHRSAPSS", CMSObjectIdentifiers.id_RSASSA_PSS_SHAKE256);
+        algorithms.put("SHAKE128WITHRSASSA-PSS", CMSObjectIdentifiers.id_RSASSA_PSS_SHAKE128);
+        algorithms.put("SHAKE256WITHRSASSA-PSS", CMSObjectIdentifiers.id_RSASSA_PSS_SHAKE256);
+        algorithms.put("SHAKE128WITHECDSA", CMSObjectIdentifiers.id_ecdsa_with_shake128);
+        algorithms.put("SHAKE256WITHECDSA", CMSObjectIdentifiers.id_ecdsa_with_shake256);
+
         algorithms.put("SM3WITHSM2", GMObjectIdentifiers.sm2sign_with_sm3);
 
         algorithms.put("SHA256WITHXMSS", BCObjectIdentifiers.xmss_SHA256ph);
         algorithms.put("SHA512WITHXMSS", BCObjectIdentifiers.xmss_SHA512ph);
         algorithms.put("SHAKE128WITHXMSS", BCObjectIdentifiers.xmss_SHAKE128ph);
         algorithms.put("SHAKE256WITHXMSS", BCObjectIdentifiers.xmss_SHAKE256ph);
+        algorithms.put("SHAKE128(512)WITHXMSS", BCObjectIdentifiers.xmss_SHAKE128_512ph);
+        algorithms.put("SHAKE256(1024)WITHXMSS", BCObjectIdentifiers.xmss_SHAKE256_1024ph);
 
         algorithms.put("SHA256WITHXMSSMT", BCObjectIdentifiers.xmss_mt_SHA256ph);
         algorithms.put("SHA512WITHXMSSMT", BCObjectIdentifiers.xmss_mt_SHA512ph);
@@ -180,6 +203,8 @@
         algorithms.put("SHA512WITHXMSSMT-SHA512", BCObjectIdentifiers.xmss_mt_SHA512ph);
         algorithms.put("SHAKE128WITHXMSSMT-SHAKE128", BCObjectIdentifiers.xmss_mt_SHAKE128ph);
         algorithms.put("SHAKE256WITHXMSSMT-SHAKE256", BCObjectIdentifiers.xmss_mt_SHAKE256ph);
+        algorithms.put("SHAKE128(512)WITHXMSSMT-SHAKE128", BCObjectIdentifiers.xmss_mt_SHAKE128_512ph);
+        algorithms.put("SHAKE256(1024)WITHXMSSMT-SHAKE256", BCObjectIdentifiers.xmss_mt_SHAKE256_1024ph);
 
         algorithms.put("LMS", PKCSObjectIdentifiers.id_alg_hss_lms_hashsig);
 
@@ -197,6 +222,47 @@
 
         algorithms.put("QTESLA-P-I", BCObjectIdentifiers.qTESLA_p_I);
         algorithms.put("QTESLA-P-III", BCObjectIdentifiers.qTESLA_p_III);
+        algorithms.put("SPHINCS+", BCObjectIdentifiers.sphincsPlus);
+        algorithms.put("SPHINCSPLUS", BCObjectIdentifiers.sphincsPlus);
+        algorithms.put("SPHINCS+-SHA2-128S", BCObjectIdentifiers.sphincsPlus_sha2_128s);
+        algorithms.put("SPHINCS+-SHA2-128F", BCObjectIdentifiers.sphincsPlus_sha2_128f);
+        algorithms.put("SPHINCS+-SHA2-192S", BCObjectIdentifiers.sphincsPlus_sha2_192s);
+        algorithms.put("SPHINCS+-SHA2-192F", BCObjectIdentifiers.sphincsPlus_sha2_192f);
+        algorithms.put("SPHINCS+-SHA2-256S", BCObjectIdentifiers.sphincsPlus_sha2_256s);
+        algorithms.put("SPHINCS+-SHA2-256F", BCObjectIdentifiers.sphincsPlus_sha2_256f);
+        algorithms.put("SPHINCS+-SHAKE-128S", BCObjectIdentifiers.sphincsPlus_shake_128s);
+        algorithms.put("SPHINCS+-SHAKE-128F", BCObjectIdentifiers.sphincsPlus_shake_128f);
+        algorithms.put("SPHINCS+-SHAKE-192S", BCObjectIdentifiers.sphincsPlus_shake_192s);
+        algorithms.put("SPHINCS+-SHAKE-192F", BCObjectIdentifiers.sphincsPlus_shake_192f);
+        algorithms.put("SPHINCS+-SHAKE-256S", BCObjectIdentifiers.sphincsPlus_shake_256s);
+        algorithms.put("SPHINCS+-SHAKE-256F", BCObjectIdentifiers.sphincsPlus_shake_256f);
+        algorithms.put("SPHINCS+-HARAKA-128S-ROBUST", BCObjectIdentifiers.sphincsPlus_haraka_128s_r3);
+        algorithms.put("SPHINCS+-HARAKA-128F-ROBUST", BCObjectIdentifiers.sphincsPlus_haraka_128f_r3);
+        algorithms.put("SPHINCS+-HARAKA-192S-ROBUST", BCObjectIdentifiers.sphincsPlus_haraka_192s_r3);
+        algorithms.put("SPHINCS+-HARAKA-192F-ROBUST", BCObjectIdentifiers.sphincsPlus_haraka_192f_r3);
+        algorithms.put("SPHINCS+-HARAKA-256S-ROBUST", BCObjectIdentifiers.sphincsPlus_haraka_256s_r3);
+        algorithms.put("SPHINCS+-HARAKA-256F-ROBUST", BCObjectIdentifiers.sphincsPlus_haraka_256f_r3);
+        algorithms.put("SPHINCS+-HARAKA-128S-SIMPLE", BCObjectIdentifiers.sphincsPlus_haraka_128s_r3_simple);
+        algorithms.put("SPHINCS+-HARAKA-128F-SIMPLE", BCObjectIdentifiers.sphincsPlus_haraka_128f_r3_simple);
+        algorithms.put("SPHINCS+-HARAKA-192S-SIMPLE", BCObjectIdentifiers.sphincsPlus_haraka_192s_r3_simple);
+        algorithms.put("SPHINCS+-HARAKA-192F-SIMPLE", BCObjectIdentifiers.sphincsPlus_haraka_192f_r3_simple);
+        algorithms.put("SPHINCS+-HARAKA-256S-SIMPLE", BCObjectIdentifiers.sphincsPlus_haraka_256s_r3_simple);
+        algorithms.put("SPHINCS+-HARAKA-256F-SIMPLE", BCObjectIdentifiers.sphincsPlus_haraka_256f_r3_simple);
+        algorithms.put("SPHINCSPLUS", BCObjectIdentifiers.sphincsPlus);
+        algorithms.put("DILITHIUM2", BCObjectIdentifiers.dilithium2);
+        algorithms.put("DILITHIUM3", BCObjectIdentifiers.dilithium3);
+        algorithms.put("DILITHIUM5", BCObjectIdentifiers.dilithium5);
+        algorithms.put("DILITHIUM2-AES", BCObjectIdentifiers.dilithium2_aes);
+        algorithms.put("DILITHIUM3-AES", BCObjectIdentifiers.dilithium3_aes);
+        algorithms.put("DILITHIUM5-AES", BCObjectIdentifiers.dilithium5_aes);
+
+        algorithms.put("FALCON-512", BCObjectIdentifiers.falcon_512);
+        algorithms.put("FALCON-1024", BCObjectIdentifiers.falcon_1024);
+
+        algorithms.put("PICNIC", BCObjectIdentifiers.picnic_signature);
+        algorithms.put("SHA512WITHPICNIC", BCObjectIdentifiers.picnic_with_sha512);
+        algorithms.put("SHA3-512WITHPICNIC", BCObjectIdentifiers.picnic_with_sha3_512);
+        algorithms.put("SHAKE256WITHPICNIC", BCObjectIdentifiers.picnic_with_shake256);
         */
         // END Android-removed: Unsupported algorithms
 
@@ -228,6 +294,15 @@
         noParams.add(NISTObjectIdentifiers.id_ecdsa_with_sha3_384);
         noParams.add(NISTObjectIdentifiers.id_ecdsa_with_sha3_512);
 
+        noParams.add(BSIObjectIdentifiers.ecdsa_plain_SHA224);
+        noParams.add(BSIObjectIdentifiers.ecdsa_plain_SHA256);
+        noParams.add(BSIObjectIdentifiers.ecdsa_plain_SHA384);
+        noParams.add(BSIObjectIdentifiers.ecdsa_plain_SHA512);
+        noParams.add(BSIObjectIdentifiers.ecdsa_plain_SHA3_224);
+        noParams.add(BSIObjectIdentifiers.ecdsa_plain_SHA3_256);
+        noParams.add(BSIObjectIdentifiers.ecdsa_plain_SHA3_384);
+        noParams.add(BSIObjectIdentifiers.ecdsa_plain_SHA3_512);
+
         //
         // RFC 4491
         //
@@ -243,6 +318,67 @@
         noParams.add(BCObjectIdentifiers.sphincs256_with_SHA3_512);
 
         //
+        // SPHINCS-PLUS
+        //
+        noParams.add(BCObjectIdentifiers.sphincsPlus);
+        noParams.add(BCObjectIdentifiers.sphincsPlus_sha2_128s_r3);
+        noParams.add(BCObjectIdentifiers.sphincsPlus_sha2_128f_r3);
+        noParams.add(BCObjectIdentifiers.sphincsPlus_shake_128s_r3);
+        noParams.add(BCObjectIdentifiers.sphincsPlus_shake_128f_r3);
+        noParams.add(BCObjectIdentifiers.sphincsPlus_haraka_128s_r3);
+        noParams.add(BCObjectIdentifiers.sphincsPlus_haraka_128f_r3);
+        noParams.add(BCObjectIdentifiers.sphincsPlus_sha2_192s_r3);
+        noParams.add(BCObjectIdentifiers.sphincsPlus_sha2_192f_r3);
+        noParams.add(BCObjectIdentifiers.sphincsPlus_shake_192s_r3);
+        noParams.add(BCObjectIdentifiers.sphincsPlus_shake_192f_r3);
+        noParams.add(BCObjectIdentifiers.sphincsPlus_haraka_192s_r3);
+        noParams.add(BCObjectIdentifiers.sphincsPlus_haraka_192f_r3);
+        noParams.add(BCObjectIdentifiers.sphincsPlus_sha2_256s_r3);
+        noParams.add(BCObjectIdentifiers.sphincsPlus_sha2_256f_r3);
+        noParams.add(BCObjectIdentifiers.sphincsPlus_shake_256s_r3);
+        noParams.add(BCObjectIdentifiers.sphincsPlus_shake_256f_r3);
+        noParams.add(BCObjectIdentifiers.sphincsPlus_haraka_256s_r3);
+        noParams.add(BCObjectIdentifiers.sphincsPlus_haraka_256f_r3);
+        noParams.add(BCObjectIdentifiers.sphincsPlus_sha2_128s);
+        noParams.add(BCObjectIdentifiers.sphincsPlus_sha2_128f);
+        noParams.add(BCObjectIdentifiers.sphincsPlus_shake_128s);
+        noParams.add(BCObjectIdentifiers.sphincsPlus_shake_128f);
+        noParams.add(BCObjectIdentifiers.sphincsPlus_sha2_192s);
+        noParams.add(BCObjectIdentifiers.sphincsPlus_sha2_192f);
+        noParams.add(BCObjectIdentifiers.sphincsPlus_shake_192s);
+        noParams.add(BCObjectIdentifiers.sphincsPlus_shake_192f);
+        noParams.add(BCObjectIdentifiers.sphincsPlus_sha2_256s);
+        noParams.add(BCObjectIdentifiers.sphincsPlus_sha2_256f);
+        noParams.add(BCObjectIdentifiers.sphincsPlus_shake_256s);
+        noParams.add(BCObjectIdentifiers.sphincsPlus_shake_256f);
+
+        //
+        // Dilithium
+        //
+        noParams.add(BCObjectIdentifiers.dilithium);
+        noParams.add(BCObjectIdentifiers.dilithium2);
+        noParams.add(BCObjectIdentifiers.dilithium3);
+        noParams.add(BCObjectIdentifiers.dilithium5);
+        noParams.add(BCObjectIdentifiers.dilithium2_aes);
+        noParams.add(BCObjectIdentifiers.dilithium3_aes);
+        noParams.add(BCObjectIdentifiers.dilithium5_aes);
+
+        //
+        // Falcon
+        //
+        noParams.add(BCObjectIdentifiers.falcon);
+        noParams.add(BCObjectIdentifiers.falcon_512);
+        noParams.add(BCObjectIdentifiers.falcon_1024);
+
+        //
+        // Picnic
+        //
+        noParams.add(BCObjectIdentifiers.picnic_signature);
+        noParams.add(BCObjectIdentifiers.picnic_with_sha512);
+        noParams.add(BCObjectIdentifiers.picnic_with_sha3_512);
+        noParams.add(BCObjectIdentifiers.picnic_with_shake256);
+
+        //
         // XMSS
         //
         noParams.add(BCObjectIdentifiers.xmss_SHA256ph);
@@ -253,6 +389,8 @@
         noParams.add(BCObjectIdentifiers.xmss_mt_SHA512ph);
         noParams.add(BCObjectIdentifiers.xmss_mt_SHAKE128ph);
         noParams.add(BCObjectIdentifiers.xmss_mt_SHAKE256ph);
+        noParams.add(BCObjectIdentifiers.xmss_mt_SHAKE128ph);
+        noParams.add(BCObjectIdentifiers.xmss_mt_SHAKE256ph);
 
         noParams.add(BCObjectIdentifiers.xmss_SHA256);
         noParams.add(BCObjectIdentifiers.xmss_SHA512);
@@ -286,6 +424,16 @@
         noParams.add(EdECObjectIdentifiers.id_Ed25519);
         noParams.add(EdECObjectIdentifiers.id_Ed448);
         */
+
+        // EdDSA
+        // noParams.add(EdECObjectIdentifiers.id_Ed25519);
+        // noParams.add(EdECObjectIdentifiers.id_Ed448);
+
+        // RFC 8702
+        // noParams.add(CMSObjectIdentifiers.id_RSASSA_PSS_SHAKE128);
+        // noParams.add(CMSObjectIdentifiers.id_RSASSA_PSS_SHAKE256);
+        // noParams.add(CMSObjectIdentifiers.id_ecdsa_with_shake128);
+        // noParams.add(CMSObjectIdentifiers.id_ecdsa_with_shake256);
         // END Android-removed: Unsupported algorithms
 
         //
@@ -298,6 +446,8 @@
         pkcs15RsaEncryption.add(PKCSObjectIdentifiers.sha512WithRSAEncryption);
         // BEGIN Android-removed: Unsupported algorithms
         /*
+        pkcs15RsaEncryption.add(PKCSObjectIdentifiers.sha512_224WithRSAEncryption);
+        pkcs15RsaEncryption.add(PKCSObjectIdentifiers.sha512_256WithRSAEncryption);
         pkcs15RsaEncryption.add(TeleTrusTObjectIdentifiers.rsaSignatureWithripemd128);
         pkcs15RsaEncryption.add(TeleTrusTObjectIdentifiers.rsaSignatureWithripemd160);
         pkcs15RsaEncryption.add(TeleTrusTObjectIdentifiers.rsaSignatureWithripemd256);
@@ -361,6 +511,8 @@
         // digestOids.put(TeleTrusTObjectIdentifiers.rsaSignatureWithripemd256, TeleTrusTObjectIdentifiers.ripemd256);
         // digestOids.put(CryptoProObjectIdentifiers.gostR3411_94_with_gostR3410_94, CryptoProObjectIdentifiers.gostR3411);
         // digestOids.put(CryptoProObjectIdentifiers.gostR3411_94_with_gostR3410_2001, CryptoProObjectIdentifiers.gostR3411);
+        // digestOids.put(PKCSObjectIdentifiers.sha512_224WithRSAEncryption, NISTObjectIdentifiers.id_sha512_224);
+        // digestOids.put(PKCSObjectIdentifiers.sha512_256WithRSAEncryption, NISTObjectIdentifiers.id_sha512_256);
         // END Android-removed: Unsupported algorithms
         digestOids.put(NISTObjectIdentifiers.dsa_with_sha224, NISTObjectIdentifiers.id_sha224);
         digestOids.put(NISTObjectIdentifiers.dsa_with_sha256, NISTObjectIdentifiers.id_sha256);
@@ -404,37 +556,54 @@
         digestOids.put(GMObjectIdentifiers.sm2sign_with_sha384, NISTObjectIdentifiers.id_sha384);
         digestOids.put(GMObjectIdentifiers.sm2sign_with_sha512, NISTObjectIdentifiers.id_sha512);
         digestOids.put(GMObjectIdentifiers.sm2sign_with_sm3, GMObjectIdentifiers.sm3);
+
+        digestOids.put(BCObjectIdentifiers.sphincsPlus_sha2_128s_r3, NISTObjectIdentifiers.id_sha256);
+        digestOids.put(BCObjectIdentifiers.sphincsPlus_sha2_128f_r3, NISTObjectIdentifiers.id_sha256);
+        digestOids.put(BCObjectIdentifiers.sphincsPlus_shake_128s_r3, NISTObjectIdentifiers.id_shake256);
+        digestOids.put(BCObjectIdentifiers.sphincsPlus_shake_128f_r3, NISTObjectIdentifiers.id_shake256);
+        digestOids.put(BCObjectIdentifiers.sphincsPlus_sha2_192s_r3, NISTObjectIdentifiers.id_sha256);
+        digestOids.put(BCObjectIdentifiers.sphincsPlus_sha2_192f_r3, NISTObjectIdentifiers.id_sha256);
+        digestOids.put(BCObjectIdentifiers.sphincsPlus_shake_192s_r3, NISTObjectIdentifiers.id_shake256);
+        digestOids.put(BCObjectIdentifiers.sphincsPlus_shake_192f_r3, NISTObjectIdentifiers.id_shake256);
+        digestOids.put(BCObjectIdentifiers.sphincsPlus_sha2_256s_r3, NISTObjectIdentifiers.id_sha256);
+        digestOids.put(BCObjectIdentifiers.sphincsPlus_sha2_256f_r3, NISTObjectIdentifiers.id_sha256);
+        digestOids.put(BCObjectIdentifiers.sphincsPlus_shake_256s_r3, NISTObjectIdentifiers.id_shake256);
+        digestOids.put(BCObjectIdentifiers.sphincsPlus_shake_256f_r3, NISTObjectIdentifiers.id_shake256);
+
+        digestOids.put(BCObjectIdentifiers.sphincsPlus_sha2_128s_r3_simple, NISTObjectIdentifiers.id_sha256);
+        digestOids.put(BCObjectIdentifiers.sphincsPlus_sha2_128f_r3_simple, NISTObjectIdentifiers.id_sha256);
+        digestOids.put(BCObjectIdentifiers.sphincsPlus_shake_128s_r3_simple, NISTObjectIdentifiers.id_shake256);
+        digestOids.put(BCObjectIdentifiers.sphincsPlus_shake_128f_r3_simple, NISTObjectIdentifiers.id_shake256);
+        digestOids.put(BCObjectIdentifiers.sphincsPlus_sha2_192s_r3_simple, NISTObjectIdentifiers.id_sha256);
+        digestOids.put(BCObjectIdentifiers.sphincsPlus_sha2_192f_r3_simple, NISTObjectIdentifiers.id_sha256);
+        digestOids.put(BCObjectIdentifiers.sphincsPlus_shake_192s_r3_simple, NISTObjectIdentifiers.id_shake256);
+        digestOids.put(BCObjectIdentifiers.sphincsPlus_shake_192f_r3_simple, NISTObjectIdentifiers.id_shake256);
+        digestOids.put(BCObjectIdentifiers.sphincsPlus_sha2_256s_r3_simple, NISTObjectIdentifiers.id_sha256);
+        digestOids.put(BCObjectIdentifiers.sphincsPlus_sha2_256f_r3_simple, NISTObjectIdentifiers.id_sha256);
+        digestOids.put(BCObjectIdentifiers.sphincsPlus_shake_256s_r3_simple, NISTObjectIdentifiers.id_shake256);
+        digestOids.put(BCObjectIdentifiers.sphincsPlus_shake_256f_r3_simple, NISTObjectIdentifiers.id_shake256);
+
+        digestOids.put(BCObjectIdentifiers.sphincsPlus_sha2_128s, NISTObjectIdentifiers.id_sha256);
+        digestOids.put(BCObjectIdentifiers.sphincsPlus_sha2_128f, NISTObjectIdentifiers.id_sha256);
+        digestOids.put(BCObjectIdentifiers.sphincsPlus_shake_128s, NISTObjectIdentifiers.id_shake256);
+        digestOids.put(BCObjectIdentifiers.sphincsPlus_shake_128f, NISTObjectIdentifiers.id_shake256);
+        digestOids.put(BCObjectIdentifiers.sphincsPlus_sha2_192s, NISTObjectIdentifiers.id_sha256);
+        digestOids.put(BCObjectIdentifiers.sphincsPlus_sha2_192f, NISTObjectIdentifiers.id_sha256);
+        digestOids.put(BCObjectIdentifiers.sphincsPlus_shake_192s, NISTObjectIdentifiers.id_shake256);
+        digestOids.put(BCObjectIdentifiers.sphincsPlus_shake_192f, NISTObjectIdentifiers.id_shake256);
+        digestOids.put(BCObjectIdentifiers.sphincsPlus_sha2_256s, NISTObjectIdentifiers.id_sha256);
+        digestOids.put(BCObjectIdentifiers.sphincsPlus_sha2_256f, NISTObjectIdentifiers.id_sha256);
+        digestOids.put(BCObjectIdentifiers.sphincsPlus_shake_256s, NISTObjectIdentifiers.id_shake256);
+        digestOids.put(BCObjectIdentifiers.sphincsPlus_shake_256f, NISTObjectIdentifiers.id_shake256);
+
+        digestOids.put(CMSObjectIdentifiers.id_RSASSA_PSS_SHAKE128, NISTObjectIdentifiers.id_shake128);
+        digestOids.put(CMSObjectIdentifiers.id_RSASSA_PSS_SHAKE256, NISTObjectIdentifiers.id_shake256);
+        digestOids.put(CMSObjectIdentifiers.id_ecdsa_with_shake128, NISTObjectIdentifiers.id_shake128);
+        digestOids.put(CMSObjectIdentifiers.id_ecdsa_with_shake256, NISTObjectIdentifiers.id_shake256);
         */
         // END Android-removed: Unsupported algorithms
     }
 
-    private static AlgorithmIdentifier generate(String signatureAlgorithm)
-    {
-        AlgorithmIdentifier sigAlgId;
-
-        String algorithmName = Strings.toUpperCase(signatureAlgorithm);
-        ASN1ObjectIdentifier sigOID = (ASN1ObjectIdentifier)algorithms.get(algorithmName);
-        if (sigOID == null)
-        {
-            throw new IllegalArgumentException("Unknown signature type requested: " + algorithmName);
-        }
-
-        if (noParams.contains(sigOID))
-        {
-            sigAlgId = new AlgorithmIdentifier(sigOID);
-        }
-        else if (params.containsKey(algorithmName))
-        {
-            sigAlgId = new AlgorithmIdentifier(sigOID, (ASN1Encodable)params.get(algorithmName));
-        }
-        else
-        {
-            sigAlgId = new AlgorithmIdentifier(sigOID, DERNull.INSTANCE);
-        }
-
-        return sigAlgId;
-    }
-
     private static RSASSAPSSparams createPSSParams(AlgorithmIdentifier hashAlgId, int saltSize)
     {
         return new RSASSAPSSparams(
@@ -446,6 +615,26 @@
 
     public AlgorithmIdentifier find(String sigAlgName)
     {
-        return generate(sigAlgName);
+        String algorithmName = Strings.toUpperCase(sigAlgName);
+        ASN1ObjectIdentifier sigOID = (ASN1ObjectIdentifier)algorithms.get(algorithmName);
+        if (sigOID == null)
+        {
+            throw new IllegalArgumentException("Unknown signature type requested: " + sigAlgName);
+        }
+
+        AlgorithmIdentifier sigAlgId;
+        if (noParams.contains(sigOID))
+        {
+            sigAlgId = new AlgorithmIdentifier(sigOID);
+        }
+        else if (params.containsKey(algorithmName))
+        {
+            sigAlgId = new AlgorithmIdentifier(sigOID, (ASN1Encodable)params.get(algorithmName));
+        }
+        else
+        {
+            sigAlgId = new AlgorithmIdentifier(sigOID, DERNull.INSTANCE);
+        }
+        return sigAlgId;
     }
-}
\ No newline at end of file
+}
diff --git a/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/operator/DefaultSignatureNameFinder.java b/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/operator/DefaultSignatureNameFinder.java
new file mode 100644
index 0000000..3cfd391
--- /dev/null
+++ b/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/operator/DefaultSignatureNameFinder.java
@@ -0,0 +1,172 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.operator;
+
+import java.util.HashMap;
+import java.util.Map;
+
+// BEGIN Android-removed: unsupported algorithms
+import com.android.internal.org.bouncycastle.asn1.ASN1Encodable;
+import com.android.internal.org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import com.android.internal.org.bouncycastle.asn1.DERNull;
+// import org.bouncycastle.asn1.bsi.BSIObjectIdentifiers;
+// import org.bouncycastle.asn1.cms.CMSObjectIdentifiers;
+// import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers;
+// import org.bouncycastle.asn1.eac.EACObjectIdentifiers;
+// import org.bouncycastle.asn1.edec.EdECObjectIdentifiers;
+import com.android.internal.org.bouncycastle.asn1.isara.IsaraObjectIdentifiers;
+import com.android.internal.org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
+import com.android.internal.org.bouncycastle.asn1.oiw.OIWObjectIdentifiers;
+import com.android.internal.org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import com.android.internal.org.bouncycastle.asn1.pkcs.RSASSAPSSparams;
+// import org.bouncycastle.asn1.rosstandart.RosstandartObjectIdentifiers;
+import com.android.internal.org.bouncycastle.asn1.teletrust.TeleTrusTObjectIdentifiers;
+import com.android.internal.org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import com.android.internal.org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
+// END Android-removed: unsupported algorithms
+
+/**
+ * Class for return signature names from OIDs or AlgorithmIdentifiers
+ * @hide This class is not part of the Android public SDK API
+ */
+public class DefaultSignatureNameFinder
+    implements AlgorithmNameFinder
+{
+    private static final Map oids = new HashMap();
+    private static final Map digests = new HashMap();
+
+    static
+    {
+        //
+        // reverse mappings
+        //
+        // BEGIN Android-removed: unsupported algorithms
+        oids.put(PKCSObjectIdentifiers.id_RSASSA_PSS, "RSASSA-PSS");
+        // oids.put(EdECObjectIdentifiers.id_Ed25519, "ED25519");
+        // oids.put(EdECObjectIdentifiers.id_Ed448, "ED448");
+        oids.put(new ASN1ObjectIdentifier("1.2.840.113549.1.1.5"), "SHA1WITHRSA");
+        oids.put(PKCSObjectIdentifiers.sha224WithRSAEncryption, "SHA224WITHRSA");
+        oids.put(PKCSObjectIdentifiers.sha256WithRSAEncryption, "SHA256WITHRSA");
+        oids.put(PKCSObjectIdentifiers.sha384WithRSAEncryption, "SHA384WITHRSA");
+        oids.put(PKCSObjectIdentifiers.sha512WithRSAEncryption, "SHA512WITHRSA");
+        // oids.put(CMSObjectIdentifiers.id_RSASSA_PSS_SHAKE128, "SHAKE128WITHRSAPSS");
+        // oids.put(CMSObjectIdentifiers.id_RSASSA_PSS_SHAKE256, "SHAKE256WITHRSAPSS");
+        // oids.put(CryptoProObjectIdentifiers.gostR3411_94_with_gostR3410_94, "GOST3411WITHGOST3410");
+        // oids.put(CryptoProObjectIdentifiers.gostR3411_94_with_gostR3410_2001, "GOST3411WITHECGOST3410");
+        // oids.put(RosstandartObjectIdentifiers.id_tc26_signwithdigest_gost_3410_12_256, "GOST3411-2012-256WITHECGOST3410-2012-256");
+        // oids.put(RosstandartObjectIdentifiers.id_tc26_signwithdigest_gost_3410_12_512, "GOST3411-2012-512WITHECGOST3410-2012-512");
+        // oids.put(BSIObjectIdentifiers.ecdsa_plain_SHA1, "SHA1WITHPLAIN-ECDSA");
+        // oids.put(BSIObjectIdentifiers.ecdsa_plain_SHA224, "SHA224WITHPLAIN-ECDSA");
+        // oids.put(BSIObjectIdentifiers.ecdsa_plain_SHA256, "SHA256WITHPLAIN-ECDSA");
+        // oids.put(BSIObjectIdentifiers.ecdsa_plain_SHA384, "SHA384WITHPLAIN-ECDSA");
+        // oids.put(BSIObjectIdentifiers.ecdsa_plain_SHA512, "SHA512WITHPLAIN-ECDSA");
+        // oids.put(BSIObjectIdentifiers.ecdsa_plain_SHA3_224, "SHA3-224WITHPLAIN-ECDSA");
+        // oids.put(BSIObjectIdentifiers.ecdsa_plain_SHA3_256, "SHA3-256WITHPLAIN-ECDSA");
+        // oids.put(BSIObjectIdentifiers.ecdsa_plain_SHA3_384, "SHA3-384WITHPLAIN-ECDSA");
+        // oids.put(BSIObjectIdentifiers.ecdsa_plain_SHA3_512, "SHA3-512WITHPLAIN-ECDSA");
+        // oids.put(BSIObjectIdentifiers.ecdsa_plain_RIPEMD160, "RIPEMD160WITHPLAIN-ECDSA");
+        // oids.put(EACObjectIdentifiers.id_TA_ECDSA_SHA_1, "SHA1WITHCVC-ECDSA");
+        // oids.put(EACObjectIdentifiers.id_TA_ECDSA_SHA_224, "SHA224WITHCVC-ECDSA");
+        // oids.put(EACObjectIdentifiers.id_TA_ECDSA_SHA_256, "SHA256WITHCVC-ECDSA");
+        // oids.put(EACObjectIdentifiers.id_TA_ECDSA_SHA_384, "SHA384WITHCVC-ECDSA");
+        // oids.put(EACObjectIdentifiers.id_TA_ECDSA_SHA_512, "SHA512WITHCVC-ECDSA");
+        oids.put(IsaraObjectIdentifiers.id_alg_xmss, "XMSS");
+        oids.put(IsaraObjectIdentifiers.id_alg_xmssmt, "XMSSMT");
+        oids.put(TeleTrusTObjectIdentifiers.rsaSignatureWithripemd128, "RIPEMD128WITHRSA");
+        oids.put(TeleTrusTObjectIdentifiers.rsaSignatureWithripemd160, "RIPEMD160WITHRSA");
+        oids.put(TeleTrusTObjectIdentifiers.rsaSignatureWithripemd256, "RIPEMD256WITHRSA");
+        oids.put(new ASN1ObjectIdentifier("1.2.840.113549.1.1.4"), "MD5WITHRSA");
+        oids.put(new ASN1ObjectIdentifier("1.2.840.113549.1.1.2"), "MD2WITHRSA");
+        oids.put(new ASN1ObjectIdentifier("1.2.840.10040.4.3"), "SHA1WITHDSA");
+        oids.put(X9ObjectIdentifiers.ecdsa_with_SHA1, "SHA1WITHECDSA");
+        oids.put(X9ObjectIdentifiers.ecdsa_with_SHA224, "SHA224WITHECDSA");
+        oids.put(X9ObjectIdentifiers.ecdsa_with_SHA256, "SHA256WITHECDSA");
+        oids.put(X9ObjectIdentifiers.ecdsa_with_SHA384, "SHA384WITHECDSA");
+        oids.put(X9ObjectIdentifiers.ecdsa_with_SHA512, "SHA512WITHECDSA");
+        // oids.put(CMSObjectIdentifiers.id_ecdsa_with_shake128, "SHAKE128WITHECDSA");
+        // oids.put(CMSObjectIdentifiers.id_ecdsa_with_shake256, "SHAKE256WITHECDSA");
+        oids.put(OIWObjectIdentifiers.sha1WithRSA, "SHA1WITHRSA");
+        oids.put(OIWObjectIdentifiers.dsaWithSHA1, "SHA1WITHDSA");
+        oids.put(NISTObjectIdentifiers.dsa_with_sha224, "SHA224WITHDSA");
+        oids.put(NISTObjectIdentifiers.dsa_with_sha256, "SHA256WITHDSA");
+        // END Android-removed: unsupported algorithms
+
+        digests.put(OIWObjectIdentifiers.idSHA1, "SHA1");
+        digests.put(NISTObjectIdentifiers.id_sha224, "SHA224");
+        digests.put(NISTObjectIdentifiers.id_sha256, "SHA256");
+        digests.put(NISTObjectIdentifiers.id_sha384, "SHA384");
+        digests.put(NISTObjectIdentifiers.id_sha512, "SHA512");
+        digests.put(NISTObjectIdentifiers.id_sha3_224, "SHA3-224");
+        digests.put(NISTObjectIdentifiers.id_sha3_256, "SHA3-256");
+        digests.put(NISTObjectIdentifiers.id_sha3_384, "SHA3-384");
+        digests.put(NISTObjectIdentifiers.id_sha3_512, "SHA3-512");
+        digests.put(TeleTrusTObjectIdentifiers.ripemd128, "RIPEMD128");
+        digests.put(TeleTrusTObjectIdentifiers.ripemd160, "RIPEMD160");
+        digests.put(TeleTrusTObjectIdentifiers.ripemd256, "RIPEMD256");
+    }
+
+    public boolean hasAlgorithmName(ASN1ObjectIdentifier objectIdentifier)
+    {
+        return oids.containsKey(objectIdentifier);
+    }
+
+    public String getAlgorithmName(ASN1ObjectIdentifier objectIdentifier)
+    {
+        String name = (String)oids.get(objectIdentifier);
+        if (name != null)
+        {
+            return name;
+        }
+        return objectIdentifier.getId();
+    }
+
+    /**
+     * Return the signature name for the passed in algorithm identifier. For signatures
+     * that require parameters, like RSASSA-PSS, this is the best one to use.
+     *
+     * @param algorithmIdentifier the AlgorithmIdentifier of interest.
+     * @return a string representation of the name.
+     */
+    public String getAlgorithmName(AlgorithmIdentifier algorithmIdentifier)
+    {
+        ASN1Encodable params = algorithmIdentifier.getParameters();
+        if (params != null && !DERNull.INSTANCE.equals(params))
+        {
+            if (algorithmIdentifier.getAlgorithm().equals(PKCSObjectIdentifiers.id_RSASSA_PSS))
+            {
+                RSASSAPSSparams rsaParams = RSASSAPSSparams.getInstance(params);
+                AlgorithmIdentifier mgfAlg = rsaParams.getMaskGenAlgorithm();
+                if (mgfAlg.getAlgorithm().equals(PKCSObjectIdentifiers.id_mgf1))
+                {
+                    AlgorithmIdentifier digAlg = rsaParams.getHashAlgorithm();
+                    ASN1ObjectIdentifier mgfHashOid = AlgorithmIdentifier.getInstance(mgfAlg.getParameters()).getAlgorithm();
+                    if (mgfHashOid.equals(digAlg.getAlgorithm()))
+                    {
+                        return getDigestName(digAlg.getAlgorithm()) + "WITHRSAANDMGF1";
+                    }
+                    else
+                    {
+                        return getDigestName(digAlg.getAlgorithm()) + "WITHRSAANDMGF1USING" + getDigestName(mgfHashOid);
+                    }
+                }
+                return getDigestName(rsaParams.getHashAlgorithm().getAlgorithm()) + "WITHRSAAND" + mgfAlg.getAlgorithm().getId();
+            }
+        }
+
+        if (oids.containsKey(algorithmIdentifier.getAlgorithm()))
+        {
+            return (String)oids.get(algorithmIdentifier.getAlgorithm());
+        }
+
+        return algorithmIdentifier.getAlgorithm().getId();
+    }
+
+    private static String getDigestName(ASN1ObjectIdentifier oid)
+    {
+        String name = (String)digests.get(oid);
+        if (name != null)
+        {
+            return name;
+        }
+        return oid.getId();
+    }
+}
diff --git a/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/operator/DigestAlgorithmIdentifierFinder.java b/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/operator/DigestAlgorithmIdentifierFinder.java
index 168c48d..3fba9b7 100644
--- a/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/operator/DigestAlgorithmIdentifierFinder.java
+++ b/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/operator/DigestAlgorithmIdentifierFinder.java
@@ -1,6 +1,7 @@
 /* GENERATED SOURCE. DO NOT MODIFY. */
 package com.android.internal.org.bouncycastle.operator;
 
+import com.android.internal.org.bouncycastle.asn1.ASN1ObjectIdentifier;
 import com.android.internal.org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 
 /**
@@ -19,6 +20,15 @@
 
     /**
      * Find the algorithm identifier that matches with
+     * the passed in digest OID.
+     *
+     * @param digestOid the OID of the digest algorithm of interest.
+     * @return an algorithm identifier for the digest signature.
+     */
+    AlgorithmIdentifier find(ASN1ObjectIdentifier digestOid);
+
+    /**
+     * Find the algorithm identifier that matches with
      * the passed in digest name.
      *
      * @param digAlgName the name of the digest algorithm of interest.
diff --git a/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/operator/InputAEADDecryptor.java b/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/operator/InputAEADDecryptor.java
new file mode 100644
index 0000000..f7b3a25
--- /dev/null
+++ b/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/operator/InputAEADDecryptor.java
@@ -0,0 +1,11 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.operator;
+
+/**
+ * Base interface for an input consuming AEAD Decryptor supporting associated text.
+ * @hide This class is not part of the Android public SDK API
+ */
+public interface InputAEADDecryptor
+    extends InputDecryptor, AADProcessor
+{
+}
diff --git a/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/operator/MacAlgorithmIdentifierFinder.java b/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/operator/MacAlgorithmIdentifierFinder.java
new file mode 100644
index 0000000..d770380
--- /dev/null
+++ b/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/operator/MacAlgorithmIdentifierFinder.java
@@ -0,0 +1,19 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.operator;
+
+import com.android.internal.org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+public interface MacAlgorithmIdentifierFinder
+{
+    /**
+     * Find the algorithm identifier that matches with
+     * the passed in digest name.
+     *
+     * @param macAlgName the name of the digest algorithm of interest.
+     * @return an algorithm identifier for the MAC.
+     */
+    AlgorithmIdentifier find(String macAlgName);
+}
\ No newline at end of file
diff --git a/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/operator/MacCaptureStream.java b/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/operator/MacCaptureStream.java
new file mode 100644
index 0000000..26b0408
--- /dev/null
+++ b/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/operator/MacCaptureStream.java
@@ -0,0 +1,69 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.operator;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+import com.android.internal.org.bouncycastle.util.Arrays;
+
+/**
+ * A generic class for capturing the mac data at the end of a encrypted data stream.
+ * <p>
+ * Note: this class will not close the underlying stream.
+ * </p>
+ * @hide This class is not part of the Android public SDK API
+ */
+public class MacCaptureStream
+    extends OutputStream
+{
+    private final OutputStream cOut;
+    private final byte[] mac;
+
+    int macIndex = 0;
+
+    public MacCaptureStream(OutputStream cOut, int macLength)
+    {
+        this.cOut = cOut;
+        this.mac = new byte[macLength];
+    }
+
+    public void write(byte[] buf, int off, int len)
+        throws IOException
+    {
+        if (len >= mac.length)
+        {
+            cOut.write(mac, 0, macIndex);
+            macIndex = mac.length;
+            System.arraycopy(buf, off + len - mac.length, mac, 0, mac.length);
+            cOut.write(buf, off, len - mac.length);
+        }
+        else
+        {
+            for (int i = 0; i != len; i++)
+            {
+                write(buf[off + i]);
+            }
+        }
+    }
+
+    public void write(int b)
+        throws IOException
+    {
+        if (macIndex == mac.length)
+        {
+             byte b1 = mac[0];
+             System.arraycopy(mac, 1, mac, 0, mac.length - 1);
+             mac[mac.length - 1] = (byte)b;
+             cOut.write(b1);
+        }
+        else
+        {
+            mac[macIndex++] = (byte)b;
+        }
+    }
+
+    public byte[] getMac()
+    {
+        return Arrays.clone(mac);
+    }
+}
diff --git a/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/operator/PBEMacCalculatorProvider.java b/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/operator/PBEMacCalculatorProvider.java
new file mode 100644
index 0000000..494d32d
--- /dev/null
+++ b/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/operator/PBEMacCalculatorProvider.java
@@ -0,0 +1,13 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.operator;
+
+import com.android.internal.org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+public interface PBEMacCalculatorProvider
+{
+    MacCalculator get(AlgorithmIdentifier algorithm, char[] password)
+        throws OperatorCreationException;
+}
diff --git a/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/operator/bc/BcDefaultDigestProvider.java b/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/operator/bc/BcDefaultDigestProvider.java
index 0f5b47e..307c667 100644
--- a/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/operator/bc/BcDefaultDigestProvider.java
+++ b/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/operator/bc/BcDefaultDigestProvider.java
@@ -7,6 +7,9 @@
 
 // Android-removed: Unsupported algorithms
 // import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers;
+// import org.bouncycastle.asn1.ASN1Integer;
+// import org.bouncycastle.asn1.gm.GMObjectIdentifiers;
+// import org.bouncycastle.asn1.misc.MiscObjectIdentifiers;
 import com.android.internal.org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
 import com.android.internal.org.bouncycastle.asn1.oiw.OIWObjectIdentifiers;
 // import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
@@ -15,6 +18,25 @@
 import com.android.internal.org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 import com.android.internal.org.bouncycastle.crypto.ExtendedDigest;
 import com.android.internal.org.bouncycastle.crypto.digests.*;
+// import org.bouncycastle.crypto.Xof;
+// import org.bouncycastle.crypto.digests.Blake3Digest;
+// import org.bouncycastle.crypto.digests.GOST3411Digest;
+// import org.bouncycastle.crypto.digests.GOST3411_2012_256Digest;
+// import org.bouncycastle.crypto.digests.GOST3411_2012_512Digest;
+// import org.bouncycastle.crypto.digests.MD2Digest;
+// import org.bouncycastle.crypto.digests.MD4Digest;
+// import org.bouncycastle.crypto.digests.MD5Digest;
+// import org.bouncycastle.crypto.digests.RIPEMD128Digest;
+// import org.bouncycastle.crypto.digests.RIPEMD160Digest;
+// import org.bouncycastle.crypto.digests.RIPEMD256Digest;
+// import org.bouncycastle.crypto.digests.SHA1Digest;
+// import org.bouncycastle.crypto.digests.SHA224Digest;
+// import org.bouncycastle.crypto.digests.SHA256Digest;
+// import org.bouncycastle.crypto.digests.SHA384Digest;
+// import org.bouncycastle.crypto.digests.SHA3Digest;
+// import org.bouncycastle.crypto.digests.SHA512Digest;
+// import org.bouncycastle.crypto.digests.SHAKEDigest;
+// import org.bouncycastle.crypto.digests.SM3Digest;
 import com.android.internal.org.bouncycastle.operator.OperatorCreationException;
 
 /**
@@ -94,6 +116,34 @@
                 return new SHA3Digest(512);
             }
         });
+        table.put(NISTObjectIdentifiers.id_shake128, new BcDigestProvider()
+        {
+            public ExtendedDigest get(AlgorithmIdentifier digestAlgorithmIdentifier)
+            {
+                return new SHAKEDigest(128);
+            }
+        });
+        table.put(NISTObjectIdentifiers.id_shake256, new BcDigestProvider()
+        {
+            public ExtendedDigest get(AlgorithmIdentifier digestAlgorithmIdentifier)
+            {
+                return new SHAKEDigest(256);
+            }
+        });
+        table.put(NISTObjectIdentifiers.id_shake128_len, new BcDigestProvider()
+        {
+            public ExtendedDigest get(AlgorithmIdentifier digestAlgorithmIdentifier)
+            {
+                return new AdjustedXof(new SHAKEDigest(128), ASN1Integer.getInstance(digestAlgorithmIdentifier.getParameters()).intValueExact());
+            }
+        });
+        table.put(NISTObjectIdentifiers.id_shake256_len, new BcDigestProvider()
+        {
+            public ExtendedDigest get(AlgorithmIdentifier digestAlgorithmIdentifier)
+            {
+                return new AdjustedXof(new SHAKEDigest(256), ASN1Integer.getInstance(digestAlgorithmIdentifier.getParameters()).intValueExact());
+            }
+        });
         table.put(PKCSObjectIdentifiers.md5, new BcDigestProvider()
         {
             public ExtendedDigest get(AlgorithmIdentifier digestAlgorithmIdentifier)
@@ -157,6 +207,20 @@
                 return new RIPEMD256Digest();
             }
         });
+        table.put(GMObjectIdentifiers.sm3, new BcDigestProvider()
+        {
+            public ExtendedDigest get(AlgorithmIdentifier digestAlgorithmIdentifier)
+            {
+                return new SM3Digest();
+            }
+        });
+        table.put(MiscObjectIdentifiers.blake3_256, new BcDigestProvider()
+        {
+            public ExtendedDigest get(AlgorithmIdentifier digestAlgorithmIdentifier)
+            {
+                return new Blake3Digest(256);
+            }
+        });
         */
         // END Android-removed: Unsupported algorithms
 
@@ -182,4 +246,69 @@
 
         return extProv.get(digestAlgorithmIdentifier);
     }
+
+    /**
+     * -len OIDs for SHAKE include an integer representing the bitlength in of the output.
+     */
+    // BEGIN Android-removed: unused
+    /*
+    private static class AdjustedXof
+        implements Xof
+    {
+        private final Xof xof;
+        private final int length;
+
+        AdjustedXof(Xof xof, int length)
+        {
+            this.xof = xof;
+            this.length = length;
+        }
+
+        public String getAlgorithmName()
+        {
+            return xof.getAlgorithmName() + "-" + length;
+        }
+
+        public int getDigestSize()
+        {
+            return (length + 7) / 8;
+        }
+
+        public void update(byte in)
+        {
+            xof.update(in);
+        }
+
+        public void update(byte[] in, int inOff, int len)
+        {
+            xof.update(in, inOff, len);
+        }
+
+        public int doFinal(byte[] out, int outOff)
+        {
+            return doFinal(out, outOff, getDigestSize());
+        }
+
+        public void reset()
+        {
+            xof.reset();
+        }
+
+        public int getByteLength()
+        {
+            return xof.getByteLength();
+        }
+
+        public int doFinal(byte[] out, int outOff, int outLen)
+        {
+            return xof.doFinal(out, outOff, outLen);
+        }
+
+        public int doOutput(byte[] out, int outOff, int outLen)
+        {
+            return xof.doOutput(out, outOff, outLen);
+        }
+    }
+    */
+    // END Android-removed: unused
 }
diff --git a/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/operator/bc/BcDigestCalculatorProvider.java b/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/operator/bc/BcDigestCalculatorProvider.java
index 176c681..e2a6607 100644
--- a/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/operator/bc/BcDigestCalculatorProvider.java
+++ b/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/operator/bc/BcDigestCalculatorProvider.java
@@ -44,7 +44,7 @@
         };
     }
 
-    private class DigestOutputStream
+    private static class DigestOutputStream
         extends OutputStream
     {
         private Digest dig;
diff --git a/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/operator/jcajce/JcaContentSignerBuilder.java b/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/operator/jcajce/JcaContentSignerBuilder.java
index 6e669d3..0a61a88 100644
--- a/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/operator/jcajce/JcaContentSignerBuilder.java
+++ b/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/operator/jcajce/JcaContentSignerBuilder.java
@@ -12,16 +12,20 @@
 import java.security.spec.AlgorithmParameterSpec;
 import java.security.spec.MGF1ParameterSpec;
 import java.security.spec.PSSParameterSpec;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 
 import com.android.internal.org.bouncycastle.asn1.ASN1EncodableVector;
 import com.android.internal.org.bouncycastle.asn1.ASN1Encoding;
 import com.android.internal.org.bouncycastle.asn1.ASN1Integer;
 import com.android.internal.org.bouncycastle.asn1.ASN1Sequence;
 import com.android.internal.org.bouncycastle.asn1.DERBitString;
+import com.android.internal.org.bouncycastle.asn1.DERNull;
 import com.android.internal.org.bouncycastle.asn1.DERSequence;
 import com.android.internal.org.bouncycastle.asn1.misc.MiscObjectIdentifiers;
 import com.android.internal.org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import com.android.internal.org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
 import com.android.internal.org.bouncycastle.asn1.pkcs.RSASSAPSSparams;
 import com.android.internal.org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 import com.android.internal.org.bouncycastle.jcajce.CompositePrivateKey;
@@ -37,6 +41,7 @@
 import com.android.internal.org.bouncycastle.operator.OperatorCreationException;
 import com.android.internal.org.bouncycastle.operator.RuntimeOperatorException;
 import com.android.internal.org.bouncycastle.operator.SignatureAlgorithmIdentifierFinder;
+import com.android.internal.org.bouncycastle.util.Strings;
 import com.android.internal.org.bouncycastle.util.io.TeeOutputStream;
 
 /**
@@ -44,17 +49,26 @@
  */
 public class JcaContentSignerBuilder
 {
+    private static final Set isAlgIdFromPrivate = new HashSet();
+
+    static
+    {
+        isAlgIdFromPrivate.add("DILITHIUM");
+        isAlgIdFromPrivate.add("SPHINCS+");
+        isAlgIdFromPrivate.add("SPHINCSPlus");
+    }
+
+    private final String signatureAlgorithm;
+
     private OperatorHelper helper = new OperatorHelper(new DefaultJcaJceHelper());
     private SecureRandom random;
-    private String signatureAlgorithm;
+
     private AlgorithmIdentifier sigAlgId;
     private AlgorithmParameterSpec sigAlgSpec;
 
     public JcaContentSignerBuilder(String signatureAlgorithm)
     {
         this.signatureAlgorithm = signatureAlgorithm;
-        this.sigAlgId = new DefaultSignatureAlgorithmIdentifierFinder().find(signatureAlgorithm);
-        this.sigAlgSpec = null;
     }
 
     public JcaContentSignerBuilder(String signatureAlgorithm, AlgorithmParameterSpec sigParamSpec)
@@ -115,8 +129,21 @@
         
         try
         {
-            final Signature sig = helper.createSignature(sigAlgId);
+            if (sigAlgSpec == null)
+            {
+                if (isAlgIdFromPrivate.contains(Strings.toUpperCase(signatureAlgorithm)))
+                {
+                    sigAlgId = PrivateKeyInfo.getInstance(privateKey.getEncoded()).getPrivateKeyAlgorithm();
+                    this.sigAlgSpec = null;
+                }
+                else
+                {
+                    this.sigAlgId = new DefaultSignatureAlgorithmIdentifierFinder().find(signatureAlgorithm);
+                    this.sigAlgSpec = null;
+                }
+            }
             final AlgorithmIdentifier signatureAlgId = sigAlgId;
+            final Signature sig = helper.createSignature(sigAlgId);
 
             if (random != null)
             {
@@ -238,8 +265,16 @@
     private static RSASSAPSSparams createPSSParams(PSSParameterSpec pssSpec)
     {
         DigestAlgorithmIdentifierFinder digFinder = new DefaultDigestAlgorithmIdentifierFinder();
-           AlgorithmIdentifier digId = digFinder.find(pssSpec.getDigestAlgorithm());
-           AlgorithmIdentifier mgfDig = digFinder.find(((MGF1ParameterSpec)pssSpec.getMGFParameters()).getDigestAlgorithm());
+        AlgorithmIdentifier digId = digFinder.find(pssSpec.getDigestAlgorithm());
+        if (digId.getParameters() == null)
+        {
+            digId = new AlgorithmIdentifier(digId.getAlgorithm(), DERNull.INSTANCE);
+        }
+        AlgorithmIdentifier mgfDig = digFinder.find(((MGF1ParameterSpec)pssSpec.getMGFParameters()).getDigestAlgorithm());
+        if (mgfDig.getParameters() == null)
+        {
+            mgfDig = new AlgorithmIdentifier(mgfDig.getAlgorithm(), DERNull.INSTANCE);
+        }
 
         return new RSASSAPSSparams(
             digId,
diff --git a/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/operator/jcajce/JcaContentVerifierProviderBuilder.java b/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/operator/jcajce/JcaContentVerifierProviderBuilder.java
index 825f829..dc2be70 100644
--- a/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/operator/jcajce/JcaContentVerifierProviderBuilder.java
+++ b/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/operator/jcajce/JcaContentVerifierProviderBuilder.java
@@ -12,8 +12,8 @@
 import java.security.cert.X509Certificate;
 import java.util.List;
 
+import com.android.internal.org.bouncycastle.asn1.ASN1BitString;
 import com.android.internal.org.bouncycastle.asn1.ASN1Sequence;
-import com.android.internal.org.bouncycastle.asn1.DERBitString;
 import com.android.internal.org.bouncycastle.asn1.misc.MiscObjectIdentifiers;
 import com.android.internal.org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 import com.android.internal.org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
@@ -282,7 +282,7 @@
         return rawSig;
     }
 
-    private class SigVerifier
+    private static class SigVerifier
         implements ContentVerifier
     {
         private final AlgorithmIdentifier algorithm;
@@ -325,7 +325,7 @@
         }
     }
 
-    private class RawSigVerifier
+    private static class RawSigVerifier
         extends SigVerifier
         implements RawContentVerifier
     {
@@ -386,7 +386,7 @@
         }
     }
 
-    private class CompositeVerifier
+    private static class CompositeVerifier
         implements ContentVerifier
     {
         private Signature[] sigs;
@@ -437,7 +437,7 @@
                 {
                     if (sigs[i] != null)
                     {
-                        if (!sigs[i].verify(DERBitString.getInstance(sigSeq.getObjectAt(i)).getBytes()))
+                        if (!sigs[i].verify(ASN1BitString.getInstance(sigSeq.getObjectAt(i)).getBytes()))
                         {
                             failed = true;
                         }
diff --git a/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/operator/jcajce/JcaDigestCalculatorProviderBuilder.java b/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/operator/jcajce/JcaDigestCalculatorProviderBuilder.java
index 862cbdc..fb1c770 100644
--- a/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/operator/jcajce/JcaDigestCalculatorProviderBuilder.java
+++ b/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/operator/jcajce/JcaDigestCalculatorProviderBuilder.java
@@ -9,6 +9,7 @@
 
 import com.android.internal.org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 import com.android.internal.org.bouncycastle.jcajce.util.DefaultJcaJceHelper;
+import com.android.internal.org.bouncycastle.jcajce.util.JcaJceHelper;
 import com.android.internal.org.bouncycastle.jcajce.util.NamedJcaJceHelper;
 import com.android.internal.org.bouncycastle.jcajce.util.ProviderJcaJceHelper;
 import com.android.internal.org.bouncycastle.operator.DigestCalculator;
@@ -26,6 +27,14 @@
     {
     }
 
+
+    public JcaDigestCalculatorProviderBuilder setHelper(JcaJceHelper helper)
+    {
+        this.helper = new OperatorHelper(helper);
+
+        return this;
+    }
+
     public JcaDigestCalculatorProviderBuilder setProvider(Provider provider)
     {
         this.helper = new OperatorHelper(new ProviderJcaJceHelper(provider));
@@ -67,7 +76,7 @@
                     {
                         return algorithm;
                     }
-                    
+
                     public OutputStream getOutputStream()
                     {
                         return stream;
@@ -82,7 +91,7 @@
         };
     }
 
-    private class DigestOutputStream
+    private static class DigestOutputStream
         extends OutputStream
     {
         private MessageDigest dig;
@@ -101,13 +110,13 @@
         public void write(byte[] bytes)
             throws IOException
         {
-           dig.update(bytes);
+            dig.update(bytes);
         }
 
         public void write(int b)
             throws IOException
         {
-           dig.update((byte)b);
+            dig.update((byte)b);
         }
 
         byte[] getDigest()
diff --git a/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/operator/jcajce/OperatorHelper.java b/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/operator/jcajce/OperatorHelper.java
index 63efaff..97ac6a1 100644
--- a/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/operator/jcajce/OperatorHelper.java
+++ b/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/operator/jcajce/OperatorHelper.java
@@ -45,12 +45,12 @@
 // import org.bouncycastle.asn1.teletrust.TeleTrusTObjectIdentifiers;
 import com.android.internal.org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 import com.android.internal.org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
-import com.android.internal.org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
 import com.android.internal.org.bouncycastle.cert.X509CertificateHolder;
 import com.android.internal.org.bouncycastle.cms.CMSException;
 import com.android.internal.org.bouncycastle.jcajce.util.AlgorithmParametersUtils;
 import com.android.internal.org.bouncycastle.jcajce.util.JcaJceHelper;
 import com.android.internal.org.bouncycastle.jcajce.util.MessageDigestUtils;
+import com.android.internal.org.bouncycastle.operator.DefaultSignatureNameFinder;
 import com.android.internal.org.bouncycastle.operator.OperatorCreationException;
 import com.android.internal.org.bouncycastle.util.Integers;
 
@@ -62,54 +62,10 @@
     private static final Map symmetricKeyAlgNames = new HashMap();
     private static final Map symmetricWrapperKeySizes = new HashMap();
 
+    private static DefaultSignatureNameFinder sigFinder = new DefaultSignatureNameFinder();
+
     static
     {
-        //
-        // reverse mappings
-        //
-        oids.put(new ASN1ObjectIdentifier("1.2.840.113549.1.1.5"), "SHA1WITHRSA");
-        oids.put(PKCSObjectIdentifiers.sha224WithRSAEncryption, "SHA224WITHRSA");
-        oids.put(PKCSObjectIdentifiers.sha256WithRSAEncryption, "SHA256WITHRSA");
-        oids.put(PKCSObjectIdentifiers.sha384WithRSAEncryption, "SHA384WITHRSA");
-        oids.put(PKCSObjectIdentifiers.sha512WithRSAEncryption, "SHA512WITHRSA");
-        // BEGIN Android-removed: Unsupported algorithms
-        /*
-        oids.put(EdECObjectIdentifiers.id_Ed25519, "Ed25519");
-        oids.put(EdECObjectIdentifiers.id_Ed448, "Ed448");
-        oids.put(CryptoProObjectIdentifiers.gostR3411_94_with_gostR3410_94, "GOST3411WITHGOST3410");
-        oids.put(CryptoProObjectIdentifiers.gostR3411_94_with_gostR3410_2001, "GOST3411WITHECGOST3410");
-        oids.put(RosstandartObjectIdentifiers.id_tc26_signwithdigest_gost_3410_12_256, "GOST3411-2012-256WITHECGOST3410-2012-256");
-        oids.put(RosstandartObjectIdentifiers.id_tc26_signwithdigest_gost_3410_12_512, "GOST3411-2012-512WITHECGOST3410-2012-512");
-        oids.put(BSIObjectIdentifiers.ecdsa_plain_SHA1, "SHA1WITHPLAIN-ECDSA");
-        oids.put(BSIObjectIdentifiers.ecdsa_plain_SHA224, "SHA224WITHPLAIN-ECDSA");
-        oids.put(BSIObjectIdentifiers.ecdsa_plain_SHA256, "SHA256WITHPLAIN-ECDSA");
-        oids.put(BSIObjectIdentifiers.ecdsa_plain_SHA384, "SHA384WITHPLAIN-ECDSA");
-        oids.put(BSIObjectIdentifiers.ecdsa_plain_SHA512, "SHA512WITHPLAIN-ECDSA");
-        oids.put(BSIObjectIdentifiers.ecdsa_plain_RIPEMD160, "RIPEMD160WITHPLAIN-ECDSA");
-        oids.put(EACObjectIdentifiers.id_TA_ECDSA_SHA_1, "SHA1WITHCVC-ECDSA");
-        oids.put(EACObjectIdentifiers.id_TA_ECDSA_SHA_224, "SHA224WITHCVC-ECDSA");
-        oids.put(EACObjectIdentifiers.id_TA_ECDSA_SHA_256, "SHA256WITHCVC-ECDSA");
-        oids.put(EACObjectIdentifiers.id_TA_ECDSA_SHA_384, "SHA384WITHCVC-ECDSA");
-        oids.put(EACObjectIdentifiers.id_TA_ECDSA_SHA_512, "SHA512WITHCVC-ECDSA");
-        oids.put(IsaraObjectIdentifiers.id_alg_xmss, "XMSS");
-        oids.put(IsaraObjectIdentifiers.id_alg_xmssmt, "XMSSMT");
-        */
-        // END Android-removed: Unsupported algorithms
-
-        oids.put(new ASN1ObjectIdentifier("1.2.840.113549.1.1.4"), "MD5WITHRSA");
-        // BEGIN Android-removed: Unsupported algorithms
-        // oids.put(new ASN1ObjectIdentifier("1.2.840.113549.1.1.2"), "MD2WITHRSA");
-        // END Android-removed: Unsupported algorithms
-        oids.put(new ASN1ObjectIdentifier("1.2.840.10040.4.3"), "SHA1WITHDSA");
-        oids.put(X9ObjectIdentifiers.ecdsa_with_SHA1, "SHA1WITHECDSA");
-        oids.put(X9ObjectIdentifiers.ecdsa_with_SHA224, "SHA224WITHECDSA");
-        oids.put(X9ObjectIdentifiers.ecdsa_with_SHA256, "SHA256WITHECDSA");
-        oids.put(X9ObjectIdentifiers.ecdsa_with_SHA384, "SHA384WITHECDSA");
-        oids.put(X9ObjectIdentifiers.ecdsa_with_SHA512, "SHA512WITHECDSA");
-        oids.put(OIWObjectIdentifiers.sha1WithRSA, "SHA1WITHRSA");
-        oids.put(OIWObjectIdentifiers.dsaWithSHA1, "SHA1WITHDSA");
-        oids.put(NISTObjectIdentifiers.dsa_with_sha224, "SHA224WITHDSA");
-        oids.put(NISTObjectIdentifiers.dsa_with_sha256, "SHA256WITHDSA");
 
         oids.put(OIWObjectIdentifiers.idSHA1, "SHA1");
         oids.put(NISTObjectIdentifiers.id_sha224, "SHA224");
@@ -125,6 +81,8 @@
         // END Android-removed: Unsupported algorithms
 
         asymmetricWrapperAlgNames.put(PKCSObjectIdentifiers.rsaEncryption, "RSA/ECB/PKCS1Padding");
+        asymmetricWrapperAlgNames.put(OIWObjectIdentifiers.elGamalAlgorithm, "Elgamal/ECB/PKCS1Padding");
+        asymmetricWrapperAlgNames.put(PKCSObjectIdentifiers.id_RSAES_OAEP, "RSA/ECB/OAEPPadding");
 
         // Android-removed: Unsupported algorithms
         // asymmetricWrapperAlgNames.put(CryptoProObjectIdentifiers.gostR3410_2001, "ECGOST3410");
@@ -322,24 +280,43 @@
     AlgorithmParameters createAlgorithmParameters(AlgorithmIdentifier cipherAlgId)
         throws OperatorCreationException
     {
-        AlgorithmParameters parameters;
+        AlgorithmParameters parameters = null;
 
         if (cipherAlgId.getAlgorithm().equals(PKCSObjectIdentifiers.rsaEncryption))
         {
             return null;
         }
 
-        try
+        if (cipherAlgId.getAlgorithm().equals(PKCSObjectIdentifiers.id_RSAES_OAEP))
         {
-            parameters = helper.createAlgorithmParameters(cipherAlgId.getAlgorithm().getId());
+            try
+            {
+                parameters = helper.createAlgorithmParameters("OAEP");
+            }
+            catch (NoSuchAlgorithmException e)
+            {
+                // try below
+            }
+            catch (NoSuchProviderException e)
+            {
+                throw new OperatorCreationException("cannot create algorithm parameters: " + e.getMessage(), e);
+            }
         }
-        catch (NoSuchAlgorithmException e)
+
+        if (parameters == null)
         {
-            return null;   // There's a good chance there aren't any!
-        }
-        catch (NoSuchProviderException e)
-        {
-            throw new OperatorCreationException("cannot create algorithm parameters: " + e.getMessage(), e);
+            try
+            {
+                parameters = helper.createAlgorithmParameters(cipherAlgId.getAlgorithm().getId());
+            }
+            catch (NoSuchAlgorithmException e)
+            {
+                return null;   // There's a good chance there aren't any!
+            }
+            catch (NoSuchProviderException e)
+            {
+                throw new OperatorCreationException("cannot create algorithm parameters: " + e.getMessage(), e);
+            }
         }
 
         try
@@ -365,6 +342,10 @@
             {
                 dig = helper.createMessageDigest("SHAKE256-" + ASN1Integer.getInstance(digAlgId.getParameters()).getValue());
             }
+            else if (digAlgId.getAlgorithm().equals(NISTObjectIdentifiers.id_shake128_len))
+            {
+                dig = helper.createMessageDigest("SHAKE128-" + ASN1Integer.getInstance(digAlgId.getParameters()).getValue());
+            }
             else
             {
                 dig = helper.createMessageDigest(MessageDigestUtils.getDigestName(digAlgId.getAlgorithm()));
@@ -412,12 +393,6 @@
 
                 sig = helper.createSignature(signatureAlgorithm);
             }
-            else if (oids.get(sigAlgId.getAlgorithm()) != null)
-            {
-                String signatureAlgorithm = (String)oids.get(sigAlgId.getAlgorithm());
-
-                sig = helper.createSignature(signatureAlgorithm);
-            }
             else
             {
                 throw e;
@@ -448,7 +423,7 @@
         return sig;
     }
 
-    public Signature createRawSignature(AlgorithmIdentifier algorithm)
+    Signature createRawSignature(AlgorithmIdentifier algorithm)
     {
         Signature sig;
 
@@ -484,27 +459,11 @@
     private static String getSignatureName(
         AlgorithmIdentifier sigAlgId)
     {
-        ASN1Encodable params = sigAlgId.getParameters();
-
-        if (params != null && !DERNull.INSTANCE.equals(params))
-        {
-            if (sigAlgId.getAlgorithm().equals(PKCSObjectIdentifiers.id_RSASSA_PSS))
-            {
-                RSASSAPSSparams rsaParams = RSASSAPSSparams.getInstance(params);
-                return getDigestName(rsaParams.getHashAlgorithm().getAlgorithm()) + "WITHRSAANDMGF1";
-            }
-        }
-
-        if (oids.containsKey(sigAlgId.getAlgorithm()))
-        {
-            return (String)oids.get(sigAlgId.getAlgorithm());
-        }
-
-        return sigAlgId.getAlgorithm().getId();
+        return sigFinder.getAlgorithmName(sigAlgId);
     }
 
     // we need to remove the - to create a correct signature name
-    private static String getDigestName(ASN1ObjectIdentifier oid)
+    static String getDigestName(ASN1ObjectIdentifier oid)
     {
         String name = MessageDigestUtils.getDigestName(oid);
 
diff --git a/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/pkcs/DeltaCertAttributeUtils.java b/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/pkcs/DeltaCertAttributeUtils.java
new file mode 100644
index 0000000..95e2641
--- /dev/null
+++ b/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/pkcs/DeltaCertAttributeUtils.java
@@ -0,0 +1,69 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.pkcs;
+
+import java.io.IOException;
+import java.util.Enumeration;
+
+import com.android.internal.org.bouncycastle.asn1.ASN1EncodableVector;
+import com.android.internal.org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import com.android.internal.org.bouncycastle.asn1.DERSequence;
+import com.android.internal.org.bouncycastle.asn1.DERSet;
+import com.android.internal.org.bouncycastle.asn1.DERTaggedObject;
+import com.android.internal.org.bouncycastle.asn1.pkcs.Attribute;
+import com.android.internal.org.bouncycastle.asn1.pkcs.CertificationRequest;
+import com.android.internal.org.bouncycastle.asn1.pkcs.CertificationRequestInfo;
+import com.android.internal.org.bouncycastle.asn1.x509.Extension;
+import com.android.internal.org.bouncycastle.operator.ContentVerifierProvider;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+public class DeltaCertAttributeUtils
+{
+    public static Extension makeDeltaCertificateExtension(DeltaCertificateRequestAttributeValue deltaReqAttr)
+        throws IOException
+    {
+         return null;
+    }
+
+    public static boolean isDeltaRequestSignatureValid(PKCS10CertificationRequest baseRequest, ContentVerifierProvider contentVerifierProvider)
+        throws PKCSException
+    {
+        Attribute[] attributes = baseRequest.getAttributes(new ASN1ObjectIdentifier("2.16.840.1.114027.80.6.2"));
+
+        DeltaCertificateRequestAttributeValue deltaReq = new DeltaCertificateRequestAttributeValue(attributes[0]);
+
+        attributes = baseRequest.getAttributes(new ASN1ObjectIdentifier("2.16.840.1.114027.80.6.3"));
+
+        CertificationRequest deltaPkcs10 = baseRequest.toASN1Structure();
+        CertificationRequestInfo deltaInfo = deltaPkcs10.getCertificationRequestInfo();
+
+        ASN1EncodableVector deltaPkcs10InfoV = new ASN1EncodableVector();
+        deltaPkcs10InfoV.add(deltaInfo.getVersion());
+        deltaPkcs10InfoV.add(deltaInfo.getSubject());
+        deltaPkcs10InfoV.add(deltaInfo.getSubjectPublicKeyInfo());
+
+        ASN1EncodableVector attrSetV = new ASN1EncodableVector();
+        for (Enumeration en = deltaInfo.getAttributes().getObjects(); en.hasMoreElements();)
+        {
+            Attribute attr = Attribute.getInstance(en.nextElement());
+
+            if (!attr.getAttrType().equals(new ASN1ObjectIdentifier("2.16.840.1.114027.80.6.3")))
+            {
+                attrSetV.add(attr);
+            }
+        }
+
+        deltaPkcs10InfoV.add(new DERTaggedObject(false, 0, new DERSet(attrSetV)));
+
+        ASN1EncodableVector deltaPkcs10V = new ASN1EncodableVector();
+
+        deltaPkcs10V.add(new DERSequence(deltaPkcs10InfoV));
+        deltaPkcs10V.add(deltaReq.getSignatureAlgorithm());
+        deltaPkcs10V.add(attributes[0].getAttributeValues()[0]);
+
+        PKCS10CertificationRequest deltaPkcs10Req = new PKCS10CertificationRequest(CertificationRequest.getInstance(new DERSequence(deltaPkcs10V)));
+
+        return deltaPkcs10Req.isSignatureValid(contentVerifierProvider);
+    }
+}
diff --git a/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/pkcs/DeltaCertificateRequestAttributeValue.java b/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/pkcs/DeltaCertificateRequestAttributeValue.java
new file mode 100644
index 0000000..75fcf27
--- /dev/null
+++ b/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/pkcs/DeltaCertificateRequestAttributeValue.java
@@ -0,0 +1,104 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.pkcs;
+
+import com.android.internal.org.bouncycastle.asn1.ASN1Encodable;
+import com.android.internal.org.bouncycastle.asn1.ASN1Primitive;
+import com.android.internal.org.bouncycastle.asn1.ASN1Sequence;
+import com.android.internal.org.bouncycastle.asn1.ASN1TaggedObject;
+import com.android.internal.org.bouncycastle.asn1.pkcs.Attribute;
+import com.android.internal.org.bouncycastle.asn1.x500.X500Name;
+import com.android.internal.org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import com.android.internal.org.bouncycastle.asn1.x509.Extensions;
+import com.android.internal.org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+
+/**
+ * The delta certificate request attribute defined in: https://datatracker.ietf.org/doc/draft-bonnell-lamps-chameleon-certs/
+ * @hide This class is not part of the Android public SDK API
+ */
+public class DeltaCertificateRequestAttributeValue
+    implements ASN1Encodable
+{
+    private final X500Name subject;
+    private final SubjectPublicKeyInfo subjectPKInfo;
+    private final Extensions extensions;
+    private final AlgorithmIdentifier signatureAlgorithm;
+    private final ASN1Sequence attrSeq;
+
+    public DeltaCertificateRequestAttributeValue(Attribute attribute)
+    {
+        this(ASN1Sequence.getInstance(attribute.getAttributeValues()[0]));
+    }
+
+    DeltaCertificateRequestAttributeValue(ASN1Sequence attrSeq)
+    {
+        this.attrSeq = attrSeq;
+        // TODO: validate attribute size
+
+        int idx = 0;
+        if (attrSeq.getObjectAt(0) instanceof ASN1TaggedObject)
+        {
+            subject = X500Name.getInstance(ASN1TaggedObject.getInstance(attrSeq.getObjectAt(0)), true);
+            idx++;
+        }
+        else
+        {
+            subject = null;
+        }
+
+        subjectPKInfo = SubjectPublicKeyInfo.getInstance(attrSeq.getObjectAt(idx));
+        idx++;
+
+        Extensions ext = null;
+        AlgorithmIdentifier sigAlg = null;
+
+        if (idx != attrSeq.size())
+        {
+            while (idx < attrSeq.size())
+            {
+                ASN1TaggedObject tagObj = ASN1TaggedObject.getInstance(attrSeq.getObjectAt(idx));
+                if (tagObj.getTagNo() == 1)
+                {
+                    ext = Extensions.getInstance(tagObj, false);
+                }
+                else if (tagObj.getTagNo() == 2)
+                {
+                    sigAlg = AlgorithmIdentifier.getInstance(tagObj, false);
+                }
+                else
+                {
+                    throw new IllegalArgumentException("unknown tag");
+                }
+                idx++;
+            }
+        }
+
+        this.extensions = ext;
+        this.signatureAlgorithm = sigAlg;
+    }
+
+    public X500Name getSubject()
+    {
+        return subject;
+    }
+
+    public SubjectPublicKeyInfo getSubjectPKInfo()
+    {
+        return subjectPKInfo;
+    }
+
+    public Extensions getExtensions()
+    {
+        return extensions;
+    }
+
+    public AlgorithmIdentifier getSignatureAlgorithm()
+    {
+        return signatureAlgorithm;
+    }
+
+    @Override
+    public ASN1Primitive toASN1Primitive()
+    {
+        return attrSeq;
+    }
+}
diff --git a/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/pkcs/DeltaCertificateRequestAttributeValueBuilder.java b/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/pkcs/DeltaCertificateRequestAttributeValueBuilder.java
new file mode 100644
index 0000000..5ffb356
--- /dev/null
+++ b/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/pkcs/DeltaCertificateRequestAttributeValueBuilder.java
@@ -0,0 +1,61 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.pkcs;
+
+import com.android.internal.org.bouncycastle.asn1.ASN1EncodableVector;
+import com.android.internal.org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import com.android.internal.org.bouncycastle.asn1.DERSequence;
+import com.android.internal.org.bouncycastle.asn1.DERSet;
+import com.android.internal.org.bouncycastle.asn1.DERTaggedObject;
+import com.android.internal.org.bouncycastle.asn1.pkcs.Attribute;
+import com.android.internal.org.bouncycastle.asn1.x500.X500Name;
+import com.android.internal.org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import com.android.internal.org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+public class DeltaCertificateRequestAttributeValueBuilder
+{
+    private final SubjectPublicKeyInfo subjectPublicKey;
+
+    private AlgorithmIdentifier signatureAlgorithm;
+    private X500Name subject;
+
+    public DeltaCertificateRequestAttributeValueBuilder(SubjectPublicKeyInfo subjectPublicKey)
+    {
+        this.subjectPublicKey = subjectPublicKey;
+    }
+
+    public DeltaCertificateRequestAttributeValueBuilder setSignatureAlgorithm(AlgorithmIdentifier signatureAlgorithm)
+    {
+       this.signatureAlgorithm = signatureAlgorithm;
+
+       return this;
+    }
+
+    public DeltaCertificateRequestAttributeValueBuilder setSubject(X500Name subject)
+    {
+       this.subject = subject;
+
+       return this;
+    }
+
+    public DeltaCertificateRequestAttributeValue build()
+    {
+        ASN1EncodableVector v = new ASN1EncodableVector();
+
+        if (subject != null)
+        {
+            v.add(new DERTaggedObject(false, 0, subject));
+        }
+        v.add(subjectPublicKey);
+        if (signatureAlgorithm != null)
+        {
+            v.add(new DERTaggedObject(false, 2, signatureAlgorithm));
+        }
+
+        
+        return new DeltaCertificateRequestAttributeValue(new Attribute(new ASN1ObjectIdentifier("2.16.840.1.114027.80.6.2"),
+                        new DERSet(new DERSequence(v))));
+    }
+}
diff --git a/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/pkcs/PKCS10CertificationRequest.java b/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/pkcs/PKCS10CertificationRequest.java
new file mode 100644
index 0000000..02c98fe
--- /dev/null
+++ b/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/pkcs/PKCS10CertificationRequest.java
@@ -0,0 +1,437 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.pkcs;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.List;
+
+import com.android.internal.org.bouncycastle.asn1.ASN1BitString;
+import com.android.internal.org.bouncycastle.asn1.ASN1Boolean;
+import com.android.internal.org.bouncycastle.asn1.ASN1Encodable;
+import com.android.internal.org.bouncycastle.asn1.ASN1EncodableVector;
+import com.android.internal.org.bouncycastle.asn1.ASN1Encoding;
+import com.android.internal.org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import com.android.internal.org.bouncycastle.asn1.ASN1OctetString;
+import com.android.internal.org.bouncycastle.asn1.ASN1Primitive;
+import com.android.internal.org.bouncycastle.asn1.ASN1Sequence;
+import com.android.internal.org.bouncycastle.asn1.ASN1Set;
+import com.android.internal.org.bouncycastle.asn1.DERSet;
+import com.android.internal.org.bouncycastle.asn1.pkcs.Attribute;
+import com.android.internal.org.bouncycastle.asn1.pkcs.CertificationRequest;
+import com.android.internal.org.bouncycastle.asn1.pkcs.CertificationRequestInfo;
+import com.android.internal.org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import com.android.internal.org.bouncycastle.asn1.x500.X500Name;
+import com.android.internal.org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import com.android.internal.org.bouncycastle.asn1.x509.Extension;
+import com.android.internal.org.bouncycastle.asn1.x509.Extensions;
+import com.android.internal.org.bouncycastle.asn1.x509.ExtensionsGenerator;
+import com.android.internal.org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import com.android.internal.org.bouncycastle.operator.ContentVerifier;
+import com.android.internal.org.bouncycastle.operator.ContentVerifierProvider;
+import com.android.internal.org.bouncycastle.util.Exceptions;
+
+/**
+ * Holding class for a PKCS#10 certification request.
+ * @hide This class is not part of the Android public SDK API
+ */
+public class PKCS10CertificationRequest
+{
+    private static Attribute[] EMPTY_ARRAY = new Attribute[0];
+
+    private final CertificationRequest certificationRequest;
+    private final boolean isAltRequest;
+    private final AlgorithmIdentifier altSignature;
+    private final SubjectPublicKeyInfo altPublicKey;
+    private final ASN1BitString altSignatureValue;
+
+    private static CertificationRequest parseBytes(byte[] encoding)
+        throws IOException
+    {
+        try
+        {
+            CertificationRequest rv = CertificationRequest.getInstance(ASN1Primitive.fromByteArray(encoding));
+
+            if (rv == null)
+            {
+                throw new PKCSIOException("empty data passed to constructor");
+            }
+
+            return rv;
+        }
+        catch (ClassCastException e)
+        {
+            throw new PKCSIOException("malformed data: " + e.getMessage(), e);
+        }
+        catch (IllegalArgumentException e)
+        {
+            throw new PKCSIOException("malformed data: " + e.getMessage(), e);
+        }
+    }
+
+    private static ASN1Encodable getSingleValue(Attribute at)
+    {
+        ASN1Encodable[] attrValues = at.getAttributeValues();
+        if (attrValues.length!= 1)
+        {
+            throw new IllegalArgumentException("single value attribute value not size of 1");
+        }
+
+        return attrValues[0];
+    }
+
+    /**
+     * Create a PKCS10CertificationRequestHolder from an underlying ASN.1 structure.
+     *
+     * @param certificationRequest the underlying ASN.1 structure representing a request.
+     */
+    public PKCS10CertificationRequest(CertificationRequest certificationRequest)
+    {
+        if (certificationRequest == null)
+        {
+            throw new NullPointerException("certificationRequest cannot be null");
+        }
+        this.certificationRequest = certificationRequest;
+
+        ASN1Set attributes = certificationRequest.getCertificationRequestInfo().getAttributes();
+
+        AlgorithmIdentifier altSig = null;
+        SubjectPublicKeyInfo altPub = null;
+        ASN1BitString altSigValue = null;
+
+        if (attributes != null)
+        {
+            for (Enumeration en = attributes.getObjects(); en.hasMoreElements();)
+            {
+                Attribute at = Attribute.getInstance(en.nextElement());
+
+                if (Extension.altSignatureAlgorithm.equals(at.getAttrType()))
+                {
+                    altSig = AlgorithmIdentifier.getInstance(getSingleValue(at));
+                }
+                if (Extension.subjectAltPublicKeyInfo.equals(at.getAttrType()))
+                {
+                    altPub = SubjectPublicKeyInfo.getInstance(getSingleValue(at));
+                }
+                if (Extension.altSignatureValue.equals(at.getAttrType()))
+                {
+                    altSigValue = ASN1BitString.getInstance(getSingleValue(at));
+                }
+            }
+        }
+
+        this.isAltRequest = (altSig != null) | (altPub != null) | (altSigValue != null);
+        if (isAltRequest)
+        {
+            if (!((altSig != null) & (altPub != null) & (altSigValue != null)))
+            {
+                throw new IllegalArgumentException("invalid alternate public key details found");
+            }
+        }
+
+        this.altSignature = altSig;
+        this.altPublicKey = altPub;
+        this.altSignatureValue = altSigValue;
+    }
+
+    /**
+     * Create a PKCS10CertificationRequestHolder from the passed in bytes.
+     *
+     * @param encoded BER/DER encoding of the CertificationRequest structure.
+     * @throws IOException in the event of corrupted data, or an incorrect structure.
+     */
+    public PKCS10CertificationRequest(byte[] encoded)
+        throws IOException
+    {
+        this(parseBytes(encoded));
+    }
+
+    /**
+     * Return the underlying ASN.1 structure for this request.
+     *
+     * @return a CertificateRequest object.
+     */
+    public CertificationRequest toASN1Structure()
+    {
+        return certificationRequest;
+    }
+
+    /**
+     * Return the subject on this request.
+     *
+     * @return the X500Name representing the request's subject.
+     */
+    public X500Name getSubject()
+    {
+        return X500Name.getInstance(certificationRequest.getCertificationRequestInfo().getSubject());
+    }
+
+    /**
+     * Return the details of the signature algorithm used to create this request.
+     *
+     * @return the AlgorithmIdentifier describing the signature algorithm used to create this request.
+     */
+    public AlgorithmIdentifier getSignatureAlgorithm()
+    {
+        return certificationRequest.getSignatureAlgorithm();
+    }
+
+    /**
+     * Return the bytes making up the signature associated with this request.
+     *
+     * @return the request signature bytes.
+     */
+    public byte[] getSignature()
+    {
+        return certificationRequest.getSignature().getOctets();
+    }
+
+    /**
+     * Return the SubjectPublicKeyInfo describing the public key this request is carrying.
+     *
+     * @return the public key ASN.1 structure contained in the request.
+     */
+    public SubjectPublicKeyInfo getSubjectPublicKeyInfo()
+    {
+        return certificationRequest.getCertificationRequestInfo().getSubjectPublicKeyInfo();
+    }
+
+    /**
+     * Return the attributes, if any associated with this request.
+     *
+     * @return an array of Attribute, zero length if none present.
+     */
+    public Attribute[] getAttributes()
+    {
+        ASN1Set attrSet = certificationRequest.getCertificationRequestInfo().getAttributes();
+
+        if (attrSet == null)
+        {
+            return EMPTY_ARRAY;
+        }
+
+        Attribute[] attrs = new Attribute[attrSet.size()];
+
+        for (int i = 0; i != attrSet.size(); i++)
+        {
+            attrs[i] = Attribute.getInstance(attrSet.getObjectAt(i));
+        }
+
+        return attrs;
+    }
+
+    /**
+     * Return an  array of attributes matching the passed in type OID.
+     *
+     * @param type the type of the attribute being looked for.
+     * @return an array of Attribute of the requested type, zero length if none present.
+     */
+    public Attribute[] getAttributes(ASN1ObjectIdentifier type)
+    {
+        ASN1Set attrSet = certificationRequest.getCertificationRequestInfo().getAttributes();
+
+        if (attrSet == null)
+        {
+            return EMPTY_ARRAY;
+        }
+
+        List list = new ArrayList();
+
+        for (int i = 0; i != attrSet.size(); i++)
+        {
+            Attribute attr = Attribute.getInstance(attrSet.getObjectAt(i));
+            if (attr.getAttrType().equals(type))
+            {
+                list.add(attr);
+            }
+        }
+
+        if (list.size() == 0)
+        {
+            return EMPTY_ARRAY;
+        }
+
+        return (Attribute[])list.toArray(new Attribute[list.size()]);
+    }
+
+    public byte[] getEncoded()
+        throws IOException
+    {
+        return certificationRequest.getEncoded();
+    }
+
+    /**
+     * Validate the signature on the PKCS10 certification request in this holder.
+     *
+     * @param verifierProvider a ContentVerifierProvider that can generate a verifier for the signature.
+     * @return true if the signature is valid, false otherwise.
+     * @throws PKCSException if the signature cannot be processed or is inappropriate.
+     */
+    public boolean isSignatureValid(ContentVerifierProvider verifierProvider)
+        throws PKCSException
+    {
+        CertificationRequestInfo requestInfo = certificationRequest.getCertificationRequestInfo();
+
+        ContentVerifier verifier;
+
+        try
+        {
+            verifier = verifierProvider.get(certificationRequest.getSignatureAlgorithm());
+
+            OutputStream sOut = verifier.getOutputStream();
+
+            sOut.write(requestInfo.getEncoded(ASN1Encoding.DER));
+
+            sOut.close();
+        }
+        catch (Exception e)
+        {
+            throw new PKCSException("unable to process signature: " + e.getMessage(), e);
+        }
+
+        return verifier.verify(this.getSignature());
+    }
+
+    /**
+     * Return true if the certification request has an alternate public key present.
+     *
+     * @return true if this is a dual key request, false otherwise.
+     */
+    public boolean hasAltPublicKey()
+    {
+        return isAltRequest;
+    }
+
+    /**
+     * Validate the alternate signature on the PKCS10 certification request in this holder.
+     *
+     * @param verifierProvider a ContentVerifierProvider that can generate a verifier for the signature.
+     * @return true if the alternate signature is valid, false otherwise.
+     * @throws PKCSException if the signature cannot be processed or is inappropriate.
+     */
+    public boolean isAltSignatureValid(ContentVerifierProvider verifierProvider)
+        throws PKCSException
+    {
+        if (!isAltRequest)
+        {
+            throw new IllegalStateException("no alternate public key present");
+        }
+
+        CertificationRequestInfo requestInfo = certificationRequest.getCertificationRequestInfo();
+        ASN1Set attributes = requestInfo.getAttributes();
+        ASN1EncodableVector atV = new ASN1EncodableVector();
+
+        for (Enumeration en = attributes.getObjects(); en.hasMoreElements();)
+        {
+            Attribute at = Attribute.getInstance(en.nextElement());
+
+            if (Extension.altSignatureValue.equals(at.getAttrType()))
+            {
+                continue;
+            }
+
+            atV.add(at);
+        }
+
+        requestInfo = new CertificationRequestInfo(requestInfo.getSubject(), requestInfo.getSubjectPublicKeyInfo(), new DERSet(atV));
+        ContentVerifier verifier;
+
+        try
+        {
+            verifier = verifierProvider.get(this.altSignature);
+
+            OutputStream sOut = verifier.getOutputStream();
+
+            sOut.write(requestInfo.getEncoded(ASN1Encoding.DER));
+
+            sOut.close();
+        }
+        catch (Exception e)
+        {
+            throw new PKCSException("unable to process signature: " + e.getMessage(), e);
+        }
+
+        return verifier.verify(this.altSignatureValue.getOctets());
+    }
+
+    /**
+     * Return any extensions requested in the PKCS#10 request. If none are present, the method
+     * will return null.
+     *
+     * @return the requested extensions, null if none are requested.
+     * @throws IllegalStateException if the extension request is and is somehow invalid.
+     */
+    public Extensions getRequestedExtensions()
+    {
+        Attribute[] attributes = getAttributes();
+        for (int i = 0; i != attributes.length; i++)
+        {
+            Attribute encodable = attributes[i];
+            if (PKCSObjectIdentifiers.pkcs_9_at_extensionRequest.equals(encodable.getAttrType()))
+            {
+                ExtensionsGenerator extensionsGenerator = new ExtensionsGenerator();
+
+                ASN1Set attrValues = encodable.getAttrValues();
+                if (attrValues == null || attrValues.size() == 0)
+                {
+                    throw new IllegalStateException("pkcs_9_at_extensionRequest present but has no value");
+                }
+
+                ASN1Sequence extensionSequence = ASN1Sequence.getInstance(attrValues.getObjectAt(0));
+
+                try
+                {
+                    for (Enumeration en = extensionSequence.getObjects(); en.hasMoreElements(); )
+                    {
+                        ASN1Sequence itemSeq = ASN1Sequence.getInstance(en.nextElement());
+
+                        boolean critical = itemSeq.size() == 3 && ASN1Boolean.getInstance(itemSeq.getObjectAt(1)).isTrue();
+                        if (itemSeq.size() == 2)
+                        {
+                            extensionsGenerator.addExtension(ASN1ObjectIdentifier.getInstance(itemSeq.getObjectAt(0)), false, ASN1OctetString.getInstance(itemSeq.getObjectAt(1)).getOctets());
+                        }
+                        else if (itemSeq.size() == 3)
+                        {
+                            extensionsGenerator.addExtension(ASN1ObjectIdentifier.getInstance(itemSeq.getObjectAt(0)), critical, ASN1OctetString.getInstance(itemSeq.getObjectAt(2)).getOctets());
+                        }
+                        else
+                        {
+                            throw new IllegalStateException("incorrect sequence size of Extension get " + itemSeq.size() + " expected 2 or three");
+                        }
+                    }
+                }
+                catch (IllegalArgumentException e)
+                {
+                    throw Exceptions.illegalStateException("asn1 processing issue: " + e.getMessage(), e);
+                }
+
+                return extensionsGenerator.generate();
+            }
+        }
+        return null;
+    }
+
+
+    public boolean equals(Object o)
+    {
+        if (o == this)
+        {
+            return true;
+        }
+
+        if (!(o instanceof PKCS10CertificationRequest))
+        {
+            return false;
+        }
+
+        PKCS10CertificationRequest other = (PKCS10CertificationRequest)o;
+
+        return this.toASN1Structure().equals(other.toASN1Structure());
+    }
+
+    public int hashCode()
+    {
+        return this.toASN1Structure().hashCode();
+    }
+}
diff --git a/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/pkcs/PKCSException.java b/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/pkcs/PKCSException.java
new file mode 100644
index 0000000..71ec05b
--- /dev/null
+++ b/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/pkcs/PKCSException.java
@@ -0,0 +1,29 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.pkcs;
+
+/**
+ * General checked Exception thrown in the cert package and its sub-packages.
+ * @hide This class is not part of the Android public SDK API
+ */
+public class PKCSException
+    extends Exception
+{
+    private Throwable cause;
+
+    public PKCSException(String msg, Throwable cause)
+    {
+        super(msg);
+
+        this.cause = cause;
+    }
+
+    public PKCSException(String msg)
+    {
+        super(msg);
+    }
+
+    public Throwable getCause()
+    {
+        return cause;
+    }
+}
diff --git a/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/pkcs/PKCSIOException.java b/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/pkcs/PKCSIOException.java
new file mode 100644
index 0000000..7132b3b
--- /dev/null
+++ b/repackaged_platform/bcpkix/src/main/java/com/android/internal/org/bouncycastle/pkcs/PKCSIOException.java
@@ -0,0 +1,32 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.pkcs;
+
+import java.io.IOException;
+
+/**
+ * General IOException thrown in the cert package and its sub-packages.
+ * @hide This class is not part of the Android public SDK API
+ */
+public class PKCSIOException
+    extends IOException
+{
+    private Throwable cause;
+
+    public PKCSIOException(String msg, Throwable cause)
+    {
+        super(msg);
+
+        this.cause = cause;
+    }
+
+    public PKCSIOException(String msg)
+    {
+        super(msg);
+    }
+
+    public Throwable getCause()
+    {
+        return cause;
+    }
+}
+
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1Absent.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1Absent.java
new file mode 100644
index 0000000..0b3f8f2
--- /dev/null
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1Absent.java
@@ -0,0 +1,47 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.asn1;
+
+import java.io.IOException;
+
+/**
+ * An ASN1 class that encodes to nothing, used in the OER library to deal with the Optional type.
+ * @hide This class is not part of the Android public SDK API
+ */
+public class ASN1Absent
+    extends ASN1Primitive
+{
+
+    public static final ASN1Absent INSTANCE = new ASN1Absent();
+
+    private ASN1Absent()
+    {
+
+    }
+
+    public int hashCode()
+    {
+        return 0;
+    }
+
+    boolean encodeConstructed()
+    {
+        return false;
+    }
+
+    int encodedLength(boolean withTag)
+        throws IOException
+    {
+        return 0;
+    }
+
+    void encode(ASN1OutputStream out, boolean withTag)
+        throws IOException
+    {
+
+    }
+
+    boolean asn1Equals(ASN1Primitive o)
+    {
+        return o == this;
+    }
+}
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1ApplicationSpecific.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1ApplicationSpecific.java
deleted file mode 100644
index 38a6d58..0000000
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1ApplicationSpecific.java
+++ /dev/null
@@ -1,248 +0,0 @@
-/* GENERATED SOURCE. DO NOT MODIFY. */
-package com.android.internal.org.bouncycastle.asn1;
-
-import java.io.IOException;
-
-import com.android.internal.org.bouncycastle.util.Arrays;
-import com.android.internal.org.bouncycastle.util.encoders.Hex;
-
-/**
- * Base class for an ASN.1 ApplicationSpecific object
- * @hide This class is not part of the Android public SDK API
- */
-public abstract class ASN1ApplicationSpecific
-    extends ASN1Primitive
-{
-    protected final boolean   isConstructed;
-    protected final int       tag;
-    protected final byte[]    octets;
-
-    ASN1ApplicationSpecific(
-        boolean isConstructed,
-        int tag,
-        byte[] octets)
-    {
-        this.isConstructed = isConstructed;
-        this.tag = tag;
-        this.octets = Arrays.clone(octets);
-    }
-
-    /**
-     * Return an ASN1ApplicationSpecific from the passed in object, which may be a byte array, or null.
-     *
-     * @param obj the object to be converted.
-     * @return obj's representation as an ASN1ApplicationSpecific object.
-     */
-    public static ASN1ApplicationSpecific getInstance(Object obj)
-    {
-        if (obj == null || obj instanceof ASN1ApplicationSpecific)
-        {
-            return (ASN1ApplicationSpecific)obj;
-        }
-        else if (obj instanceof byte[])
-        {
-            try
-            {
-                return ASN1ApplicationSpecific.getInstance(ASN1Primitive.fromByteArray((byte[])obj));
-            }
-            catch (IOException e)
-            {
-                throw new IllegalArgumentException("Failed to construct object from byte[]: " + e.getMessage());
-            }
-        }
-
-        throw new IllegalArgumentException("unknown object in getInstance: " + obj.getClass().getName());
-    }
-
-    protected static int getLengthOfHeader(byte[] data)
-    {
-        int length = data[1] & 0xff; // TODO: assumes 1 byte tag
-
-        if (length == 0x80)
-        {
-            return 2;      // indefinite-length encoding
-        }
-
-        if (length > 127)
-        {
-            int size = length & 0x7f;
-
-            // Note: The invalid long form "0xff" (see X.690 8.1.3.5c) will be caught here
-            if (size > 4)
-            {
-                throw new IllegalStateException("DER length more than 4 bytes: " + size);
-            }
-
-            return size + 2;
-        }
-
-        return 2;
-    }
-
-    /**
-     * Return true if the object is marked as constructed, false otherwise.
-     *
-     * @return true if constructed, otherwise false.
-     */
-    public boolean isConstructed()
-    {
-        return isConstructed;
-    }
-
-    /**
-     * Return the contents of this object as a byte[]
-     *
-     * @return the encoded contents of the object.
-     */
-    public byte[] getContents()
-    {
-        return Arrays.clone(octets);
-    }
-
-    /**
-     * Return the tag number associated with this object,
-     *
-     * @return the application tag number.
-     */
-    public int getApplicationTag() 
-    {
-        return tag;
-    }
-
-    /**
-     * Return the enclosed object assuming explicit tagging.
-     *
-     * @return  the resulting object
-     * @throws IOException if reconstruction fails.
-     */
-    public ASN1Primitive getObject()
-        throws IOException 
-    {
-        return ASN1Primitive.fromByteArray(getContents());
-    }
-
-    /**
-     * Return the enclosed object assuming implicit tagging.
-     *
-     * @param derTagNo the type tag that should be applied to the object's contents.
-     * @return  the resulting object
-     * @throws IOException if reconstruction fails.
-     */
-    public ASN1Primitive getObject(int derTagNo)
-        throws IOException
-    {
-        if (derTagNo >= 0x1f)
-        {
-            throw new IOException("unsupported tag number");
-        }
-
-        byte[] orig = this.getEncoded();
-        byte[] tmp = replaceTagNumber(derTagNo, orig);
-
-        if ((orig[0] & BERTags.CONSTRUCTED) != 0)
-        {
-            tmp[0] |= BERTags.CONSTRUCTED;
-        }
-
-        return ASN1Primitive.fromByteArray(tmp);
-    }
-
-    int encodedLength()
-        throws IOException
-    {
-        return StreamUtil.calculateTagLength(tag) + StreamUtil.calculateBodyLength(octets.length) + octets.length;
-    }
-
-    /* (non-Javadoc)
-     * @see org.bouncycastle.asn1.ASN1Primitive#encode(org.bouncycastle.asn1.DEROutputStream)
-     */
-    void encode(ASN1OutputStream out, boolean withTag) throws IOException
-    {
-        int flags = BERTags.APPLICATION;
-        if (isConstructed)
-        {
-            flags |= BERTags.CONSTRUCTED;
-        }
-
-        out.writeEncoded(withTag, flags, tag, octets);
-    }
-
-    boolean asn1Equals(
-        ASN1Primitive o)
-    {
-        if (!(o instanceof ASN1ApplicationSpecific))
-        {
-            return false;
-        }
-
-        ASN1ApplicationSpecific other = (ASN1ApplicationSpecific)o;
-
-        return isConstructed == other.isConstructed
-            && tag == other.tag
-            && Arrays.areEqual(octets, other.octets);
-    }
-
-    public int hashCode()
-    {
-        return (isConstructed ? 1 : 0) ^ tag ^ Arrays.hashCode(octets);
-    }
-
-    private byte[] replaceTagNumber(int newTag, byte[] input)
-        throws IOException
-    {
-        int tagNo = input[0] & 0x1f;
-        int index = 1;
-        //
-        // with tagged object tag number is bottom 5 bits, or stored at the start of the content
-        //
-        if (tagNo == 0x1f)
-        {
-            int b = input[index++] & 0xff;
-
-            // X.690-0207 8.1.2.4.2
-            // "c) bits 7 to 1 of the first subsequent octet shall not all be zero."
-            if ((b & 0x7f) == 0) // Note: -1 will pass
-            {
-                throw new IOException("corrupted stream - invalid high tag number found");
-            }
-
-            while ((b & 0x80) != 0)
-            {
-                b = input[index++] & 0xff;
-            }
-        }
-
-        byte[] tmp = new byte[input.length - index + 1];
-
-        System.arraycopy(input, index, tmp, 1, tmp.length - 1);
-
-        tmp[0] = (byte)newTag;
-
-        return tmp;
-    }
-
-    public String toString()
-    {
-        StringBuffer sb = new StringBuffer();
-        sb.append("[");
-        if (isConstructed())
-        {
-            sb.append("CONSTRUCTED ");
-        }
-        sb.append("APPLICATION ");
-        sb.append(Integer.toString(getApplicationTag()));
-        sb.append("]");
-        // @todo content encoding somehow?
-        if (this.octets != null)
-        {
-            sb.append(" #");
-            sb.append(Hex.toHexString(this.octets));
-        }
-        else
-        {
-            sb.append(" #null");
-        }
-        sb.append(" ");
-        return sb.toString();
-    }
-}
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1BMPString.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1BMPString.java
new file mode 100644
index 0000000..3b455f5
--- /dev/null
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1BMPString.java
@@ -0,0 +1,215 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.asn1;
+
+import java.io.IOException;
+
+import com.android.internal.org.bouncycastle.util.Arrays;
+
+/**
+ * ASN.1 BMPString object encodes BMP (<i>Basic Multilingual Plane</i>) subset
+ * (aka UCS-2) of UNICODE (ISO 10646) characters in codepoints 0 to 65535.
+ * <p>
+ * At ISO-10646:2011 the term "BMP" has been withdrawn, and replaced by
+ * term "UCS-2".
+ * </p>
+ * @hide This class is not part of the Android public SDK API
+ */
+public abstract class ASN1BMPString
+    extends ASN1Primitive
+    implements ASN1String
+{
+    static final ASN1UniversalType TYPE = new ASN1UniversalType(ASN1BMPString.class, BERTags.BMP_STRING)
+    {
+        ASN1Primitive fromImplicitPrimitive(DEROctetString octetString)
+        {
+            return createPrimitive(octetString.getOctets());
+        }
+    };
+
+    /**
+     * Return a BMP String from the given object.
+     *
+     * @param obj the object we want converted.
+     * @exception IllegalArgumentException if the object cannot be converted.
+     * @return an ASN1BMPString instance, or null.
+     */
+    public static ASN1BMPString getInstance(Object obj)
+    {
+        if (obj == null || obj instanceof ASN1BMPString)
+        {
+            return (ASN1BMPString)obj;
+        }
+        if (obj instanceof ASN1Encodable)
+        {
+            ASN1Primitive primitive = ((ASN1Encodable)obj).toASN1Primitive();
+            if (primitive instanceof ASN1BMPString)
+            {
+                return (ASN1BMPString)primitive;
+            }
+        }
+        if (obj instanceof byte[])
+        {
+            try
+            {
+                return (ASN1BMPString)TYPE.fromByteArray((byte[])obj);
+            }
+            catch (Exception e)
+            {
+                throw new IllegalArgumentException("encoding error in getInstance: " + e.toString());
+            }
+        }
+
+        throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
+    }
+
+    /**
+     * Return a BMP String from a tagged object.
+     *
+     * @param taggedObject      the tagged object holding the object we want
+     * @param explicit true if the object is meant to be explicitly tagged false
+     *                 otherwise.
+     * @exception IllegalArgumentException if the tagged object cannot be converted.
+     * @return an ASN1BMPString instance.
+     */
+    public static ASN1BMPString getInstance(ASN1TaggedObject taggedObject, boolean explicit)
+    {
+        return (ASN1BMPString)TYPE.getContextInstance(taggedObject, explicit);
+    }
+
+    final char[] string;
+
+    ASN1BMPString(String string)
+    {
+        if (string == null)
+        {
+            throw new NullPointerException("'string' cannot be null");
+        }
+
+        this.string = string.toCharArray();
+    }
+
+    ASN1BMPString(byte[] string)
+    {
+        if (string == null)
+        {
+            throw new NullPointerException("'string' cannot be null");
+        }
+
+        int byteLen = string.length;
+        if (0 != (byteLen & 1))
+        {
+            throw new IllegalArgumentException("malformed BMPString encoding encountered");
+        }
+
+        int charLen = byteLen / 2;
+        char[] cs = new char[charLen];
+
+        for (int i = 0; i != charLen; i++)
+        {
+            cs[i] = (char)((string[2 * i] << 8) | (string[2 * i + 1] & 0xff));
+        }
+
+        this.string = cs;
+    }
+
+    ASN1BMPString(char[] string)
+    {
+        if (string == null)
+        {
+            throw new NullPointerException("'string' cannot be null");
+        }
+
+        this.string = string;
+    }
+
+    public final String getString()
+    {
+        return new String(string);
+    }
+
+    public String toString()
+    {
+        return getString();
+    }
+
+    final boolean asn1Equals(ASN1Primitive other)
+    {
+        if (!(other instanceof ASN1BMPString))
+        {
+            return false;
+        }
+
+        ASN1BMPString that = (ASN1BMPString)other;
+
+        return Arrays.areEqual(this.string, that.string);
+    }
+
+    public final int hashCode()
+    {
+        return Arrays.hashCode(string);
+    }
+
+    final boolean encodeConstructed()
+    {
+        return false;
+    }
+
+    final int encodedLength(boolean withTag)
+    {
+        return ASN1OutputStream.getLengthOfEncodingDL(withTag, string.length * 2);
+    }
+
+    final void encode(ASN1OutputStream out, boolean withTag) throws IOException
+    {
+        int count = string.length;
+
+        out.writeIdentifier(withTag, BERTags.BMP_STRING);
+        out.writeDL(count * 2);
+
+        byte[] buf = new byte[8];
+
+        int i = 0, limit = count & -4;
+        while (i < limit)
+        {
+            char c0 = string[i], c1 = string[i + 1], c2 = string[i + 2], c3 = string[i + 3];
+            i += 4;
+
+            buf[0] = (byte)(c0 >> 8);
+            buf[1] = (byte)c0;
+            buf[2] = (byte)(c1 >> 8);
+            buf[3] = (byte)c1;
+            buf[4] = (byte)(c2 >> 8);
+            buf[5] = (byte)c2;
+            buf[6] = (byte)(c3 >> 8);
+            buf[7] = (byte)c3;
+
+            out.write(buf, 0, 8);
+        }
+        if (i < count)
+        {
+            int bufPos = 0;
+            do
+            {
+                char c0 = string[i];
+                i += 1;
+
+                buf[bufPos++] = (byte)(c0 >> 8);
+                buf[bufPos++] = (byte)c0;
+            }
+            while (i < count);
+
+            out.write(buf, 0, bufPos);
+        }
+    }
+
+    static ASN1BMPString createPrimitive(byte[] contents)
+    {
+        return new DERBMPString(contents);
+    }
+
+    static ASN1BMPString createPrimitive(char[] string)
+    {
+        // TODO ASN1InputStream has a validator/converter that should be unified in this class somehow
+        return new DERBMPString(string);
+    }
+}
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1BitString.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1BitString.java
index 001dd61..056e9dd 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1BitString.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1BitString.java
@@ -1,12 +1,11 @@
 /* GENERATED SOURCE. DO NOT MODIFY. */
 package com.android.internal.org.bouncycastle.asn1;
 
-import java.io.EOFException;
+import java.io.ByteArrayInputStream;
 import java.io.IOException;
 import java.io.InputStream;
 
 import com.android.internal.org.bouncycastle.util.Arrays;
-import com.android.internal.org.bouncycastle.util.io.Streams;
 
 /**
  * Base class for BIT STRING objects
@@ -14,12 +13,57 @@
  */
 public abstract class ASN1BitString
     extends ASN1Primitive
-    implements ASN1String
+    implements ASN1String, ASN1BitStringParser
 {
-    private static final char[]  table = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
+    static final ASN1UniversalType TYPE = new ASN1UniversalType(ASN1BitString.class, BERTags.BIT_STRING)
+    {
+        ASN1Primitive fromImplicitPrimitive(DEROctetString octetString)
+        {
+            return createPrimitive(octetString.getOctets());
+        }
 
-    protected final byte[]      data;
-    protected final int         padBits;
+        ASN1Primitive fromImplicitConstructed(ASN1Sequence sequence)
+        {
+            return sequence.toASN1BitString();
+        }
+    };
+
+    public static ASN1BitString getInstance(Object obj)
+    {
+        if (obj == null || obj instanceof ASN1BitString)
+        {
+            return (ASN1BitString)obj;
+        }
+//      else if (obj instanceof ASN1BitStringParser)
+        else if (obj instanceof ASN1Encodable)
+        {
+            ASN1Primitive primitive = ((ASN1Encodable)obj).toASN1Primitive();
+            if (primitive instanceof ASN1BitString)
+            {
+                return (ASN1BitString)primitive;
+            }
+        }
+        else if (obj instanceof byte[])
+        {
+            try
+            {
+                return (ASN1BitString)TYPE.fromByteArray((byte[])obj);
+            }
+            catch (IOException e)
+            {
+                throw new IllegalArgumentException("failed to construct BIT STRING from byte[]: " + e.getMessage());
+            }
+        }
+
+        throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
+    }
+
+    public static ASN1BitString getInstance(ASN1TaggedObject taggedObject, boolean explicit)
+    {
+        return (ASN1BitString)TYPE.getContextInstance(taggedObject, explicit);
+    }
+
+    private static final char[]  table = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
 
     /**
      * @param bitString an int containing the BIT STRING
@@ -101,15 +145,16 @@
         return result;
     }
 
-    protected ASN1BitString(byte data, int padBits)
+    final byte[] contents;
+
+    ASN1BitString(byte data, int padBits)
     {
         if (padBits > 7 || padBits < 0)
         {
             throw new IllegalArgumentException("pad bits cannot be greater than 7 or less than 0");
         }
 
-        this.data = new byte[]{ data };
-        this.padBits = padBits;
+        this.contents = new byte[]{ (byte)padBits, data };
     }
 
     /**
@@ -118,9 +163,7 @@
      * @param data the octets making up the bit string.
      * @param padBits the number of extra bits at the end of the string.
      */
-    public ASN1BitString(
-        byte[]  data,
-        int     padBits)
+    ASN1BitString(byte[] data, int padBits)
     {
         if (data == null)
         {
@@ -135,8 +178,58 @@
             throw new IllegalArgumentException("pad bits cannot be greater than 7 or less than 0");
         }
 
-        this.data = Arrays.clone(data);
-        this.padBits = padBits;
+        this.contents = Arrays.prepend(data, (byte)padBits);
+    }
+
+    ASN1BitString(byte[] contents, boolean check)
+    {
+        if (check)
+        {
+            if (null == contents)
+            {
+                throw new NullPointerException("'contents' cannot be null");
+            }
+            if (contents.length < 1)
+            {
+                throw new IllegalArgumentException("'contents' cannot be empty");
+            }
+
+            int padBits = contents[0] & 0xFF;
+            if (padBits > 0)
+            {
+                if (contents.length < 2)
+                {
+                    throw new IllegalArgumentException("zero length data with non-zero pad bits");
+                }
+                if (padBits > 7)
+                {
+                    throw new IllegalArgumentException("pad bits cannot be greater than 7 or less than 0");
+                }
+            }
+        }
+
+        this.contents = contents;
+    }
+
+    public InputStream getBitStream() throws IOException
+    {
+        return new ByteArrayInputStream(contents, 1, contents.length - 1);
+    }
+
+    public InputStream getOctetStream() throws IOException
+    {
+        int padBits = contents[0] & 0xFF;
+        if (0 != padBits)
+        {
+            throw new IOException("expected octet-aligned bitstring, but found padBits: " + padBits);
+        }
+
+        return getBitStream();
+    }
+
+    public ASN1BitStringParser parser()
+    {
+        return this;
     }
 
     /**
@@ -146,8 +239,6 @@
      */
     public String getString()
     {
-        StringBuffer buf = new StringBuffer("#");
-
         byte[] string;
         try
         {
@@ -158,10 +249,14 @@
             throw new ASN1ParsingException("Internal error encoding BitString: " + e.getMessage(), e);
         }
 
+        StringBuffer buf = new StringBuffer(1 + string.length * 2);
+        buf.append('#');
+
         for (int i = 0; i != string.length; i++)
         {
-            buf.append(table[(string[i] >>> 4) & 0xf]);
-            buf.append(table[string[i] & 0xf]);
+            byte b = string[i];
+            buf.append(table[(b >>> 4) & 0xf]);
+            buf.append(table[b & 0xf]);
         }
 
         return buf.toString();
@@ -173,15 +268,16 @@
     public int intValue()
     {
         int value = 0;
-        int end = Math.min(4, data.length - 1);
-        for (int i = 0; i < end; ++i)
+        int end = Math.min(5, contents.length - 1);
+        for (int i = 1; i < end; ++i)
         {
-            value |= (data[i] & 0xFF) << (8 * i);
+            value |= (contents[i] & 0xFF) << (8 * (i - 1));
         }
-        if (0 <= end && end < 4)
+        if (1 <= end && end < 5)
         {
-            byte der = (byte)(data[end] & (0xFF << padBits));
-            value |= (der & 0xFF) << (8 * end);
+            int padBits = contents[0] & 0xFF;
+            byte der = (byte)(contents[end] & (0xFF << padBits));
+            value |= (der & 0xFF) << (8 * (end - 1));
         }
         return value;
     }
@@ -195,30 +291,31 @@
      */
     public byte[] getOctets()
     {
-        if (padBits != 0)
+        if (contents[0] != 0)
         {
             throw new IllegalStateException("attempt to get non-octet aligned data from BIT STRING");
         }
 
-        return Arrays.clone(data);
+        return Arrays.copyOfRange(contents, 1, contents.length);
     }
 
     public byte[] getBytes()
     {
-        if (0 == data.length)
+        if (contents.length == 1)
         {
-            return data;
+            return ASN1OctetString.EMPTY_OCTETS;
         }
 
-        byte[] rv = Arrays.clone(data);
+        int padBits = contents[0] & 0xFF;
+        byte[] rv = Arrays.copyOfRange(contents, 1, contents.length);
         // DER requires pad bits be zero
-        rv[data.length - 1] &= (0xFF << padBits);
+        rv[rv.length - 1] &= (byte)(0xFF << padBits);
         return rv;
     }
 
     public int getPadBits()
     {
-        return padBits;
+        return contents[0] & 0xFF;
     }
 
     public String toString()
@@ -228,85 +325,56 @@
 
     public int hashCode()
     {
-        int end = data.length;
-        if (--end < 0)
+        if (contents.length < 2)
         {
             return 1;
         }
 
-        byte der = (byte)(data[end] & (0xFF << padBits));
+        int padBits = contents[0] & 0xFF;
+        int last = contents.length - 1;
 
-        int hc = Arrays.hashCode(data, 0, end);
+        byte lastOctetDER = (byte)(contents[last] & (0xFF << padBits));
+
+        int hc = Arrays.hashCode(contents, 0, last);
         hc *= 257;
-        hc ^= der;
-        return hc ^ padBits;
+        hc ^= lastOctetDER;
+        return hc;
     }
 
-    boolean asn1Equals(
-        ASN1Primitive o)
+    boolean asn1Equals(ASN1Primitive other)
     {
-        if (!(o instanceof ASN1BitString))
+        if (!(other instanceof ASN1BitString))
         {
             return false;
         }
 
-        ASN1BitString other = (ASN1BitString)o;
-        if (padBits != other.padBits)
+        ASN1BitString that = (ASN1BitString)other;
+        byte[] thisContents = this.contents, thatContents = that.contents;
+
+        int length = thisContents.length;
+        if (thatContents.length != length)
         {
             return false;
         }
-        byte[] a = data, b = other.data;
-        int end = a.length;
-        if (end != b.length)
-        {
-            return false;
-        }
-        if (--end < 0)
+        if (length == 1)
         {
             return true;
         }
-        for (int i = 0; i < end; ++i)
+
+        int last = length - 1;
+        for (int i = 0; i < last; ++i)
         {
-            if (a[i] != b[i])
+            if (thisContents[i] != thatContents[i])
             {
                 return false;
             }
         }
 
-        byte derA = (byte)(a[end] & (0xFF << padBits));
-        byte derB = (byte)(b[end] & (0xFF << padBits));
+        int padBits = thisContents[0] & 0xFF;
+        byte thisLastOctetDER = (byte)(thisContents[last] & (0xFF << padBits));
+        byte thatLastOctetDER = (byte)(thatContents[last] & (0xFF << padBits));
 
-        return derA == derB;
-    }
-
-    static ASN1BitString fromInputStream(int length, InputStream stream)
-        throws IOException
-    {
-        if (length < 1)
-        {
-            throw new IllegalArgumentException("truncated BIT STRING detected");
-        }
-
-        int padBits = stream.read();
-        byte[] data = new byte[length - 1];
-
-        if (data.length != 0)
-        {
-            if (Streams.readFully(stream, data) != data.length)
-            {
-                throw new EOFException("EOF encountered in middle of BIT STRING");
-            }
-
-            if (padBits > 0 && padBits < 8)
-            {
-                if (data[data.length - 1] != (byte)(data[data.length - 1] & (0xFF << padBits)))
-                {
-                    return new DLBitString(data, padBits);
-                }
-            }
-        }
-
-        return new DERBitString(data, padBits);
+        return thisLastOctetDER == thatLastOctetDER;
     }
 
     public ASN1Primitive getLoadedObject()
@@ -316,13 +384,37 @@
 
     ASN1Primitive toDERObject()
     {
-        return new DERBitString(data, padBits);
+        return new DERBitString(contents, false);
     }
 
     ASN1Primitive toDLObject()
     {
-        return new DLBitString(data, padBits);
+        return new DLBitString(contents, false);
     }
 
-    abstract void encode(ASN1OutputStream out, boolean withTag) throws IOException;
+    static ASN1BitString createPrimitive(byte[] contents)
+    {
+        int length = contents.length;
+        if (length < 1)
+        {
+            throw new IllegalArgumentException("truncated BIT STRING detected");
+        }
+
+        int padBits = contents[0] & 0xFF;
+        if (padBits > 0)
+        {
+            if (padBits > 7 || length < 2)
+            {
+                throw new IllegalArgumentException("invalid pad bits detected");
+            }
+
+            byte finalOctet = contents[length - 1];
+            if (finalOctet != (byte)(finalOctet & (0xFF << padBits)))
+            {
+                return new DLBitString(contents, false);
+            }
+        }
+
+        return new DERBitString(contents, false);
+    }
 }
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1BitStringParser.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1BitStringParser.java
new file mode 100644
index 0000000..8ecd50f
--- /dev/null
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1BitStringParser.java
@@ -0,0 +1,42 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.asn1;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * A basic parser for a BIT STRING object
+ * @hide This class is not part of the Android public SDK API
+ */
+public interface ASN1BitStringParser
+    extends ASN1Encodable, InMemoryRepresentable
+{
+    /**
+     * Return an InputStream representing the contents of the BIT STRING. The final
+     * byte, if any, may include pad bits. See {@link #getPadBits()}.
+     *
+     * @return an InputStream with its source as the BIT STRING content.
+     */
+    public InputStream getBitStream() throws IOException;
+
+    /**
+     * Return an InputStream representing the contents of the BIT STRING, where the
+     * content is expected to be octet-aligned (this will be automatically checked
+     * during parsing).
+     *
+     * @return an InputStream with its source as the BIT STRING content.
+     */
+    public InputStream getOctetStream() throws IOException;
+
+    /**
+     * Return the number of pad bits, if any, in the final byte, if any, read from
+     * {@link #getBitStream()}. This number is in the range zero to seven. That
+     * number of the least significant bits of the final byte, if any, are not part
+     * of the contents and should be ignored. NOTE: Must be called AFTER the stream
+     * has been fully processed. (Does not need to be called if
+     * {@link #getOctetStream()} was used instead of {@link #getBitStream()}).
+     *
+     * @return the number of pad bits. In the range zero to seven.
+     */
+    public int getPadBits();
+}
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1Boolean.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1Boolean.java
index 80d935a..17a34fa 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1Boolean.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1Boolean.java
@@ -18,6 +18,14 @@
 public class ASN1Boolean
     extends ASN1Primitive
 {
+    static final ASN1UniversalType TYPE = new ASN1UniversalType(ASN1Boolean.class, BERTags.BOOLEAN)
+    {
+        ASN1Primitive fromImplicitPrimitive(DEROctetString octetString)
+        {
+            return createPrimitive(octetString.getOctets());
+        }
+    };
+
     private static final byte FALSE_VALUE = 0x00;
     private static final byte TRUE_VALUE = (byte)0xFF;
 
@@ -46,7 +54,7 @@
             byte[] enc = (byte[])obj;
             try
             {
-                return (ASN1Boolean)fromByteArray(enc);
+                return (ASN1Boolean)TYPE.fromByteArray(enc);
             }
             catch (IOException e)
             {
@@ -91,25 +99,16 @@
     /**
      * Return a Boolean from a tagged object.
      *
-     * @param obj the tagged object holding the object we want
+     * @param taggedObject the tagged object holding the object we want
      * @param explicit true if the object is meant to be explicitly
      *              tagged false otherwise.
      * @exception IllegalArgumentException if the tagged object cannot
      *               be converted.
      * @return an ASN1Boolean instance.
      */
-    public static ASN1Boolean getInstance(ASN1TaggedObject obj, boolean explicit)
+    public static ASN1Boolean getInstance(ASN1TaggedObject taggedObject, boolean explicit)
     {
-        ASN1Primitive o = obj.getObject();
-
-        if (explicit || o instanceof ASN1Boolean)
-        {
-            return getInstance(o);
-        }
-        else
-        {
-            return ASN1Boolean.fromOctetString(ASN1OctetString.getInstance(o).getOctets());
-        }
+        return (ASN1Boolean)TYPE.getContextInstance(taggedObject, explicit);
     }
 
     private ASN1Boolean(byte value)
@@ -122,19 +121,19 @@
         return value != FALSE_VALUE;
     }
 
-    boolean isConstructed()
+    boolean encodeConstructed()
     {
         return false;
     }
 
-    int encodedLength()
+    int encodedLength(boolean withTag)
     {
-        return 3;
+        return ASN1OutputStream.getLengthOfEncodingDL(withTag, 1);
     }
 
     void encode(ASN1OutputStream out, boolean withTag) throws IOException
     {
-        out.writeEncoded(withTag, BERTags.BOOLEAN, value);
+        out.writeEncodingDL(withTag, BERTags.BOOLEAN, value);
     }
 
     boolean asn1Equals(ASN1Primitive other)
@@ -164,14 +163,14 @@
       return isTrue() ? "TRUE" : "FALSE";
     }
 
-    static ASN1Boolean fromOctetString(byte[] value)
+    static ASN1Boolean createPrimitive(byte[] contents)
     {
-        if (value.length != 1)
+        if (contents.length != 1)
         {
             throw new IllegalArgumentException("BOOLEAN value should have 1 byte in it");
         }
 
-        byte b = value[0];
+        byte b = contents[0];
         switch (b)
         {
         case FALSE_VALUE:   return FALSE;
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1EncodableVector.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1EncodableVector.java
index aabcc62..b61d74e 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1EncodableVector.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1EncodableVector.java
@@ -50,6 +50,16 @@
         this.elementCount = minCapacity;
     }
 
+    public void addAll(ASN1Encodable[] others)
+    {
+        if (null == others)
+        {
+            throw new NullPointerException("'others' cannot be null");
+        }
+
+        doAddAll(others, "'others' elements cannot be null");
+    }
+
     public void addAll(ASN1EncodableVector other)
     {
         if (null == other)
@@ -57,7 +67,12 @@
             throw new NullPointerException("'other' cannot be null");
         }
 
-        int otherElementCount = other.size();
+        doAddAll(other.elements, "'other' elements cannot be null");
+    }
+
+    private void doAddAll(ASN1Encodable[] others, String nullMsg)
+    {
+        int otherElementCount = others.length;
         if (otherElementCount < 1)
         {
             return;
@@ -73,10 +88,10 @@
         int i = 0;
         do
         {
-            ASN1Encodable otherElement = other.get(i);
+            ASN1Encodable otherElement = others[i];
             if (null == otherElement)
             {
-                throw new NullPointerException("'other' elements cannot be null");
+                throw new NullPointerException(nullMsg);
             }
 
             this.elements[elementCount + i] = otherElement;
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1Enumerated.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1Enumerated.java
index d95d62e..6857a3e 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1Enumerated.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1Enumerated.java
@@ -13,8 +13,13 @@
 public class ASN1Enumerated
     extends ASN1Primitive
 {
-    private final byte[] bytes;
-    private final int start;
+    static final ASN1UniversalType TYPE = new ASN1UniversalType(ASN1Enumerated.class, BERTags.ENUMERATED)
+    {
+        ASN1Primitive fromImplicitPrimitive(DEROctetString octetString)
+        {
+            return createPrimitive(octetString.getOctets(), false);
+        }
+    };
 
     /**
      * return an enumerated from the passed in object
@@ -35,7 +40,7 @@
         {
             try
             {
-                return (ASN1Enumerated)fromByteArray((byte[])obj);
+                return (ASN1Enumerated)TYPE.fromByteArray((byte[])obj);
             }
             catch (Exception e)
             {
@@ -49,29 +54,21 @@
     /**
      * return an Enumerated from a tagged object.
      *
-     * @param obj the tagged object holding the object we want
+     * @param taggedObject the tagged object holding the object we want
      * @param explicit true if the object is meant to be explicitly
      *              tagged false otherwise.
      * @exception IllegalArgumentException if the tagged object cannot
      *               be converted.
      * @return an ASN1Enumerated instance, or null.
      */
-    public static ASN1Enumerated getInstance(
-        ASN1TaggedObject obj,
-        boolean          explicit)
+    public static ASN1Enumerated getInstance(ASN1TaggedObject taggedObject, boolean explicit)
     {
-        ASN1Primitive o = obj.getObject();
-
-        if (explicit || o instanceof ASN1Enumerated)
-        {
-            return getInstance(o);
-        }
-        else
-        {
-            return fromOctetString(ASN1OctetString.getInstance(o).getOctets());
-        }
+        return (ASN1Enumerated)TYPE.getContextInstance(taggedObject, explicit);
     }
 
+    private final byte[] contents;
+    private final int start;
+
     /**
      * Constructor from int.
      *
@@ -84,7 +81,7 @@
             throw new IllegalArgumentException("enumerated must be non-negative");
         }
 
-        this.bytes = BigInteger.valueOf(value).toByteArray();
+        this.contents = BigInteger.valueOf(value).toByteArray();
         this.start = 0;
     }
 
@@ -100,67 +97,78 @@
             throw new IllegalArgumentException("enumerated must be non-negative");
         }
 
-        this.bytes = value.toByteArray();
+        this.contents = value.toByteArray();
         this.start = 0;
     }
 
     /**
      * Constructor from encoded BigInteger.
      *
-     * @param bytes the value of this enumerated as an encoded BigInteger (signed).
+     * @param contents the value of this enumerated as an encoded BigInteger (signed).
      */
-    public ASN1Enumerated(byte[] bytes)
+    public ASN1Enumerated(byte[] contents)
     {
-        if (ASN1Integer.isMalformed(bytes))
+        this(contents, true);
+    }
+
+    ASN1Enumerated(byte[] contents, boolean clone)
+    {
+        if (ASN1Integer.isMalformed(contents))
         {
             throw new IllegalArgumentException("malformed enumerated");
         }
-        if (0 != (bytes[0] & 0x80))
+        if (0 != (contents[0] & 0x80))
         {
             throw new IllegalArgumentException("enumerated must be non-negative");
         }
 
-        this.bytes = Arrays.clone(bytes);
-        this.start = ASN1Integer.signBytesToSkip(bytes); 
+        this.contents = clone ? Arrays.clone(contents) : contents;
+        this.start = ASN1Integer.signBytesToSkip(contents); 
     }
 
     public BigInteger getValue()
     {
-        return new BigInteger(bytes);
+        return new BigInteger(contents);
+    }
+
+    public boolean hasValue(int x)
+    {
+        return (contents.length - start) <= 4
+            && ASN1Integer.intValue(contents, start, ASN1Integer.SIGN_EXT_SIGNED) == x;
     }
 
     public boolean hasValue(BigInteger x)
     {
         return null != x
             // Fast check to avoid allocation
-            && ASN1Integer.intValue(bytes, start, ASN1Integer.SIGN_EXT_SIGNED) == x.intValue()
+            && ASN1Integer.intValue(contents, start, ASN1Integer.SIGN_EXT_SIGNED) == x.intValue()
             && getValue().equals(x);
     }
 
     public int intValueExact()
     {
-        int count = bytes.length - start;
+        int count = contents.length - start;
         if (count > 4)
         {
             throw new ArithmeticException("ASN.1 Enumerated out of int range");
         }
 
-        return ASN1Integer.intValue(bytes, start, ASN1Integer.SIGN_EXT_SIGNED); 
+        return ASN1Integer.intValue(contents, start, ASN1Integer.SIGN_EXT_SIGNED); 
     }
 
-    boolean isConstructed()
+    boolean encodeConstructed()
     {
         return false;
     }
 
-    int encodedLength()
+    int encodedLength(boolean withTag)
     {
-        return 1 + StreamUtil.calculateBodyLength(bytes.length) + bytes.length;
+        return ASN1OutputStream.getLengthOfEncodingDL(withTag, contents.length);
     }
 
     void encode(ASN1OutputStream out, boolean withTag) throws IOException
     {
-        out.writeEncoded(withTag, BERTags.ENUMERATED, bytes);
+        out.writeEncodingDL(withTag, BERTags.ENUMERATED, contents);
     }
 
     boolean asn1Equals(
@@ -173,39 +181,39 @@
 
         ASN1Enumerated other = (ASN1Enumerated)o;
 
-        return Arrays.areEqual(this.bytes, other.bytes);
+        return Arrays.areEqual(this.contents, other.contents);
     }
 
     public int hashCode()
     {
-        return Arrays.hashCode(bytes);
+        return Arrays.hashCode(contents);
     }
 
-    private static ASN1Enumerated[] cache = new ASN1Enumerated[12];
+    private static final ASN1Enumerated[] cache = new ASN1Enumerated[12];
 
-    static ASN1Enumerated fromOctetString(byte[] enc)
+    static ASN1Enumerated createPrimitive(byte[] contents, boolean clone)
     {
-        if (enc.length > 1)
+        if (contents.length > 1)
         {
-            return new ASN1Enumerated(enc);
+            return new ASN1Enumerated(contents, clone);
         }
 
-        if (enc.length == 0)
+        if (contents.length == 0)
         {
             throw new IllegalArgumentException("ENUMERATED has zero length");
         }
-        int value = enc[0] & 0xff;
+        int value = contents[0] & 0xff;
 
         if (value >= cache.length)
         {
-            return new ASN1Enumerated(enc);
+            return new ASN1Enumerated(contents, clone);
         }
 
         ASN1Enumerated possibleMatch = cache[value];
 
         if (possibleMatch == null)
         {
-            possibleMatch = cache[value] = new ASN1Enumerated(enc);
+            possibleMatch = cache[value] = new ASN1Enumerated(contents, clone);
         }
 
         return possibleMatch;
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1External.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1External.java
index 4f6aa4c..7cfda82 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1External.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1External.java
@@ -3,6 +3,8 @@
 
 import java.io.IOException;
 
+import com.android.internal.org.bouncycastle.util.Objects;
+
 /**
  * Class representing the DER-type External
  * @hide This class is not part of the Android public SDK API
@@ -10,101 +12,124 @@
 public abstract class ASN1External
     extends ASN1Primitive
 {
-    protected ASN1ObjectIdentifier directReference;
-    protected ASN1Integer indirectReference;
-    protected ASN1Primitive dataValueDescriptor;
-    protected int encoding;
-    protected ASN1Primitive externalContent;
+    static final ASN1UniversalType TYPE = new ASN1UniversalType(ASN1External.class, BERTags.EXTERNAL)
+    {
+        ASN1Primitive fromImplicitConstructed(ASN1Sequence sequence)
+        {
+            // TODO Ideally ASN1External would have no subclasses and just hold the sequence
+            return sequence.toASN1External();
+        }
+    };
 
-    /**
-     * Construct an EXTERNAL object, the input encoding vector must have exactly two elements on it.
-     * <p>
-     * Acceptable input formats are:
-     * <ul>
-     * <li> {@link ASN1ObjectIdentifier} + data {@link DERTaggedObject} (direct reference form)</li>
-     * <li> {@link ASN1Integer} + data {@link DERTaggedObject} (indirect reference form)</li>
-     * <li> Anything but {@link DERTaggedObject} + data {@link DERTaggedObject} (data value form)</li>
-     * </ul>
-     *
-     * @throws IllegalArgumentException if input size is wrong, or
-     */
-    public ASN1External(ASN1EncodableVector vector)
+    public static ASN1External getInstance(Object obj)
+    {
+        if (obj == null || obj instanceof ASN1External)
+        {
+            return (ASN1External)obj;
+        }
+        else if (obj instanceof ASN1Encodable)
+        {
+            ASN1Primitive primitive = ((ASN1Encodable)obj).toASN1Primitive();
+            if (primitive instanceof ASN1External)
+            {
+                return (ASN1External)primitive;
+            }
+        }
+        else if (obj instanceof byte[])
+        {
+            try
+            {
+                return (ASN1External)TYPE.fromByteArray((byte[])obj);
+            }
+            catch (IOException e)
+            {
+                throw new IllegalArgumentException("failed to construct external from byte[]: " + e.getMessage());
+            }
+        }
+
+        throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
+    }
+
+    public static ASN1External getInstance(ASN1TaggedObject taggedObject, boolean explicit)
+    {
+        return (ASN1External)TYPE.getContextInstance(taggedObject, explicit);
+    }
+
+    ASN1ObjectIdentifier directReference;
+    ASN1Integer indirectReference;
+    // TODO Actually use ASN1ObjectDescriptor for this
+    ASN1Primitive dataValueDescriptor;
+    int encoding;
+    ASN1Primitive externalContent;
+
+    ASN1External(ASN1Sequence sequence)
     {
         int offset = 0;
 
-        ASN1Primitive enc = getObjFromVector(vector, offset);
-        if (enc instanceof ASN1ObjectIdentifier)
+        ASN1Primitive asn1 = getObjFromSequence(sequence, offset);
+        if (asn1 instanceof ASN1ObjectIdentifier)
         {
-            directReference = (ASN1ObjectIdentifier)enc;
-            offset++;
-            enc = getObjFromVector(vector, offset);
+            directReference = (ASN1ObjectIdentifier)asn1;
+            asn1 = getObjFromSequence(sequence, ++offset);
         }
-        if (enc instanceof ASN1Integer)
+        if (asn1 instanceof ASN1Integer)
         {
-            indirectReference = (ASN1Integer) enc;
-            offset++;
-            enc = getObjFromVector(vector, offset);
+            indirectReference = (ASN1Integer)asn1;
+            asn1 = getObjFromSequence(sequence, ++offset);
         }
-        if (!(enc instanceof ASN1TaggedObject))
+        if (!(asn1 instanceof ASN1TaggedObject))
         {
-            dataValueDescriptor = (ASN1Primitive) enc;
-            offset++;
-            enc = getObjFromVector(vector, offset);
+            dataValueDescriptor = asn1;
+            asn1 = getObjFromSequence(sequence, ++offset);
         }
 
-        if (vector.size() != offset + 1)
+        if (sequence.size() != offset + 1)
         {
-            throw new IllegalArgumentException("input vector too large");
+            throw new IllegalArgumentException("input sequence too large");
         }
 
-        if (!(enc instanceof ASN1TaggedObject))
+        if (!(asn1 instanceof ASN1TaggedObject))
         {
-            throw new IllegalArgumentException("No tagged object found in vector. Structure doesn't seem to be of type External");
+            throw new IllegalArgumentException(
+                "No tagged object found in sequence. Structure doesn't seem to be of type External");
         }
-        ASN1TaggedObject obj = (ASN1TaggedObject)enc;
-        setEncoding(obj.getTagNo());
-        externalContent = obj.getObject();
+
+        ASN1TaggedObject obj = (ASN1TaggedObject)asn1;
+        this.encoding = checkEncoding(obj.getTagNo());
+        this.externalContent = getExternalContent(obj);
     }
 
-    private ASN1Primitive getObjFromVector(ASN1EncodableVector v, int index)
+    ASN1External(ASN1ObjectIdentifier directReference, ASN1Integer indirectReference, ASN1Primitive dataValueDescriptor,
+        DERTaggedObject externalData)
     {
-        if (v.size() <= index)
-        {
-            throw new IllegalArgumentException("too few objects in input vector");
-        }
-
-        return v.get(index).toASN1Primitive();
+        this.directReference = directReference;
+        this.indirectReference = indirectReference;
+        this.dataValueDescriptor = dataValueDescriptor;
+        this.encoding = checkEncoding(externalData.getTagNo());
+        this.externalContent = getExternalContent(externalData);
     }
 
-    /**
-     * Creates a new instance of External
-     * See X.690 for more informations about the meaning of these parameters
-     * @param directReference The direct reference or <code>null</code> if not set.
-     * @param indirectReference The indirect reference or <code>null</code> if not set.
-     * @param dataValueDescriptor The data value descriptor or <code>null</code> if not set.
-     * @param externalData The external data in its encoded form.
-     */
-    public ASN1External(ASN1ObjectIdentifier directReference, ASN1Integer indirectReference, ASN1Primitive dataValueDescriptor, DERTaggedObject externalData)
+    ASN1External(ASN1ObjectIdentifier directReference, ASN1Integer indirectReference, ASN1Primitive dataValueDescriptor,
+        int encoding, ASN1Primitive externalData)
     {
-        this(directReference, indirectReference, dataValueDescriptor, externalData.getTagNo(), externalData.toASN1Primitive());
+        this.directReference = directReference;
+        this.indirectReference = indirectReference;
+        this.dataValueDescriptor = dataValueDescriptor;
+        this.encoding = checkEncoding(encoding);
+        this.externalContent = checkExternalContent(encoding, externalData);
     }
 
-    /**
-     * Creates a new instance of External.
-     * See X.690 for more informations about the meaning of these parameters
-     * @param directReference The direct reference or <code>null</code> if not set.
-     * @param indirectReference The indirect reference or <code>null</code> if not set.
-     * @param dataValueDescriptor The data value descriptor or <code>null</code> if not set.
-     * @param encoding The encoding to be used for the external data
-     * @param externalData The external data
-     */
-    public ASN1External(ASN1ObjectIdentifier directReference, ASN1Integer indirectReference, ASN1Primitive dataValueDescriptor, int encoding, ASN1Primitive externalData)
+    abstract ASN1Sequence buildSequence();
+
+    int encodedLength(boolean withTag) throws IOException
     {
-        setDirectReference(directReference);
-        setIndirectReference(indirectReference);
-        setDataValueDescriptor(dataValueDescriptor);
-        setEncoding(encoding);
-        setExternalContent(externalData.toASN1Primitive());
+        return buildSequence().encodedLength(withTag);
+    }
+
+    void encode(ASN1OutputStream out, boolean withTag) throws IOException
+    {
+        out.writeIdentifier(withTag, BERTags.CONSTRUCTED | BERTags.EXTERNAL);
+        buildSequence().encode(out, false);
     }
 
     ASN1Primitive toDERObject()
@@ -117,75 +142,38 @@
         return new DLExternal(directReference, indirectReference, dataValueDescriptor, encoding, externalContent);
     }
 
-    /* (non-Javadoc)
-     * @see java.lang.Object#hashCode()
-     */
     public int hashCode()
     {
-        int ret = 0;
-        if (directReference != null)
-        {
-            ret = directReference.hashCode();
-        }
-        if (indirectReference != null)
-        {
-            ret ^= indirectReference.hashCode();
-        }
-        if (dataValueDescriptor != null)
-        {
-            ret ^= dataValueDescriptor.hashCode();
-        }
-        ret ^= externalContent.hashCode();
-        return ret;
+        return Objects.hashCode(this.directReference)
+            ^  Objects.hashCode(this.indirectReference)
+            ^  Objects.hashCode(this.dataValueDescriptor)
+            ^  this.encoding
+            ^  this.externalContent.hashCode();
     }
 
-    boolean isConstructed()
+    boolean encodeConstructed()
     {
         return true;
     }
 
-    int encodedLength()
-        throws IOException
+    boolean asn1Equals(ASN1Primitive primitive)
     {
-        return this.getEncoded().length;
-    }
-
-    /* (non-Javadoc)
-     * @see org.bouncycastle.asn1.ASN1Primitive#asn1Equals(org.bouncycastle.asn1.ASN1Primitive)
-     */
-    boolean asn1Equals(ASN1Primitive o)
-    {
-        if (!(o instanceof ASN1External))
-        {
-            return false;
-        }
-        if (this == o)
+        if (this == primitive)
         {
             return true;
         }
-        ASN1External other = (ASN1External)o;
-        if (directReference != null)
+        if (!(primitive instanceof ASN1External))
         {
-            if (other.directReference == null || !other.directReference.equals(directReference))  
-            {
-                return false;
-            }
+            return false;
         }
-        if (indirectReference != null)
-        {
-            if (other.indirectReference == null || !other.indirectReference.equals(indirectReference))
-            {
-                return false;
-            }
-        }
-        if (dataValueDescriptor != null)
-        {
-            if (other.dataValueDescriptor == null || !other.dataValueDescriptor.equals(dataValueDescriptor))
-            {
-                return false;
-            }
-        }
-        return externalContent.equals(other.externalContent);
+
+        ASN1External that = (ASN1External)primitive;
+
+        return Objects.areEqual(this.directReference, that.directReference)
+            && Objects.areEqual(this.indirectReference, that.indirectReference)
+            && Objects.areEqual(this.dataValueDescriptor, that.dataValueDescriptor)
+            && this.encoding == that.encoding
+            && this.externalContent.equals(that.externalContent);
     }
 
     /**
@@ -219,7 +207,7 @@
     {
         return encoding;
     }
-    
+
     /**
      * Returns the content of this element
      * @return The content
@@ -228,7 +216,7 @@
     {
         return externalContent;
     }
-    
+
     /**
      * Returns the indirect reference of this element
      * @return The reference
@@ -237,27 +225,9 @@
     {
         return indirectReference;
     }
-    
-    /**
-     * Sets the data value descriptor
-     * @param dataValueDescriptor The descriptor
-     */
-    private void setDataValueDescriptor(ASN1Primitive dataValueDescriptor)
-    {
-        this.dataValueDescriptor = dataValueDescriptor;
-    }
 
     /**
-     * Sets the direct reference of the external element
-     * @param directReferemce The reference
-     */
-    private void setDirectReference(ASN1ObjectIdentifier directReferemce)
-    {
-        this.directReference = directReferemce;
-    }
-    
-    /**
-     * Sets the encoding of the content. Valid values are
+     * Checks the encoding of the content. Valid values are
      * <ul>
      * <li><code>0</code> single-ASN1-type</li>
      * <li><code>1</code> OCTET STRING</li>
@@ -265,30 +235,57 @@
      * </ul>
      * @param encoding The encoding
      */
-    private void setEncoding(int encoding)
+    private static int checkEncoding(int encoding)
     {
         if (encoding < 0 || encoding > 2)
         {
             throw new IllegalArgumentException("invalid encoding value: " + encoding);
         }
-        this.encoding = encoding;
+
+        return encoding;
     }
-    
-    /**
-     * Sets the content of this element
-     * @param externalContent The content
-     */
-    private void setExternalContent(ASN1Primitive externalContent)
+
+    private static ASN1Primitive checkExternalContent(int tagNo, ASN1Primitive externalContent)
     {
-        this.externalContent = externalContent;
+        switch (tagNo)
+        {
+        case 1:
+            return ASN1OctetString.TYPE.checkedCast(externalContent);
+        case 2:
+            return ASN1BitString.TYPE.checkedCast(externalContent);
+        default:
+            return externalContent;
+        }
     }
-    
-    /**
-     * Sets the indirect reference of this element
-     * @param indirectReference The reference
-     */
-    private void setIndirectReference(ASN1Integer indirectReference)
+
+    private static ASN1Primitive getExternalContent(ASN1TaggedObject encoding)
     {
-        this.indirectReference = indirectReference;
+        int tagClass = encoding.getTagClass(), tagNo = encoding.getTagNo();
+        if (BERTags.CONTEXT_SPECIFIC != tagClass)
+        {
+            throw new IllegalArgumentException("invalid tag: " + ASN1Util.getTagText(tagClass, tagNo));
+        }
+
+        switch (tagNo)
+        {
+        case 0:
+            return encoding.getExplicitBaseObject().toASN1Primitive();
+        case 1:
+            return ASN1OctetString.getInstance(encoding, false);
+        case 2:
+            return ASN1BitString.getInstance(encoding, false);
+        default:
+            throw new IllegalArgumentException("invalid tag: " + ASN1Util.getTagText(tagClass, tagNo));
+        }
+    }
+
+    private static ASN1Primitive getObjFromSequence(ASN1Sequence sequence, int index)
+    {
+        if (sequence.size() <= index)
+        {
+            throw new IllegalArgumentException("too few objects in input sequence");
+        }
+
+        return sequence.getObjectAt(index).toASN1Primitive();
     }
 }
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1ApplicationSpecificParser.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1ExternalParser.java
similarity index 81%
rename from repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1ApplicationSpecificParser.java
rename to repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1ExternalParser.java
index f4bc6e3..370be2b 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1ApplicationSpecificParser.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1ExternalParser.java
@@ -4,10 +4,10 @@
 import java.io.IOException;
 
 /**
- * Interface to parse ASN.1 ApplicationSpecific objects.
+ * Parser DER EXTERNAL tagged objects.
  * @hide This class is not part of the Android public SDK API
  */
-public interface ASN1ApplicationSpecificParser
+public interface ASN1ExternalParser
     extends ASN1Encodable, InMemoryRepresentable
 {
     /**
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1GeneralString.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1GeneralString.java
new file mode 100644
index 0000000..4794b81
--- /dev/null
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1GeneralString.java
@@ -0,0 +1,153 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.asn1;
+
+import java.io.IOException;
+
+import com.android.internal.org.bouncycastle.util.Arrays;
+import com.android.internal.org.bouncycastle.util.Strings;
+
+/**
+ * ASN.1 GENERAL-STRING data type.
+ * <p>
+ * This is an 8-bit encoded ISO 646 (ASCII) character set
+ * with optional escapes to other character sets.
+ * </p>
+ * @hide This class is not part of the Android public SDK API
+ */
+public abstract class ASN1GeneralString 
+    extends ASN1Primitive
+    implements ASN1String
+{
+    static final ASN1UniversalType TYPE = new ASN1UniversalType(ASN1GeneralString.class, BERTags.GENERAL_STRING)
+    {
+        ASN1Primitive fromImplicitPrimitive(DEROctetString octetString)
+        {
+            return createPrimitive(octetString.getOctets());
+        }
+    };
+
+    /**
+     * Return a GeneralString from the given object.
+     *
+     * @param obj the object we want converted.
+     * @exception IllegalArgumentException if the object cannot be converted.
+     * @return an ASN1GeneralString instance, or null.
+     */
+    public static ASN1GeneralString getInstance(Object obj)
+    {
+        if (obj == null || obj instanceof ASN1GeneralString) 
+        {
+            return (ASN1GeneralString) obj;
+        }
+        if (obj instanceof ASN1Encodable)
+        {
+            ASN1Primitive primitive = ((ASN1Encodable)obj).toASN1Primitive();
+            if (primitive instanceof ASN1GeneralString)
+            {
+                return (ASN1GeneralString)primitive;
+            }
+        }
+        if (obj instanceof byte[])
+        {
+            try
+            {
+                return (ASN1GeneralString)TYPE.fromByteArray((byte[])obj);
+            }
+            catch (Exception e)
+            {
+                throw new IllegalArgumentException("encoding error in getInstance: " + e.toString());
+            }
+        }
+
+        throw new IllegalArgumentException("illegal object in getInstance: "
+                + obj.getClass().getName());
+    }
+
+    /**
+     * Return a GeneralString from a tagged object.
+     *
+     * @param taggedObject      the tagged object holding the object we want
+     * @param explicit true if the object is meant to be explicitly tagged false
+     *                 otherwise.
+     * @exception IllegalArgumentException if the tagged object cannot be converted.
+     * @return an ASN1GeneralString instance.
+     */
+    public static ASN1GeneralString getInstance(ASN1TaggedObject taggedObject, boolean explicit)
+    {
+        return (ASN1GeneralString)TYPE.getContextInstance(taggedObject, explicit);
+    }
+
+    final byte[] contents;
+
+    ASN1GeneralString(String string) 
+    {
+        this.contents = Strings.toByteArray(string);
+    }
+
+    ASN1GeneralString(byte[] contents, boolean clone)
+    {
+        this.contents = clone ? Arrays.clone(contents) : contents;
+    }
+
+    /**
+     * Return a Java String representation of our contained String.
+     *
+     * @return a Java String representing our contents.
+     */
+    public final String getString() 
+    {
+        return Strings.fromByteArray(contents);
+    }
+
+    public String toString()
+    {
+        return getString();
+    }
+
+    /**
+     * Return a byte array representation of our contained String.
+     *
+     * @return a byte array representing our contents.
+     */
+    public final byte[] getOctets() 
+    {
+        return Arrays.clone(contents);
+    }
+
+    final boolean encodeConstructed()
+    {
+        return false;
+    }
+
+    final int encodedLength(boolean withTag)
+    {
+        return ASN1OutputStream.getLengthOfEncodingDL(withTag, contents.length);
+    }
+
+    final void encode(ASN1OutputStream out, boolean withTag) throws IOException
+    {
+        out.writeEncodingDL(withTag, BERTags.GENERAL_STRING, contents);
+    }
+
+    final boolean asn1Equals(ASN1Primitive other)
+    {
+        if (!(other instanceof ASN1GeneralString))
+        {
+            return false;
+        }
+
+        ASN1GeneralString that = (ASN1GeneralString)other;
+
+        return Arrays.areEqual(this.contents, that.contents);
+    }
+
+    public final int hashCode() 
+    {
+        return Arrays.hashCode(contents);
+    }
+
+    static ASN1GeneralString createPrimitive(byte[] contents)
+    {
+        return new DERGeneralString(contents, false);
+    }
+}
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1GeneralizedTime.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1GeneralizedTime.java
index 5e75554..0388518 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1GeneralizedTime.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1GeneralizedTime.java
@@ -48,7 +48,13 @@
 public class ASN1GeneralizedTime
     extends ASN1Primitive
 {
-    protected byte[] time;
+    static final ASN1UniversalType TYPE = new ASN1UniversalType(ASN1GeneralizedTime.class, BERTags.GENERALIZED_TIME)
+    {
+        ASN1Primitive fromImplicitPrimitive(DEROctetString octetString)
+        {
+            return createPrimitive(octetString.getOctets());
+        }
+    };
 
     /**
      * return a generalized time from the passed in object
@@ -64,12 +70,19 @@
         {
             return (ASN1GeneralizedTime)obj;
         }
-
+        if (obj instanceof ASN1Encodable)
+        {
+            ASN1Primitive primitive = ((ASN1Encodable)obj).toASN1Primitive();
+            if (primitive instanceof ASN1GeneralizedTime)
+            {
+                return (ASN1GeneralizedTime)primitive;
+            }
+        }
         if (obj instanceof byte[])
         {
             try
             {
-                return (ASN1GeneralizedTime)fromByteArray((byte[])obj);
+                return (ASN1GeneralizedTime)TYPE.fromByteArray((byte[])obj);
             }
             catch (Exception e)
             {
@@ -83,29 +96,19 @@
     /**
      * return a Generalized Time object from a tagged object.
      *
-     * @param obj      the tagged object holding the object we want
-     * @param explicit true if the object is meant to be explicitly
-     *                 tagged false otherwise.
+     * @param taggedObject the tagged object holding the object we want
+     * @param explicit     true if the object is meant to be explicitly tagged false
+     *                     otherwise.
      * @return an ASN1GeneralizedTime instance.
-     * @throws IllegalArgumentException if the tagged object cannot
-     * be converted.
+     * @throws IllegalArgumentException if the tagged object cannot be converted.
      */
-    public static ASN1GeneralizedTime getInstance(
-        ASN1TaggedObject obj,
-        boolean explicit)
+    public static ASN1GeneralizedTime getInstance(ASN1TaggedObject taggedObject, boolean explicit)
     {
-        ASN1Primitive o = obj.getObject();
-
-        if (explicit || o instanceof ASN1GeneralizedTime)
-        {
-            return getInstance(o);
-        }
-        else
-        {
-            return new ASN1GeneralizedTime(ASN1OctetString.getInstance(o).getOctets());
-        }
+        return (ASN1GeneralizedTime)TYPE.getContextInstance(taggedObject, explicit);
     }
 
+    final byte[] contents;
+
     /**
      * The correct format for this is YYYYMMDDHHMMSS[.f]Z, or without the Z
      * for local time, or Z+-HHMM on the end, for difference between local
@@ -118,7 +121,7 @@
     public ASN1GeneralizedTime(
         String time)
     {
-        this.time = Strings.toByteArray(time);
+        this.contents = Strings.toByteArray(time);
         try
         {
             this.getDate();
@@ -143,7 +146,7 @@
 
         dateF.setTimeZone(new SimpleTimeZone(0, "Z"));
 
-        this.time = Strings.toByteArray(dateF.format(time));
+        this.contents = Strings.toByteArray(dateF.format(time));
     }
 
     /**
@@ -165,7 +168,7 @@
 
         dateF.setTimeZone(new SimpleTimeZone(0, "Z"));
 
-        this.time = Strings.toByteArray(dateF.format(time));
+        this.contents = Strings.toByteArray(dateF.format(time));
     }
 
     ASN1GeneralizedTime(
@@ -175,7 +178,7 @@
         {
             throw new IllegalArgumentException("GeneralizedTime string too short");
         }
-        this.time = bytes;
+        this.contents = bytes;
 
         if (!(isDigit(0) && isDigit(1) && isDigit(2) && isDigit(3)))
         {
@@ -190,7 +193,7 @@
      */
     public String getTimeString()
     {
-        return Strings.fromByteArray(time);
+        return Strings.fromByteArray(contents);
     }
 
     /**
@@ -208,7 +211,7 @@
      */
     public String getTime()
     {
-        String stime = Strings.fromByteArray(time);
+        String stime = Strings.fromByteArray(contents);
 
         //
         // standardise the format.
@@ -360,34 +363,26 @@
         throws ParseException
     {
         SimpleDateFormat dateF;
-        String stime = Strings.fromByteArray(time);
+        String stime = Strings.fromByteArray(contents);
         String d = stime;
 
         if (stime.endsWith("Z"))
         {
             if (hasFractionalSeconds())
             {
-                // Android-changed: Use localized version
-                // dateF = new SimpleDateFormat("yyyyMMddHHmmss.SSS'Z'");
-                dateF = new SimpleDateFormat("yyyyMMddHHmmss.SSS'Z'", Locale.US);
+                dateF = new SimpleDateFormat("yyyyMMddHHmmss.SSS'Z'", LocaleUtil.EN_Locale);
             }
             else if (hasSeconds())
             {
-                // Android-changed: Use localized version
-                // dateF = new SimpleDateFormat("yyyyMMddHHmmss'Z'");
-                dateF = new SimpleDateFormat("yyyyMMddHHmmss'Z'", Locale.US);
+                dateF = new SimpleDateFormat("yyyyMMddHHmmss'Z'", LocaleUtil.EN_Locale);
             }
             else if (hasMinutes())
             {
-                // Android-changed: Use localized version
-                // dateF = new SimpleDateFormat("yyyyMMddHHmm'Z'");
-                dateF = new SimpleDateFormat("yyyyMMddHHmm'Z'", Locale.US);
+                dateF = new SimpleDateFormat("yyyyMMddHHmm'Z'", LocaleUtil.EN_Locale);
             }
             else
             {
-                // Android-changed: Use localized version
-                // dateF = new SimpleDateFormat("yyyyMMddHH'Z'");
-                dateF = new SimpleDateFormat("yyyyMMddHH'Z'", Locale.US);
+                dateF = new SimpleDateFormat("yyyyMMddHH'Z'", LocaleUtil.EN_Locale);
             }
 
             dateF.setTimeZone(new SimpleTimeZone(0, "Z"));
@@ -432,14 +427,14 @@
             d = pruneFractionalSeconds(d);
         }
         
-        return DateUtil.epochAdjust(dateF.parse(d));
+        return dateF.parse(d);
     }
 
     protected boolean hasFractionalSeconds()
     {
-        for (int i = 0; i != time.length; i++)
+        for (int i = 0; i != contents.length; i++)
         {
-            if (time[i] == '.')
+            if (contents[i] == '.')
             {
                 if (i == 14)
                 {
@@ -462,49 +457,46 @@
 
     private boolean isDigit(int pos)
     {
-        return time.length > pos && time[pos] >= '0' && time[pos] <= '9';
+        return contents.length > pos && contents[pos] >= '0' && contents[pos] <= '9';
     }
 
-    boolean isConstructed()
+    final boolean encodeConstructed()
     {
         return false;
     }
 
-    int encodedLength()
+    int encodedLength(boolean withTag)
     {
-        int length = time.length;
-
-        return 1 + StreamUtil.calculateBodyLength(length) + length;
+        return ASN1OutputStream.getLengthOfEncodingDL(withTag, contents.length);
     }
 
     void encode(ASN1OutputStream out, boolean withTag) throws IOException
     {
-        out.writeEncoded(withTag, BERTags.GENERALIZED_TIME, time);
+        out.writeEncodingDL(withTag, BERTags.GENERALIZED_TIME, contents);
     }
 
     ASN1Primitive toDERObject()
     {
-        return new DERGeneralizedTime(time);
+        return new DERGeneralizedTime(contents);
     }
 
-    ASN1Primitive toDLObject()
-    {
-        return new DERGeneralizedTime(time);
-    }
-
-    boolean asn1Equals(
-        ASN1Primitive o)
+    boolean asn1Equals(ASN1Primitive o)
     {
         if (!(o instanceof ASN1GeneralizedTime))
         {
             return false;
         }
 
-        return Arrays.areEqual(time, ((ASN1GeneralizedTime)o).time);
+        return Arrays.areEqual(contents, ((ASN1GeneralizedTime)o).contents);
     }
 
     public int hashCode()
     {
-        return Arrays.hashCode(time);
+        return Arrays.hashCode(contents);
+    }
+
+    static ASN1GeneralizedTime createPrimitive(byte[] contents)
+    {
+        return new ASN1GeneralizedTime(contents);
     }
 }
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1GraphicString.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1GraphicString.java
new file mode 100644
index 0000000..9f68f9b
--- /dev/null
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1GraphicString.java
@@ -0,0 +1,132 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.asn1;
+
+import java.io.IOException;
+
+import com.android.internal.org.bouncycastle.util.Arrays;
+import com.android.internal.org.bouncycastle.util.Strings;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+public abstract class ASN1GraphicString
+    extends ASN1Primitive
+    implements ASN1String
+{
+    static final ASN1UniversalType TYPE = new ASN1UniversalType(ASN1GraphicString.class, BERTags.GRAPHIC_STRING)
+    {
+        ASN1Primitive fromImplicitPrimitive(DEROctetString octetString)
+        {
+            return createPrimitive(octetString.getOctets());
+        }
+    };
+
+    /**
+     * Return a GraphicString from the passed in object.
+     *
+     * @param obj an ASN1GraphicString or an object that can be converted into one.
+     * @exception IllegalArgumentException if the object cannot be converted.
+     * @return an ASN1GraphicString instance, or null.
+     */
+    public static ASN1GraphicString getInstance(Object obj)
+    {
+        if (obj == null || obj instanceof ASN1GraphicString)
+        {
+            return (ASN1GraphicString)obj;
+        }
+        if (obj instanceof ASN1Encodable)
+        {
+            ASN1Primitive primitive = ((ASN1Encodable)obj).toASN1Primitive();
+            if (primitive instanceof ASN1GraphicString)
+            {
+                return (ASN1GraphicString)primitive;
+            }
+        }
+        if (obj instanceof byte[])
+        {
+            try
+            {
+                return (ASN1GraphicString)TYPE.fromByteArray((byte[])obj);
+            }
+            catch (Exception e)
+            {
+                throw new IllegalArgumentException("encoding error in getInstance: " + e.toString());
+            }
+        }
+
+        throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
+    }
+
+    /**
+     * Return a GraphicString from a tagged object.
+     *
+     * @param taggedObject the tagged object holding the object we want.
+     * @param explicit     true if the object is meant to be explicitly tagged,
+     *                     false otherwise.
+     * @exception IllegalArgumentException if the tagged object cannot be converted.
+     * @return an ASN1GraphicString instance, or null.
+     */
+    public static ASN1GraphicString getInstance(ASN1TaggedObject taggedObject, boolean explicit)
+    {
+        return (ASN1GraphicString)TYPE.getContextInstance(taggedObject, explicit);
+    }
+
+    final byte[] contents;
+
+    ASN1GraphicString(byte[] contents, boolean clone)
+    {
+        if (null == contents)
+        {
+            throw new NullPointerException("'contents' cannot be null");
+        }
+
+        this.contents = clone ? Arrays.clone(contents) : contents;
+    }
+
+    public final byte[] getOctets()
+    {
+        return Arrays.clone(contents);
+    }
+
+    final boolean encodeConstructed()
+    {
+        return false;
+    }
+
+    final int encodedLength(boolean withTag)
+    {
+        return ASN1OutputStream.getLengthOfEncodingDL(withTag, contents.length);
+    }
+
+    final void encode(ASN1OutputStream out, boolean withTag) throws IOException
+    {
+        out.writeEncodingDL(withTag, BERTags.GRAPHIC_STRING, contents);
+    }
+
+    final boolean asn1Equals(ASN1Primitive other)
+    {
+        if (!(other instanceof ASN1GraphicString))
+        {
+            return false;
+        }
+
+        ASN1GraphicString that = (ASN1GraphicString)other;
+
+        return Arrays.areEqual(this.contents, that.contents);
+    }
+
+    public final int hashCode()
+    {
+        return Arrays.hashCode(contents);
+    }
+
+    public final String getString()
+    {
+        return Strings.fromByteArray(contents);
+    }
+
+    static ASN1GraphicString createPrimitive(byte[] contents)
+    {
+        return new DERGraphicString(contents, false);
+    }
+}
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1IA5String.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1IA5String.java
new file mode 100644
index 0000000..7e87652
--- /dev/null
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1IA5String.java
@@ -0,0 +1,172 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.asn1;
+
+import java.io.IOException;
+
+import com.android.internal.org.bouncycastle.util.Arrays;
+import com.android.internal.org.bouncycastle.util.Strings;
+
+/**
+ * ASN.1 IA5String object - this is a ISO 646 (ASCII) string encoding code points 0 to 127.
+ * <p>
+ * Explicit character set escape sequences are not allowed.
+ * </p>
+ * @hide This class is not part of the Android public SDK API
+ */
+public abstract class ASN1IA5String
+    extends ASN1Primitive
+    implements ASN1String
+{
+    static final ASN1UniversalType TYPE = new ASN1UniversalType(ASN1IA5String.class, BERTags.IA5_STRING)
+    {
+        ASN1Primitive fromImplicitPrimitive(DEROctetString octetString)
+        {
+            return createPrimitive(octetString.getOctets());
+        }
+    };
+
+    /**
+     * Return an IA5 string from the passed in object
+     *
+     * @param obj an ASN1IA5String or an object that can be converted into one.
+     * @exception IllegalArgumentException if the object cannot be converted.
+     * @return a ASN1IA5String instance, or null.
+     */
+    public static ASN1IA5String getInstance(Object obj)
+    {
+        if (obj == null || obj instanceof ASN1IA5String)
+        {
+            return (ASN1IA5String)obj;
+        }
+        if (obj instanceof ASN1Encodable)
+        {
+            ASN1Primitive primitive = ((ASN1Encodable)obj).toASN1Primitive();
+            if (primitive instanceof ASN1IA5String)
+            {
+                return (ASN1IA5String)primitive;
+            }
+        }
+        if (obj instanceof byte[])
+        {
+            try
+            {
+                return (ASN1IA5String)TYPE.fromByteArray((byte[])obj);
+            }
+            catch (Exception e)
+            {
+                throw new IllegalArgumentException("encoding error in getInstance: " + e.toString());
+            }
+        }
+
+        throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
+    }
+
+    /**
+     * Return an IA5 String from a tagged object.
+     *
+     * @param taggedObject the tagged object holding the object we want
+     * @param explicit true if the object is meant to be explicitly
+     *              tagged false otherwise.
+     * @exception IllegalArgumentException if the tagged object cannot
+     *               be converted.
+     * @return an ASN1IA5String instance, or null.
+     */
+    public static ASN1IA5String getInstance(ASN1TaggedObject taggedObject, boolean explicit)
+    {
+        return (ASN1IA5String)TYPE.getContextInstance(taggedObject, explicit);
+    }
+
+    final byte[] contents;
+
+    ASN1IA5String(String string, boolean validate)
+    {
+        if (string == null)
+        {
+            throw new NullPointerException("'string' cannot be null");
+        }
+        if (validate && !isIA5String(string))
+        {
+            throw new IllegalArgumentException("'string' contains illegal characters");
+        }
+
+        this.contents = Strings.toByteArray(string);
+    }
+
+    ASN1IA5String(byte[] contents, boolean clone)
+    {
+        this.contents = clone ? Arrays.clone(contents) : contents;
+    }
+
+    public final String getString()
+    {
+        return Strings.fromByteArray(contents);
+    }
+
+    public String toString()
+    {
+        return getString();
+    }
+
+    public final byte[] getOctets()
+    {
+        return Arrays.clone(contents);
+    }
+
+    final boolean encodeConstructed()
+    {
+        return false;
+    }
+
+    final int encodedLength(boolean withTag)
+    {
+        return ASN1OutputStream.getLengthOfEncodingDL(withTag, contents.length);
+    }
+
+    final void encode(ASN1OutputStream out, boolean withTag) throws IOException
+    {
+        out.writeEncodingDL(withTag, BERTags.IA5_STRING, contents);
+    }
+
+    final boolean asn1Equals(ASN1Primitive other)
+    {
+        if (!(other instanceof ASN1IA5String))
+        {
+            return false;
+        }
+
+        ASN1IA5String that = (ASN1IA5String)other;
+
+        return Arrays.areEqual(this.contents, that.contents);
+    }
+
+    public final int hashCode()
+    {
+        return Arrays.hashCode(contents);
+    }
+
+    /**
+     * return true if the passed in String can be represented without
+     * loss as an IA5String, false otherwise.
+     *
+     * @param str the string to check.
+     * @return true if character set in IA5String set, false otherwise.
+     */
+    public static boolean isIA5String(String str)
+    {
+        for (int i = str.length() - 1; i >= 0; i--)
+        {
+            char ch = str.charAt(i);
+            if (ch > 0x007f)
+            {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    static ASN1IA5String createPrimitive(byte[] contents)
+    {
+        return new DERIA5String(contents, false);
+    }
+}
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1InputStream.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1InputStream.java
index 482fcff..d9ad81f 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1InputStream.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1InputStream.java
@@ -22,11 +22,9 @@
 {
     private final int limit;
     private final boolean lazyEvaluate;
-
     private final byte[][] tmpBuffers;
 
-    public ASN1InputStream(
-        InputStream is)
+    public ASN1InputStream(InputStream is)
     {
         this(is, StreamUtil.findLimit(is));
     }
@@ -37,8 +35,7 @@
      * 
      * @param input array containing ASN.1 encoded data.
      */
-    public ASN1InputStream(
-        byte[] input)
+    public ASN1InputStream(byte[] input)
     {
         this(new ByteArrayInputStream(input), input.length);
     }
@@ -50,22 +47,18 @@
      * @param input array containing ASN.1 encoded data.
      * @param lazyEvaluate true if parsing inside constructed objects can be delayed.
      */
-    public ASN1InputStream(
-        byte[] input,
-        boolean lazyEvaluate)
+    public ASN1InputStream(byte[] input, boolean lazyEvaluate)
     {
         this(new ByteArrayInputStream(input), input.length, lazyEvaluate);
     }
-    
+
     /**
      * Create an ASN1InputStream where no DER object will be longer than limit.
      * 
      * @param input stream containing ASN.1 encoded data.
      * @param limit maximum size of a DER encoded object.
      */
-    public ASN1InputStream(
-        InputStream input,
-        int         limit)
+    public ASN1InputStream(InputStream input, int limit)
     {
         this(input, limit, false);
     }
@@ -77,9 +70,7 @@
      * @param input stream containing ASN.1 encoded data.
      * @param lazyEvaluate true if parsing inside constructed objects can be delayed.
      */
-    public ASN1InputStream(
-        InputStream input,
-        boolean     lazyEvaluate)
+    public ASN1InputStream(InputStream input, boolean lazyEvaluate)
     {
         this(input, StreamUtil.findLimit(input), lazyEvaluate);
     }
@@ -92,15 +83,17 @@
      * @param limit maximum size of a DER encoded object.
      * @param lazyEvaluate true if parsing inside constructed objects can be delayed.
      */
-    public ASN1InputStream(
-        InputStream input,
-        int         limit,
-        boolean     lazyEvaluate)
+    public ASN1InputStream(InputStream input, int limit, boolean lazyEvaluate)
+    {
+        this(input, limit, lazyEvaluate, new byte[11][]);
+    }
+
+    private ASN1InputStream(InputStream input, int limit, boolean lazyEvaluate, byte[][] tmpBuffers)
     {
         super(input);
         this.limit = limit;
         this.lazyEvaluate = lazyEvaluate;
-        this.tmpBuffers = new byte[11][];
+        this.tmpBuffers = tmpBuffers;
     }
 
     int getLimit()
@@ -118,7 +111,7 @@
         byte[]  bytes)
         throws IOException
     {
-        if (Streams.readFully(this, bytes) != bytes.length)
+        if (Streams.readFully(this, bytes, 0, bytes.length) != bytes.length)
         {
             throw new EOFException("EOF encountered in middle of object");
         }
@@ -139,82 +132,57 @@
         int       length)
         throws IOException
     {
-        boolean isConstructed = (tag & CONSTRUCTED) != 0;
+        // TODO[asn1] Special-case zero length first?
 
         DefiniteLengthInputStream defIn = new DefiniteLengthInputStream(this, length, limit);
 
-        if ((tag & APPLICATION) != 0)
+        if (0 == (tag & FLAGS))
         {
-            return new DLApplicationSpecific(isConstructed, tagNo, defIn.toByteArray());
+            return createPrimitiveDERObject(tagNo, defIn, tmpBuffers);
         }
 
-        if ((tag & TAGGED) != 0)
+        int tagClass = tag & PRIVATE;
+        if (0 != tagClass)
         {
-            return new ASN1StreamParser(defIn).readTaggedObject(isConstructed, tagNo);
+            boolean isConstructed = (tag & CONSTRUCTED) != 0;
+            return readTaggedObjectDL(tagClass, tagNo, isConstructed, defIn);
         }
 
-        if (isConstructed)
+        switch (tagNo)
         {
-            // TODO There are other tags that may be constructed (e.g. BIT_STRING)
-            switch (tagNo)
+        case BIT_STRING:
+        {
+            return buildConstructedBitString(readVector(defIn));
+        }
+        case OCTET_STRING:
+        {
+            //
+            // yes, people actually do this...
+            //
+            return buildConstructedOctetString(readVector(defIn));
+        }
+        case SEQUENCE:
+        {
+            if (defIn.getRemaining() < 1)
             {
-                case OCTET_STRING:
-                    //
-                    // yes, people actually do this...
-                    //
-                    ASN1EncodableVector v = readVector(defIn);
-                    ASN1OctetString[] strings = new ASN1OctetString[v.size()];
-
-                    for (int i = 0; i != strings.length; i++)
-                    {
-                        ASN1Encodable asn1Obj = v.get(i);
-                        if (asn1Obj instanceof ASN1OctetString)
-                        {
-                            strings[i] = (ASN1OctetString)asn1Obj;
-                        }
-                        else
-                        {
-                            throw new ASN1Exception("unknown object encountered in constructed OCTET STRING: " + asn1Obj.getClass());
-                        }
-                    }
-
-                    return new BEROctetString(strings);
-                case SEQUENCE:
-                    if (lazyEvaluate)
-                    {
-                        return new LazyEncodedSequence(defIn.toByteArray());
-                    }
-                    else
-                    {
-                        return DLFactory.createSequence(readVector(defIn));   
-                    }
-                case SET:
-                    return DLFactory.createSet(readVector(defIn));
-                case EXTERNAL:
-                    return new DLExternal(readVector(defIn));
-                default:
-                    throw new IOException("unknown tag " + tagNo + " encountered");
+                return DLFactory.EMPTY_SEQUENCE;
+            }
+            else if (lazyEvaluate)
+            {
+                return new LazyEncodedSequence(defIn.toByteArray());
+            }
+            else
+            {
+                return DLFactory.createSequence(readVector(defIn));
             }
         }
-
-        return createPrimitiveDERObject(tagNo, defIn, tmpBuffers);
-    }
-
-    ASN1EncodableVector readVector(DefiniteLengthInputStream dIn) throws IOException
-    {
-        if (dIn.getRemaining() < 1)
-        {
-            return new ASN1EncodableVector(0);
+        case SET:
+            return DLFactory.createSet(readVector(defIn));
+        case EXTERNAL:
+            return DLFactory.createSequence(readVector(defIn)).toASN1External();
+        default:
+            throw new IOException("unknown tag " + tagNo + " encountered");
         }
-
-        ASN1InputStream subStream = new ASN1InputStream(dIn);
-        ASN1EncodableVector v = new ASN1EncodableVector();
-        ASN1Primitive p;
-        while ((p = subStream.readObject()) != null)
-        {
-            v.add(p);
-        }
-        return v;
     }
 
     public ASN1Primitive readObject()
@@ -231,55 +199,12 @@
             return null;
         }
 
-        //
-        // calculate tag number
-        //
         int tagNo = readTagNumber(this, tag);
-
-        boolean isConstructed = (tag & CONSTRUCTED) != 0;
-
-        //
-        // calculate length
-        //
         int length = readLength();
 
-        if (length < 0) // indefinite-length method
+        if (length >= 0)
         {
-            if (!isConstructed)
-            {
-                throw new IOException("indefinite-length primitive encoding encountered");
-            }
-
-            IndefiniteLengthInputStream indIn = new IndefiniteLengthInputStream(this, limit);
-            ASN1StreamParser sp = new ASN1StreamParser(indIn, limit);
-
-            if ((tag & APPLICATION) != 0)
-            {
-                return new BERApplicationSpecificParser(tagNo, sp).getLoadedObject();
-            }
-
-            if ((tag & TAGGED) != 0)
-            {
-                return new BERTaggedObjectParser(true, tagNo, sp).getLoadedObject();
-            }
-
-            // TODO There are other tags that may be constructed (e.g. BIT_STRING)
-            switch (tagNo)
-            {
-                case OCTET_STRING:
-                    return new BEROctetStringParser(sp).getLoadedObject();
-                case SEQUENCE:
-                    return new BERSequenceParser(sp).getLoadedObject();
-                case SET:
-                    return new BERSetParser(sp).getLoadedObject();
-                case EXTERNAL:
-                    return new DERExternalParser(sp).getLoadedObject();
-                default:
-                    throw new IOException("unknown BER object encountered");
-            }
-        }
-        else
-        {
+            // definite-length
             try
             {
                 return buildObject(tag, tagNo, length);
@@ -289,6 +214,124 @@
                 throw new ASN1Exception("corrupted stream detected", e);
             }
         }
+
+        // indefinite-length
+
+        if (0 == (tag & CONSTRUCTED))
+        {
+            throw new IOException("indefinite-length primitive encoding encountered");
+        }
+
+        IndefiniteLengthInputStream indIn = new IndefiniteLengthInputStream(this, limit);
+        ASN1StreamParser sp = new ASN1StreamParser(indIn, limit, tmpBuffers);
+
+        int tagClass = tag & PRIVATE;
+        if (0 != tagClass)
+        {
+            return sp.loadTaggedIL(tagClass, tagNo);
+        }
+
+        switch (tagNo)
+        {
+        case BIT_STRING:
+            return BERBitStringParser.parse(sp);
+        case OCTET_STRING:
+            return BEROctetStringParser.parse(sp);
+        case EXTERNAL:
+            // TODO[asn1] BERExternalParser
+            return DERExternalParser.parse(sp);
+        case SEQUENCE:
+            return BERSequenceParser.parse(sp);
+        case SET:
+            return BERSetParser.parse(sp);
+        default:
+            throw new IOException("unknown BER object encountered");
+        }
+    }
+
+    ASN1BitString buildConstructedBitString(ASN1EncodableVector contentsElements) throws IOException
+    {
+        ASN1BitString[] strings = new ASN1BitString[contentsElements.size()];
+
+        for (int i = 0; i != strings.length; i++)
+        {
+            ASN1Encodable asn1Obj = contentsElements.get(i);
+            if (asn1Obj instanceof ASN1BitString)
+            {
+                strings[i] = (ASN1BitString)asn1Obj;
+            }
+            else
+            {
+                throw new ASN1Exception(
+                    "unknown object encountered in constructed BIT STRING: " + asn1Obj.getClass());
+            }
+        }
+
+        // TODO Probably ought to be DLBitString
+        return new BERBitString(strings);
+    }
+
+    ASN1OctetString buildConstructedOctetString(ASN1EncodableVector contentsElements) throws IOException
+    {
+        ASN1OctetString[] strings = new ASN1OctetString[contentsElements.size()];
+
+        for (int i = 0; i != strings.length; i++)
+        {
+            ASN1Encodable asn1Obj = contentsElements.get(i);
+            if (asn1Obj instanceof ASN1OctetString)
+            {
+                strings[i] = (ASN1OctetString)asn1Obj;
+            }
+            else
+            {
+                throw new ASN1Exception(
+                    "unknown object encountered in constructed OCTET STRING: " + asn1Obj.getClass());
+            }
+        }
+
+        // TODO Probably ought to be DEROctetString (no DLOctetString available)
+        return new BEROctetString(strings);
+    }
+
+    ASN1Primitive readTaggedObjectDL(int tagClass, int tagNo, boolean constructed, DefiniteLengthInputStream defIn)
+        throws IOException
+    {
+        if (!constructed)
+        {
+            byte[] contentsOctets = defIn.toByteArray();
+            return ASN1TaggedObject.createPrimitive(tagClass, tagNo, contentsOctets);
+        }
+
+        ASN1EncodableVector contentsElements = readVector(defIn);
+        return ASN1TaggedObject.createConstructedDL(tagClass, tagNo, contentsElements);
+    }
+
+    ASN1EncodableVector readVector() throws IOException
+    {
+        ASN1Primitive p = readObject();
+        if (null == p)
+        {
+            return new ASN1EncodableVector(0);
+        }
+
+        ASN1EncodableVector v = new ASN1EncodableVector();
+        do
+        {
+            v.add(p);
+        }
+        while ((p = readObject()) != null);
+        return v;
+    }
+
+    ASN1EncodableVector readVector(DefiniteLengthInputStream defIn) throws IOException
+    {
+        int remaining = defIn.getRemaining();
+        if (remaining < 1)
+        {
+            return new ASN1EncodableVector(0);
+        }
+
+        return new ASN1InputStream(defIn, remaining, lazyEvaluate, tmpBuffers).readVector();
     }
 
     static int readTagNumber(InputStream s, int tag) 
@@ -301,32 +344,44 @@
         //
         if (tagNo == 0x1f)
         {
-            tagNo = 0;
-
             int b = s.read();
+            if (b < 31)
+            {
+                if (b < 0)
+                {
+                    throw new EOFException("EOF found inside tag value.");
+                }
+                throw new IOException("corrupted stream - high tag number < 31 found");
+            }
+
+            tagNo = b & 0x7f;
 
             // X.690-0207 8.1.2.4.2
             // "c) bits 7 to 1 of the first subsequent octet shall not all be zero."
-            if ((b & 0x7f) == 0) // Note: -1 will pass
+            if (0 == tagNo)
             {
                 throw new IOException("corrupted stream - invalid high tag number found");
             }
 
-            while ((b >= 0) && ((b & 0x80) != 0))
+            while ((b & 0x80) != 0)
             {
-                tagNo |= (b & 0x7f);
-                tagNo <<= 7;
-                b = s.read();
-            }
+                if ((tagNo >>> 24) != 0)
+                {
+                    throw new IOException("Tag number more than 31 bits");
+                }
 
-            if (b < 0)
-            {
-                throw new EOFException("EOF found inside tag value.");
+                tagNo <<= 7;
+
+                b = s.read();
+                if (b < 0)
+                {
+                    throw new EOFException("EOF found inside tag value.");
+                }
+
+                tagNo |= (b & 0x7f);
             }
-            
-            tagNo |= (b & 0x7f);
         }
-        
+
         return tagNo;
     }
 
@@ -334,48 +389,48 @@
         throws IOException
     {
         int length = s.read();
+        if (0 == (length >>> 7))
+        {
+            // definite-length short form 
+            return length;
+        }
+        if (0x80 == length)
+        {
+            // indefinite-length
+            return -1;
+        }
         if (length < 0)
         {
             throw new EOFException("EOF found when length expected");
         }
-
-        if (length == 0x80)
+        if (0xFF == length)
         {
-            return -1;      // indefinite-length encoding
+            throw new IOException("invalid long form definite-length 0xFF");
         }
 
-        if (length > 127)
+        int octetsCount = length & 0x7F, octetsPos = 0;
+
+        length = 0;
+        do
         {
-            int size = length & 0x7f;
-
-            // Note: The invalid long form "0xff" (see X.690 8.1.3.5c) will be caught here
-            if (size > 4)
+            int octet = s.read();
+            if (octet < 0)
             {
-                throw new IOException("DER length more than 4 bytes: " + size);
+                throw new EOFException("EOF found reading length");
             }
 
-            length = 0;
-            for (int i = 0; i < size; i++)
+            if ((length >>> 23) != 0)
             {
-                int next = s.read();
-
-                if (next < 0)
-                {
-                    throw new EOFException("EOF found reading length");
-                }
-
-                length = (length << 8) + next;
+                throw new IOException("long form definite-length more than 31 bits");
             }
 
-            if (length < 0)
-            {
-                throw new IOException("corrupted stream - negative length found");
-            }
+            length = (length << 8) + octet;
+        }
+        while (++octetsPos < octetsCount);
 
-            if (length >= limit && !isParsing)   // after all we must have read at least 1 byte
-            {
-                throw new IOException("corrupted stream - out of bounds length found: " + length + " >= " + limit);
-            }
+        if (length >= limit && !isParsing)   // after all we must have read at least 1 byte
+        {
+            throw new IOException("corrupted stream - out of bounds length found: " + length + " >= " + limit);
         }
 
         return length;
@@ -459,50 +514,79 @@
         byte[][] tmpBuffers)
         throws IOException
     {
-        switch (tagNo)
+        /*
+         * TODO[asn1] Lookup the universal type object and get it to parse the stream directly (possibly with
+         * access to a single temporary buffer replacing tmpBuffers).
+         */
+        try
         {
+            switch (tagNo)
+            {
             case BIT_STRING:
-                return ASN1BitString.fromInputStream(defIn.getRemaining(), defIn);
+                return ASN1BitString.createPrimitive(defIn.toByteArray());
             case BMP_STRING:
-                return new DERBMPString(getBMPCharBuffer(defIn));
+                return ASN1BMPString.createPrimitive(getBMPCharBuffer(defIn));
             case BOOLEAN:
-                return ASN1Boolean.fromOctetString(getBuffer(defIn, tmpBuffers));
+                return ASN1Boolean.createPrimitive(getBuffer(defIn, tmpBuffers));
             case ENUMERATED:
-                return ASN1Enumerated.fromOctetString(getBuffer(defIn, tmpBuffers));
-            case GENERALIZED_TIME:
-                return new ASN1GeneralizedTime(defIn.toByteArray());
+                // TODO Ideally only clone if we used a buffer
+                return ASN1Enumerated.createPrimitive(getBuffer(defIn, tmpBuffers), true);
             case GENERAL_STRING:
-                return new DERGeneralString(defIn.toByteArray());
-            case IA5_STRING:
-                return new DERIA5String(defIn.toByteArray());
-            case INTEGER:
-                return new ASN1Integer(defIn.toByteArray(), false);
-            case NULL:
-                return DERNull.INSTANCE;   // actual content is ignored (enforce 0 length?)
-            case NUMERIC_STRING:
-                return new DERNumericString(defIn.toByteArray());
-            case OBJECT_IDENTIFIER:
-                return ASN1ObjectIdentifier.fromOctetString(getBuffer(defIn, tmpBuffers));
-            case OCTET_STRING:
-                return new DEROctetString(defIn.toByteArray());
-            case PRINTABLE_STRING:
-                return new DERPrintableString(defIn.toByteArray());
-            case T61_STRING:
-                return new DERT61String(defIn.toByteArray());
-            case UNIVERSAL_STRING:
-                return new DERUniversalString(defIn.toByteArray());
-            case UTC_TIME:
-                return new ASN1UTCTime(defIn.toByteArray());
-            case UTF8_STRING:
-                return new DERUTF8String(defIn.toByteArray());
-            case VISIBLE_STRING:
-                return new DERVisibleString(defIn.toByteArray());
+                return ASN1GeneralString.createPrimitive(defIn.toByteArray());
+            case GENERALIZED_TIME:
+                return ASN1GeneralizedTime.createPrimitive(defIn.toByteArray());
             case GRAPHIC_STRING:
-                return new DERGraphicString(defIn.toByteArray());
+                return ASN1GraphicString.createPrimitive(defIn.toByteArray());
+            case IA5_STRING:
+                return ASN1IA5String.createPrimitive(defIn.toByteArray());
+            case INTEGER:
+                return ASN1Integer.createPrimitive(defIn.toByteArray());
+            case NULL:
+                return ASN1Null.createPrimitive(defIn.toByteArray());
+            case NUMERIC_STRING:
+                return ASN1NumericString.createPrimitive(defIn.toByteArray());
+            case OBJECT_DESCRIPTOR:
+                return ASN1ObjectDescriptor.createPrimitive(defIn.toByteArray());
+            case OBJECT_IDENTIFIER:
+                // TODO Ideally only clone if we used a buffer
+                return ASN1ObjectIdentifier.createPrimitive(getBuffer(defIn, tmpBuffers), true);
+            case OCTET_STRING:
+                return ASN1OctetString.createPrimitive(defIn.toByteArray());
+            case PRINTABLE_STRING:
+                return ASN1PrintableString.createPrimitive(defIn.toByteArray());
+            case RELATIVE_OID:
+                return ASN1RelativeOID.createPrimitive(defIn.toByteArray(), false);
+            case T61_STRING:
+                return ASN1T61String.createPrimitive(defIn.toByteArray());
+            case UNIVERSAL_STRING:
+                return ASN1UniversalString.createPrimitive(defIn.toByteArray());
+            case UTC_TIME:
+                return ASN1UTCTime.createPrimitive(defIn.toByteArray());
+            case UTF8_STRING:
+                return ASN1UTF8String.createPrimitive(defIn.toByteArray());
             case VIDEOTEX_STRING:
-                return new DERVideotexString(defIn.toByteArray());
+                return ASN1VideotexString.createPrimitive(defIn.toByteArray());
+            case VISIBLE_STRING:
+                return ASN1VisibleString.createPrimitive(defIn.toByteArray());
+            case TIME:
+            case DATE:
+            case TIME_OF_DAY:
+            case DATE_TIME:
+            case DURATION:
+            case OBJECT_IDENTIFIER_IRI:
+            case RELATIVE_OID_IRI:
+                throw new IOException("unsupported tag " + tagNo + " encountered");
             default:
                 throw new IOException("unknown tag " + tagNo + " encountered");
+            }
+        }
+        catch (IllegalArgumentException e)
+        {
+            throw new ASN1Exception(e.getMessage(), e);
+        }
+        catch (IllegalStateException e)
+        {
+            throw new ASN1Exception(e.getMessage(), e);
         }
     }
 }
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1Integer.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1Integer.java
index d09e343..a07c379 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1Integer.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1Integer.java
@@ -14,6 +14,14 @@
 public class ASN1Integer
     extends ASN1Primitive
 {
+    static final ASN1UniversalType TYPE = new ASN1UniversalType(ASN1Integer.class, BERTags.INTEGER)
+    {
+        ASN1Primitive fromImplicitPrimitive(DEROctetString octetString)
+        {
+            return createPrimitive(octetString.getOctets());
+        }
+    };
+
     static final int SIGN_EXT_SIGNED = 0xFFFFFFFF;
     static final int SIGN_EXT_UNSIGNED = 0xFF;
 
@@ -39,7 +47,7 @@
         {
             try
             {
-                return (ASN1Integer)fromByteArray((byte[])obj);
+                return (ASN1Integer)TYPE.fromByteArray((byte[])obj);
             }
             catch (Exception e)
             {
@@ -53,27 +61,16 @@
     /**
      * Return an Integer from a tagged object.
      *
-     * @param obj      the tagged object holding the object we want
+     * @param taggedObject the tagged object holding the object we want
      * @param explicit true if the object is meant to be explicitly
      *                 tagged false otherwise.
      * @return an ASN1Integer instance.
      * @throws IllegalArgumentException if the tagged object cannot
      * be converted.
      */
-    public static ASN1Integer getInstance(
-        ASN1TaggedObject obj,
-        boolean explicit)
+    public static ASN1Integer getInstance(ASN1TaggedObject taggedObject, boolean explicit)
     {
-        ASN1Primitive o = obj.getObject();
-
-        if (explicit || o instanceof ASN1Integer)
-        {
-            return getInstance(o);
-        }
-        else
-        {
-            return new ASN1Integer(ASN1OctetString.getInstance(o).getOctets());
-        }
+        return (ASN1Integer)TYPE.getContextInstance(taggedObject, explicit);
     }
 
     /**
@@ -152,6 +149,18 @@
         return new BigInteger(bytes);
     }
 
+    public boolean hasValue(int x)
+    {
+        return (bytes.length - start) <= 4
+            && intValue(bytes, start, SIGN_EXT_SIGNED) == x;
+    }
+
+    public boolean hasValue(long x)
+    {
+        return (bytes.length - start) <= 8
+            && longValue(bytes, start, SIGN_EXT_SIGNED) == x;
+    }
+
     public boolean hasValue(BigInteger x)
     {
         return null != x
@@ -193,19 +202,19 @@
         return longValue(bytes, start, SIGN_EXT_SIGNED);
     }
 
-    boolean isConstructed()
+    boolean encodeConstructed()
     {
         return false;
     }
 
-    int encodedLength()
+    int encodedLength(boolean withTag)
     {
-        return 1 + StreamUtil.calculateBodyLength(bytes.length) + bytes.length;
+        return ASN1OutputStream.getLengthOfEncodingDL(withTag, bytes.length);
     }
 
     void encode(ASN1OutputStream out, boolean withTag) throws IOException
     {
-        out.writeEncoded(withTag, BERTags.INTEGER, bytes);
+        out.writeEncodingDL(withTag, BERTags.INTEGER, bytes);
     }
 
     public int hashCode()
@@ -230,6 +239,11 @@
         return getValue().toString();
     }
 
+    static ASN1Integer createPrimitive(byte[] contents)
+    {
+        return new ASN1Integer(contents, false);
+    }
+
     static int intValue(byte[] bytes, int start, int signExt)
     {
         int length = bytes.length;
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1Null.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1Null.java
index d47488e..921386d 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1Null.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1Null.java
@@ -1,7 +1,4 @@
 /* GENERATED SOURCE. DO NOT MODIFY. */
-/***************************************************************/
-/******    DO NOT EDIT THIS CLASS bc-java SOURCE FILE     ******/
-/***************************************************************/
 package com.android.internal.org.bouncycastle.asn1;
 
 import java.io.IOException;
@@ -13,10 +10,13 @@
 public abstract class ASN1Null
     extends ASN1Primitive
 {
-    ASN1Null()
+    static final ASN1UniversalType TYPE = new ASN1UniversalType(ASN1Null.class, BERTags.NULL)
     {
-
-    }
+        ASN1Primitive fromImplicitPrimitive(DEROctetString octetString)
+        {
+            return createPrimitive(octetString.getOctets());
+        }
+    };
 
     /**
      * Return an instance of ASN.1 NULL from the passed in object.
@@ -44,21 +44,26 @@
         {
             try
             {
-                return ASN1Null.getInstance(ASN1Primitive.fromByteArray((byte[])o));
+                return (ASN1Null)TYPE.fromByteArray((byte[])o);
             }
             catch (IOException e)
             {
                 throw new IllegalArgumentException("failed to construct NULL from byte[]: " + e.getMessage());
             }
-            catch (ClassCastException e)
-            {
-                throw new IllegalArgumentException("unknown object in getInstance(): " + o.getClass().getName());
-            }
         }
 
         return null;
     }
 
+    public static ASN1Null getInstance(ASN1TaggedObject taggedObject, boolean explicit)
+    {
+        return (ASN1Null)TYPE.getContextInstance(taggedObject, explicit);
+    }
+
+    ASN1Null()
+    {
+    }
+
     public int hashCode()
     {
         return -1;
@@ -75,10 +80,17 @@
         return true;
     }
 
-    abstract void encode(ASN1OutputStream out, boolean withTag) throws IOException;
-
     public String toString()
     {
          return "NULL";
     }
+
+    static ASN1Null createPrimitive(byte[] contents)
+    {
+        if (0 != contents.length)
+        {
+            throw new IllegalStateException("malformed NULL encoding encountered");
+        }
+        return DERNull.INSTANCE;
+    }
 }
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1NumericString.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1NumericString.java
new file mode 100644
index 0000000..324060e
--- /dev/null
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1NumericString.java
@@ -0,0 +1,215 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.asn1;
+
+import java.io.IOException;
+
+import com.android.internal.org.bouncycastle.util.Arrays;
+import com.android.internal.org.bouncycastle.util.Strings;
+
+/**
+ * NumericString object - this is an ascii string of characters {0,1,2,3,4,5,6,7,8,9, }.
+ * ASN.1 NUMERIC-STRING object.
+ * <p>
+ * This is an ASCII string of characters {0,1,2,3,4,5,6,7,8,9} + space.
+ * <p>
+ * See X.680 section 37.2.
+ * <p>
+ * Explicit character set escape sequences are not allowed.
+ * @hide This class is not part of the Android public SDK API
+ */
+public abstract class ASN1NumericString
+    extends ASN1Primitive
+    implements ASN1String
+{
+    static final ASN1UniversalType TYPE = new ASN1UniversalType(ASN1NumericString.class, BERTags.NUMERIC_STRING)
+    {
+        ASN1Primitive fromImplicitPrimitive(DEROctetString octetString)
+        {
+            return createPrimitive(octetString.getOctets());
+        }
+    };
+
+    /**
+     * Return a Numeric string from the passed in object
+     *
+     * @param obj an ASN1NumericString or an object that can be converted into one.
+     * @exception IllegalArgumentException if the object cannot be converted.
+     * @return an ASN1NumericString instance, or null
+     */
+    public static ASN1NumericString getInstance(Object obj)
+    {
+        if (obj == null || obj instanceof ASN1NumericString)
+        {
+            return (ASN1NumericString)obj;
+        }
+        if (obj instanceof ASN1Encodable)
+        {
+            ASN1Primitive primitive = ((ASN1Encodable)obj).toASN1Primitive();
+            if (primitive instanceof ASN1NumericString)
+            {
+                return (ASN1NumericString)primitive;
+            }
+        }
+        if (obj instanceof byte[])
+        {
+            try
+            {
+                return (ASN1NumericString)TYPE.fromByteArray((byte[])obj);
+            }
+            catch (Exception e)
+            {
+                throw new IllegalArgumentException("encoding error in getInstance: " + e.toString());
+            }
+        }
+
+        throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
+    }
+
+    /**
+     * Return an Numeric String from a tagged object.
+     *
+     * @param taggedObject the tagged object holding the object we want
+     * @param explicit     true if the object is meant to be explicitly tagged false
+     *                     otherwise.
+     * @exception IllegalArgumentException if the tagged object cannot be converted.
+     * @return an ASN1NumericString instance, or null.
+     */
+    public static ASN1NumericString getInstance(ASN1TaggedObject taggedObject, boolean explicit)
+    {
+        return (ASN1NumericString)TYPE.getContextInstance(taggedObject, explicit);
+    }
+
+    final byte[] contents;
+
+    /**
+     * Constructor with optional validation.
+     *
+     * @param string the base string to wrap.
+     * @param validate whether or not to check the string.
+     * @throws IllegalArgumentException if validate is true and the string
+     * contains characters that should not be in a NumericString.
+     */
+    ASN1NumericString(String string, boolean validate)
+    {
+        if (validate && !isNumericString(string))
+        {
+            throw new IllegalArgumentException("string contains illegal characters");
+        }
+
+        this.contents = Strings.toByteArray(string);
+    }
+
+    ASN1NumericString(byte[] contents, boolean clone)
+    {
+        this.contents = clone ? Arrays.clone(contents) : contents;
+    }
+
+    public final String getString()
+    {
+        return Strings.fromByteArray(contents);
+    }
+
+    public String toString()
+    {
+        return getString();
+    }
+
+    public final byte[] getOctets()
+    {
+        return Arrays.clone(contents);
+    }
+
+    final boolean encodeConstructed()
+    {
+        return false;
+    }
+
+    final int encodedLength(boolean withTag)
+    {
+        return ASN1OutputStream.getLengthOfEncodingDL(withTag, contents.length);
+    }
+
+    final void encode(ASN1OutputStream out, boolean withTag) throws IOException
+    {
+        out.writeEncodingDL(withTag, BERTags.NUMERIC_STRING, contents);
+    }
+
+    public final int hashCode()
+    {
+        return Arrays.hashCode(contents);
+    }
+
+    final boolean asn1Equals(ASN1Primitive other)
+    {
+        if (!(other instanceof ASN1NumericString))
+        {
+            return false;
+        }
+
+        ASN1NumericString that = (ASN1NumericString)other;
+
+        return Arrays.areEqual(this.contents, that.contents);
+    }
+
+    /**
+     * Return true if the string can be represented as a NumericString ('0'..'9', ' ')
+     *
+     * @param str string to validate.
+     * @return true if numeric, false otherwise.
+     */
+    public static boolean isNumericString(String str)
+    {
+        for (int i = str.length() - 1; i >= 0; i--)
+        {
+            char ch = str.charAt(i);
+
+            if (ch > 0x007f)
+            {
+                return false;
+            }
+
+            if (('0' <= ch && ch <= '9') || ch == ' ')
+            {
+                continue;
+            }
+
+            return false;
+        }
+
+        return true;
+    }
+
+    static boolean isNumericString(byte[] contents)
+    {
+        for (int i = 0; i < contents.length; ++i)
+        {
+            switch (contents[i])
+            {
+            case 0x20:
+            case 0x30:
+            case 0x31:
+            case 0x32:
+            case 0x33:
+            case 0x34:
+            case 0x35:
+            case 0x36:
+            case 0x37:
+            case 0x38:
+            case 0x39:
+                break;
+            default:
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    static ASN1NumericString createPrimitive(byte[] contents)
+    {
+        // TODO Validation - sort out exception types
+//        if (!isNumericString(contents))
+
+        return new DERNumericString(contents, false);
+    }
+}
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1Object.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1Object.java
index adcd257..82c48f2 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1Object.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1Object.java
@@ -16,12 +16,12 @@
 {
     public void encodeTo(OutputStream output) throws IOException
     {
-        ASN1OutputStream.create(output).writeObject(this);
+        toASN1Primitive().encodeTo(output);
     }
 
     public void encodeTo(OutputStream output, String encoding) throws IOException
     {
-        ASN1OutputStream.create(output, encoding).writeObject(this);
+        toASN1Primitive().encodeTo(output, encoding);
     }
 
     /**
@@ -33,7 +33,7 @@
     public byte[] getEncoded() throws IOException
     {
         ByteArrayOutputStream bOut = new ByteArrayOutputStream();
-        encodeTo(bOut);
+        toASN1Primitive().encodeTo(bOut);
         return bOut.toByteArray();
     }
 
@@ -47,7 +47,7 @@
     public byte[] getEncoded(String encoding) throws IOException
     {
         ByteArrayOutputStream bOut = new ByteArrayOutputStream();
-        encodeTo(bOut, encoding);
+        toASN1Primitive().encodeTo(bOut, encoding);
         return bOut.toByteArray();
     }
 
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1ObjectDescriptor.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1ObjectDescriptor.java
new file mode 100644
index 0000000..bf99df3
--- /dev/null
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1ObjectDescriptor.java
@@ -0,0 +1,145 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.asn1;
+
+import java.io.IOException;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+public final class ASN1ObjectDescriptor
+    extends ASN1Primitive
+{
+    static final ASN1UniversalType TYPE = new ASN1UniversalType(ASN1ObjectDescriptor.class, BERTags.OBJECT_DESCRIPTOR)
+    {
+        ASN1Primitive fromImplicitPrimitive(DEROctetString octetString)
+        {
+            return new ASN1ObjectDescriptor(
+                (ASN1GraphicString)ASN1GraphicString.TYPE.fromImplicitPrimitive(octetString));
+        }
+
+        ASN1Primitive fromImplicitConstructed(ASN1Sequence sequence)
+        {
+            return new ASN1ObjectDescriptor(
+                (ASN1GraphicString)ASN1GraphicString.TYPE.fromImplicitConstructed(sequence));
+        }
+    };
+
+    /**
+     * Return an ObjectDescriptor from the passed in object.
+     *
+     * @param obj an ASN1ObjectDescriptor or an object that can be converted into one.
+     * @exception IllegalArgumentException if the object cannot be converted.
+     * @return an ASN1ObjectDescriptor instance, or null.
+     */
+    public static ASN1ObjectDescriptor getInstance(Object obj)
+    {
+        if (obj == null || obj instanceof ASN1ObjectDescriptor)
+        {
+            return (ASN1ObjectDescriptor)obj;
+        }
+        else if (obj instanceof ASN1Encodable)
+        {
+            ASN1Primitive primitive = ((ASN1Encodable)obj).toASN1Primitive();
+            if (primitive instanceof ASN1ObjectDescriptor)
+            {
+                return (ASN1ObjectDescriptor)primitive;
+            }
+        }
+        else if (obj instanceof byte[])
+        {
+            try
+            {
+                return (ASN1ObjectDescriptor)TYPE.fromByteArray((byte[])obj);
+            }
+            catch (IOException e)
+            {
+                throw new IllegalArgumentException("failed to construct object descriptor from byte[]: " + e.getMessage());
+            }
+        }
+
+        throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
+    }
+
+    /**
+     * Return an ObjectDescriptor from a tagged object.
+     *
+     * @param taggedObject the tagged object holding the object we want.
+     * @param explicit     true if the object is meant to be explicitly tagged,
+     *                     false otherwise.
+     * @exception IllegalArgumentException if the tagged object cannot be converted.
+     * @return an ASN1ObjectDescriptor instance, or null.
+     */
+    public static ASN1ObjectDescriptor getInstance(ASN1TaggedObject taggedObject, boolean explicit)
+    {
+        return (ASN1ObjectDescriptor)TYPE.getContextInstance(taggedObject, explicit);
+    }
+
+    private final ASN1GraphicString baseGraphicString;
+
+    public ASN1ObjectDescriptor(ASN1GraphicString baseGraphicString)
+    {
+        if (null == baseGraphicString)
+        {
+            throw new NullPointerException("'baseGraphicString' cannot be null");
+        }
+
+        this.baseGraphicString = baseGraphicString;
+    }
+
+    public ASN1GraphicString getBaseGraphicString()
+    {
+        return baseGraphicString;
+    }
+
+    boolean encodeConstructed()
+    {
+        return false;
+    }
+
+    int encodedLength(boolean withTag)
+    {
+        return baseGraphicString.encodedLength(withTag);
+    }
+
+    void encode(ASN1OutputStream out, boolean withTag) throws IOException
+    {
+        out.writeIdentifier(withTag, BERTags.OBJECT_DESCRIPTOR);
+        baseGraphicString.encode(out, false);
+    }
+
+    ASN1Primitive toDERObject()
+    {
+        ASN1GraphicString der = (ASN1GraphicString)baseGraphicString.toDERObject();
+
+        return der == baseGraphicString ? this : new ASN1ObjectDescriptor(der);
+    }
+
+    ASN1Primitive toDLObject()
+    {
+        ASN1GraphicString dl = (ASN1GraphicString)baseGraphicString.toDLObject();
+
+        return dl == baseGraphicString ? this : new ASN1ObjectDescriptor(dl);
+    }
+
+    boolean asn1Equals(ASN1Primitive other)
+    {
+        if (!(other instanceof ASN1ObjectDescriptor))
+        {
+            return false;
+        }
+
+        ASN1ObjectDescriptor that = (ASN1ObjectDescriptor)other;
+
+        return this.baseGraphicString.asn1Equals(that.baseGraphicString);
+    }
+
+    public int hashCode()
+    {
+        return ~baseGraphicString.hashCode();
+    }
+
+    static ASN1ObjectDescriptor createPrimitive(byte[] contents)
+    {
+        return new ASN1ObjectDescriptor(ASN1GraphicString.createPrimitive(contents));
+    }
+}
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1ObjectIdentifier.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1ObjectIdentifier.java
index 0a0eac7..528d8bf 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1ObjectIdentifier.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1ObjectIdentifier.java
@@ -16,9 +16,18 @@
 public class ASN1ObjectIdentifier
     extends ASN1Primitive
 {
-    private final String identifier;
+    static final ASN1UniversalType TYPE = new ASN1UniversalType(ASN1ObjectIdentifier.class, BERTags.OBJECT_IDENTIFIER)
+    {
+        ASN1Primitive fromImplicitPrimitive(DEROctetString octetString)
+        {
+            return createPrimitive(octetString.getOctets(), false);
+        }
+    };
 
-    private byte[] body;
+    public static ASN1ObjectIdentifier fromContents(byte[] contents)
+    {
+        return createPrimitive(contents, true);
+    }
 
     /**
      * Return an OID from the passed in object
@@ -27,30 +36,25 @@
      * @return an ASN1ObjectIdentifier instance, or null.
      * @throws IllegalArgumentException if the object cannot be converted.
      */
-    public static ASN1ObjectIdentifier getInstance(
-        Object obj)
+    public static ASN1ObjectIdentifier getInstance(Object obj)
     {
         if (obj == null || obj instanceof ASN1ObjectIdentifier)
         {
             return (ASN1ObjectIdentifier)obj;
         }
-
-        if (obj instanceof ASN1Encodable)
+        else if (obj instanceof ASN1Encodable)
         {
             ASN1Primitive primitive = ((ASN1Encodable)obj).toASN1Primitive();
-
             if (primitive instanceof ASN1ObjectIdentifier)
             {
                 return (ASN1ObjectIdentifier)primitive;
             }
         }
-
-        if (obj instanceof byte[])
+        else if (obj instanceof byte[])
         {
-            byte[] enc = (byte[])obj;
             try
             {
-                return (ASN1ObjectIdentifier)fromByteArray(enc);
+                return (ASN1ObjectIdentifier)TYPE.fromByteArray((byte[])obj);
             }
             catch (IOException e)
             {
@@ -64,47 +68,60 @@
     /**
      * Return an OBJECT IDENTIFIER from a tagged object.
      *
-     * @param obj      the tagged object holding the object we want
+     * @param taggedObject      the tagged object holding the object we want
      * @param explicit true if the object is meant to be explicitly
      *                 tagged false otherwise.
      * @return an ASN1ObjectIdentifier instance, or null.
      * @throws IllegalArgumentException if the tagged object cannot
      * be converted.
      */
-    public static ASN1ObjectIdentifier getInstance(
-        ASN1TaggedObject obj,
-        boolean explicit)
+    public static ASN1ObjectIdentifier getInstance(ASN1TaggedObject taggedObject, boolean explicit)
     {
-        ASN1Primitive o = obj.getObject();
+        /*
+         * TODO[asn1] This block here is for backward compatibility, but should eventually be removed.
+         * 
+         * - see https://github.com/bcgit/bc-java/issues/1015
+         */
+        if (!explicit && !taggedObject.isParsed() && BERTags.CONTEXT_SPECIFIC == taggedObject.getTagClass())
+        {
+            ASN1Primitive base = taggedObject.getBaseObject().toASN1Primitive();
+            if (!(base instanceof ASN1ObjectIdentifier))
+            {
+                return fromContents(ASN1OctetString.getInstance(base).getOctets());
+            }
+        }
 
-        if (explicit || o instanceof ASN1ObjectIdentifier)
-        {
-            return getInstance(o);
-        }
-        else
-        {
-            return ASN1ObjectIdentifier.fromOctetString(ASN1OctetString.getInstance(o).getOctets());
-        }
+        return (ASN1ObjectIdentifier)TYPE.getContextInstance(taggedObject, explicit);
     }
 
-    private static final long LONG_LIMIT = (Long.MAX_VALUE >> 7) - 0x7f;
+    private static final long LONG_LIMIT = (Long.MAX_VALUE >> 7) - 0x7F;
 
-    ASN1ObjectIdentifier(
-        byte[] bytes)
+    private static final ConcurrentMap<OidHandle, ASN1ObjectIdentifier> pool =
+        new ConcurrentHashMap<OidHandle, ASN1ObjectIdentifier>();
+
+    private final String identifier;
+    private byte[] contents;
+
+    ASN1ObjectIdentifier(byte[] contents, boolean clone)
     {
-        StringBuffer objId = new StringBuffer();
+        if (contents.length == 0)
+        {
+            throw new IllegalArgumentException("empty OBJECT IDENTIFIER with no sub-identifiers");
+        }
+
+        StringBuilder objId = new StringBuilder();
         long value = 0;
         BigInteger bigValue = null;
         boolean first = true;
 
-        for (int i = 0; i != bytes.length; i++)
+        for (int i = 0; i != contents.length; i++)
         {
-            int b = bytes[i] & 0xff;
+            int b = contents[i] & 0xff;
 
             if (value <= LONG_LIMIT)
             {
-                value += (b & 0x7f);
-                if ((b & 0x80) == 0)             // end of number reached
+                value += b & 0x7F;
+                if ((b & 0x80) == 0)
                 {
                     if (first)
                     {
@@ -140,7 +157,7 @@
                 {
                     bigValue = BigInteger.valueOf(value);
                 }
-                bigValue = bigValue.or(BigInteger.valueOf(b & 0x7f));
+                bigValue = bigValue.or(BigInteger.valueOf(b & 0x7F));
                 if ((b & 0x80) == 0)
                 {
                     if (first)
@@ -164,7 +181,7 @@
 
         // Android-changed: Intern the identifier so there aren't hundreds of duplicates in practice.
         this.identifier = objId.toString().intern();
-        this.body = Arrays.clone(bytes);
+        this.contents = clone ? Arrays.clone(contents) : contents;
     }
 
     /**
@@ -196,7 +213,7 @@
      */
     ASN1ObjectIdentifier(ASN1ObjectIdentifier oid, String branchID)
     {
-        if (!isValidBranchID(branchID, 0))
+        if (!ASN1RelativeOID.isValidIdentifier(branchID, 0))
         {
             throw new IllegalArgumentException("string " + branchID + " not a valid OID branch");
         }
@@ -237,44 +254,6 @@
         return id.length() > stemId.length() && id.charAt(stemId.length()) == '.' && id.startsWith(stemId);
     }
 
-    private void writeField(
-        ByteArrayOutputStream out,
-        long fieldValue)
-    {
-        byte[] result = new byte[9];
-        int pos = 8;
-        result[pos] = (byte)((int)fieldValue & 0x7f);
-        while (fieldValue >= (1L << 7))
-        {
-            fieldValue >>= 7;
-            result[--pos] = (byte)((int)fieldValue & 0x7f | 0x80);
-        }
-        out.write(result, pos, 9 - pos);
-    }
-
-    private void writeField(
-        ByteArrayOutputStream out,
-        BigInteger fieldValue)
-    {
-        int byteCount = (fieldValue.bitLength() + 6) / 7;
-        if (byteCount == 0)
-        {
-            out.write(0);
-        }
-        else
-        {
-            BigInteger tmpValue = fieldValue;
-            byte[] tmp = new byte[byteCount];
-            for (int i = byteCount - 1; i >= 0; i--)
-            {
-                tmp[i] = (byte)((tmpValue.intValue() & 0x7f) | 0x80);
-                tmpValue = tmpValue.shiftRight(7);
-            }
-            tmp[byteCount - 1] &= 0x7f;
-            out.write(tmp, 0, tmp.length);
-        }
-    }
-
     private void doOutput(ByteArrayOutputStream aOut)
     {
         OIDTokenizer tok = new OIDTokenizer(identifier);
@@ -283,11 +262,11 @@
         String secondToken = tok.nextToken();
         if (secondToken.length() <= 18)
         {
-            writeField(aOut, first + Long.parseLong(secondToken));
+            ASN1RelativeOID.writeField(aOut, first + Long.parseLong(secondToken));
         }
         else
         {
-            writeField(aOut, new BigInteger(secondToken).add(BigInteger.valueOf(first)));
+            ASN1RelativeOID.writeField(aOut, new BigInteger(secondToken).add(BigInteger.valueOf(first)));
         }
 
         while (tok.hasMoreTokens())
@@ -295,45 +274,42 @@
             String token = tok.nextToken();
             if (token.length() <= 18)
             {
-                writeField(aOut, Long.parseLong(token));
+                ASN1RelativeOID.writeField(aOut, Long.parseLong(token));
             }
             else
             {
-                writeField(aOut, new BigInteger(token));
+                ASN1RelativeOID.writeField(aOut, new BigInteger(token));
             }
         }
     }
 
-    private synchronized byte[] getBody()
+    private synchronized byte[] getContents()
     {
-        if (body == null)
+        if (contents == null)
         {
             ByteArrayOutputStream bOut = new ByteArrayOutputStream();
 
             doOutput(bOut);
 
-            body = bOut.toByteArray();
+            contents = bOut.toByteArray();
         }
 
-        return body;
+        return contents;
     }
 
-    boolean isConstructed()
+    boolean encodeConstructed()
     {
         return false;
     }
 
-    int encodedLength()
-        throws IOException
+    int encodedLength(boolean withTag)
     {
-        int length = getBody().length;
-
-        return 1 + StreamUtil.calculateBodyLength(length) + length;
+        return ASN1OutputStream.getLengthOfEncodingDL(withTag, getContents().length);
     }
 
     void encode(ASN1OutputStream out, boolean withTag) throws IOException
     {
-        out.writeEncoded(withTag, BERTags.OBJECT_IDENTIFIER, getBody());
+        out.writeEncodingDL(withTag, BERTags.OBJECT_IDENTIFIER, getContents());
     }
 
     public int hashCode()
@@ -362,45 +338,6 @@
         return getId();
     }
 
-    private static boolean isValidBranchID(
-        String branchID, int start)
-    {
-        int digitCount = 0;
-
-        int pos = branchID.length();
-        while (--pos >= start)
-        {
-            char ch = branchID.charAt(pos);
-
-            if (ch == '.')
-            {
-                if (0 == digitCount
-                    || (digitCount > 1 && branchID.charAt(pos + 1) == '0'))
-                {
-                    return false;
-                }
-
-                digitCount = 0;
-            }
-            else if ('0' <= ch && ch <= '9')
-            {
-                ++digitCount;
-            }
-            else
-            {
-                return false;
-            }
-        }
-
-        if (0 == digitCount
-            || (digitCount > 1 && branchID.charAt(pos + 1) == '0'))
-        {
-            return false;
-        }
-
-        return true;
-    }
-
     private static boolean isValidIdentifier(
         String identifier)
     {
@@ -415,7 +352,7 @@
             return false;
         }
 
-        return isValidBranchID(identifier, 2);
+        return ASN1RelativeOID.isValidIdentifier(identifier, 2);
     }
 
     /**
@@ -430,30 +367,35 @@
      */
     public ASN1ObjectIdentifier intern()
     {
-        final OidHandle hdl = new OidHandle(getBody());
+        final OidHandle hdl = new OidHandle(getContents());
         ASN1ObjectIdentifier oid = pool.get(hdl);
         if (oid == null)
         {
-            oid = pool.putIfAbsent(hdl, this);
-            if (oid == null)
+            synchronized (pool)
             {
-                oid = this;
+                if (!pool.containsKey(hdl))
+                {
+                    pool.put(hdl, this);
+                    return this;
+                }
+                else
+                {
+                    return pool.get(hdl);
+                }
             }
         }
         return oid;
     }
 
-    private static final ConcurrentMap<OidHandle, ASN1ObjectIdentifier> pool = new ConcurrentHashMap<OidHandle, ASN1ObjectIdentifier>();
-
     private static class OidHandle
     {
         private final int key;
-        private final byte[] enc;
+        private final byte[] contents;
 
-        OidHandle(byte[] enc)
+        OidHandle(byte[] contents)
         {
-            this.key = Arrays.hashCode(enc);
-            this.enc = enc;
+            this.key = Arrays.hashCode(contents);
+            this.contents = contents;
         }
 
         public int hashCode()
@@ -465,20 +407,20 @@
         {
             if (o instanceof OidHandle)
             {
-                return Arrays.areEqual(enc, ((OidHandle)o).enc);
+                return Arrays.areEqual(contents, ((OidHandle)o).contents);
             }
 
             return false;
         }
     }
 
-    static ASN1ObjectIdentifier fromOctetString(byte[] enc)
+    static ASN1ObjectIdentifier createPrimitive(byte[] contents, boolean clone)
     {
-        final OidHandle hdl = new OidHandle(enc);
+        final OidHandle hdl = new OidHandle(contents);
         ASN1ObjectIdentifier oid = pool.get(hdl);
         if (oid == null)
         {
-            return new ASN1ObjectIdentifier(enc);
+            return new ASN1ObjectIdentifier(contents, clone);
         }
         return oid;
     }
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1OctetString.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1OctetString.java
index f7b9617..95f4f95 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1OctetString.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1OctetString.java
@@ -102,7 +102,18 @@
     extends ASN1Primitive
     implements ASN1OctetStringParser
 {
-    byte[]  string;
+    static final ASN1UniversalType TYPE = new ASN1UniversalType(ASN1OctetString.class, BERTags.OCTET_STRING)
+    {
+        ASN1Primitive fromImplicitPrimitive(DEROctetString octetString)
+        {
+            return octetString;
+        }
+
+        ASN1Primitive fromImplicitConstructed(ASN1Sequence sequence)
+        {
+            return sequence.toASN1OctetString();
+        }
+    };
 
     /**
      * return an Octet String from a tagged object.
@@ -113,70 +124,9 @@
      * @exception IllegalArgumentException if the tagged object cannot
      *              be converted.
      */
-    public static ASN1OctetString getInstance(
-        ASN1TaggedObject    taggedObject,
-        boolean             explicit)
+    public static ASN1OctetString getInstance(ASN1TaggedObject taggedObject, boolean explicit)
     {
-        if (explicit)
-        {
-            if (!taggedObject.isExplicit())
-            {
-                throw new IllegalArgumentException("object implicit - explicit expected.");
-            }
-
-            return getInstance(taggedObject.getObject());
-        }
-
-        ASN1Primitive o = taggedObject.getObject();
-
-        /*
-         * constructed object which appears to be explicitly tagged and it's really implicit means
-         * we have to add the surrounding octet string.
-         */
-        if (taggedObject.isExplicit())
-        {
-            ASN1OctetString singleSegment = getInstance(o);
-
-            if (taggedObject instanceof BERTaggedObject)
-            {
-                return new BEROctetString(new ASN1OctetString[]{ singleSegment });
-            }
-
-            // TODO Should really be similar to the BERTaggedObject case above:
-//            return new DLOctetString(new ASN1OctetString[]{ singleSegment });
-            return (ASN1OctetString)new BEROctetString(new ASN1OctetString[]{ singleSegment }).toDLObject();
-        }
-
-        if (o instanceof ASN1OctetString)
-        {
-            ASN1OctetString s = (ASN1OctetString)o;
-
-            if (taggedObject instanceof BERTaggedObject)
-            {
-                return s;
-            }
-
-            return (ASN1OctetString)s.toDLObject();
-        }
-
-        /*
-         * in this case the parser returns a sequence, convert it into an octet string.
-         */
-        if (o instanceof ASN1Sequence)
-        {
-            ASN1Sequence s = (ASN1Sequence)o;
-
-            if (taggedObject instanceof BERTaggedObject)
-            {
-                return BEROctetString.fromSequence(s);
-            }
-
-            // TODO Should really be similar to the BERTaggedObject case above:
-//            return DLOctetString.fromSequence(s);
-            return (ASN1OctetString)BEROctetString.fromSequence(s).toDLObject();
-        }
-
-        throw new IllegalArgumentException("unknown object in getInstance: " + taggedObject.getClass().getName());
+        return (ASN1OctetString)TYPE.getContextInstance(taggedObject, explicit);
     }
 
     /**
@@ -185,37 +135,40 @@
      * @param obj the object we want converted.
      * @exception IllegalArgumentException if the object cannot be converted.
      */
-    public static ASN1OctetString getInstance(
-        Object  obj)
+    public static ASN1OctetString getInstance(Object obj)
     {
         if (obj == null || obj instanceof ASN1OctetString)
         {
             return (ASN1OctetString)obj;
         }
+//      else if (obj instanceof ASN1OctetStringParser)
+        else if (obj instanceof ASN1Encodable)
+        {
+            ASN1Primitive primitive = ((ASN1Encodable)obj).toASN1Primitive();
+            if (primitive instanceof ASN1OctetString)
+            {
+                return (ASN1OctetString)primitive;
+            }
+        }
         else if (obj instanceof byte[])
         {
             try
             {
-                return getInstance(fromByteArray((byte[])obj));
+                return (ASN1OctetString)TYPE.fromByteArray((byte[])obj);
             }
             catch (IOException e)
             {
                 throw new IllegalArgumentException("failed to construct OCTET STRING from byte[]: " + e.getMessage());
             }
         }
-        else if (obj instanceof ASN1Encodable)
-        {
-            ASN1Primitive primitive = ((ASN1Encodable)obj).toASN1Primitive();
-
-            if (primitive instanceof ASN1OctetString)
-            {
-                return (ASN1OctetString)primitive;
-            }
-        }
 
         throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
     }
 
+    static final byte[] EMPTY_OCTETS = new byte[0];
+
+    byte[] string;
+
     /**
      * Base constructor.
      *
@@ -261,6 +214,11 @@
         return string;
     }
 
+    public int getOctetsLength()
+    {
+        return getOctets().length;
+    }
+
     public int hashCode()
     {
         return Arrays.hashCode(this.getOctets());
@@ -294,10 +252,13 @@
         return new DEROctetString(string);
     }
 
-    abstract void encode(ASN1OutputStream out, boolean withTag) throws IOException;
-
     public String toString()
     {
       return "#" + Strings.fromByteArray(Hex.encode(string));
     }
+
+    static ASN1OctetString createPrimitive(byte[] contents)
+    {
+        return new DEROctetString(contents);
+    }
 }
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1OutputStream.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1OutputStream.java
index 7447d3b..1792e04 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1OutputStream.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1OutputStream.java
@@ -34,250 +34,33 @@
 
     private OutputStream os;
 
-    /**
-     * @deprecated Use {@link ASN1OutputStream#create(OutputStream)} instead.
-     */
-    public ASN1OutputStream(OutputStream os)
+    ASN1OutputStream(OutputStream os)
     {
         this.os = os;
     }
 
-    final void writeLength(
-        int length)
-        throws IOException
+    public void close() throws IOException
     {
-        if (length > 127)
-        {
-            int size = 1;
-            int val = length;
-
-            while ((val >>>= 8) != 0)
-            {
-                size++;
-            }
-
-            write((byte)(size | 0x80));
-
-            for (int i = (size - 1) * 8; i >= 0; i -= 8)
-            {
-                write((byte)(length >> i));
-            }
-        }
-        else
-        {
-            write((byte)length);
-        }
+        os.close();
     }
 
-    final void write(int b)
-        throws IOException
+    public void flush() throws IOException
     {
-        os.write(b);
+        os.flush();
     }
 
-    final void write(byte[] bytes, int off, int len)
-        throws IOException
+    public final void writeObject(ASN1Encodable encodable) throws IOException
     {
-        os.write(bytes, off, len);
-    }
-
-    final void writeElements(ASN1Encodable[] elements)
-        throws IOException
-    {
-        int count = elements.length;
-        for (int i = 0; i < count; ++i)
-        {
-            ASN1Primitive primitive = elements[i].toASN1Primitive();
-
-            writePrimitive(primitive, true);
-        }
-    }
-
-    final void writeElements(Enumeration elements)
-        throws IOException
-    {
-        while (elements.hasMoreElements())
-        {
-            ASN1Primitive primitive = ((ASN1Encodable)elements.nextElement()).toASN1Primitive();
-
-            writePrimitive(primitive, true);
-        }
-    }
-
-    final void writeEncoded(
-        boolean withTag,
-        int     tag,
-        byte    contents)
-        throws IOException
-    {
-        if (withTag)
-        {
-            write(tag);
-        }
-        writeLength(1);
-        write(contents);
-    }
-
-    final void writeEncoded(
-        boolean withTag,
-        int     tag,
-        byte[]  contents)
-        throws IOException
-    {
-        if (withTag)
-        {
-            write(tag);
-        }
-        writeLength(contents.length);
-        write(contents, 0, contents.length);
-    }
-
-    final void writeEncoded(
-        boolean withTag,
-        int     tag,
-        byte[]  contents,
-        int     contentsOff,
-        int     contentsLen)
-        throws IOException
-    {
-        if (withTag)
-        {
-            write(tag);
-        }
-        writeLength(contentsLen);
-        write(contents, contentsOff, contentsLen);
-    }
-
-    final void writeEncoded(
-        boolean withTag,
-        int     tag,
-        byte    headByte,
-        byte[]  tailBytes)
-        throws IOException
-    {
-        if (withTag)
-        {
-            write(tag);
-        }
-        writeLength(1 + tailBytes.length);
-        write(headByte);
-        write(tailBytes, 0, tailBytes.length);
-    }
-
-    final void writeEncoded(
-        boolean withTag,
-        int     tag,
-        byte    headByte,
-        byte[]  body,
-        int     bodyOff,
-        int     bodyLen,
-        byte    tailByte)
-        throws IOException
-    {
-        if (withTag)
-        {
-            write(tag);
-        }
-        writeLength(2 + bodyLen);
-        write(headByte);
-        write(body, bodyOff, bodyLen);
-        write(tailByte);
-    }
-
-    final void writeEncoded(boolean withTag, int flags, int tagNo, byte[] contents)
-        throws IOException
-    {
-        writeTag(withTag, flags, tagNo);
-        writeLength(contents.length);
-        write(contents, 0, contents.length);
-    }
-
-    final void writeEncodedIndef(boolean withTag, int flags, int tagNo, byte[] contents)
-        throws IOException
-    {
-        writeTag(withTag, flags, tagNo);
-        write(0x80);
-        write(contents, 0, contents.length);
-        write(0x00);
-        write(0x00);
-    }
-
-    final void writeEncodedIndef(boolean withTag, int tag, ASN1Encodable[] elements)
-        throws IOException
-    {
-        if (withTag)
-        {
-            write(tag);
-        }
-        write(0x80);
-        writeElements(elements);
-        write(0x00);
-        write(0x00);
-    }
-
-    final void writeEncodedIndef(boolean withTag, int tag, Enumeration elements)
-        throws IOException
-    {
-        if (withTag)
-        {
-            write(tag);
-        }
-        write(0x80);
-        writeElements(elements);
-        write(0x00);
-        write(0x00);
-    }
-
-    final void writeTag(boolean withTag, int flags, int tagNo)
-        throws IOException
-    {
-        if (!withTag)
-        {
-            return;
-        }
-
-        if (tagNo < 31)
-        {
-            write(flags | tagNo);
-        }
-        else
-        {
-            write(flags | 0x1f);
-            if (tagNo < 128)
-            {
-                write(tagNo);
-            }
-            else
-            {
-                byte[] stack = new byte[5];
-                int pos = stack.length;
-
-                stack[--pos] = (byte)(tagNo & 0x7F);
-
-                do
-                {
-                    tagNo >>= 7;
-                    stack[--pos] = (byte)(tagNo & 0x7F | 0x80);
-                }
-                while (tagNo > 127);
-
-                write(stack, pos, stack.length - pos);
-            }
-        }
-    }
-
-    public void writeObject(ASN1Encodable obj) throws IOException
-    {
-        if (null == obj)
+        if (null == encodable)
         {
             throw new IOException("null object detected");
         }
 
-        writePrimitive(obj.toASN1Primitive(), true);
+        writePrimitive(encodable.toASN1Primitive(), true);
         flushInternal();
     }
 
-    public void writeObject(ASN1Primitive primitive) throws IOException
+    public final void writeObject(ASN1Primitive primitive) throws IOException
     {
         if (null == primitive)
         {
@@ -288,25 +71,7 @@
         flushInternal();
     }
 
-    void writePrimitive(ASN1Primitive primitive, boolean withTag) throws IOException
-    {
-        primitive.encode(this, withTag);
-    }
-
-    public void close()
-        throws IOException
-    {
-        os.close();
-    }
-
-    public void flush()
-        throws IOException
-    {
-        os.flush();
-    }
-
-    void flushInternal()
-        throws IOException
+    void flushInternal() throws IOException
     {
         // Placeholder to support future internal buffering
     }
@@ -316,8 +81,192 @@
         return new DEROutputStream(os);
     }
 
-    ASN1OutputStream getDLSubStream()
+    DLOutputStream getDLSubStream()
     {
         return new DLOutputStream(os);
     }
+
+    final void writeDL(int length) throws IOException
+    {
+        if (length < 128)
+        {
+            write(length);
+        }
+        else
+        {
+            byte[] stack = new byte[5];
+            int pos = stack.length;
+
+            do
+            {
+                stack[--pos] = (byte)length;
+                length >>>= 8;
+            }
+            while (length != 0);
+
+            int count = stack.length - pos;
+            stack[--pos] = (byte)(0x80 | count);
+
+            write(stack, pos, count + 1);
+        }
+    }
+
+    final void write(int b) throws IOException
+    {
+        os.write(b);
+    }
+
+    final void write(byte[] bytes, int off, int len) throws IOException
+    {
+        os.write(bytes, off, len);
+    }
+
+    void writeElements(ASN1Encodable[] elements)
+        throws IOException
+    {
+        for (int i = 0, count = elements.length; i < count; ++i)
+        {
+            elements[i].toASN1Primitive().encode(this, true);
+        }
+    }
+
+    final void writeEncodingDL(boolean withID, int identifier, byte contents) throws IOException
+    {
+        writeIdentifier(withID, identifier);
+        writeDL(1);
+        write(contents);
+    }
+
+    final void writeEncodingDL(boolean withID, int identifier, byte[] contents) throws IOException
+    {
+        writeIdentifier(withID, identifier);
+        writeDL(contents.length);
+        write(contents, 0, contents.length);
+    }
+
+    final void writeEncodingDL(boolean withID, int identifier, byte[] contents, int contentsOff, int contentsLen)
+        throws IOException
+    {
+        writeIdentifier(withID, identifier);
+        writeDL(contentsLen);
+        write(contents, contentsOff, contentsLen);
+    }
+
+    final void writeEncodingDL(boolean withID, int identifier, byte contentsPrefix, byte[] contents, int contentsOff,
+        int contentsLen) throws IOException
+    {
+        writeIdentifier(withID, identifier);
+        writeDL(1 + contentsLen);
+        write(contentsPrefix);
+        write(contents, contentsOff, contentsLen);
+    }
+
+    final void writeEncodingDL(boolean withID, int identifier, byte[] contents, int contentsOff, int contentsLen,
+        byte contentsSuffix) throws IOException
+    {
+        writeIdentifier(withID, identifier);
+        writeDL(contentsLen + 1);
+        write(contents, contentsOff, contentsLen);
+        write(contentsSuffix);
+    }
+
+    final void writeEncodingDL(boolean withID, int flags, int tag, byte[] contents) throws IOException
+    {
+        writeIdentifier(withID, flags, tag);
+        writeDL(contents.length);
+        write(contents, 0, contents.length);
+    }
+
+    final void writeEncodingIL(boolean withID, int identifier, ASN1Encodable[] elements) throws IOException
+    {
+        writeIdentifier(withID, identifier);
+        write(0x80);
+        writeElements(elements);
+        write(0x00);
+        write(0x00);
+    }
+
+    final void writeIdentifier(boolean withID, int identifier) throws IOException
+    {
+        if (withID)
+        {
+            write(identifier);
+        }
+    }
+
+    final void writeIdentifier(boolean withID, int flags, int tag) throws IOException
+    {
+        if (!withID)
+        {
+            // Don't write the identifier
+        }
+        else if (tag < 31)
+        {
+            write(flags | tag);
+        }
+        else
+        {
+            byte[] stack = new byte[6];
+            int pos = stack.length;
+
+            stack[--pos] = (byte)(tag & 0x7F);
+            while (tag > 127)
+            {
+                tag >>>= 7;
+                stack[--pos] = (byte)(tag & 0x7F | 0x80);
+            }
+
+            stack[--pos] = (byte)(flags | 0x1F);
+
+            write(stack, pos, stack.length - pos);
+        }
+    }
+
+    void writePrimitive(ASN1Primitive primitive, boolean withID) throws IOException
+    {
+        primitive.encode(this, withID);
+    }
+
+    void writePrimitives(ASN1Primitive[] primitives) throws IOException
+    {
+        for (int i = 0, count = primitives.length; i < count; ++i)
+        {
+            primitives[i].encode(this, true);
+        }
+    }
+
+    static int getLengthOfDL(int dl)
+    {
+        if (dl < 128)
+        {
+            return 1;
+        }
+
+        int length = 2;
+        while ((dl >>>= 8) != 0)
+        {
+            ++length;
+        }
+        return length;
+    }
+
+    static int getLengthOfEncodingDL(boolean withID, int contentsLength)
+    {
+        return (withID ? 1 : 0) + getLengthOfDL(contentsLength) + contentsLength;
+    }
+
+    static int getLengthOfIdentifier(int tag)
+    {
+        if (tag < 31)
+        {
+            return 1;
+        }
+
+        int length = 2;
+        while ((tag >>>= 7) != 0)
+        {
+            ++length;
+        }
+        return length;
+    }
 }
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1Primitive.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1Primitive.java
index f8dafff..e9e7d39 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1Primitive.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1Primitive.java
@@ -17,12 +17,16 @@
 
     public void encodeTo(OutputStream output) throws IOException
     {
-        ASN1OutputStream.create(output).writeObject(this);
+        ASN1OutputStream asn1Out = ASN1OutputStream.create(output); 
+        asn1Out.writePrimitive(this, true);
+        asn1Out.flushInternal();
     }
 
     public void encodeTo(OutputStream output, String encoding) throws IOException
     {
-        ASN1OutputStream.create(output, encoding).writeObject(this);
+        ASN1OutputStream asn1Out = ASN1OutputStream.create(output, encoding); 
+        asn1Out.writePrimitive(this, true);
+        asn1Out.flushInternal();
     }
 
     /**
@@ -105,14 +109,9 @@
      * Return true if this objected is a CONSTRUCTED one, false otherwise.
      * @return true if CONSTRUCTED bit set on object's tag, false otherwise.
      */
-    abstract boolean isConstructed();
+    abstract boolean encodeConstructed();
 
-    /**
-     * Return the length of the encoding this object will produce.
-     * @return the length of the object's encoding.
-     * @throws IOException if the encoding length cannot be calculated.
-     */
-    abstract int encodedLength() throws IOException;
+    abstract int encodedLength(boolean withTag) throws IOException;
 
     abstract void encode(ASN1OutputStream out, boolean withTag) throws IOException;
 
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1PrintableString.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1PrintableString.java
new file mode 100644
index 0000000..05d7e87
--- /dev/null
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1PrintableString.java
@@ -0,0 +1,231 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.asn1;
+
+import java.io.IOException;
+
+import com.android.internal.org.bouncycastle.util.Arrays;
+import com.android.internal.org.bouncycastle.util.Strings;
+
+/**
+ * ASN.1 PrintableString object.
+ * <p>
+ * X.680 section 37.4 defines PrintableString character codes as ASCII subset of following characters:
+ * </p>
+ * <ul>
+ * <li>Latin capital letters: 'A' .. 'Z'</li>
+ * <li>Latin small letters: 'a' .. 'z'</li>
+ * <li>Digits: '0'..'9'</li>
+ * <li>Space</li>
+ * <li>Apostrophe: '\''</li>
+ * <li>Left parenthesis: '('</li>
+ * <li>Right parenthesis: ')'</li>
+ * <li>Plus sign: '+'</li>
+ * <li>Comma: ','</li>
+ * <li>Hyphen-minus: '-'</li>
+ * <li>Full stop: '.'</li>
+ * <li>Solidus: '/'</li>
+ * <li>Colon: ':'</li>
+ * <li>Equals sign: '='</li>
+ * <li>Question mark: '?'</li>
+ * </ul>
+ * <p>
+ * Explicit character set escape sequences are not allowed.
+ * </p>
+ * @hide This class is not part of the Android public SDK API
+ */
+public abstract class ASN1PrintableString
+    extends ASN1Primitive
+    implements ASN1String
+{
+    static final ASN1UniversalType TYPE = new ASN1UniversalType(ASN1PrintableString.class, BERTags.PRINTABLE_STRING)
+    {
+        ASN1Primitive fromImplicitPrimitive(DEROctetString octetString)
+        {
+            return createPrimitive(octetString.getOctets());
+        }
+    };
+
+    /**
+     * Return a printable string from the passed in object.
+     *
+     * @param obj an ASN1PrintableString or an object that can be converted into one.
+     * @exception IllegalArgumentException if the object cannot be converted.
+     * @return an ASN1PrintableString instance, or null.
+     */
+    public static ASN1PrintableString getInstance(Object obj)
+    {
+        if (obj == null || obj instanceof ASN1PrintableString)
+        {
+            return (ASN1PrintableString)obj;
+        }
+        if (obj instanceof ASN1Encodable)
+        {
+            ASN1Primitive primitive = ((ASN1Encodable)obj).toASN1Primitive();
+            if (primitive instanceof ASN1PrintableString)
+            {
+                return (ASN1PrintableString)primitive;
+            }
+        }
+        if (obj instanceof byte[])
+        {
+            try
+            {
+                return (ASN1PrintableString)TYPE.fromByteArray((byte[])obj);
+            }
+            catch (Exception e)
+            {
+                throw new IllegalArgumentException("encoding error in getInstance: " + e.toString());
+            }
+        }
+
+        throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
+    }
+
+    /**
+     * Return a Printable String from a tagged object.
+     *
+     * @param taggedObject the tagged object holding the object we want
+     * @param explicit true if the object is meant to be explicitly
+     *              tagged false otherwise.
+     * @exception IllegalArgumentException if the tagged object cannot
+     *               be converted.
+     * @return an ASN1PrintableString instance, or null.
+     */
+    public static ASN1PrintableString getInstance(ASN1TaggedObject taggedObject, boolean explicit)
+    {
+        return (ASN1PrintableString)TYPE.getContextInstance(taggedObject, explicit);
+    }
+
+    final byte[] contents;
+
+    /**
+     * Constructor with optional validation.
+     *
+     * @param string the base string to wrap.
+     * @param validate whether or not to check the string.
+     * @throws IllegalArgumentException if validate is true and the string
+     * contains characters that should not be in a PrintableString.
+     */
+    ASN1PrintableString(String string, boolean validate)
+    {
+        if (validate && !isPrintableString(string))
+        {
+            throw new IllegalArgumentException("string contains illegal characters");
+        }
+
+        this.contents = Strings.toByteArray(string);
+    }
+
+    ASN1PrintableString(byte[] contents, boolean clone)
+    {
+        this.contents = clone ? Arrays.clone(contents) : contents;
+    }
+
+    public final String getString()
+    {
+        return Strings.fromByteArray(contents);
+    }
+
+    public final byte[] getOctets()
+    {
+        return Arrays.clone(contents);
+    }
+
+    final boolean encodeConstructed()
+    {
+        return false;
+    }
+
+    final int encodedLength(boolean withTag)
+    {
+        return ASN1OutputStream.getLengthOfEncodingDL(withTag, contents.length);
+    }
+
+    final void encode(ASN1OutputStream out, boolean withTag) throws IOException
+    {
+        out.writeEncodingDL(withTag, BERTags.PRINTABLE_STRING, contents);
+    }
+
+    final boolean asn1Equals(ASN1Primitive other)
+    {
+        if (!(other instanceof ASN1PrintableString))
+        {
+            return false;
+        }
+
+        ASN1PrintableString that = (ASN1PrintableString)other;
+
+        return Arrays.areEqual(this.contents, that.contents);
+    }
+
+    public final int hashCode()
+    {
+        return Arrays.hashCode(contents);
+    }
+
+    public String toString()
+    {
+        return getString();
+    }
+
+    /**
+     * return true if the passed in String can be represented without
+     * loss as a PrintableString, false otherwise.
+     *
+     * @return true if in printable set, false otherwise.
+     */
+    public static boolean isPrintableString(
+        String  str)
+    {
+        for (int i = str.length() - 1; i >= 0; i--)
+        {
+            char    ch = str.charAt(i);
+
+            if (ch > 0x007f)
+            {
+                return false;
+            }
+
+            if ('a' <= ch && ch <= 'z')
+            {
+                continue;
+            }
+
+            if ('A' <= ch && ch <= 'Z')
+            {
+                continue;
+            }
+
+            if ('0' <= ch && ch <= '9')
+            {
+                continue;
+            }
+
+            switch (ch)
+            {
+            case ' ':
+            case '\'':
+            case '(':
+            case ')':
+            case '+':
+            case '-':
+            case '.':
+            case ':':
+            case '=':
+            case '?':
+            case '/':
+            case ',':
+                continue;
+            }
+
+            return false;
+        }
+
+        return true;
+    }
+
+    static ASN1PrintableString createPrimitive(byte[] contents)
+    {
+        return new DERPrintableString(contents, false);
+    }
+}
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1RelativeOID.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1RelativeOID.java
new file mode 100644
index 0000000..e6b61b1
--- /dev/null
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1RelativeOID.java
@@ -0,0 +1,317 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.asn1;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.math.BigInteger;
+
+import com.android.internal.org.bouncycastle.util.Arrays;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+public class ASN1RelativeOID
+    extends ASN1Primitive
+{
+    static final ASN1UniversalType TYPE = new ASN1UniversalType(ASN1RelativeOID.class, BERTags.RELATIVE_OID)
+    {
+        ASN1Primitive fromImplicitPrimitive(DEROctetString octetString)
+        {
+            return createPrimitive(octetString.getOctets(), false);
+        }
+    };
+
+    public static ASN1RelativeOID fromContents(byte[] contents)
+    {
+        return createPrimitive(contents, true);
+    }
+
+    public static ASN1RelativeOID getInstance(Object obj)
+    {
+        if (obj == null || obj instanceof ASN1RelativeOID)
+        {
+            return (ASN1RelativeOID)obj;
+        }
+        if (obj instanceof ASN1Encodable)
+        {
+            ASN1Primitive primitive = ((ASN1Encodable)obj).toASN1Primitive();
+            if (primitive instanceof ASN1RelativeOID)
+            {
+                return (ASN1RelativeOID)primitive;
+            }
+        }
+        else if (obj instanceof byte[])
+        {
+            byte[] enc = (byte[])obj;
+            try
+            {
+                return (ASN1RelativeOID)TYPE.fromByteArray(enc);
+            }
+            catch (IOException e)
+            {
+                throw new IllegalArgumentException("failed to construct relative OID from byte[]: " + e.getMessage());
+            }
+        }
+
+        throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
+    }
+
+    public static ASN1RelativeOID getInstance(ASN1TaggedObject taggedObject, boolean explicit)
+    {
+        return (ASN1RelativeOID)TYPE.getContextInstance(taggedObject, explicit);
+    }
+
+    private static final long LONG_LIMIT = (Long.MAX_VALUE >> 7) - 0x7F;
+
+    private final String identifier;
+    private byte[] contents;
+
+    public ASN1RelativeOID(String identifier)
+    {
+        if (identifier == null)
+        {
+            throw new NullPointerException("'identifier' cannot be null");
+        }
+        if (!isValidIdentifier(identifier, 0))
+        {
+            throw new IllegalArgumentException("string " + identifier + " not a relative OID");
+        }
+
+        this.identifier = identifier;
+    }
+
+    ASN1RelativeOID(ASN1RelativeOID oid, String branchID)
+    {
+        if (!isValidIdentifier(branchID, 0))
+        {
+            throw new IllegalArgumentException("string " + branchID + " not a valid OID branch");
+        }
+
+        this.identifier = oid.getId() + "." + branchID;
+    }
+
+    private ASN1RelativeOID(byte[] contents, boolean clone)
+    {
+        StringBuffer objId = new StringBuffer();
+        long value = 0;
+        BigInteger bigValue = null;
+        boolean first = true;
+
+        for (int i = 0; i != contents.length; i++)
+        {
+            int b = contents[i] & 0xff;
+
+            if (value <= LONG_LIMIT)
+            {
+                value += b & 0x7F;
+                if ((b & 0x80) == 0)
+                {
+                    if (first)
+                    {
+                        first = false;
+                    }
+                    else
+                    {
+                        objId.append('.');
+                    }
+
+                    objId.append(value);
+                    value = 0;
+                }
+                else
+                {
+                    value <<= 7;
+                }
+            }
+            else
+            {
+                if (bigValue == null)
+                {
+                    bigValue = BigInteger.valueOf(value);
+                }
+                bigValue = bigValue.or(BigInteger.valueOf(b & 0x7F));
+                if ((b & 0x80) == 0)
+                {
+                    if (first)
+                    {
+                        first = false;
+                    }
+                    else
+                    {
+                        objId.append('.');
+                    }
+
+                    objId.append(bigValue);
+                    bigValue = null;
+                    value = 0;
+                }
+                else
+                {
+                    bigValue = bigValue.shiftLeft(7);
+                }
+            }
+        }
+
+        this.identifier = objId.toString();
+        this.contents = clone ? Arrays.clone(contents) : contents;
+    }
+
+    public ASN1RelativeOID branch(String branchID)
+    {
+        return new ASN1RelativeOID(this, branchID);
+    }
+
+    public String getId()
+    {
+        return identifier;
+    }
+
+    public int hashCode()
+    {
+        return identifier.hashCode();
+    }
+
+    public String toString()
+    {
+        return getId();
+    }
+
+    boolean asn1Equals(ASN1Primitive other)
+    {
+        if (this == other)
+        {
+            return true;
+        }
+        if (!(other instanceof ASN1RelativeOID))
+        {
+            return false;
+        }
+
+        ASN1RelativeOID that = (ASN1RelativeOID)other;
+
+        return this.identifier.equals(that.identifier);
+    }
+
+    int encodedLength(boolean withTag)
+    {
+        return ASN1OutputStream.getLengthOfEncodingDL(withTag, getContents().length);
+    }
+
+    void encode(ASN1OutputStream out, boolean withTag) throws IOException
+    {
+        out.writeEncodingDL(withTag, BERTags.RELATIVE_OID, getContents());
+    }
+
+    boolean encodeConstructed()
+    {
+        return false;
+    }
+
+    private void doOutput(ByteArrayOutputStream aOut)
+    {
+        OIDTokenizer tok = new OIDTokenizer(identifier);
+        while (tok.hasMoreTokens())
+        {
+            String token = tok.nextToken();
+            if (token.length() <= 18)
+            {
+                writeField(aOut, Long.parseLong(token));
+            }
+            else
+            {
+                writeField(aOut, new BigInteger(token));
+            }
+        }
+    }
+
+    private synchronized byte[] getContents()
+    {
+        if (contents == null)
+        {
+            ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+
+            doOutput(bOut);
+
+            contents = bOut.toByteArray();
+        }
+
+        return contents;
+    }
+
+    static ASN1RelativeOID createPrimitive(byte[] contents, boolean clone)
+    {
+        return new ASN1RelativeOID(contents, clone);
+    }
+
+    static boolean isValidIdentifier(String identifier, int from)
+    {
+        int digitCount = 0;
+
+        int pos = identifier.length();
+        while (--pos >= from)
+        {
+            char ch = identifier.charAt(pos);
+
+            if (ch == '.')
+            {
+                if (0 == digitCount
+                    || (digitCount > 1 && identifier.charAt(pos + 1) == '0'))
+                {
+                    return false;
+                }
+
+                digitCount = 0;
+            }
+            else if ('0' <= ch && ch <= '9')
+            {
+                ++digitCount;
+            }
+            else
+            {
+                return false;
+            }
+        }
+
+        if (0 == digitCount
+            || (digitCount > 1 && identifier.charAt(pos + 1) == '0'))
+        {
+            return false;
+        }
+
+        return true;
+    }
+
+    static void writeField(ByteArrayOutputStream out, long fieldValue)
+    {
+        byte[] result = new byte[9];
+        int pos = 8;
+        result[pos] = (byte)((int)fieldValue & 0x7F);
+        while (fieldValue >= (1L << 7))
+        {
+            fieldValue >>= 7;
+            result[--pos] = (byte)((int)fieldValue | 0x80);
+        }
+        out.write(result, pos, 9 - pos);
+    }
+
+    static void writeField(ByteArrayOutputStream out, BigInteger fieldValue)
+    {
+        int byteCount = (fieldValue.bitLength() + 6) / 7;
+        if (byteCount == 0)
+        {
+            out.write(0);
+        }
+        else
+        {
+            BigInteger tmpValue = fieldValue;
+            byte[] tmp = new byte[byteCount];
+            for (int i = byteCount - 1; i >= 0; i--)
+            {
+                tmp[i] = (byte)(tmpValue.intValue() | 0x80);
+                tmpValue = tmpValue.shiftRight(7);
+            }
+            tmp[byteCount - 1] &= 0x7F;
+            out.write(tmp, 0, tmp.length);
+        }
+    }
+}
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1Sequence.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1Sequence.java
index afbb74f..fbd27d5 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1Sequence.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1Sequence.java
@@ -62,8 +62,13 @@
     extends ASN1Primitive
     implements com.android.internal.org.bouncycastle.util.Iterable<ASN1Encodable>
 {
-    // NOTE: Only non-final to support LazyEncodedSequence
-    ASN1Encodable[] elements;
+    static final ASN1UniversalType TYPE = new ASN1UniversalType(ASN1Sequence.class, BERTags.SEQUENCE)
+    {
+        ASN1Primitive fromImplicitConstructed(ASN1Sequence sequence)
+        {
+            return sequence;
+        }
+    };
 
     /**
      * Return an ASN1Sequence from the given object.
@@ -72,37 +77,32 @@
      * @exception IllegalArgumentException if the object cannot be converted.
      * @return an ASN1Sequence instance, or null.
      */
-    public static ASN1Sequence getInstance(
-        Object  obj)
+    public static ASN1Sequence getInstance(Object obj)
     {
         if (obj == null || obj instanceof ASN1Sequence)
         {
             return (ASN1Sequence)obj;
         }
-        else if (obj instanceof ASN1SequenceParser)
+//      else if (obj instanceof ASN1SequenceParser)
+        else if (obj instanceof ASN1Encodable)
         {
-            return ASN1Sequence.getInstance(((ASN1SequenceParser)obj).toASN1Primitive());
+            ASN1Primitive primitive = ((ASN1Encodable)obj).toASN1Primitive();
+            if (primitive instanceof ASN1Sequence)
+            {
+                return (ASN1Sequence)primitive;
+            }
         }
         else if (obj instanceof byte[])
         {
             try
             {
-                return ASN1Sequence.getInstance(fromByteArray((byte[])obj));
+                return (ASN1Sequence)TYPE.fromByteArray((byte[])obj);
             }
             catch (IOException e)
             {
                 throw new IllegalArgumentException("failed to construct sequence from byte[]: " + e.getMessage());
             }
         }
-        else if (obj instanceof ASN1Encodable)
-        {
-            ASN1Primitive primitive = ((ASN1Encodable)obj).toASN1Primitive();
-
-            if (primitive instanceof ASN1Sequence)
-            {
-                return (ASN1Sequence)primitive;
-            }
-        }
 
         throw new IllegalArgumentException("unknown object in getInstance: " + obj.getClass().getName());
     }
@@ -124,51 +124,14 @@
      *          be converted.
      * @return an ASN1Sequence instance.
      */
-    public static ASN1Sequence getInstance(
-        ASN1TaggedObject    taggedObject,
-        boolean             explicit)
+    public static ASN1Sequence getInstance(ASN1TaggedObject taggedObject, boolean explicit)
     {
-        if (explicit)
-        {
-            if (!taggedObject.isExplicit())
-            {
-                throw new IllegalArgumentException("object implicit - explicit expected.");
-            }
-
-            return getInstance(taggedObject.getObject());
-        }
-
-        ASN1Primitive o = taggedObject.getObject();
-
-        /*
-         * constructed object which appears to be explicitly tagged when it should be implicit means
-         * we have to add the surrounding sequence.
-         */
-        if (taggedObject.isExplicit())
-        {
-            if (taggedObject instanceof BERTaggedObject)
-            {
-                return new BERSequence(o);
-            }
-
-            return new DLSequence(o);
-        }
-
-        if (o instanceof ASN1Sequence)
-        {
-            ASN1Sequence s = (ASN1Sequence)o;
-
-            if (taggedObject instanceof BERTaggedObject)
-            {
-                return s;
-            }
-
-            return (ASN1Sequence)s.toDLObject();
-        }
-
-        throw new IllegalArgumentException("unknown object in getInstance: " + taggedObject.getClass().getName());
+        return (ASN1Sequence)TYPE.getContextInstance(taggedObject, explicit);
     }
 
+    // NOTE: Only non-final to support LazyEncodedSequence
+    ASN1Encodable[] elements;
+
     /**
      * Create an empty SEQUENCE
      */
@@ -342,6 +305,7 @@
 
         ASN1Sequence that = (ASN1Sequence)other;
 
+        // NOTE: Call size() here (on both) to 'force' a LazyEncodedSequence
         int count = this.size();
         if (that.size() != count)
         {
@@ -380,13 +344,19 @@
         return new DLSequence(elements, false);
     }
 
-    boolean isConstructed()
+    abstract ASN1BitString toASN1BitString();
+
+    abstract ASN1External toASN1External();
+
+    abstract ASN1OctetString toASN1OctetString();
+
+    abstract ASN1Set toASN1Set();
+
+    boolean encodeConstructed()
     {
         return true;
     }
 
-    abstract void encode(ASN1OutputStream out, boolean withTag) throws IOException;
-
     public String toString() 
     {
         // NOTE: Call size() here to 'force' a LazyEncodedSequence
@@ -415,4 +385,28 @@
     {
         return new Arrays.Iterator<ASN1Encodable>(elements);
     }
+
+    ASN1BitString[] getConstructedBitStrings()
+    {
+        // NOTE: Call size() here to 'force' a LazyEncodedSequence
+        int count = size();
+        ASN1BitString[] bitStrings = new ASN1BitString[count];
+        for (int i = 0; i < count; ++i)
+        {
+            bitStrings[i] = ASN1BitString.getInstance(elements[i]);
+        }
+        return bitStrings;
+    }
+
+    ASN1OctetString[] getConstructedOctetStrings()
+    {
+        // NOTE: Call size() here to 'force' a LazyEncodedSequence
+        int count = size();
+        ASN1OctetString[] octetStrings = new ASN1OctetString[count];
+        for (int i = 0; i < count; ++i)
+        {
+            octetStrings[i] = ASN1OctetString.getInstance(elements[i]);
+        }
+        return octetStrings;
+    }
 }
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1Set.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1Set.java
index 110d200..e46a47e 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1Set.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1Set.java
@@ -100,8 +100,13 @@
     extends ASN1Primitive
     implements com.android.internal.org.bouncycastle.util.Iterable<ASN1Encodable>
 {
-    protected final ASN1Encodable[] elements;
-    protected final boolean isSorted;
+    static final ASN1UniversalType TYPE = new ASN1UniversalType(ASN1Set.class, BERTags.SET)
+    {
+        ASN1Primitive fromImplicitConstructed(ASN1Sequence sequence)
+        {
+            return sequence.toASN1Set();
+        }
+    };
 
     /**
      * return an ASN1Set from the given object.
@@ -110,37 +115,32 @@
      * @exception IllegalArgumentException if the object cannot be converted.
      * @return an ASN1Set instance, or null.
      */
-    public static ASN1Set getInstance(
-        Object  obj)
+    public static ASN1Set getInstance(Object obj)
     {
         if (obj == null || obj instanceof ASN1Set)
         {
             return (ASN1Set)obj;
         }
-        else if (obj instanceof ASN1SetParser)
+//      else if (obj instanceof ASN1SetParser)
+        else if (obj instanceof ASN1Encodable)
         {
-            return ASN1Set.getInstance(((ASN1SetParser)obj).toASN1Primitive());
+            ASN1Primitive primitive = ((ASN1Encodable)obj).toASN1Primitive();
+            if (primitive instanceof ASN1Set)
+            {
+                return (ASN1Set)primitive;
+            }
         }
         else if (obj instanceof byte[])
         {
             try
             {
-                return ASN1Set.getInstance(ASN1Primitive.fromByteArray((byte[])obj));
+                return (ASN1Set)TYPE.fromByteArray((byte[])obj);
             }
             catch (IOException e)
             {
                 throw new IllegalArgumentException("failed to construct set from byte[]: " + e.getMessage());
             }
         }
-        else if (obj instanceof ASN1Encodable)
-        {
-            ASN1Primitive primitive = ((ASN1Encodable)obj).toASN1Primitive();
-
-            if (primitive instanceof ASN1Set)
-            {
-                return (ASN1Set)primitive;
-            }
-        }
 
         throw new IllegalArgumentException("unknown object in getInstance: " + obj.getClass().getName());
     }
@@ -162,73 +162,19 @@
      *          be converted.
      * @return an ASN1Set instance.
      */
-    public static ASN1Set getInstance(
-        ASN1TaggedObject    taggedObject,
-        boolean             explicit)
+    public static ASN1Set getInstance(ASN1TaggedObject taggedObject, boolean explicit)
     {
-        if (explicit)
-        {
-            if (!taggedObject.isExplicit())
-            {
-                throw new IllegalArgumentException("object implicit - explicit expected.");
-            }
-
-            return getInstance(taggedObject.getObject());
-        }
-
-        ASN1Primitive o = taggedObject.getObject();
-
-        /*
-         * constructed object which appears to be explicitly tagged and it's really implicit means
-         * we have to add the surrounding set.
-         */
-        if (taggedObject.isExplicit())
-        {
-            if (taggedObject instanceof BERTaggedObject)
-            {
-                return new BERSet(o);
-            }
-
-            return new DLSet(o);
-        }
-
-        if (o instanceof ASN1Set)
-        {
-            ASN1Set s = (ASN1Set)o;
-
-            if (taggedObject instanceof BERTaggedObject)
-            {
-                return s;
-            }
-
-            return (ASN1Set)s.toDLObject();
-        }
-
-        /*
-         * in this case the parser returns a sequence, convert it into a set.
-         */
-        if (o instanceof ASN1Sequence)
-        {
-            ASN1Sequence s = (ASN1Sequence)o;
-
-            // NOTE: Will force() a LazyEncodedSequence
-            ASN1Encodable[] elements = s.toArrayInternal();
-
-            if (taggedObject instanceof BERTaggedObject)
-            {
-                return new BERSet(false, elements);
-            }
-
-            return new DLSet(false, elements);
-        }
-
-        throw new IllegalArgumentException("unknown object in getInstance: " + taggedObject.getClass().getName());
+        return (ASN1Set)TYPE.getContextInstance(taggedObject, explicit);
     }
 
+    protected final ASN1Encodable[] elements;
+
+    protected ASN1Encodable[] sortedElements;
+
     protected ASN1Set()
     {
         this.elements = ASN1EncodableVector.EMPTY_ELEMENTS;
-        this.isSorted = true;
+        this.sortedElements = elements;
     }
 
     /**
@@ -243,7 +189,7 @@
         }
 
         this.elements = new ASN1Encodable[]{ element };
-        this.isSorted = true;
+        this.sortedElements = elements;
     }
 
     /**
@@ -270,7 +216,7 @@
         }
 
         this.elements = tmp;
-        this.isSorted = doSort || tmp.length < 2;
+        this.sortedElements = (doSort || tmp.length < 2) ? elements : null;
     }
 
     /**
@@ -292,13 +238,19 @@
         }
 
         this.elements = tmp;
-        this.isSorted = doSort || tmp.length < 2;
+        this.sortedElements = (doSort || tmp.length < 2) ? elements : null;
     }
 
     ASN1Set(boolean isSorted, ASN1Encodable[] elements)
     {
         this.elements = elements;
-        this.isSorted = isSorted || elements.length < 2;
+        this.sortedElements = (isSorted || elements.length < 2) ? elements : null;
+    }
+
+    ASN1Set(ASN1Encodable[] elements, ASN1Encodable[] sortedElements)
+    {
+        this.elements = elements;
+        this.sortedElements = sortedElements;
     }
 
     public Enumeration getObjects()
@@ -410,18 +362,13 @@
      */
     ASN1Primitive toDERObject()
     {
-        ASN1Encodable[] tmp;
-        if (isSorted)
+        if (sortedElements == null)
         {
-            tmp = elements;
-        }
-        else
-        {
-            tmp = (ASN1Encodable[])elements.clone();
-            sort(tmp);
+            sortedElements = (ASN1Encodable[])elements.clone();
+            sort(sortedElements);
         }
 
-        return new DERSet(true, tmp);
+        return new DERSet(true, sortedElements);
     }
 
     /**
@@ -430,7 +377,7 @@
      */
     ASN1Primitive toDLObject()
     {
-        return new DLSet(isSorted, elements);
+        return new DLSet(elements, sortedElements);
     }
 
     boolean asn1Equals(ASN1Primitive other)
@@ -465,13 +412,11 @@
         return true;
     }
 
-    boolean isConstructed()
+    boolean encodeConstructed()
     {
         return true;
     }
 
-    abstract void encode(ASN1OutputStream out, boolean withTag) throws IOException;
-
     public String toString() 
     {
         int count = size();
@@ -531,8 +476,8 @@
          * primitive form accordingly. Failing to ignore the CONSTRUCTED bit could therefore lead to
          * ordering inversions.
          */
-        int a0 = a[0] & ~BERTags.CONSTRUCTED;
-        int b0 = b[0] & ~BERTags.CONSTRUCTED;
+        int a0 = a[0] & (~BERTags.CONSTRUCTED & 0xff);
+        int b0 = b[0] & (~BERTags.CONSTRUCTED & 0xff);
         if (a0 != b0)
         {
             return a0 < b0;
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1StreamParser.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1StreamParser.java
index 888a2f6..594c821 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1StreamParser.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1StreamParser.java
@@ -12,214 +12,257 @@
 public class ASN1StreamParser
 {
     private final InputStream _in;
-    private final int         _limit;
+    private final int _limit;
     private final byte[][] tmpBuffers;
 
-    public ASN1StreamParser(
-        InputStream in)
+    public ASN1StreamParser(InputStream in)
     {
         this(in, StreamUtil.findLimit(in));
     }
 
-    public ASN1StreamParser(
-        InputStream in,
-        int         limit)
-    {
-        this._in = in;
-        this._limit = limit;
-
-        this.tmpBuffers = new byte[11][];
-    }
-
-    public ASN1StreamParser(
-        byte[] encoding)
+    public ASN1StreamParser(byte[] encoding)
     {
         this(new ByteArrayInputStream(encoding), encoding.length);
     }
 
-    ASN1Encodable readIndef(int tagValue) throws IOException
+    public ASN1StreamParser(InputStream in, int limit)
     {
-        // Note: INDEF => CONSTRUCTED
-
-        // TODO There are other tags that may be constructed (e.g. BIT_STRING)
-        switch (tagValue)
-        {
-            case BERTags.EXTERNAL:
-                return new DERExternalParser(this);
-            case BERTags.OCTET_STRING:
-                return new BEROctetStringParser(this);
-            case BERTags.SEQUENCE:
-                return new BERSequenceParser(this);
-            case BERTags.SET:
-                return new BERSetParser(this);
-            default:
-                throw new ASN1Exception("unknown BER object encountered: 0x" + Integer.toHexString(tagValue));
-        }
+        this(in, limit, new byte[11][]);
     }
 
-    ASN1Encodable readImplicit(boolean constructed, int tag) throws IOException
+    ASN1StreamParser(InputStream in, int limit, byte[][] tmpBuffers)
     {
-        if (_in instanceof IndefiniteLengthInputStream)
-        {
-            if (!constructed)
-            {
-                throw new IOException("indefinite-length primitive encoding encountered");
-            }
-            
-            return readIndef(tag);
-        }
-
-        if (constructed)
-        {
-            switch (tag)
-            {
-                case BERTags.SET:
-                    return new DLSetParser(this);
-                case BERTags.SEQUENCE:
-                    return new DLSequenceParser(this);
-                case BERTags.OCTET_STRING:
-                    return new BEROctetStringParser(this);
-            }
-        }
-        else
-        {
-            switch (tag)
-            {
-                case BERTags.SET:
-                    throw new ASN1Exception("sequences must use constructed encoding (see X.690 8.9.1/8.10.1)");
-                case BERTags.SEQUENCE:
-                    throw new ASN1Exception("sets must use constructed encoding (see X.690 8.11.1/8.12.1)");
-                case BERTags.OCTET_STRING:
-                    return new DEROctetStringParser((DefiniteLengthInputStream)_in);
-            }
-        }
-
-        throw new ASN1Exception("implicit tagging not implemented");
+        this._in = in;
+        this._limit = limit;
+        this.tmpBuffers = tmpBuffers;
     }
 
-    ASN1Primitive readTaggedObject(boolean constructed, int tag) throws IOException
+    public ASN1Encodable readObject() throws IOException
     {
-        if (!constructed)
-        {
-            // Note: !CONSTRUCTED => IMPLICIT
-            DefiniteLengthInputStream defIn = (DefiniteLengthInputStream)_in;
-            return new DLTaggedObject(false, tag, new DEROctetString(defIn.toByteArray()));
-        }
-
-        ASN1EncodableVector v = readVector();
-
-        if (_in instanceof IndefiniteLengthInputStream)
-        {
-            return v.size() == 1
-                ?   new BERTaggedObject(true, tag, v.get(0))
-                :   new BERTaggedObject(false, tag, BERFactory.createSequence(v));
-        }
-
-        return v.size() == 1
-            ?   new DLTaggedObject(true, tag, v.get(0))
-            :   new DLTaggedObject(false, tag, DLFactory.createSequence(v));
-    }
-
-    public ASN1Encodable readObject()
-        throws IOException
-    {
-        int tag = _in.read();
-        if (tag == -1)
+        int tagHdr = _in.read();
+        if (tagHdr < 0)
         {
             return null;
         }
 
+        return implParseObject(tagHdr);
+    }
+
+    ASN1Encodable implParseObject(int tagHdr) throws IOException
+    {
         //
-        // turn of looking for "00" while we resolve the tag
+        // turn off looking for "00" while we resolve the tag
         //
         set00Check(false);
 
         //
         // calculate tag number
         //
-        int tagNo = ASN1InputStream.readTagNumber(_in, tag);
-
-        boolean isConstructed = (tag & BERTags.CONSTRUCTED) != 0;
+        int tagNo = ASN1InputStream.readTagNumber(_in, tagHdr);
 
         //
         // calculate length
         //
         int length = ASN1InputStream.readLength(_in, _limit,
-            tagNo == BERTags.OCTET_STRING || tagNo == BERTags.SEQUENCE || tagNo == BERTags.SET || tagNo == BERTags.EXTERNAL);
+            tagNo == BERTags.BIT_STRING || tagNo == BERTags.OCTET_STRING || tagNo == BERTags.SEQUENCE
+                || tagNo == BERTags.SET || tagNo == BERTags.EXTERNAL);
 
         if (length < 0) // indefinite-length method
         {
-            if (!isConstructed)
+            if (0 == (tagHdr & BERTags.CONSTRUCTED))
             {
                 throw new IOException("indefinite-length primitive encoding encountered");
             }
 
             IndefiniteLengthInputStream indIn = new IndefiniteLengthInputStream(_in, _limit);
-            ASN1StreamParser sp = new ASN1StreamParser(indIn, _limit);
+            ASN1StreamParser sp = new ASN1StreamParser(indIn, _limit, tmpBuffers);
 
-            if ((tag & BERTags.APPLICATION) != 0)
+            int tagClass = tagHdr & BERTags.PRIVATE;
+            if (0 != tagClass)
             {
-                return new BERApplicationSpecificParser(tagNo, sp);
+                return new BERTaggedObjectParser(tagClass, tagNo, sp);
             }
 
-            if ((tag & BERTags.TAGGED) != 0)
-            {
-                return new BERTaggedObjectParser(true, tagNo, sp);
-            }
-
-            return sp.readIndef(tagNo);
+            return sp.parseImplicitConstructedIL(tagNo);
         }
         else
         {
             DefiniteLengthInputStream defIn = new DefiniteLengthInputStream(_in, length, _limit);
 
-            if ((tag & BERTags.APPLICATION) != 0)
+            if (0 == (tagHdr & BERTags.FLAGS))
             {
-                return new DLApplicationSpecific(isConstructed, tagNo, defIn.toByteArray());
+                return parseImplicitPrimitive(tagNo, defIn);
             }
 
-            if ((tag & BERTags.TAGGED) != 0)
+            ASN1StreamParser sp = new ASN1StreamParser(defIn, defIn.getLimit(), tmpBuffers);
+
+            int tagClass = tagHdr & BERTags.PRIVATE;
+            if (0 != tagClass)
             {
-                return new BERTaggedObjectParser(isConstructed, tagNo, new ASN1StreamParser(defIn));
+                boolean isConstructed = (tagHdr & BERTags.CONSTRUCTED) != 0;
+
+                return new DLTaggedObjectParser(tagClass, tagNo, isConstructed, sp);
             }
 
-            if (isConstructed)
-            {
-                // TODO There are other tags that may be constructed (e.g. BIT_STRING)
-                switch (tagNo)
-                {
-                    case BERTags.OCTET_STRING:
-                        //
-                        // yes, people actually do this...
-                        //
-                        return new BEROctetStringParser(new ASN1StreamParser(defIn));
-                    case BERTags.SEQUENCE:
-                        return new DLSequenceParser(new ASN1StreamParser(defIn));
-                    case BERTags.SET:
-                        return new DLSetParser(new ASN1StreamParser(defIn));
-                    case BERTags.EXTERNAL:
-                        return new DERExternalParser(new ASN1StreamParser(defIn));
-                    default:
-                        throw new IOException("unknown tag " + tagNo + " encountered");
-                }
-            }
+            return sp.parseImplicitConstructedDL(tagNo);
+        }
+    }
 
-            // Some primitive encodings can be handled by parsers too...
-            switch (tagNo)
-            {
-                case BERTags.OCTET_STRING:
-                    return new DEROctetStringParser(defIn);
-            }
+    ASN1Primitive loadTaggedDL(int tagClass, int tagNo, boolean constructed) throws IOException
+    {
+        if (!constructed)
+        {
+            byte[] contentsOctets = ((DefiniteLengthInputStream) _in).toByteArray();
+            return ASN1TaggedObject.createPrimitive(tagClass, tagNo, contentsOctets);
+        }
 
-            try
+        ASN1EncodableVector contentsElements = readVector();
+        return ASN1TaggedObject.createConstructedDL(tagClass, tagNo, contentsElements);
+    }
+
+    ASN1Primitive loadTaggedIL(int tagClass, int tagNo) throws IOException
+    {
+        ASN1EncodableVector contentsElements = readVector();
+        return ASN1TaggedObject.createConstructedIL(tagClass, tagNo, contentsElements);
+    }
+
+    ASN1Encodable parseImplicitConstructedDL(int univTagNo) throws IOException
+    {
+        switch (univTagNo)
+        {
+        case BERTags.BIT_STRING:
+            // TODO[asn1] DLConstructedBitStringParser
+            return new BERBitStringParser(this);
+        case BERTags.EXTERNAL:
+            return new DERExternalParser(this);
+        case BERTags.OCTET_STRING:
+            // TODO[asn1] DLConstructedOctetStringParser
+            return new BEROctetStringParser(this);
+        case BERTags.SET:
+            return new DLSetParser(this);
+        case BERTags.SEQUENCE:
+            return new DLSequenceParser(this);
+        default:
+            // -DM toHexString
+            throw new ASN1Exception("unknown DL object encountered: 0x" + Integer.toHexString(univTagNo));
+        }
+    }
+
+    ASN1Encodable parseImplicitConstructedIL(int univTagNo) throws IOException
+    {
+        switch (univTagNo)
+        {
+        case BERTags.BIT_STRING:
+            return new BERBitStringParser(this);
+        case BERTags.OCTET_STRING:
+            return new BEROctetStringParser(this);
+        case BERTags.EXTERNAL:
+            // TODO[asn1] BERExternalParser
+            return new DERExternalParser(this);
+        case BERTags.SEQUENCE:
+            return new BERSequenceParser(this);
+        case BERTags.SET:
+            return new BERSetParser(this);
+        default:
+            throw new ASN1Exception("unknown BER object encountered: 0x" + Integer.toHexString(univTagNo));
+        }
+    }
+
+    ASN1Encodable parseImplicitPrimitive(int univTagNo) throws IOException
+    {
+        return parseImplicitPrimitive(univTagNo, (DefiniteLengthInputStream)_in);
+    }
+
+    ASN1Encodable parseImplicitPrimitive(int univTagNo, DefiniteLengthInputStream defIn) throws IOException
+    {
+        // Some primitive encodings can be handled by parsers too...
+        switch (univTagNo)
+        {
+        case BERTags.BIT_STRING:
+            return new DLBitStringParser(defIn);
+        case BERTags.EXTERNAL:
+            throw new ASN1Exception("externals must use constructed encoding (see X.690 8.18)");
+        case BERTags.OCTET_STRING:
+            return new DEROctetStringParser(defIn);
+        case BERTags.SET:
+            throw new ASN1Exception("sequences must use constructed encoding (see X.690 8.9.1/8.10.1)");
+        case BERTags.SEQUENCE:
+            throw new ASN1Exception("sets must use constructed encoding (see X.690 8.11.1/8.12.1)");
+        }
+
+        try
+        {
+            return ASN1InputStream.createPrimitiveDERObject(univTagNo, defIn, tmpBuffers);
+        }
+        catch (IllegalArgumentException e)
+        {
+            throw new ASN1Exception("corrupted stream detected", e);
+        }
+    }
+
+    ASN1Encodable parseObject(int univTagNo) throws IOException
+    {
+        if (univTagNo < 0 || univTagNo > 30)
+        {
+            throw new IllegalArgumentException("invalid universal tag number: " + univTagNo);
+        }
+
+        int tagHdr = _in.read();
+        if (tagHdr < 0)
+        {
+            return null;
+        }
+
+        if ((tagHdr & ~BERTags.CONSTRUCTED) != univTagNo)
+        {
+            throw new IOException("unexpected identifier encountered: " + tagHdr);
+        }
+
+        return implParseObject(tagHdr);
+    }
+
+    ASN1TaggedObjectParser parseTaggedObject() throws IOException
+    {
+        int tagHdr = _in.read();
+        if (tagHdr < 0)
+        {
+            return null;
+        }
+
+        int tagClass = tagHdr & BERTags.PRIVATE;
+        if (0 == tagClass)
+        {
+            throw new ASN1Exception("no tagged object found");
+        }
+
+        return (ASN1TaggedObjectParser)implParseObject(tagHdr);
+    }
+
+    // TODO[asn1] Prefer 'loadVector'
+    ASN1EncodableVector readVector() throws IOException
+    {
+        int tagHdr = _in.read();
+        if (tagHdr < 0)
+        {
+            return new ASN1EncodableVector(0);
+        }
+
+        ASN1EncodableVector v = new ASN1EncodableVector();
+        do
+        {
+            ASN1Encodable obj = implParseObject(tagHdr);
+
+            if (obj instanceof InMemoryRepresentable)
             {
-                return ASN1InputStream.createPrimitiveDERObject(tagNo, defIn, tmpBuffers);
+                v.add(((InMemoryRepresentable) obj).getLoadedObject());
             }
-            catch (IllegalArgumentException e)
+            else
             {
-                throw new ASN1Exception("corrupted stream detected", e);
+                v.add(obj.toASN1Primitive());
             }
         }
+        while ((tagHdr = _in.read()) >= 0);
+        return v;
     }
 
     private void set00Check(boolean enabled)
@@ -229,28 +272,4 @@
             ((IndefiniteLengthInputStream)_in).setEofOn00(enabled);
         }
     }
-
-    ASN1EncodableVector readVector() throws IOException
-    {
-        ASN1Encodable obj = readObject();
-        if (null == obj)
-        {
-            return new ASN1EncodableVector(0);
-        }
-
-        ASN1EncodableVector v = new ASN1EncodableVector();
-        do
-        {
-            if (obj instanceof InMemoryRepresentable)
-            {
-                v.add(((InMemoryRepresentable)obj).getLoadedObject());
-            }
-            else
-            {
-                v.add(obj.toASN1Primitive());
-            }
-        }
-        while ((obj = readObject()) != null);
-        return v;
-    }
 }
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1T61String.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1T61String.java
new file mode 100644
index 0000000..fb219bf
--- /dev/null
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1T61String.java
@@ -0,0 +1,147 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.asn1;
+
+import java.io.IOException;
+
+import com.android.internal.org.bouncycastle.util.Arrays;
+import com.android.internal.org.bouncycastle.util.Strings;
+
+/**
+ * ASN.1 T61String (also the teletex string), try not to use this if you don't need to. The standard support the encoding for
+ * this has been withdrawn.
+ * @hide This class is not part of the Android public SDK API
+ */
+public abstract class ASN1T61String
+    extends ASN1Primitive
+    implements ASN1String
+{
+    static final ASN1UniversalType TYPE = new ASN1UniversalType(ASN1T61String.class, BERTags.T61_STRING)
+    {
+        ASN1Primitive fromImplicitPrimitive(DEROctetString octetString)
+        {
+            return createPrimitive(octetString.getOctets());
+        }
+    };
+
+    /**
+     * Return a T61 string from the passed in object.
+     *
+     * @param obj an ASN1T61String or an object that can be converted into one.
+     * @exception IllegalArgumentException if the object cannot be converted.
+     * @return an ASN1T61String instance, or null
+     */
+    public static ASN1T61String getInstance(Object obj)
+    {
+        if (obj == null || obj instanceof ASN1T61String)
+        {
+            return (ASN1T61String)obj;
+        }
+        if (obj instanceof ASN1Encodable)
+        {
+            ASN1Primitive primitive = ((ASN1Encodable)obj).toASN1Primitive();
+            if (primitive instanceof ASN1T61String)
+            {
+                return (ASN1T61String)primitive;
+            }
+        }
+        if (obj instanceof byte[])
+        {
+            try
+            {
+                return (ASN1T61String)TYPE.fromByteArray((byte[])obj);
+            }
+            catch (Exception e)
+            {
+                throw new IllegalArgumentException("encoding error in getInstance: " + e.toString());
+            }
+        }
+
+        throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
+    }
+
+    /**
+     * Return an T61 String from a tagged object.
+     *
+     * @param taggedObject      the tagged object holding the object we want
+     * @param explicit true if the object is meant to be explicitly tagged false
+     *                 otherwise.
+     * @exception IllegalArgumentException if the tagged object cannot be converted.
+     * @return an ASN1T61String instance, or null
+     */
+    public static ASN1T61String getInstance(ASN1TaggedObject taggedObject, boolean explicit)
+    {
+        return (ASN1T61String)TYPE.getContextInstance(taggedObject, explicit);
+    }
+
+    final byte[] contents;
+
+    ASN1T61String(String string)
+    {
+        this.contents = Strings.toByteArray(string);
+    }
+
+    ASN1T61String(byte[] contents, boolean clone)
+    {
+        this.contents = clone ? Arrays.clone(contents) : contents;
+    }
+
+    /**
+     * Decode the encoded string and return it, 8 bit encoding assumed.
+     * @return the decoded String
+     */
+    public final String getString()
+    {
+        return Strings.fromByteArray(contents);
+    }
+
+    public String toString()
+    {
+        return getString();
+    }
+
+    final boolean encodeConstructed()
+    {
+        return false;
+    }
+
+    final int encodedLength(boolean withTag)
+    {
+        return ASN1OutputStream.getLengthOfEncodingDL(withTag, contents.length);
+    }
+
+    final void encode(ASN1OutputStream out, boolean withTag) throws IOException
+    {
+        out.writeEncodingDL(withTag, BERTags.T61_STRING, contents);
+    }
+
+    /**
+     * Return the encoded string as a byte array.
+     * @return the actual bytes making up the encoded body of the T61 string.
+     */
+    public final byte[] getOctets()
+    {
+        return Arrays.clone(contents);
+    }
+
+    final boolean asn1Equals(ASN1Primitive other)
+    {
+        if (!(other instanceof ASN1T61String))
+        {
+            return false;
+        }
+
+        ASN1T61String that = (ASN1T61String)other;
+
+        return Arrays.areEqual(this.contents, that.contents);
+    }
+
+    public final int hashCode()
+    {
+        return Arrays.hashCode(contents);
+    }
+
+    static ASN1T61String createPrimitive(byte[] contents)
+    {
+        return new DERT61String(contents, false);
+    }
+}
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1Tag.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1Tag.java
new file mode 100644
index 0000000..fb3c2a4
--- /dev/null
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1Tag.java
@@ -0,0 +1,29 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.asn1;
+
+final class ASN1Tag
+{
+    static ASN1Tag create(int tagClass, int tagNumber)
+    {
+        return new ASN1Tag(tagClass, tagNumber);
+    }
+
+    private final int tagClass;
+    private final int tagNumber;
+
+    private ASN1Tag(int tagClass, int tagNumber)
+    {
+        this.tagClass = tagClass;
+        this.tagNumber = tagNumber;
+    }
+
+    int getTagClass()
+    {
+        return tagClass;
+    }
+
+    int getTagNumber()
+    {
+        return tagNumber;
+    }
+}
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1TaggedObject.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1TaggedObject.java
index c0f9627..b5ff2a8 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1TaggedObject.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1TaggedObject.java
@@ -3,6 +3,8 @@
 
 import java.io.IOException;
 
+import com.android.internal.org.bouncycastle.util.Arrays;
+
 /**
  * ASN.1 TaggedObject - in ASN.1 notation this is any object preceded by
  * a [n] where n is some number - these are assumed to follow the construction
@@ -13,34 +15,32 @@
     extends ASN1Primitive
     implements ASN1TaggedObjectParser
 {
-    final int           tagNo;
-    final boolean       explicit;
-    final ASN1Encodable obj;
+    private static final int DECLARED_EXPLICIT = 1;
+    private static final int DECLARED_IMPLICIT = 2;
+    // TODO It will probably be better to track parsing constructed vs primitive instead
+    private static final int PARSED_EXPLICIT = 3;
+    private static final int PARSED_IMPLICIT = 4;
 
-    static public ASN1TaggedObject getInstance(
-        ASN1TaggedObject    obj,
-        boolean             explicit)
-    {
-        if (explicit)
-        {
-            return getInstance(obj.getObject());
-        }
-
-        throw new IllegalArgumentException("implicitly tagged tagged object");
-    }
-
-    static public ASN1TaggedObject getInstance(
-        Object obj) 
+    public static ASN1TaggedObject getInstance(Object obj)
     {
         if (obj == null || obj instanceof ASN1TaggedObject) 
         {
             return (ASN1TaggedObject)obj;
         }
+//      else if (obj instanceof ASN1TaggedObjectParser)
+        else if (obj instanceof ASN1Encodable)
+        {
+            ASN1Primitive primitive = ((ASN1Encodable)obj).toASN1Primitive();
+            if (primitive instanceof ASN1TaggedObject)
+            {
+                return (ASN1TaggedObject)primitive;
+            }
+        }
         else if (obj instanceof byte[])
         {
             try
             {
-                return ASN1TaggedObject.getInstance(fromByteArray((byte[])obj));
+                return checkedCast(fromByteArray((byte[])obj));
             }
             catch (IOException e)
             {
@@ -51,6 +51,58 @@
         throw new IllegalArgumentException("unknown object in getInstance: " + obj.getClass().getName());
     }
 
+    public static ASN1TaggedObject getInstance(Object obj, int tagClass)
+    {
+        if (obj == null)
+        {
+            throw new NullPointerException("'obj' cannot be null");
+        }
+
+        ASN1TaggedObject taggedObject = getInstance(obj);
+        if (tagClass != taggedObject.getTagClass())
+        {
+            throw new IllegalArgumentException("unexpected tag in getInstance: " + ASN1Util.getTagText(taggedObject));
+        }
+
+        return taggedObject;
+    }
+
+    public static ASN1TaggedObject getInstance(Object obj, int tagClass, int tagNo)
+    {
+        if (obj == null)
+        {
+            throw new NullPointerException("'obj' cannot be null");
+        }
+
+        ASN1TaggedObject taggedObject = getInstance(obj);
+        if (!taggedObject.hasTag(tagClass, tagNo))
+        {
+            throw new IllegalArgumentException("unexpected tag in getInstance: " + ASN1Util.getTagText(taggedObject));
+        }
+
+        return taggedObject;
+    }
+
+    public static ASN1TaggedObject getInstance(ASN1TaggedObject taggedObject, boolean declaredExplicit)
+    {
+        if (BERTags.CONTEXT_SPECIFIC != taggedObject.getTagClass())
+        {
+            throw new IllegalStateException("this method only valid for CONTEXT_SPECIFIC tags");
+        }
+
+        if (declaredExplicit)
+        {
+            return taggedObject.getExplicitBaseTagged();
+        }
+
+        throw new IllegalArgumentException("this method not valid for implicitly tagged tagged objects");
+    }
+
+    final int           explicitness;
+    final int           tagClass;
+    final int           tagNo;
+    final ASN1Encodable obj;
+
     /**
      * Create a tagged object with the style given by the value of explicit.
      * <p>
@@ -61,22 +113,34 @@
      * @param tagNo the tag number for this object.
      * @param obj the tagged object.
      */
-    public ASN1TaggedObject(
-        boolean         explicit,
-        int             tagNo,
-        ASN1Encodable   obj)
+    protected ASN1TaggedObject(boolean explicit, int tagNo, ASN1Encodable obj)
+    {
+        this(explicit, BERTags.CONTEXT_SPECIFIC, tagNo, obj);
+    }
+
+    protected ASN1TaggedObject(boolean explicit, int tagClass, int tagNo, ASN1Encodable obj)
+    {
+        this(explicit ? DECLARED_EXPLICIT : DECLARED_IMPLICIT, tagClass, tagNo, obj);
+    }
+
+    ASN1TaggedObject(int explicitness, int tagClass, int tagNo, ASN1Encodable obj)
     {
         if (null == obj)
         {
             throw new NullPointerException("'obj' cannot be null");
         }
+        if (tagClass == BERTags.UNIVERSAL || (tagClass & BERTags.PRIVATE) != tagClass)
+        {
+            throw new IllegalArgumentException("invalid tag class: " + tagClass);
+        }
 
+        this.explicitness = (obj instanceof ASN1Choice) ? DECLARED_EXPLICIT : explicitness;
+        this.tagClass = tagClass;
         this.tagNo = tagNo;
-        this.explicit = explicit || (obj instanceof ASN1Choice);
         this.obj = obj;
     }
 
-    boolean asn1Equals(ASN1Primitive other)
+    final boolean asn1Equals(ASN1Primitive other)
     {
         if (!(other instanceof ASN1TaggedObject))
         {
@@ -85,20 +149,58 @@
 
         ASN1TaggedObject that = (ASN1TaggedObject)other;
 
-        if (this.tagNo != that.tagNo || this.explicit != that.explicit)
+        if (this.tagNo != that.tagNo ||
+            this.tagClass != that.tagClass)
         {
             return false;
         }
 
+        if (this.explicitness != that.explicitness)
+        {
+            /*
+             * TODO This seems incorrect for some cases of implicit tags e.g. if one is a
+             * declared-implicit SET and the other a parsed object.
+             */
+            if (this.isExplicit() != that.isExplicit())
+            {
+                return false;
+            }
+        }
+
         ASN1Primitive p1 = this.obj.toASN1Primitive();
         ASN1Primitive p2 = that.obj.toASN1Primitive();
 
-        return p1 == p2 || p1.asn1Equals(p2);
+        if (p1 == p2)
+        {
+            return true;
+        }
+
+        if (!this.isExplicit())
+        {
+            try
+            {
+                byte[] d1 = this.getEncoded();
+                byte[] d2 = that.getEncoded();
+                
+                return Arrays.areEqual(d1, d2);
+            }
+            catch (IOException e)
+            {
+                return false;
+            }
+        }
+
+        return p1.asn1Equals(p2);
     }
 
     public int hashCode()
     {
-        return tagNo ^ (explicit ? 0x0F : 0xF0) ^ obj.toASN1Primitive().hashCode();
+        return (tagClass * 7919) ^ tagNo ^ (isExplicit() ? 0x0F : 0xF0) ^ obj.toASN1Primitive().hashCode();
+    }
+
+    public int getTagClass()
+    {
+        return tagClass;
     }
 
     /**
@@ -111,18 +213,24 @@
         return tagNo;
     }
 
-    /**
-     * return whether or not the object may be explicitly tagged. 
-     * <p>
-     * Note: if the object has been read from an input stream, the only
-     * time you can be sure if isExplicit is returning the true state of
-     * affairs is if it returns false. An implicitly tagged object may appear
-     * to be explicitly tagged, so you need to understand the context under
-     * which the reading was done as well, see getObject below.
-     */
-    public boolean isExplicit()
+    public boolean hasContextTag()
     {
-        return explicit;
+        return this.tagClass == BERTags.CONTEXT_SPECIFIC;
+    }
+
+    public boolean hasContextTag(int tagNo)
+    {
+        return this.tagClass == BERTags.CONTEXT_SPECIFIC && this.tagNo == tagNo;
+    }
+
+    public boolean hasTag(int tagClass, int tagNo)
+    {
+        return this.tagClass == tagClass && this.tagNo == tagNo;
+    }
+
+    public boolean hasTagClass(int tagClass)
+    {
+        return this.tagClass == tagClass;
     }
 
     /**
@@ -138,52 +246,246 @@
     }
 
     /**
-     * Return the object held in this tagged object as a parser assuming it has
-     * the type of the passed in tag. If the object doesn't have a parser
-     * associated with it, the base object is returned.
+     * return whether or not the object may be explicitly tagged. 
+     * <p>
+     * Note: if the object has been read from an input stream, the only
+     * time you can be sure if isExplicit is returning the true state of
+     * affairs is if it returns false. An implicitly tagged object may appear
+     * to be explicitly tagged, so you need to understand the context under
+     * which the reading was done as well, see getObject below.
      */
-    public ASN1Encodable getObjectParser(
-        int     tag,
-        boolean isExplicit)
-        throws IOException
+    public boolean isExplicit()
     {
-        switch (tag)
+        // TODO New methods like 'isKnownExplicit' etc. to distinguish uncertain cases?
+        switch (explicitness)
         {
-        case BERTags.SET:
-            return ASN1Set.getInstance(this, isExplicit).parser();
-        case BERTags.SEQUENCE:
-            return ASN1Sequence.getInstance(this, isExplicit).parser();
+        case DECLARED_EXPLICIT:
+        case PARSED_EXPLICIT:
+            return true;
+        default:
+            return false;
+        }
+    }
+
+    boolean isParsed()
+    {
+        switch (explicitness)
+        {
+        case PARSED_EXPLICIT:
+        case PARSED_IMPLICIT:
+            return true;
+        default:
+            return false;
+        }
+    }
+
+    /**
+     * Needed for open types, until we have better type-guided parsing support. Use sparingly for other
+     * purposes, and prefer {@link #getExplicitBaseTagged()}, {@link #getImplicitBaseTagged(int, int)} or
+     * {@link #getBaseUniversal(boolean, int)} where possible. Before using, check for matching tag
+     * {@link #getTagClass() class} and {@link #getTagNo() number}.
+     */
+    public ASN1Object getBaseObject()
+    {
+        return obj instanceof ASN1Object ? (ASN1Object)obj : obj.toASN1Primitive();
+    }
+
+    /**
+     * Needed for open types, until we have better type-guided parsing support. Use
+     * sparingly for other purposes, and prefer {@link #getExplicitBaseTagged()} or
+     * {@link #getBaseUniversal(boolean, int)} where possible. Before using, check
+     * for matching tag {@link #getTagClass() class} and {@link #getTagNo() number}.
+     */
+    public ASN1Object getExplicitBaseObject()
+    {
+        if (!isExplicit())
+        {
+            throw new IllegalStateException("object implicit - explicit expected.");
+        }
+
+        return obj instanceof ASN1Object ? (ASN1Object)obj : obj.toASN1Primitive();
+    }
+
+    public ASN1TaggedObject getExplicitBaseTagged()
+    {
+        if (!isExplicit())
+        {
+            throw new IllegalStateException("object implicit - explicit expected.");
+        }
+
+        return checkedCast(obj.toASN1Primitive());
+    }
+
+    public ASN1TaggedObject getImplicitBaseTagged(int baseTagClass, int baseTagNo)
+    {
+        if (baseTagClass == BERTags.UNIVERSAL || (baseTagClass & BERTags.PRIVATE) != baseTagClass)
+        {
+            throw new IllegalArgumentException("invalid base tag class: " + baseTagClass);
+        }
+
+        switch (explicitness)
+        {
+        case DECLARED_EXPLICIT:
+            throw new IllegalStateException("object explicit - implicit expected.");
+
+        case DECLARED_IMPLICIT:
+        {
+            ASN1TaggedObject declared = checkedCast(obj.toASN1Primitive());
+            return ASN1Util.checkTag(declared, baseTagClass, baseTagNo);
+        }
+
+        // Parsed; return a virtual tag (i.e. that couldn't have been present in the encoding)
+        default:
+            return replaceTag(baseTagClass, baseTagNo);
+        }
+    }
+
+    /**
+     * Note: tagged objects are generally context dependent. Before trying to
+     * extract a tagged object this way, make sure you have checked that both the
+     * {@link #getTagClass() tag class} and {@link #getTagNo() tag number} match
+     * what you are looking for.
+     * 
+     * @param declaredExplicit Whether the tagged type for this object was declared
+     *                         EXPLICIT.
+     * @param tagNo            The universal {@link BERTags tag number} of the
+     *                         expected base object.
+     */
+    public ASN1Primitive getBaseUniversal(boolean declaredExplicit, int tagNo)
+    {
+        ASN1UniversalType universalType = ASN1UniversalTypes.get(tagNo);
+        if (null == universalType)
+        {
+            throw new IllegalArgumentException("unsupported UNIVERSAL tag number: " + tagNo);
+        }
+
+        return getBaseUniversal(declaredExplicit, universalType);
+    }
+
+    ASN1Primitive getBaseUniversal(boolean declaredExplicit, ASN1UniversalType universalType)
+    {
+        if (declaredExplicit)
+        {
+            if (!isExplicit())
+            {
+                throw new IllegalStateException("object explicit - implicit expected.");
+            }
+
+            return universalType.checkedCast(obj.toASN1Primitive());
+        }
+
+        if (DECLARED_EXPLICIT == explicitness)
+        {
+            throw new IllegalStateException("object explicit - implicit expected.");
+        }
+
+        ASN1Primitive primitive = obj.toASN1Primitive();
+        switch (explicitness)
+        {
+        case PARSED_EXPLICIT:
+            return universalType.fromImplicitConstructed(rebuildConstructed(primitive));
+        case PARSED_IMPLICIT:
+        {
+            if (primitive instanceof ASN1Sequence)
+            {
+                return universalType.fromImplicitConstructed((ASN1Sequence)primitive);
+            }
+            return universalType.fromImplicitPrimitive((DEROctetString)primitive);
+        }
+        default:
+            return universalType.checkedCast(primitive);
+        }
+    }
+
+    public ASN1Encodable parseBaseUniversal(boolean declaredExplicit, int baseTagNo) throws IOException
+    {
+        ASN1Primitive primitive = getBaseUniversal(declaredExplicit, baseTagNo);
+
+        switch (baseTagNo)
+        {
+        case BERTags.BIT_STRING:
+            return ((ASN1BitString)primitive).parser();
         case BERTags.OCTET_STRING:
-            return ASN1OctetString.getInstance(this, isExplicit).parser();
+            return ((ASN1OctetString)primitive).parser();
+        case BERTags.SEQUENCE:
+            return ((ASN1Sequence)primitive).parser();
+        case BERTags.SET:
+            return ((ASN1Set)primitive).parser();
         }
 
-        if (isExplicit)
-        {
-            return getObject();
-        }
-
-        throw new ASN1Exception("implicit tagging not implemented for tag: " + tag);
+        return primitive;
     }
 
-    public ASN1Primitive getLoadedObject()
+    public ASN1Encodable parseExplicitBaseObject() throws IOException
     {
-        return this.toASN1Primitive();
+        return getExplicitBaseObject();
     }
 
+    public ASN1TaggedObjectParser parseExplicitBaseTagged() throws IOException
+    {
+        return getExplicitBaseTagged();
+    }
+
+    public ASN1TaggedObjectParser parseImplicitBaseTagged(int baseTagClass, int baseTagNo) throws IOException
+    {
+        return getImplicitBaseTagged(baseTagClass, baseTagNo);
+    }
+
+    public final ASN1Primitive getLoadedObject()
+    {
+        return this;
+    }
+
+    abstract ASN1Sequence rebuildConstructed(ASN1Primitive primitive);
+
+    abstract ASN1TaggedObject replaceTag(int tagClass, int tagNo);
+
     ASN1Primitive toDERObject()
     {
-        return new DERTaggedObject(explicit, tagNo, obj);
+        return new DERTaggedObject(explicitness, tagClass, tagNo, obj);
     }
 
     ASN1Primitive toDLObject()
     {
-        return new DLTaggedObject(explicit, tagNo, obj);
+        return new DLTaggedObject(explicitness, tagClass, tagNo, obj);
     }
 
-    abstract void encode(ASN1OutputStream out, boolean withTag) throws IOException;
-
     public String toString()
     {
-        return "[" + tagNo + "]" + obj;
+        return ASN1Util.getTagText(tagClass, tagNo) + obj;
+    }
+
+    static ASN1Primitive createConstructedDL(int tagClass, int tagNo, ASN1EncodableVector contentsElements)
+    {
+        boolean maybeExplicit = (contentsElements.size() == 1);
+
+        return maybeExplicit
+            ?   new DLTaggedObject(PARSED_EXPLICIT, tagClass, tagNo, contentsElements.get(0))
+            :   new DLTaggedObject(PARSED_IMPLICIT, tagClass, tagNo, DLFactory.createSequence(contentsElements));
+    }
+
+    static ASN1Primitive createConstructedIL(int tagClass, int tagNo, ASN1EncodableVector contentsElements)
+    {
+        boolean maybeExplicit = (contentsElements.size() == 1);
+
+        return maybeExplicit
+            ?   new BERTaggedObject(PARSED_EXPLICIT, tagClass, tagNo, contentsElements.get(0))
+            :   new BERTaggedObject(PARSED_IMPLICIT, tagClass, tagNo, BERFactory.createSequence(contentsElements));
+    }
+
+    static ASN1Primitive createPrimitive(int tagClass, int tagNo, byte[] contentsOctets)
+    {
+        // Note: !CONSTRUCTED => IMPLICIT
+        return new DLTaggedObject(PARSED_IMPLICIT, tagClass, tagNo, new DEROctetString(contentsOctets));
+    }
+
+    private static ASN1TaggedObject checkedCast(ASN1Primitive primitive)
+    {
+        if (primitive instanceof ASN1TaggedObject)
+        {
+            return (ASN1TaggedObject)primitive;
+        }
+
+        throw new IllegalStateException("unexpected object: " + primitive.getClass().getName());
     }
 }
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1TaggedObjectParser.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1TaggedObjectParser.java
index 41fcf42..c150907 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1TaggedObjectParser.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1TaggedObjectParser.java
@@ -11,19 +11,38 @@
     extends ASN1Encodable, InMemoryRepresentable
 {
     /**
-     * Return the tag number associated with the underlying tagged object.
-     * @return the object's tag number.
+     * Return the tag class associated with this object.
+     *
+     * @return the tag class.
+     */
+    int getTagClass();
+
+    /**
+     * Return the tag number associated with this object.
+     *
+     * @return the tag number.
      */
     int getTagNo();
 
+    boolean hasContextTag();
+
+    boolean hasContextTag(int tagNo);
+
+    boolean hasTag(int tagClass, int tagNo);
+
+    boolean hasTagClass(int tagClass);
+
+    ASN1Encodable parseBaseUniversal(boolean declaredExplicit, int baseTagNo) throws IOException;
+
     /**
-     * Return a parser for the actual object tagged.
-     *
-     * @param tag the primitive tag value for the object tagged originally.
-     * @param isExplicit true if the tagging was done explicitly.
-     * @return a parser for the tagged object.
-     * @throws IOException if a parser cannot be constructed.
+     * Needed for open types, until we have better type-guided parsing support. Use sparingly for other
+     * purposes, and prefer {@link #parseExplicitBaseTagged()} or {@link #parseBaseUniversal(boolean, int)}
+     * where possible. Before using, check for matching tag {@link #getTagClass() class} and
+     * {@link #getTagNo() number}.
      */
-    ASN1Encodable getObjectParser(int tag, boolean isExplicit)
-        throws IOException;
+    ASN1Encodable parseExplicitBaseObject() throws IOException;
+
+    ASN1TaggedObjectParser parseExplicitBaseTagged() throws IOException;
+
+    ASN1TaggedObjectParser parseImplicitBaseTagged(int baseTagClass, int baseTagNo) throws IOException;
 }
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1Type.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1Type.java
new file mode 100644
index 0000000..1116801
--- /dev/null
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1Type.java
@@ -0,0 +1,27 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.asn1;
+
+abstract class ASN1Type
+{
+    final Class javaClass;
+
+    ASN1Type(Class javaClass)
+    {
+        this.javaClass = javaClass;
+    }
+
+    final Class getJavaClass()
+    {
+        return javaClass;
+    }
+
+    public final boolean equals(Object that)
+    {
+        return this == that;
+    }
+
+    public final int hashCode()
+    {
+        return super.hashCode();
+    }
+}
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1UTCTime.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1UTCTime.java
index 7bd9298..adfe70c 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1UTCTime.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1UTCTime.java
@@ -38,7 +38,13 @@
 public class ASN1UTCTime
     extends ASN1Primitive
 {
-    private byte[]      time;
+    static final ASN1UniversalType TYPE = new ASN1UniversalType(ASN1UTCTime.class, BERTags.UTC_TIME)
+    {
+        ASN1Primitive fromImplicitPrimitive(DEROctetString octetString)
+        {
+            return createPrimitive(octetString.getOctets());
+        }
+    };
 
     /**
      * Return an UTC Time from the passed in object.
@@ -54,12 +60,19 @@
         {
             return (ASN1UTCTime)obj;
         }
-
+        if (obj instanceof ASN1Encodable)
+        {
+            ASN1Primitive primitive = ((ASN1Encodable)obj).toASN1Primitive();
+            if (primitive instanceof ASN1UTCTime)
+            {
+                return (ASN1UTCTime)primitive;
+            }
+        }
         if (obj instanceof byte[])
         {
             try
             {
-                return (ASN1UTCTime)fromByteArray((byte[])obj);
+                return (ASN1UTCTime)TYPE.fromByteArray((byte[])obj);
             }
             catch (Exception e)
             {
@@ -73,29 +86,19 @@
     /**
      * Return an UTC Time from a tagged object.
      *
-     * @param obj the tagged object holding the object we want
-     * @param explicit true if the object is meant to be explicitly
-     *              tagged false otherwise.
-     * @exception IllegalArgumentException if the tagged object cannot
-     *               be converted.
+     * @param taggedObject the tagged object holding the object we want
+     * @param explicit     true if the object is meant to be explicitly tagged false
+     *                     otherwise.
+     * @exception IllegalArgumentException if the tagged object cannot be converted.
      * @return an ASN1UTCTime instance, or null.
      */
-    public static ASN1UTCTime getInstance(
-        ASN1TaggedObject obj,
-        boolean          explicit)
+    public static ASN1UTCTime getInstance(ASN1TaggedObject taggedObject, boolean explicit)
     {
-        ASN1Object o = obj.getObject();
-
-        if (explicit || o instanceof ASN1UTCTime)
-        {
-            return getInstance(o);
-        }
-        else
-        {
-            return new ASN1UTCTime(ASN1OctetString.getInstance(o).getOctets());
-        }
+        return (ASN1UTCTime)TYPE.getContextInstance(taggedObject, explicit);
     }
 
+    final byte[] contents;
+
     /**
      * The correct format for this is YYMMDDHHMMSSZ (it used to be that seconds were
      * never encoded. When you're creating one of these objects from scratch, that's
@@ -109,7 +112,7 @@
     public ASN1UTCTime(
         String time)
     {
-        this.time = Strings.toByteArray(time);
+        this.contents = Strings.toByteArray(time);
         try
         {
             this.getDate();
@@ -133,7 +136,7 @@
 
         dateF.setTimeZone(new SimpleTimeZone(0,"Z"));
 
-        this.time = Strings.toByteArray(dateF.format(time));
+        this.contents = Strings.toByteArray(dateF.format(time));
     }
 
     /**
@@ -155,17 +158,16 @@
 
         dateF.setTimeZone(new SimpleTimeZone(0,"Z"));
 
-        this.time = Strings.toByteArray(dateF.format(time));
+        this.contents = Strings.toByteArray(dateF.format(time));
     }
 
-    ASN1UTCTime(
-        byte[] time)
+    ASN1UTCTime(byte[] contents)
     {
-        if (time.length < 2)
+        if (contents.length < 2)
         {
             throw new IllegalArgumentException("UTCTime string too short");
         }
-        this.time = time;
+        this.contents = contents;
         if (!(isDigit(0) && isDigit(1)))
         {
             throw new IllegalArgumentException("illegal characters in UTCTime string");
@@ -186,7 +188,7 @@
         // SimpleDateFormat dateF = new SimpleDateFormat("yyMMddHHmmssz");
         SimpleDateFormat dateF = new SimpleDateFormat("yyMMddHHmmssz", Locale.US);
 
-        return DateUtil.epochAdjust(dateF.parse(getTime()));
+        return dateF.parse(getTime());
     }
 
     /**
@@ -204,8 +206,8 @@
         SimpleDateFormat dateF = new SimpleDateFormat("yyyyMMddHHmmssz", Locale.US);
 
         dateF.setTimeZone(new SimpleTimeZone(0,"Z"));
-        
-        return DateUtil.epochAdjust(dateF.parse(getAdjustedTime()));
+
+        return dateF.parse(getAdjustedTime());
     }
 
     /**
@@ -226,7 +228,7 @@
      */
     public String getTime()
     {
-        String stime = Strings.fromByteArray(time);
+        String stime = Strings.fromByteArray(contents);
 
         //
         // standardise the format.
@@ -287,24 +289,22 @@
 
     private boolean isDigit(int pos)
     {
-        return time.length > pos && time[pos] >= '0' && time[pos] <= '9';
+        return contents.length > pos && contents[pos] >= '0' && contents[pos] <= '9';
     }
 
-    boolean isConstructed()
+    final boolean encodeConstructed()
     {
         return false;
     }
 
-    int encodedLength()
+    int encodedLength(boolean withTag)
     {
-        int length = time.length;
-
-        return 1 + StreamUtil.calculateBodyLength(length) + length;
+        return ASN1OutputStream.getLengthOfEncodingDL(withTag, contents.length);
     }
 
     void encode(ASN1OutputStream out, boolean withTag) throws IOException
     {
-        out.writeEncoded(withTag, BERTags.UTC_TIME, time);
+        out.writeEncodingDL(withTag, BERTags.UTC_TIME, contents);
     }
 
     boolean asn1Equals(
@@ -315,16 +315,21 @@
             return false;
         }
 
-        return Arrays.areEqual(time, ((ASN1UTCTime)o).time);
+        return Arrays.areEqual(contents, ((ASN1UTCTime)o).contents);
     }
 
     public int hashCode()
     {
-        return Arrays.hashCode(time);
+        return Arrays.hashCode(contents);
     }
 
     public String toString()
     {
-      return Strings.fromByteArray(time);
+      return Strings.fromByteArray(contents);
+    }
+
+    static ASN1UTCTime createPrimitive(byte[] contents)
+    {
+        return new ASN1UTCTime(contents);
     }
 }
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1UTF8String.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1UTF8String.java
new file mode 100644
index 0000000..629d83e
--- /dev/null
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1UTF8String.java
@@ -0,0 +1,133 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.asn1;
+
+import java.io.IOException;
+
+import com.android.internal.org.bouncycastle.util.Arrays;
+import com.android.internal.org.bouncycastle.util.Strings;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+public abstract class ASN1UTF8String
+    extends ASN1Primitive
+    implements ASN1String
+{
+    static final ASN1UniversalType TYPE = new ASN1UniversalType(ASN1UTF8String.class, BERTags.UTF8_STRING)
+    {
+        ASN1Primitive fromImplicitPrimitive(DEROctetString octetString)
+        {
+            return createPrimitive(octetString.getOctets());
+        }
+    };
+
+    /**
+     * Return a UTF8 string from the passed in object.
+     *
+     * @param obj an ASN1UTF8String or an object that can be converted into one.
+     * @exception IllegalArgumentException if the object cannot be converted.
+     * @return an ASN1UTF8String instance, or null
+     */
+    public static ASN1UTF8String getInstance(Object obj)
+    {
+        if (obj == null || obj instanceof ASN1UTF8String)
+        {
+            return (ASN1UTF8String)obj;
+        }
+        if (obj instanceof ASN1Encodable)
+        {
+            ASN1Primitive primitive = ((ASN1Encodable)obj).toASN1Primitive();
+            if (primitive instanceof ASN1UTF8String)
+            {
+                return (ASN1UTF8String)primitive;
+            }
+        }
+        if (obj instanceof byte[])
+        {
+            try
+            {
+                return (ASN1UTF8String)TYPE.fromByteArray((byte[])obj);
+            }
+            catch (Exception e)
+            {
+                throw new IllegalArgumentException("encoding error in getInstance: " + e.toString());
+            }
+        }
+
+        throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
+    }
+
+    /**
+     * Return an UTF8 String from a tagged object.
+     * 
+     * @param taggedObject the tagged object holding the object we want
+     * @param explicit     true if the object is meant to be explicitly tagged false
+     *                     otherwise.
+     * @exception IllegalArgumentException if the tagged object cannot be converted.
+     * @return a DERUTF8String instance, or null
+     */
+    public static ASN1UTF8String getInstance(ASN1TaggedObject taggedObject, boolean explicit)
+    {
+        return (ASN1UTF8String)TYPE.getContextInstance(taggedObject, explicit);
+    }
+
+    final byte[] contents;
+
+    ASN1UTF8String(String string)
+    {
+        this(Strings.toUTF8ByteArray(string), false);
+    }
+
+    ASN1UTF8String(byte[] contents, boolean clone)
+    {
+        this.contents = clone ? Arrays.clone(contents) : contents;
+    }
+
+    public final String getString()
+    {
+        return Strings.fromUTF8ByteArray(contents);
+    }
+
+    // TODO Not sure this is useful unless all ASN.1 types have a meaningful one
+    public String toString()
+    {
+        return getString();
+    }
+
+    public final int hashCode()
+    {
+        return Arrays.hashCode(contents);
+    }
+
+    final boolean asn1Equals(ASN1Primitive other)
+    {
+        if (!(other instanceof ASN1UTF8String))
+        {
+            return false;
+        }
+
+        ASN1UTF8String that = (ASN1UTF8String)other;
+
+        return Arrays.areEqual(this.contents, that.contents);
+    }
+
+    final boolean encodeConstructed()
+    {
+        return false;
+    }
+
+    final int encodedLength(boolean withTag)
+    {
+        return ASN1OutputStream.getLengthOfEncodingDL(withTag, contents.length);
+    }
+
+    final void encode(ASN1OutputStream out, boolean withTag) throws IOException
+    {
+        out.writeEncodingDL(withTag, BERTags.UTF8_STRING, contents);
+    }
+
+    static ASN1UTF8String createPrimitive(byte[] contents)
+    {
+        return new DERUTF8String(contents, false);
+    }
+}
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1UniversalString.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1UniversalString.java
new file mode 100644
index 0000000..d1e4805
--- /dev/null
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1UniversalString.java
@@ -0,0 +1,180 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.asn1;
+
+import java.io.IOException;
+
+import com.android.internal.org.bouncycastle.util.Arrays;
+
+/**
+ * ASN.1 UniversalString object - encodes UNICODE (ISO 10646) characters using 32-bit format. In Java we
+ * have no way of representing this directly so we rely on byte arrays to carry these.
+ * @hide This class is not part of the Android public SDK API
+ */
+public abstract class ASN1UniversalString
+    extends ASN1Primitive
+    implements ASN1String
+{
+    static final ASN1UniversalType TYPE = new ASN1UniversalType(ASN1UniversalString.class, BERTags.UNIVERSAL_STRING)
+    {
+        ASN1Primitive fromImplicitPrimitive(DEROctetString octetString)
+        {
+            return createPrimitive(octetString.getOctets());
+        }
+    };
+
+    private static final char[]  table = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
+
+    /**
+     * Return a Universal String from the passed in object.
+     *
+     * @param obj an ASN1UniversalString or an object that can be converted into
+     *            one.
+     * @exception IllegalArgumentException if the object cannot be converted.
+     * @return an ASN1UniversalString instance, or null
+     */
+    public static ASN1UniversalString getInstance(Object obj)
+    {
+        if (obj == null || obj instanceof ASN1UniversalString)
+        {
+            return (ASN1UniversalString)obj;
+        }
+        if (obj instanceof ASN1Encodable)
+        {
+            ASN1Primitive primitive = ((ASN1Encodable)obj).toASN1Primitive();
+            if (primitive instanceof ASN1UniversalString)
+            {
+                return (ASN1UniversalString)primitive;
+            }
+        }
+        if (obj instanceof byte[])
+        {
+            try
+            {
+                return (ASN1UniversalString)TYPE.fromByteArray((byte[])obj);
+            }
+            catch (Exception e)
+            {
+                throw new IllegalArgumentException("encoding error getInstance: " + e.toString());
+            }
+        }
+
+        throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
+    }
+
+    /**
+     * Return a Universal String from a tagged object.
+     *
+     * @param taggedObject      the tagged object holding the object we want
+     * @param explicit true if the object is meant to be explicitly tagged false
+     *                 otherwise.
+     * @exception IllegalArgumentException if the tagged object cannot be converted.
+     * @return a ASN1UniversalString instance, or null
+     */
+    public static ASN1UniversalString getInstance(ASN1TaggedObject taggedObject, boolean explicit)
+    {
+        return (ASN1UniversalString)TYPE.getContextInstance(taggedObject, explicit);
+    }
+
+    final byte[] contents;
+
+    ASN1UniversalString(byte[] contents, boolean clone)
+    {
+        this.contents = clone ? Arrays.clone(contents) : contents;
+    }
+
+    public final String getString()
+    {
+        int dl = contents.length;
+        StringBuffer buf = new StringBuffer(3 + 2 * (ASN1OutputStream.getLengthOfDL(dl) + dl));
+        buf.append("#1C");
+        encodeHexDL(buf, dl);
+
+        for (int i = 0; i < dl; ++i)
+        {
+            encodeHexByte(buf, contents[i]);
+        }
+
+        return buf.toString();
+    }
+
+    public String toString()
+    {
+        return getString();
+    }
+
+    public final byte[] getOctets()
+    {
+        return Arrays.clone(contents);
+    }
+
+    final boolean encodeConstructed()
+    {
+        return false;
+    }
+
+    final int encodedLength(boolean withTag)
+    {
+        return ASN1OutputStream.getLengthOfEncodingDL(withTag, contents.length);
+    }
+
+    final void encode(ASN1OutputStream out, boolean withTag) throws IOException
+    {
+        out.writeEncodingDL(withTag, BERTags.UNIVERSAL_STRING, contents);
+    }
+
+    final boolean asn1Equals(ASN1Primitive other)
+    {
+        if (!(other instanceof ASN1UniversalString))
+        {
+            return false;
+        }
+
+        ASN1UniversalString that = (ASN1UniversalString)other;
+
+        return Arrays.areEqual(this.contents, that.contents);
+    }
+
+    public final int hashCode()
+    {
+        return Arrays.hashCode(contents);
+    }
+
+    static ASN1UniversalString createPrimitive(byte[] contents)
+    {
+        return new DERUniversalString(contents, false);
+    }
+
+    private static void encodeHexByte(StringBuffer buf, int i)
+    {
+        buf.append(table[(i >>> 4) & 0xF]);
+        buf.append(table[i & 0xF]);
+    }
+
+    private static void encodeHexDL(StringBuffer buf, int dl)
+    {
+        if (dl < 128)
+        {
+            encodeHexByte(buf, dl);
+            return;
+        }
+
+        byte[] stack = new byte[5];
+        int pos = 5;
+
+        do
+        {
+            stack[--pos] = (byte)dl;
+            dl >>>= 8;
+        }
+        while (dl != 0);
+
+        int count = stack.length - pos;
+        stack[--pos] = (byte)(0x80 | count);
+
+        do
+        {
+            encodeHexByte(buf, stack[pos++]);
+        }
+        while (pos < stack.length);
+    }
+}
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1UniversalType.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1UniversalType.java
new file mode 100644
index 0000000..e3cb935
--- /dev/null
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1UniversalType.java
@@ -0,0 +1,57 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.asn1;
+
+import java.io.IOException;
+
+abstract class ASN1UniversalType
+    extends ASN1Type
+{
+    final ASN1Tag tag;
+
+    ASN1UniversalType(Class javaClass, int tagNumber)
+    {
+        super(javaClass);
+
+        this.tag = ASN1Tag.create(BERTags.UNIVERSAL, tagNumber);
+    }
+
+    final ASN1Primitive checkedCast(ASN1Primitive primitive)
+    {
+        if (javaClass.isInstance(primitive))
+        {
+            return primitive;
+        }
+
+        throw new IllegalStateException("unexpected object: " + primitive.getClass().getName());
+    }
+
+    ASN1Primitive fromImplicitPrimitive(DEROctetString octetString)
+    {
+        throw new IllegalStateException("unexpected implicit primitive encoding");
+    }
+
+    ASN1Primitive fromImplicitConstructed(ASN1Sequence sequence)
+    {
+        throw new IllegalStateException("unexpected implicit constructed encoding");
+    }
+
+    final ASN1Primitive fromByteArray(byte[] bytes) throws IOException
+    {
+        return checkedCast(ASN1Primitive.fromByteArray(bytes));
+    }
+
+    final ASN1Primitive getContextInstance(ASN1TaggedObject taggedObject, boolean declaredExplicit)
+    {
+        if (BERTags.CONTEXT_SPECIFIC != taggedObject.getTagClass())
+        {
+            throw new IllegalStateException("this method only valid for CONTEXT_SPECIFIC tags");
+        }
+
+        return checkedCast(taggedObject.getBaseUniversal(declaredExplicit, this));
+    }
+
+    final ASN1Tag getTag()
+    {
+        return tag;
+    }
+}
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1UniversalTypes.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1UniversalTypes.java
new file mode 100644
index 0000000..9163f3c
--- /dev/null
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1UniversalTypes.java
@@ -0,0 +1,72 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.asn1;
+
+final class ASN1UniversalTypes
+{
+    private ASN1UniversalTypes()
+    {
+    }
+
+    static ASN1UniversalType get(int tagNumber)
+    {
+        switch (tagNumber)
+        {
+        case BERTags.BOOLEAN:
+            return ASN1Boolean.TYPE;
+        case BERTags.INTEGER:
+            return ASN1Integer.TYPE;
+        case BERTags.BIT_STRING:
+            return ASN1BitString.TYPE;
+        case BERTags.OCTET_STRING:
+            return ASN1OctetString.TYPE;
+        case BERTags.NULL:
+            return ASN1Null.TYPE;
+        case BERTags.OBJECT_IDENTIFIER:
+            return ASN1ObjectIdentifier.TYPE;
+        case BERTags.OBJECT_DESCRIPTOR:         // [UNIVERSAL 7] IMPLICIT GraphicString
+            return ASN1ObjectDescriptor.TYPE;
+        case BERTags.EXTERNAL:
+            return ASN1External.TYPE;
+        case BERTags.ENUMERATED:
+            return ASN1Enumerated.TYPE;
+        case BERTags.UTF8_STRING:               // [UNIVERSAL 12] IMPLICIT OCTET STRING (encode as if)
+            return ASN1UTF8String.TYPE;
+        case BERTags.RELATIVE_OID:
+            return ASN1RelativeOID.TYPE;
+        case BERTags.SEQUENCE:
+            return ASN1Sequence.TYPE;
+        case BERTags.SET:
+            return ASN1Set.TYPE;
+        case BERTags.NUMERIC_STRING:            // [UNIVERSAL 18] IMPLICIT OCTET STRING (encode as if)
+            return ASN1NumericString.TYPE;
+        case BERTags.PRINTABLE_STRING:          // [UNIVERSAL 19] IMPLICIT OCTET STRING (encode as if)
+            return ASN1PrintableString.TYPE;
+        case BERTags.T61_STRING:                // [UNIVERSAL 20] IMPLICIT OCTET STRING (encode as if)
+            return ASN1T61String.TYPE;
+        case BERTags.VIDEOTEX_STRING:           // [UNIVERSAL 21] IMPLICIT OCTET STRING (encode as if)
+            return ASN1VideotexString.TYPE;
+        case BERTags.IA5_STRING:                // [UNIVERSAL 22] IMPLICIT OCTET STRING (encode as if)
+            return ASN1IA5String.TYPE;
+        case BERTags.UTC_TIME:                  // [UNIVERSAL 23] IMPLICIT VisibleString (restricted values)
+            return ASN1UTCTime.TYPE;
+        case BERTags.GENERALIZED_TIME:          // [UNIVERSAL 24] IMPLICIT VisibleString (restricted values)
+            return ASN1GeneralizedTime.TYPE;
+        case BERTags.GRAPHIC_STRING:            // [UNIVERSAL 25] IMPLICIT OCTET STRING (encode as if)
+            return ASN1GraphicString.TYPE;
+        case BERTags.VISIBLE_STRING:            // [UNIVERSAL 26] IMPLICIT OCTET STRING (encode as if)
+            return ASN1VisibleString.TYPE;
+        case BERTags.GENERAL_STRING:            // [UNIVERSAL 27] IMPLICIT OCTET STRING (encode as if)
+            return ASN1GeneralString.TYPE;
+        case BERTags.UNIVERSAL_STRING:          // [UNIVERSAL 28] IMPLICIT OCTET STRING (encode as if)
+            return ASN1UniversalString.TYPE;
+        case BERTags.BMP_STRING:                // [UNIVERSAL 30] IMPLICIT OCTET STRING (encode as if)
+            return ASN1BMPString.TYPE;
+
+        case BERTags.REAL:
+        case BERTags.EMBEDDED_PDV:
+        case BERTags.UNRESTRICTED_STRING:
+        default:
+            return null;
+        }
+    }
+}
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1Util.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1Util.java
new file mode 100644
index 0000000..17ba06e
--- /dev/null
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1Util.java
@@ -0,0 +1,331 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.asn1;
+
+import java.io.IOException;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+public abstract class ASN1Util
+{
+    static ASN1TaggedObject checkTag(ASN1TaggedObject taggedObject, int tagClass, int tagNo)
+    {
+        if (!taggedObject.hasTag(tagClass, tagNo))
+        {
+            String expected = getTagText(tagClass, tagNo);
+            String found = getTagText(taggedObject);
+            throw new IllegalStateException("Expected " + expected + " tag but found " + found);
+        }
+        return taggedObject;
+    }
+
+    static ASN1TaggedObjectParser checkTag(ASN1TaggedObjectParser taggedObjectParser, int tagClass, int tagNo)
+    {
+        if (!taggedObjectParser.hasTag(tagClass, tagNo))
+        {
+            String expected = getTagText(tagClass, tagNo);
+            String found = getTagText(taggedObjectParser);
+            throw new IllegalStateException("Expected " + expected + " tag but found " + found);
+        }
+        return taggedObjectParser;
+    }
+
+
+    /*
+     * Tag text methods
+     */
+
+    static String getTagText(ASN1Tag tag)
+    {
+        return getTagText(tag.getTagClass(), tag.getTagNumber());
+    }
+
+    public static String getTagText(ASN1TaggedObject taggedObject)
+    {
+        return getTagText(taggedObject.getTagClass(), taggedObject.getTagNo());
+    }
+
+    public static String getTagText(ASN1TaggedObjectParser taggedObjectParser)
+    {
+        return getTagText(taggedObjectParser.getTagClass(), taggedObjectParser.getTagNo());
+    }
+
+    public static String getTagText(int tagClass, int tagNo)
+    {
+        switch (tagClass)
+        {
+        case BERTags.APPLICATION:
+            return "[APPLICATION " + tagNo + "]";
+        case BERTags.CONTEXT_SPECIFIC:
+            return "[CONTEXT " + tagNo + "]";
+        case BERTags.PRIVATE:
+            return "[PRIVATE " + tagNo + "]";
+        default:
+            return "[UNIVERSAL " + tagNo + "]";
+        }
+    }
+
+
+    /*
+     * Wrappers for ASN1TaggedObject#getExplicitBaseObject
+     */
+
+    public static ASN1Object getExplicitBaseObject(ASN1TaggedObject taggedObject, int tagClass, int tagNo)
+    {
+        return checkTag(taggedObject, tagClass, tagNo).getExplicitBaseObject();
+    }
+
+    public static ASN1Object getExplicitContextBaseObject(ASN1TaggedObject taggedObject, int tagNo)
+    {
+        return getExplicitBaseObject(taggedObject, BERTags.CONTEXT_SPECIFIC, tagNo);
+    }
+
+    public static ASN1Object tryGetExplicitBaseObject(ASN1TaggedObject taggedObject, int tagClass, int tagNo)
+    {
+        if (!taggedObject.hasTag(tagClass, tagNo))
+        {
+            return null;
+        }
+
+        return taggedObject.getExplicitBaseObject();
+    }
+
+    public static ASN1Object tryGetExplicitContextBaseObject(ASN1TaggedObject taggedObject, int tagNo)
+    {
+        return tryGetExplicitBaseObject(taggedObject, BERTags.CONTEXT_SPECIFIC, tagNo);
+    }
+
+
+    /*
+     * Wrappers for ASN1TaggedObject#getExplicitBaseTagged
+     */
+
+    public static ASN1TaggedObject getExplicitBaseTagged(ASN1TaggedObject taggedObject, int tagClass, int tagNo)
+    {
+        return checkTag(taggedObject, tagClass, tagNo).getExplicitBaseTagged();
+    }
+
+    public static ASN1TaggedObject getExplicitContextBaseTagged(ASN1TaggedObject taggedObject, int tagNo)
+    {
+        return getExplicitBaseTagged(taggedObject, BERTags.CONTEXT_SPECIFIC, tagNo);
+    }
+
+    public static ASN1TaggedObject tryGetExplicitBaseTagged(ASN1TaggedObject taggedObject, int tagClass, int tagNo)
+    {
+        if (!taggedObject.hasTag(tagClass, tagNo))
+        {
+            return null;
+        }
+
+        return taggedObject.getExplicitBaseTagged();
+    }
+
+    public static ASN1TaggedObject tryGetExplicitContextBaseTagged(ASN1TaggedObject taggedObject, int tagNo)
+    {
+        return tryGetExplicitBaseTagged(taggedObject, BERTags.CONTEXT_SPECIFIC, tagNo);
+    }
+
+
+    /*
+     * Wrappers for ASN1TaggedObject#getImplicitBaseTagged
+     */
+
+    public static ASN1TaggedObject getImplicitBaseTagged(ASN1TaggedObject taggedObject, int tagClass, int tagNo,
+        int baseTagClass, int baseTagNo)
+    {
+        return checkTag(taggedObject, tagClass, tagNo).getImplicitBaseTagged(baseTagClass, baseTagNo);
+    }
+
+    public static ASN1TaggedObject getImplicitContextBaseTagged(ASN1TaggedObject taggedObject, int tagNo,
+        int baseTagClass, int baseTagNo)
+    {
+        return getImplicitBaseTagged(taggedObject, BERTags.CONTEXT_SPECIFIC, tagNo, baseTagClass, baseTagNo);
+    }
+
+    public static ASN1TaggedObject tryGetImplicitBaseTagged(ASN1TaggedObject taggedObject, int tagClass, int tagNo,
+        int baseTagClass, int baseTagNo)
+    {
+        if (!taggedObject.hasTag(tagClass, tagNo))
+        {
+            return null;
+        }
+
+        return taggedObject.getImplicitBaseTagged(baseTagClass, baseTagNo);
+    }
+
+    public static ASN1TaggedObject tryGetImplicitContextBaseTagged(ASN1TaggedObject taggedObject, int tagNo,
+        int baseTagClass, int baseTagNo)
+    {
+        return tryGetImplicitBaseTagged(taggedObject, BERTags.CONTEXT_SPECIFIC, tagNo, baseTagClass, baseTagNo);
+    }
+
+
+    /*
+     * Wrappers for ASN1TaggedObject#getBaseUniversal
+     */
+
+    public static ASN1Primitive getBaseUniversal(ASN1TaggedObject taggedObject, int tagClass, int tagNo,
+        boolean declaredExplicit, int baseTagNo)
+    {
+        return checkTag(taggedObject, tagClass, tagNo).getBaseUniversal(declaredExplicit, baseTagNo);  
+    }
+
+    public static ASN1Primitive getContextBaseUniversal(ASN1TaggedObject taggedObject, int tagNo,
+        boolean declaredExplicit, int baseTagNo)
+    {
+        return getBaseUniversal(taggedObject, BERTags.CONTEXT_SPECIFIC, tagNo, declaredExplicit, baseTagNo);
+    }
+
+    public static ASN1Primitive tryGetBaseUniversal(ASN1TaggedObject taggedObject, int tagClass, int tagNo,
+        boolean declaredExplicit, int baseTagNo)
+    {
+        if (!taggedObject.hasTag(tagClass, tagNo))
+        {
+            return null;
+        }
+
+        return taggedObject.getBaseUniversal(declaredExplicit, baseTagNo);  
+    }
+
+    public static ASN1Primitive tryGetContextBaseUniversal(ASN1TaggedObject taggedObject, int tagNo,
+        boolean declaredExplicit, int baseTagNo)
+    {
+        return tryGetBaseUniversal(taggedObject, BERTags.CONTEXT_SPECIFIC, tagNo, declaredExplicit, baseTagNo);
+    }
+
+
+    /*
+     * Wrappers for ASN1TaggedObjectParser#parseExplicitBaseTagged
+     */
+
+    public static ASN1TaggedObjectParser parseExplicitBaseTagged(ASN1TaggedObjectParser taggedObjectParser,
+        int tagClass, int tagNo) throws IOException
+    {
+        return checkTag(taggedObjectParser, tagClass, tagNo).parseExplicitBaseTagged();
+    }
+
+    public static ASN1TaggedObjectParser parseExplicitContextBaseTagged(ASN1TaggedObjectParser taggedObjectParser,
+        int tagNo) throws IOException
+    {
+        return parseExplicitBaseTagged(taggedObjectParser, BERTags.CONTEXT_SPECIFIC, tagNo);
+    }
+
+    public static ASN1TaggedObjectParser tryParseExplicitBaseTagged(ASN1TaggedObjectParser taggedObjectParser,
+        int tagClass, int tagNo) throws IOException
+    {
+        if (!taggedObjectParser.hasTag(tagClass, tagNo))
+        {
+            return null;
+        }
+
+        return taggedObjectParser.parseExplicitBaseTagged();
+    }
+
+    public static ASN1TaggedObjectParser tryParseExplicitContextBaseTagged(ASN1TaggedObjectParser taggedObjectParser,
+        int tagNo) throws IOException
+    {
+        return tryParseExplicitBaseTagged(taggedObjectParser, BERTags.CONTEXT_SPECIFIC, tagNo);
+    }
+
+
+    /*
+     * Wrappers for ASN1TaggedObjectParser#parseImplicitBaseTagged
+     */
+
+    public static ASN1TaggedObjectParser parseImplicitBaseTagged(ASN1TaggedObjectParser taggedObjectParser,
+        int tagClass, int tagNo, int baseTagClass, int baseTagNo) throws IOException
+    {
+        return checkTag(taggedObjectParser, tagClass, tagNo).parseImplicitBaseTagged(baseTagClass, baseTagNo);
+    }
+
+    public static ASN1TaggedObjectParser parseImplicitContextBaseTagged(ASN1TaggedObjectParser taggedObjectParser,
+        int tagNo, int baseTagClass, int baseTagNo) throws IOException
+    {
+        return parseImplicitBaseTagged(taggedObjectParser, BERTags.CONTEXT_SPECIFIC, tagNo, baseTagClass, baseTagNo);
+    }
+
+    public static ASN1TaggedObjectParser tryParseImplicitBaseTagged(ASN1TaggedObjectParser taggedObjectParser,
+        int tagClass, int tagNo, int baseTagClass, int baseTagNo) throws IOException
+    {
+        if (!taggedObjectParser.hasTag(tagClass, tagNo))
+        {
+            return null;
+        }
+
+        return taggedObjectParser.parseImplicitBaseTagged(baseTagClass, baseTagNo);
+    }
+
+    public static ASN1TaggedObjectParser tryParseImplicitContextBaseTagged(ASN1TaggedObjectParser taggedObjectParser,
+        int tagNo, int baseTagClass, int baseTagNo) throws IOException
+    {
+        return tryParseImplicitBaseTagged(taggedObjectParser, BERTags.CONTEXT_SPECIFIC, tagNo, baseTagClass, baseTagNo);
+    }
+
+
+    /*
+     * Wrappers for ASN1TaggedObjectParser#parseBaseUniversal
+     */
+
+    public static ASN1Encodable parseBaseUniversal(ASN1TaggedObjectParser taggedObjectParser, int tagClass,
+        int tagNo, boolean declaredExplicit, int baseTagNo) throws IOException
+    {
+        return checkTag(taggedObjectParser, tagClass, tagNo).parseBaseUniversal(declaredExplicit, baseTagNo);
+    }
+
+    public static ASN1Encodable parseContextBaseUniversal(ASN1TaggedObjectParser taggedObjectParser, int tagNo,
+        boolean declaredExplicit, int baseTagNo) throws IOException
+    {
+        return parseBaseUniversal(taggedObjectParser, BERTags.CONTEXT_SPECIFIC, tagNo, declaredExplicit, baseTagNo);
+    }
+
+    public static ASN1Encodable tryParseBaseUniversal(ASN1TaggedObjectParser taggedObjectParser, int tagClass,
+        int tagNo, boolean declaredExplicit, int baseTagNo) throws IOException
+    {
+        if (!taggedObjectParser.hasTag(tagClass, tagNo))
+        {
+            return null;
+        }
+
+        return taggedObjectParser.parseBaseUniversal(declaredExplicit, baseTagNo);
+    }
+
+    public static ASN1Encodable tryParseContextBaseUniversal(ASN1TaggedObjectParser taggedObjectParser, int tagNo,
+        boolean declaredExplicit, int baseTagNo) throws IOException
+    {
+        return tryParseBaseUniversal(taggedObjectParser, BERTags.CONTEXT_SPECIFIC, tagNo, declaredExplicit, baseTagNo);
+    }
+
+
+    /*
+     * Wrappers for ASN1TaggedObjectParser#parseExplicitBaseObject
+     */
+
+    public static ASN1Encodable parseExplicitBaseObject(ASN1TaggedObjectParser taggedObjectParser, int tagClass,
+        int tagNo) throws IOException
+    {
+        return checkTag(taggedObjectParser, tagClass, tagNo).parseExplicitBaseObject();
+    }
+
+    public static ASN1Encodable parseExplicitContextBaseObject(ASN1TaggedObjectParser taggedObjectParser, int tagNo)
+        throws IOException
+    {
+        return parseExplicitBaseObject(taggedObjectParser, BERTags.CONTEXT_SPECIFIC, tagNo);
+    }
+
+    public static ASN1Encodable tryParseExplicitBaseObject(ASN1TaggedObjectParser taggedObjectParser, int tagClass,
+        int tagNo) throws IOException
+    {
+        if (!taggedObjectParser.hasTag(tagClass, tagNo))
+        {
+            return null;
+        }
+
+        return taggedObjectParser.parseExplicitBaseObject();
+    }
+
+    public static ASN1Encodable tryParseExplicitContextBaseObject(ASN1TaggedObjectParser taggedObjectParser, int tagNo)
+        throws IOException
+    {
+        return tryParseExplicitBaseObject(taggedObjectParser, BERTags.CONTEXT_SPECIFIC, tagNo);
+    }
+}
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1VideotexString.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1VideotexString.java
new file mode 100644
index 0000000..52425b4
--- /dev/null
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1VideotexString.java
@@ -0,0 +1,131 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.asn1;
+
+import java.io.IOException;
+
+import com.android.internal.org.bouncycastle.util.Arrays;
+import com.android.internal.org.bouncycastle.util.Strings;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+public abstract class ASN1VideotexString
+    extends ASN1Primitive
+    implements ASN1String
+{
+    static final ASN1UniversalType TYPE = new ASN1UniversalType(ASN1VideotexString.class, BERTags.VIDEOTEX_STRING)
+    {
+        ASN1Primitive fromImplicitPrimitive(DEROctetString octetString)
+        {
+            return createPrimitive(octetString.getOctets());
+        }
+    };
+
+    /**
+     * return a Videotex String from the passed in object
+     *
+     * @param obj an ASN1VideotexString or an object that can be converted into one.
+     * @exception IllegalArgumentException if the object cannot be converted.
+     * @return an ASN1VideotexString instance, or null.
+     */
+    public static ASN1VideotexString getInstance(Object obj)
+    {
+        if (obj == null || obj instanceof ASN1VideotexString)
+        {
+            return (ASN1VideotexString)obj;
+        }
+        if (obj instanceof ASN1Encodable)
+        {
+            ASN1Primitive primitive = ((ASN1Encodable)obj).toASN1Primitive();
+            if (primitive instanceof ASN1VideotexString)
+            {
+                return (ASN1VideotexString)primitive;
+            }
+        }
+        if (obj instanceof byte[])
+        {
+            try
+            {
+                return (ASN1VideotexString)TYPE.fromByteArray((byte[])obj);
+            }
+            catch (Exception e)
+            {
+                throw new IllegalArgumentException("encoding error in getInstance: " + e.toString());
+            }
+        }
+
+        throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
+    }
+
+    /**
+     * return a Videotex String from a tagged object.
+     *
+     * @param taggedObject the tagged object holding the object we want
+     * @param explicit     true if the object is meant to be explicitly tagged false
+     *                     otherwise.
+     * @exception IllegalArgumentException if the tagged object cannot be converted.
+     * @return an ASN1VideotexString instance, or null.
+     */
+    public static ASN1VideotexString getInstance(ASN1TaggedObject taggedObject, boolean explicit)
+    {
+        return (ASN1VideotexString)TYPE.getContextInstance(taggedObject, explicit);
+    }
+
+    final byte[] contents;
+
+    /**
+     * basic constructor - with bytes.
+     * @param string the byte encoding of the characters making up the string.
+     */
+    ASN1VideotexString(byte[] contents, boolean clone)
+    {
+        this.contents = clone ? Arrays.clone(contents) : contents;
+    }
+
+    public final byte[] getOctets()
+    {
+        return Arrays.clone(contents);
+    }
+
+    final boolean encodeConstructed()
+    {
+        return false;
+    }
+
+    final int encodedLength(boolean withTag)
+    {
+        return ASN1OutputStream.getLengthOfEncodingDL(withTag, contents.length);
+    }
+
+    final void encode(ASN1OutputStream out, boolean withTag) throws IOException
+    {
+        out.writeEncodingDL(withTag, BERTags.VIDEOTEX_STRING, contents);
+    }
+
+    final boolean asn1Equals(ASN1Primitive other)
+    {
+        if (!(other instanceof ASN1VideotexString))
+        {
+            return false;
+        }
+
+        ASN1VideotexString that = (ASN1VideotexString)other;
+
+        return Arrays.areEqual(this.contents, that.contents);
+    }
+
+    public final int hashCode()
+    {
+        return Arrays.hashCode(contents);
+    }
+
+    public final String getString()
+    {
+        return Strings.fromByteArray(contents);
+    }
+
+    static ASN1VideotexString createPrimitive(byte[] contents)
+    {
+        return new DERVideotexString(contents, false);
+    }
+}
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1VisibleString.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1VisibleString.java
new file mode 100644
index 0000000..1a70386
--- /dev/null
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ASN1VisibleString.java
@@ -0,0 +1,143 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.asn1;
+
+import java.io.IOException;
+
+import com.android.internal.org.bouncycastle.util.Arrays;
+import com.android.internal.org.bouncycastle.util.Strings;
+
+/**
+ * ASN.1 VisibleString object encoding ISO 646 (ASCII) character code points 32 to 126.
+ * <p>
+ * Explicit character set escape sequences are not allowed.
+ * </p>
+ * @hide This class is not part of the Android public SDK API
+ */
+public abstract class ASN1VisibleString
+    extends ASN1Primitive
+    implements ASN1String
+{
+    static final ASN1UniversalType TYPE = new ASN1UniversalType(ASN1VisibleString.class, BERTags.VISIBLE_STRING)
+    {
+        ASN1Primitive fromImplicitPrimitive(DEROctetString octetString)
+        {
+            return createPrimitive(octetString.getOctets());
+        }
+    };
+
+    /**
+     * Return a Visible String from the passed in object.
+     *
+     * @param obj an ASN1VisibleString or an object that can be converted into one.
+     * @exception IllegalArgumentException if the object cannot be converted.
+     * @return an ASN1VisibleString instance, or null
+     */
+    public static ASN1VisibleString getInstance(
+        Object  obj)
+    {
+        if (obj == null || obj instanceof ASN1VisibleString)
+        {
+            return (ASN1VisibleString)obj;
+        }
+        if (obj instanceof ASN1Encodable)
+        {
+            ASN1Primitive primitive = ((ASN1Encodable)obj).toASN1Primitive();
+            if (primitive instanceof ASN1VisibleString)
+            {
+                return (ASN1VisibleString)primitive;
+            }
+        }
+        if (obj instanceof byte[])
+        {
+            try
+            {
+                return (ASN1VisibleString)TYPE.fromByteArray((byte[])obj);
+            }
+            catch (Exception e)
+            {
+                throw new IllegalArgumentException("encoding error in getInstance: " + e.toString());
+            }
+        }
+
+        throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
+    }
+
+    /**
+     * Return a Visible String from a tagged object.
+     *
+     * @param taggedObject the tagged object holding the object we want
+     * @param explicit true if the object is meant to be explicitly
+     *              tagged false otherwise.
+     * @exception IllegalArgumentException if the tagged object cannot
+     *               be converted.
+     * @return an ASN1VisibleString instance, or null
+     */
+    public static ASN1VisibleString getInstance(ASN1TaggedObject taggedObject, boolean explicit)
+    {
+        return (ASN1VisibleString)TYPE.getContextInstance(taggedObject, explicit);
+    }
+
+    final byte[] contents;
+
+    ASN1VisibleString(String string)
+    {
+        this.contents = Strings.toByteArray(string);
+    }
+
+    ASN1VisibleString(byte[] contents, boolean clone)
+    {
+        this.contents = clone ? Arrays.clone(contents) : contents;
+    }
+
+    public final String getString()
+    {
+        return Strings.fromByteArray(contents);
+    }
+
+    public String toString()
+    {
+        return getString();
+    }
+
+    public final byte[] getOctets()
+    {
+        return Arrays.clone(contents);
+    }
+
+    final boolean encodeConstructed()
+    {
+        return false;
+    }
+
+    final int encodedLength(boolean withTag)
+    {
+        return ASN1OutputStream.getLengthOfEncodingDL(withTag, contents.length);
+    }
+
+    final void encode(ASN1OutputStream out, boolean withTag) throws IOException
+    {
+        out.writeEncodingDL(withTag, BERTags.VISIBLE_STRING, contents);
+    }
+
+    final boolean asn1Equals(ASN1Primitive other)
+    {
+        if (!(other instanceof ASN1VisibleString))
+        {
+            return false;
+        }
+
+        ASN1VisibleString that = (ASN1VisibleString)other;
+
+        return Arrays.areEqual(this.contents, that.contents);
+    }
+
+    public final int hashCode()
+    {
+        return Arrays.hashCode(contents);
+    }
+
+    static ASN1VisibleString createPrimitive(byte[] contents)
+    {
+        return new DERVisibleString(contents, false);
+    }
+}
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/BERApplicationSpecific.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/BERApplicationSpecific.java
deleted file mode 100644
index cb5023a..0000000
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/BERApplicationSpecific.java
+++ /dev/null
@@ -1,112 +0,0 @@
-/* GENERATED SOURCE. DO NOT MODIFY. */
-package com.android.internal.org.bouncycastle.asn1;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-
-/**
- * An indefinite-length encoding version of an ASN.1 ApplicationSpecific object.
- * @hide This class is not part of the Android public SDK API
- */
-public class BERApplicationSpecific
-    extends ASN1ApplicationSpecific
-{
-    BERApplicationSpecific(
-        boolean isConstructed,
-        int tag,
-        byte[] octets)
-    {
-        super(isConstructed, tag, octets);
-    }
-
-    /**
-     * Create an application specific object with a tagging of explicit/constructed.
-     *
-     * @param tag the tag number for this object.
-     * @param object the object to be contained.
-     */
-    public BERApplicationSpecific(
-        int tag,
-        ASN1Encodable object)
-        throws IOException
-    {
-        this(true, tag, object);
-    }
-
-    /**
-     * Create an application specific object with the tagging style given by the value of constructed.
-     *
-     * @param constructed true if the object is constructed.
-     * @param tag the tag number for this object.
-     * @param object the object to be contained.
-     */
-    public BERApplicationSpecific(
-        boolean constructed,
-        int tag,
-        ASN1Encodable object)
-        throws IOException
-    {
-        super(constructed || object.toASN1Primitive().isConstructed(), tag, getEncoding(constructed, object));
-    }
-
-    private static byte[] getEncoding(boolean explicit, ASN1Encodable object)
-        throws IOException
-    {
-        byte[] data = object.toASN1Primitive().getEncoded(ASN1Encoding.BER);
-
-        if (explicit)
-        {
-            return data;
-        }
-        else
-        {
-            int lenBytes = getLengthOfHeader(data);
-            byte[] tmp = new byte[data.length - lenBytes];
-            System.arraycopy(data, lenBytes, tmp, 0, tmp.length);
-            return tmp;
-        }
-    }
-
-    /**
-     * Create an application specific object which is marked as constructed
-     *
-     * @param tagNo the tag number for this object.
-     * @param vec the objects making up the application specific object.
-     */
-    public BERApplicationSpecific(int tagNo, ASN1EncodableVector vec)
-    {
-        super(true, tagNo, getEncodedVector(vec));
-    }
-
-    private static byte[] getEncodedVector(ASN1EncodableVector vec)
-    {
-        ByteArrayOutputStream bOut = new ByteArrayOutputStream();
-
-        for (int i = 0; i != vec.size(); i++)
-        {
-            try
-            {
-                bOut.write(((ASN1Object)vec.get(i)).getEncoded(ASN1Encoding.BER));
-            }
-            catch (IOException e)
-            {
-                throw new ASN1ParsingException("malformed object: " + e, e);
-            }
-        }
-        return bOut.toByteArray();
-    }
-
-    /* (non-Javadoc)
-     * @see org.bouncycastle.asn1.ASN1Primitive#encode(org.bouncycastle.asn1.DEROutputStream)
-     */
-    void encode(ASN1OutputStream out, boolean withTag) throws IOException
-    {
-        int flags = BERTags.APPLICATION;
-        if (isConstructed)
-        {
-            flags |= BERTags.CONSTRUCTED;
-        }
-
-        out.writeEncodedIndef(withTag, flags, tag, octets);
-    }
-}
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/BERApplicationSpecificParser.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/BERApplicationSpecificParser.java
deleted file mode 100644
index a1617b4..0000000
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/BERApplicationSpecificParser.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/* GENERATED SOURCE. DO NOT MODIFY. */
-package com.android.internal.org.bouncycastle.asn1;
-
-import java.io.IOException;
-
-/**
- * A parser for indefinite-length ASN.1 ApplicationSpecific objects.
- * @hide This class is not part of the Android public SDK API
- */
-public class BERApplicationSpecificParser
-    implements ASN1ApplicationSpecificParser
-{
-    private final int tag;
-    private final ASN1StreamParser parser;
-
-    BERApplicationSpecificParser(int tag, ASN1StreamParser parser)
-    {
-        this.tag = tag;
-        this.parser = parser;
-    }
-
-    /**
-     * Return the object contained in this application specific object,
-     * @return the contained object.
-     * @throws IOException if the underlying stream cannot be read, or does not contain an ASN.1 encoding.
-     */
-    public ASN1Encodable readObject()
-        throws IOException
-    {
-        return parser.readObject();
-    }
-
-    /**
-     * Return an in-memory, encodable, representation of the application specific object.
-     *
-     * @return a BERApplicationSpecific.
-     * @throws IOException if there is an issue loading the data.
-     */
-    public ASN1Primitive getLoadedObject()
-        throws IOException
-    {
-         return new BERApplicationSpecific(tag, parser.readVector());
-    }
-
-    /**
-     * Return a BERApplicationSpecific representing this parser and its contents.
-     *
-     * @return a BERApplicationSpecific
-     */
-    public ASN1Primitive toASN1Primitive()
-    {
-        try
-        {
-            return getLoadedObject();
-        }
-        catch (IOException e)
-        {
-            throw new ASN1ParsingException(e.getMessage(), e);
-        }
-    }
-}
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/BERBitString.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/BERBitString.java
new file mode 100644
index 0000000..c25bf0d
--- /dev/null
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/BERBitString.java
@@ -0,0 +1,192 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.asn1;
+
+import java.io.IOException;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+public class BERBitString
+    extends ASN1BitString
+{
+    private static final int DEFAULT_SEGMENT_LIMIT = 1000;
+
+    private final int segmentLimit;
+    private final ASN1BitString[] elements;
+
+    /**
+     * Convert a vector of bit strings into a single bit string
+     */
+    static byte[] flattenBitStrings(ASN1BitString[] bitStrings)
+    {
+        int count = bitStrings.length;
+        switch (count)
+        {
+        case 0:
+            // No bits
+            return new byte[]{ 0 };
+        case 1:
+            return bitStrings[0].contents;
+        default:
+        {
+            int last = count - 1, totalLength = 0;
+            for (int i = 0; i < last; ++i)
+            {
+                byte[] elementContents = bitStrings[i].contents;
+                if (elementContents[0] != 0)
+                {
+                    throw new IllegalArgumentException("only the last nested bitstring can have padding");
+                }
+
+                totalLength += elementContents.length - 1;
+            }
+
+            // Last one can have padding
+            byte[] lastElementContents = bitStrings[last].contents;
+            byte padBits = lastElementContents[0];
+            totalLength += lastElementContents.length;
+
+            byte[] contents = new byte[totalLength];
+            contents[0] = padBits;
+
+            int pos = 1;
+            for (int i = 0; i < count; ++i)
+            {
+                byte[] elementContents = bitStrings[i].contents;
+                int length = elementContents.length - 1;
+                System.arraycopy(elementContents, 1, contents, pos, length);
+                pos += length;
+            }
+
+//            assert pos == totalLength;
+            return contents;
+        }
+        }
+    }
+    
+    public BERBitString(byte[] data)
+    {
+        this(data, 0);
+    }
+
+    public BERBitString(byte data, int padBits)
+    {
+        super(data, padBits);
+        this.elements = null;
+        this.segmentLimit = DEFAULT_SEGMENT_LIMIT;
+    }
+
+    public BERBitString(byte[] data, int padBits)
+    {
+        this(data, padBits, DEFAULT_SEGMENT_LIMIT);
+    }
+
+    public BERBitString(byte[] data, int padBits, int segmentLimit)
+    {
+        super(data, padBits);
+        this.elements = null;
+        this.segmentLimit = segmentLimit;
+    }
+
+    public BERBitString(ASN1Encodable obj) throws IOException
+    {
+        this(obj.toASN1Primitive().getEncoded(ASN1Encoding.DER), 0);
+    }
+
+    public BERBitString(ASN1BitString[] elements)
+    {
+        this(elements, DEFAULT_SEGMENT_LIMIT);
+    }
+
+    public BERBitString(ASN1BitString[] elements, int segmentLimit)
+    {
+        super(flattenBitStrings(elements), false);
+        this.elements = elements;
+        this.segmentLimit = segmentLimit;
+    }    
+
+    BERBitString(byte[] contents, boolean check)
+    {
+        super(contents, check);
+        this.elements = null;
+        this.segmentLimit = DEFAULT_SEGMENT_LIMIT;
+    }
+
+    boolean encodeConstructed()
+    {
+        return null != elements || contents.length > segmentLimit;
+    }
+
+    int encodedLength(boolean withTag)
+        throws IOException
+    {
+        if (!encodeConstructed())
+        {
+            return DLBitString.encodedLength(withTag, contents.length);
+        }
+
+        int totalLength = withTag ? 4 : 3;
+
+        if (null != elements)
+        {
+            for (int i = 0; i < elements.length; ++i)
+            {
+                totalLength += elements[i].encodedLength(true);
+            }
+        }
+        else if (contents.length < 2)
+        {
+            // No bits
+        }
+        else
+        {
+            int extraSegments = (contents.length - 2) / (segmentLimit - 1);
+            totalLength += extraSegments * DLBitString.encodedLength(true, segmentLimit);
+
+            int lastSegmentLength = contents.length - (extraSegments * (segmentLimit - 1));
+            totalLength += DLBitString.encodedLength(true, lastSegmentLength);
+        }
+
+        return totalLength;
+    }
+
+    void encode(ASN1OutputStream out, boolean withTag) throws IOException
+    {
+        if (!encodeConstructed())
+        {
+            DLBitString.encode(out, withTag, contents, 0, contents.length);
+            return;
+        }
+
+        out.writeIdentifier(withTag, BERTags.CONSTRUCTED | BERTags.BIT_STRING);
+        out.write(0x80);
+
+        if (null != elements)
+        {
+            out.writePrimitives(elements);
+        }
+        else if (contents.length < 2)
+        {
+            // No bits
+        }
+        else
+        {
+            byte pad = contents[0];
+            int length = contents.length;
+            int remaining = length - 1;
+            int segmentLength = segmentLimit - 1;
+
+            while (remaining > segmentLength)
+            {
+                DLBitString.encode(out, true, (byte)0, contents, length - remaining, segmentLength);
+                remaining -= segmentLength;
+            }
+
+            DLBitString.encode(out, true, pad, contents, length - remaining, remaining);
+        }
+
+        out.write(0x00);
+        out.write(0x00);
+    }
+}
+
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/BERBitStringParser.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/BERBitStringParser.java
new file mode 100644
index 0000000..c410260
--- /dev/null
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/BERBitStringParser.java
@@ -0,0 +1,68 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.asn1;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import com.android.internal.org.bouncycastle.util.io.Streams;
+
+/**
+ * A parser for indefinite-length BIT STRINGs.
+ * 
+ * @deprecated Check for 'ASN1BitStringParser' instead 
+ * @hide This class is not part of the Android public SDK API
+ */
+public class BERBitStringParser
+    implements ASN1BitStringParser
+{
+    private ASN1StreamParser _parser;
+
+    private ConstructedBitStream _bitStream;
+
+    BERBitStringParser(
+        ASN1StreamParser parser)
+    {
+        _parser = parser;
+    }
+
+    public InputStream getOctetStream() throws IOException
+    {
+        return _bitStream = new ConstructedBitStream(_parser, true);
+    }
+
+    public InputStream getBitStream() throws IOException
+    {
+        return _bitStream = new ConstructedBitStream(_parser, false);
+    }
+
+    public int getPadBits()
+    {
+        return _bitStream.getPadBits();
+    }
+
+    public ASN1Primitive getLoadedObject()
+        throws IOException
+    {
+        return parse(_parser);
+    }
+
+    public ASN1Primitive toASN1Primitive()
+    {
+        try
+        {
+            return getLoadedObject();
+        }
+        catch (IOException e)
+        {
+            throw new ASN1ParsingException("IOException converting stream to byte array: " + e.getMessage(), e);
+        }
+    }
+
+    static BERBitString parse(ASN1StreamParser sp) throws IOException
+    {
+        ConstructedBitStream bitStream = new ConstructedBitStream(sp, false);
+        byte[] data = Streams.readAll(bitStream);
+        int padBits = bitStream.getPadBits();
+        return new BERBitString(data, padBits);
+    }
+}
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/BERConstructedOctetString.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/BERConstructedOctetString.java
deleted file mode 100644
index 6a67881..0000000
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/BERConstructedOctetString.java
+++ /dev/null
@@ -1,146 +0,0 @@
-/* GENERATED SOURCE. DO NOT MODIFY. */
-package com.android.internal.org.bouncycastle.asn1;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.util.Enumeration;
-import java.util.Vector;
-
-/**
- * @deprecated use BEROctetString
- * @hide This class is not part of the Android public SDK API
- */
-public class BERConstructedOctetString
-    extends BEROctetString
-{
-    private static final int MAX_LENGTH = 1000;
-
-    /**
-     * convert a vector of octet strings into a single byte string
-     */
-    static private byte[] toBytes(
-        Vector  octs)
-    {
-        ByteArrayOutputStream   bOut = new ByteArrayOutputStream();
-
-        for (int i = 0; i != octs.size(); i++)
-        {
-            try
-            {
-                DEROctetString  o = (DEROctetString)octs.elementAt(i);
-
-                bOut.write(o.getOctets());
-            }
-            catch (ClassCastException e)
-            {
-                throw new IllegalArgumentException(octs.elementAt(i).getClass().getName() + " found in input should only contain DEROctetString");
-            }
-            catch (IOException e)
-            {
-                throw new IllegalArgumentException("exception converting octets " + e.toString());
-            }
-        }
-
-        return bOut.toByteArray();
-    }
-
-    private Vector  octs;
-
-    /**
-     * @param string the octets making up the octet string.
-     */
-    public BERConstructedOctetString(
-        byte[]  string)
-    {
-        super(string);
-    }
-
-    public BERConstructedOctetString(
-        Vector  octs)
-    {
-        super(toBytes(octs));
-
-        this.octs = octs;
-    }
-
-    public BERConstructedOctetString(
-        ASN1Primitive  obj)
-    {
-        super(toByteArray(obj));
-    }
-
-    private static byte[] toByteArray(ASN1Primitive obj)
-    {
-        try
-        {
-            return obj.getEncoded();
-        }
-        catch (IOException e)
-        {
-            throw new IllegalArgumentException("Unable to encode object");
-        }
-    }
-
-    public BERConstructedOctetString(
-        ASN1Encodable  obj)
-    {
-        this(obj.toASN1Primitive());
-    }
-
-    public byte[] getOctets()
-    {
-        return string;
-    }
-
-    /**
-     * return the DER octets that make up this string.
-     */
-    public Enumeration getObjects()
-    {
-        if (octs == null)
-        {
-            return generateOcts().elements();
-        }
-
-        return octs.elements();
-    }
-
-    private Vector generateOcts() 
-    { 
-        Vector vec = new Vector(); 
-        for (int i = 0; i < string.length; i += MAX_LENGTH) 
-        { 
-            int end; 
-
-            if (i + MAX_LENGTH > string.length) 
-            { 
-                end = string.length; 
-            } 
-            else 
-            { 
-                end = i + MAX_LENGTH; 
-            } 
-
-            byte[] nStr = new byte[end - i]; 
-
-            System.arraycopy(string, i, nStr, 0, nStr.length); 
-
-            vec.addElement(new DEROctetString(nStr)); 
-         } 
-        
-         return vec; 
-    }
-
-    public static BEROctetString fromSequence(ASN1Sequence seq)
-    {
-        Vector      v = new Vector();
-        Enumeration e = seq.getObjects();
-
-        while (e.hasMoreElements())
-        {
-            v.addElement(e.nextElement());
-        }
-
-        return new BERConstructedOctetString(v);
-    }
-}
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/BERGenerator.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/BERGenerator.java
index fa9f0df..2528b43 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/BERGenerator.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/BERGenerator.java
@@ -8,7 +8,7 @@
  * Base class for generators for indefinite-length structures.
  * @hide This class is not part of the Android public SDK API
  */
-public class BERGenerator
+public abstract class BERGenerator
     extends ASN1Generator
 {
     private boolean _tagged = false;
@@ -45,7 +45,7 @@
     {
         if (_tagged)
         {
-            int tagNum = _tagNo | BERTags.TAGGED;
+            int tagNum = _tagNo | BERTags.CONTEXT_SPECIFIC;
 
             if (_isExplicit)
             {
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/BEROctetString.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/BEROctetString.java
index 8710d81..cac742c 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/BEROctetString.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/BEROctetString.java
@@ -1,10 +1,7 @@
 /* GENERATED SOURCE. DO NOT MODIFY. */
 package com.android.internal.org.bouncycastle.asn1;
 
-import java.io.ByteArrayOutputStream;
 import java.io.IOException;
-import java.util.Enumeration;
-import java.util.NoSuchElementException;
 
 /**
  * ASN.1 OctetStrings, with indefinite length rules, and <i>constructed form</i> support.
@@ -24,172 +21,148 @@
 public class BEROctetString
     extends ASN1OctetString
 {
-    private static final int DEFAULT_CHUNK_SIZE = 1000;
+    private static final int DEFAULT_SEGMENT_LIMIT = 1000;
 
-    private final int chunkSize;
-    private final ASN1OctetString[] octs;
+    private final int segmentLimit;
+    private final ASN1OctetString[] elements;
 
     /**
      * Convert a vector of octet strings into a single byte string
      */
-    static private byte[] toBytes(
-        ASN1OctetString[]  octs)
+    static byte[] flattenOctetStrings(ASN1OctetString[] octetStrings)
     {
-        ByteArrayOutputStream bOut = new ByteArrayOutputStream();
-
-        for (int i = 0; i != octs.length; i++)
+        int count = octetStrings.length;
+        switch (count)
         {
-            try
+        case 0:
+            return EMPTY_OCTETS;
+        case 1:
+            return octetStrings[0].string;
+        default:
+        {
+            int totalOctets = 0;
+            for (int i = 0; i < count; ++i)
             {
-                bOut.write(octs[i].getOctets());
+                totalOctets += octetStrings[i].string.length;
             }
-            catch (IOException e)
+
+            byte[] string = new byte[totalOctets];
+            for (int i = 0, pos = 0; i < count; ++i)
             {
-                throw new IllegalArgumentException("exception converting octets " + e.toString());
+                byte[] octets = octetStrings[i].string;
+                System.arraycopy(octets, 0, string, pos, octets.length);
+                pos += octets.length;
             }
+
+//            assert pos == totalOctets;
+            return string;
         }
-
-        return bOut.toByteArray();
+        }
     }
 
     /**
      * Create an OCTET-STRING object from a byte[]
      * @param string the octets making up the octet string.
      */
-    public BEROctetString(
-        byte[] string)
+    public BEROctetString(byte[] string)
     {
-        this(string, DEFAULT_CHUNK_SIZE);
+        this(string, DEFAULT_SEGMENT_LIMIT);
     }
 
     /**
      * Multiple {@link ASN1OctetString} data blocks are input,
      * the result is <i>constructed form</i>.
      *
-     * @param octs an array of OCTET STRING to construct the BER OCTET STRING from.
+     * @param elements an array of OCTET STRING to construct the BER OCTET STRING from.
      */
-    public BEROctetString(
-        ASN1OctetString[] octs)
+    public BEROctetString(ASN1OctetString[] elements)
     {
-        this(octs, DEFAULT_CHUNK_SIZE);
+        this(elements, DEFAULT_SEGMENT_LIMIT);
     }
 
     /**
      * Create an OCTET-STRING object from a byte[]
      * @param string the octets making up the octet string.
-     * @param chunkSize the number of octets stored in each DER encoded component OCTET STRING.
+     * @param segmentLimit the number of octets stored in each DER encoded component OCTET STRING.
      */
-    public BEROctetString(
-        byte[] string,
-        int    chunkSize)
+    public BEROctetString(byte[] string, int segmentLimit)
     {
-        this(string, null, chunkSize);
+        this(string, null, segmentLimit);
     }
 
     /**
      * Multiple {@link ASN1OctetString} data blocks are input,
      * the result is <i>constructed form</i>.
      *
-     * @param octs an array of OCTET STRING to construct the BER OCTET STRING from.
-     * @param chunkSize the number of octets stored in each DER encoded component OCTET STRING.
+     * @param elements an array of OCTET STRING to construct the BER OCTET STRING from.
+     * @param segmentLimit the number of octets stored in each DER encoded component OCTET STRING.
      */
-    public BEROctetString(
-        ASN1OctetString[] octs,
-        int chunkSize)
+    public BEROctetString(ASN1OctetString[] elements, int segmentLimit)
     {
-        this(toBytes(octs), octs, chunkSize);
+        this(flattenOctetStrings(elements), elements, segmentLimit);
     }
 
-    private BEROctetString(byte[] string, ASN1OctetString[] octs, int chunkSize)
+    private BEROctetString(byte[] string, ASN1OctetString[] elements, int segmentLimit)
     {
         super(string);
-        this.octs = octs;
-        this.chunkSize = chunkSize;
+        this.elements = elements;
+        this.segmentLimit = segmentLimit;
     }
 
-    /**
-     * Return the OCTET STRINGs that make up this string.
-     *
-     * @return an Enumeration of the component OCTET STRINGs.
-     */
-    public Enumeration getObjects()
-    {
-        if (octs == null)
-        {
-            return new Enumeration()
-            {
-                int pos = 0;
-
-                public boolean hasMoreElements()
-                {
-                    return pos < string.length;
-                }
-
-                public Object nextElement()
-                {
-                    if (pos < string.length)
-                    {
-                        int length = Math.min(string.length - pos, chunkSize);
-                        byte[] chunk = new byte[length];
-                        System.arraycopy(string, pos, chunk, 0, length);
-                        pos += length;
-                        return new DEROctetString(chunk);
-                    }
-                    throw new NoSuchElementException();
-                }
-            };
-        }
-
-        return new Enumeration()
-        {
-            int counter = 0;
-
-            public boolean hasMoreElements()
-            {
-                return counter < octs.length;
-            }
-
-            public Object nextElement()
-            {
-                if (counter < octs.length)
-                {
-                    return octs[counter++];
-                }
-                throw new NoSuchElementException();
-            }
-        };
-    }
-
-    boolean isConstructed()
+    boolean encodeConstructed()
     {
         return true;
     }
 
-    int encodedLength()
+    int encodedLength(boolean withTag)
         throws IOException
     {
-        int length = 0;
-        for (Enumeration e = getObjects(); e.hasMoreElements();)
+        int totalLength = withTag ? 4 : 3;
+
+        if (null != elements)
         {
-            length += ((ASN1Encodable)e.nextElement()).toASN1Primitive().encodedLength();
+            for (int i = 0; i < elements.length; ++i)
+            {
+                totalLength += elements[i].encodedLength(true);
+            }
+        }
+        else
+        {
+            int fullSegments = string.length / segmentLimit;
+            totalLength += fullSegments * DEROctetString.encodedLength(true, segmentLimit);
+
+            int lastSegmentLength = string.length - (fullSegments * segmentLimit);
+            if (lastSegmentLength > 0)
+            {
+                totalLength += DEROctetString.encodedLength(true, lastSegmentLength);
+            }
         }
 
-        return 2 + length + 2;
+        return totalLength;
     }
 
     void encode(ASN1OutputStream out, boolean withTag) throws IOException
     {
-        out.writeEncodedIndef(withTag, BERTags.CONSTRUCTED | BERTags.OCTET_STRING,  getObjects());
-    }
+        out.writeIdentifier(withTag, BERTags.CONSTRUCTED | BERTags.OCTET_STRING);
+        out.write(0x80);
 
-    static BEROctetString fromSequence(ASN1Sequence seq)
-    {
-        int count = seq.size();
-        ASN1OctetString[] v = new ASN1OctetString[count];
-        for (int i = 0; i < count; ++i)
+        if (null != elements)
         {
-            v[i] = ASN1OctetString.getInstance(seq.getObjectAt(i));
+            out.writePrimitives(elements);
         }
-        return new BEROctetString(v);
+        else
+        {
+            int pos = 0;
+            while (pos < string.length)
+            {
+                int segmentLength = Math.min(string.length - pos, segmentLimit);
+                DEROctetString.encode(out, true, string, pos, segmentLength);
+                pos += segmentLength;
+            }
+        }
+
+        out.write(0x00);
+        out.write(0x00);
     }
 }
+
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/BEROctetStringGenerator.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/BEROctetStringGenerator.java
index 877563d..7ff6cab 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/BEROctetStringGenerator.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/BEROctetStringGenerator.java
@@ -98,23 +98,32 @@
 
         public void write(byte[] b, int off, int len) throws IOException
         {
-            while (len > 0)
+            int bufLen = _buf.length;
+            int available = bufLen - _off;
+            if (len < available)
             {
-                int numToCopy = Math.min(len, _buf.length - _off);
-                System.arraycopy(b, off, _buf, _off, numToCopy);
-
-                _off += numToCopy;
-                if (_off < _buf.length)
-                {
-                    break;
-                }
-
-                DEROctetString.encode(_derOut, true, _buf, 0, _buf.length);
-                _off = 0;
-
-                off += numToCopy;
-                len -= numToCopy;
+                System.arraycopy(b, off, _buf, _off, len);
+                _off += len;
+                return;
             }
+
+            int count = 0;
+            if (_off > 0)
+            {
+                System.arraycopy(b, off, _buf, _off, available);
+                count += available;
+                DEROctetString.encode(_derOut, true, _buf, 0, bufLen);
+            }
+
+            int remaining;
+            while ((remaining = (len - count)) >= bufLen)
+            {
+                DEROctetString.encode(_derOut, true, b, off + count, bufLen);
+                count += bufLen;
+            }
+
+            System.arraycopy(b, off + count, _buf, 0, remaining);
+            this._off = remaining;
         }
 
         public void close()
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/BEROctetStringParser.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/BEROctetStringParser.java
index 64ce07d..10d6bad 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/BEROctetStringParser.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/BEROctetStringParser.java
@@ -8,6 +8,8 @@
 
 /**
  * A parser for indefinite-length OCTET STRINGs.
+ * 
+ * @deprecated Check for 'ASN1OctetStringParser' instead 
  * @hide This class is not part of the Android public SDK API
  */
 public class BEROctetStringParser
@@ -40,7 +42,7 @@
     public ASN1Primitive getLoadedObject()
         throws IOException
     {
-        return new BEROctetString(Streams.readAll(getOctetStream()));
+        return parse(_parser);
     }
 
     /**
@@ -59,4 +61,9 @@
             throw new ASN1ParsingException("IOException converting stream to byte array: " + e.getMessage(), e);
         }
     }
+
+    static BEROctetString parse(ASN1StreamParser sp) throws IOException
+    {
+        return new BEROctetString(Streams.readAll(new ConstructedOctetStream(sp)));
+    }
 }
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/BERSequence.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/BERSequence.java
index e3770e4..ef999b3 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/BERSequence.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/BERSequence.java
@@ -46,22 +46,42 @@
         super(elements);
     }
 
-    int encodedLength() throws IOException
+    int encodedLength(boolean withTag) throws IOException
     {
-        int count = elements.length;
-        int totalLength = 0;
+        int totalLength = withTag ? 4 : 3;
 
-        for (int i = 0; i < count; ++i)
+        for (int i = 0, count = elements.length; i < count; ++i)
         {
             ASN1Primitive p = elements[i].toASN1Primitive();
-            totalLength += p.encodedLength();
+            totalLength += p.encodedLength(true);
         }
 
-        return 2 + totalLength + 2;
+        return totalLength;
     }
 
     void encode(ASN1OutputStream out, boolean withTag) throws IOException
     {
-        out.writeEncodedIndef(withTag, BERTags.SEQUENCE | BERTags.CONSTRUCTED, elements);
+        out.writeEncodingIL(withTag, BERTags.CONSTRUCTED | BERTags.SEQUENCE, elements);
+    }
+
+    ASN1BitString toASN1BitString()
+    {
+        return new BERBitString(getConstructedBitStrings());
+    }
+
+    ASN1External toASN1External()
+    {
+        // TODO There is currently no BERExternal class
+        return ((ASN1Sequence)toDLObject()).toASN1External();
+    }
+
+    ASN1OctetString toASN1OctetString()
+    {
+        return new BEROctetString(getConstructedOctetStrings());
+    }
+
+    ASN1Set toASN1Set()
+    {
+        return new BERSet(false, toArrayInternal());
     }
 }
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/BERSequenceParser.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/BERSequenceParser.java
index d2ce184..3a242fa 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/BERSequenceParser.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/BERSequenceParser.java
@@ -5,6 +5,8 @@
 
 /**
  * Parser for indefinite-length SEQUENCEs.
+ * 
+ * @deprecated Check for 'ASN1SequenceParser' instead 
  * @hide This class is not part of the Android public SDK API
  */
 public class BERSequenceParser
@@ -38,7 +40,7 @@
     public ASN1Primitive getLoadedObject()
         throws IOException
     {
-        return new BERSequence(_parser.readVector());
+        return parse(_parser);
     }
 
     /**
@@ -57,4 +59,9 @@
             throw new IllegalStateException(e.getMessage());
         }
     }
+
+    static BERSequence parse(ASN1StreamParser sp) throws IOException
+    {
+        return new BERSequence(sp.readVector());
+    }
 }
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/BERSet.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/BERSet.java
index fbc6fd1..e955ae6 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/BERSet.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/BERSet.java
@@ -61,22 +61,21 @@
         super(isSorted, elements);
     }
 
-    int encodedLength() throws IOException
+    int encodedLength(boolean withTag) throws IOException
     {
-        int count = elements.length;
-        int totalLength = 0;
+        int totalLength = withTag ? 4 : 3;
 
-        for (int i = 0; i < count; ++i)
+        for (int i = 0, count = elements.length; i < count; ++i)
         {
             ASN1Primitive p = elements[i].toASN1Primitive();
-            totalLength += p.encodedLength();
+            totalLength += p.encodedLength(true);
         }
 
-        return 2 + totalLength + 2;
+        return totalLength;
     }
 
     void encode(ASN1OutputStream out, boolean withTag) throws IOException
     {
-        out.writeEncodedIndef(withTag, BERTags.SET | BERTags.CONSTRUCTED, elements);
+        out.writeEncodingIL(withTag, BERTags.CONSTRUCTED | BERTags.SET, elements);
     }
 }
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/BERSetParser.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/BERSetParser.java
index e66b756..4f04d58 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/BERSetParser.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/BERSetParser.java
@@ -5,6 +5,8 @@
 
 /**
  * Parser for indefinite-length SETs.
+ * 
+ * @deprecated Check for 'ASN1SetParser' instead 
  * @hide This class is not part of the Android public SDK API
  */
 public class BERSetParser
@@ -38,7 +40,7 @@
     public ASN1Primitive getLoadedObject()
         throws IOException
     {
-        return new BERSet(_parser.readVector());
+        return parse(_parser);
     }
 
     /**
@@ -57,4 +59,9 @@
             throw new ASN1ParsingException(e.getMessage(), e);
         }
     }
-}
\ No newline at end of file
+
+    static BERSet parse(ASN1StreamParser sp) throws IOException
+    {
+        return new BERSet(sp.readVector());
+    }
+}
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/BERTaggedObject.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/BERTaggedObject.java
index 3c301b7..2911664 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/BERTaggedObject.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/BERTaggedObject.java
@@ -2,7 +2,6 @@
 package com.android.internal.org.bouncycastle.asn1;
 
 import java.io.IOException;
-import java.util.Enumeration;
 
 /**
  * BER TaggedObject - in ASN.1 notation this is any object preceded by
@@ -17,124 +16,96 @@
      * @param tagNo the tag number for this object.
      * @param obj the tagged object.
      */
-    public BERTaggedObject(
-        int             tagNo,
-        ASN1Encodable    obj)
+    public BERTaggedObject(int tagNo, ASN1Encodable obj)
     {
         super(true, tagNo, obj);
     }
 
+    public BERTaggedObject(int tagClass, int tagNo, ASN1Encodable obj)
+    {
+        super(true, tagClass, tagNo, obj);
+    }
+
     /**
      * @param explicit true if an explicitly tagged object.
      * @param tagNo the tag number for this object.
      * @param obj the tagged object.
      */
-    public BERTaggedObject(
-        boolean         explicit,
-        int             tagNo,
-        ASN1Encodable    obj)
+    public BERTaggedObject(boolean explicit, int tagNo, ASN1Encodable obj)
     {
         super(explicit, tagNo, obj);
     }
 
-    /**
-     * create an implicitly tagged object that contains a zero
-     * length sequence.
-     */
-    public BERTaggedObject(
-        int             tagNo)
+    public BERTaggedObject(boolean explicit, int tagClass, int tagNo, ASN1Encodable obj)
     {
-        super(false, tagNo, new BERSequence());
+        super(explicit, tagClass, tagNo, obj);
     }
 
-    boolean isConstructed()
+    BERTaggedObject(int explicitness, int tagClass, int tagNo, ASN1Encodable obj)
     {
-        return explicit || obj.toASN1Primitive().isConstructed();
+        super(explicitness, tagClass, tagNo, obj);
     }
 
-    int encodedLength()
-        throws IOException
+    boolean encodeConstructed()
+    {
+        return isExplicit() || obj.toASN1Primitive().encodeConstructed();
+    }
+
+    int encodedLength(boolean withTag) throws IOException
     {
         ASN1Primitive primitive = obj.toASN1Primitive();
-        int length = primitive.encodedLength();
+        boolean explicit = isExplicit();
+
+        int length = primitive.encodedLength(explicit);
 
         if (explicit)
         {
-            return StreamUtil.calculateTagLength(tagNo) + StreamUtil.calculateBodyLength(length) + length;
+            length += 3;
         }
-        else
-        {
-            // header length already in calculation
-            length = length - 1;
 
-            return StreamUtil.calculateTagLength(tagNo) + length;
-        }
+        length += withTag ? ASN1OutputStream.getLengthOfIdentifier(tagNo) : 0;
+
+        return length;
     }
 
     void encode(ASN1OutputStream out, boolean withTag) throws IOException
     {
-        out.writeTag(withTag, BERTags.CONSTRUCTED | BERTags.TAGGED, tagNo);
-        out.write(0x80);
+//        assert out.getClass().isAssignableFrom(ASN1OutputStream.class);
 
-        if (!explicit)
+        ASN1Primitive primitive = obj.toASN1Primitive();
+        boolean explicit = isExplicit();
+
+        if (withTag)
         {
-            Enumeration e;
-            if (obj instanceof ASN1OctetString)
+            int flags = tagClass;
+            if (explicit || primitive.encodeConstructed())
             {
-                if (obj instanceof BEROctetString)
-                {
-                    e = ((BEROctetString)obj).getObjects();
-                }
-                else
-                {
-                    ASN1OctetString octs = (ASN1OctetString)obj;
-                    BEROctetString berO = new BEROctetString(octs.getOctets());
-                    e = berO.getObjects();
-                }
-            }
-            else if (obj instanceof ASN1Sequence)
-            {
-                e = ((ASN1Sequence)obj).getObjects();
-            }
-            else if (obj instanceof ASN1Set)
-            {
-                e = ((ASN1Set)obj).getObjects();
-            }
-            else
-            {
-                throw new ASN1Exception("not implemented: " + obj.getClass().getName());
+                flags |= BERTags.CONSTRUCTED;
             }
 
-            out.writeElements(e);
+            out.writeIdentifier(true, flags, tagNo);
+        }
+
+        if (explicit)
+        {
+            out.write(0x80);
+            primitive.encode(out, true);
+            out.write(0x00);
+            out.write(0x00);
         }
         else
         {
-            out.writePrimitive(obj.toASN1Primitive(), true);
+            primitive.encode(out, false);
         }
+    }
 
-        out.write(0x00);
-        out.write(0x00);
+    ASN1Sequence rebuildConstructed(ASN1Primitive primitive)
+    {
+        return new BERSequence(primitive);
+    }
 
-//        ASN1Primitive primitive = obj.toASN1Primitive();
-//
-//        int flags = BERTags.TAGGED;
-//        if (explicit || primitive.isConstructed())
-//        {
-//            flags |= BERTags.CONSTRUCTED;
-//        }
-//
-//        out.writeTag(withTag, flags, tagNo);
-//
-//        if (explicit)
-//        {
-//            out.write(0x80);
-//            out.writePrimitive(obj.toASN1Primitive(), true);
-//            out.write(0x00);
-//            out.write(0x00);
-//        }
-//        else
-//        {
-//            out.writePrimitive(obj.toASN1Primitive(), false);
-//        }
+    ASN1TaggedObject replaceTag(int tagClass, int tagNo)
+    {
+        return new BERTaggedObject(explicitness, tagClass, tagNo, obj);
     }
 }
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/BERTaggedObjectParser.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/BERTaggedObjectParser.java
index 0b8ddfd..64b3313 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/BERTaggedObjectParser.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/BERTaggedObjectParser.java
@@ -5,68 +5,49 @@
 
 /**
  * Parser for indefinite-length tagged objects.
- * @hide This class is not part of the Android public SDK API
  */
-public class BERTaggedObjectParser
+class BERTaggedObjectParser
     implements ASN1TaggedObjectParser
 {
-    private boolean _constructed;
-    private int _tagNumber;
-    private ASN1StreamParser _parser;
+    final int _tagClass;
+    final int _tagNo;
+    final ASN1StreamParser _parser;
 
-    BERTaggedObjectParser(
-        boolean             constructed,
-        int                 tagNumber,
-        ASN1StreamParser    parser)
+    BERTaggedObjectParser(int tagClass, int tagNo, ASN1StreamParser parser)
     {
-        _constructed = constructed;
-        _tagNumber = tagNumber;
+        _tagClass = tagClass;
+        _tagNo = tagNo;
         _parser = parser;
     }
 
-    /**
-     * Return true if this tagged object is marked as constructed.
-     *
-     * @return true if constructed, false otherwise.
-     */
-    public boolean isConstructed()
+    public int getTagClass()
     {
-        return _constructed;
+        return _tagClass;
     }
 
-    /**
-     * Return the tag number associated with this object.
-     *
-     * @return the tag number.
-     */
     public int getTagNo()
     {
-        return _tagNumber;
+        return _tagNo;
     }
 
-    /**
-     * Return an object parser for the contents of this tagged object.
-     *
-     * @param tag the actual tag number of the object (needed if implicit).
-     * @param isExplicit true if the contained object was explicitly tagged, false if implicit.
-     * @return an ASN.1 encodable object parser.
-     * @throws IOException if there is an issue building the object parser from the stream.
-     */
-    public ASN1Encodable getObjectParser(
-        int     tag,
-        boolean isExplicit)
-        throws IOException
+    public boolean hasContextTag()
     {
-        if (isExplicit)
-        {
-            if (!_constructed)
-            {
-                throw new IOException("Explicit tags must be constructed (see X.690 8.14.2)");
-            }
-            return _parser.readObject();
-        }
+        return this._tagClass == BERTags.CONTEXT_SPECIFIC;
+    }
 
-        return _parser.readImplicit(_constructed, tag);
+    public boolean hasContextTag(int tagNo)
+    {
+        return this._tagClass == BERTags.CONTEXT_SPECIFIC && this._tagNo == tagNo;
+    }
+
+    public boolean hasTag(int tagClass, int tagNo)
+    {
+        return this._tagClass == tagClass && this._tagNo == tagNo;
+    }
+
+    public boolean hasTagClass(int tagClass)
+    {
+        return this._tagClass == tagClass;
     }
 
     /**
@@ -78,7 +59,32 @@
     public ASN1Primitive getLoadedObject()
         throws IOException
     {
-        return _parser.readTaggedObject(_constructed, _tagNumber);
+        return _parser.loadTaggedIL(_tagClass, _tagNo);
+    }
+
+    public ASN1Encodable parseBaseUniversal(boolean declaredExplicit, int baseTagNo) throws IOException
+    {
+        if (declaredExplicit)
+        {
+            return _parser.parseObject(baseTagNo);
+        }
+
+        return _parser.parseImplicitConstructedIL(baseTagNo);
+    }
+
+    public ASN1Encodable parseExplicitBaseObject() throws IOException
+    {
+        return _parser.readObject();
+    }
+
+    public ASN1TaggedObjectParser parseExplicitBaseTagged() throws IOException
+    {
+        return _parser.parseTaggedObject();
+    }
+
+    public ASN1TaggedObjectParser parseImplicitBaseTagged(int baseTagClass, int baseTagNo) throws IOException
+    {
+        return new BERTaggedObjectParser(baseTagClass, baseTagNo, _parser);
     }
 
     /**
@@ -90,7 +96,7 @@
     {
         try
         {
-            return this.getLoadedObject();
+            return getLoadedObject();
         }
         catch (IOException e)
         {
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/BERTags.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/BERTags.java
index 87649f6..3c81b2a 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/BERTags.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/BERTags.java
@@ -6,20 +6,26 @@
  */
 public interface BERTags
 {
+    // 0x00: Reserved for use by the encoding rules
     public static final int BOOLEAN             = 0x01;
     public static final int INTEGER             = 0x02;
     public static final int BIT_STRING          = 0x03;
     public static final int OCTET_STRING        = 0x04;
     public static final int NULL                = 0x05;
     public static final int OBJECT_IDENTIFIER   = 0x06;
+    public static final int OBJECT_DESCRIPTOR   = 0x07;
     public static final int EXTERNAL            = 0x08;
+    public static final int REAL                = 0x09;
     public static final int ENUMERATED          = 0x0a; // decimal 10
+    public static final int EMBEDDED_PDV        = 0x0b; // decimal 11
+    public static final int UTF8_STRING         = 0x0c; // decimal 12
+    public static final int RELATIVE_OID        = 0x0d; // decimal 13
+    public static final int TIME                = 0x0e;
+    // 0x0f: Reserved for future editions of this Recommendation | International Standard
     public static final int SEQUENCE            = 0x10; // decimal 16
     public static final int SEQUENCE_OF         = 0x10; // for completeness - used to model a SEQUENCE of the same type.
     public static final int SET                 = 0x11; // decimal 17
     public static final int SET_OF              = 0x11; // for completeness - used to model a SET of the same type.
-
-
     public static final int NUMERIC_STRING      = 0x12; // decimal 18
     public static final int PRINTABLE_STRING    = 0x13; // decimal 19
     public static final int T61_STRING          = 0x14; // decimal 20
@@ -31,10 +37,23 @@
     public static final int VISIBLE_STRING      = 0x1a; // decimal 26
     public static final int GENERAL_STRING      = 0x1b; // decimal 27
     public static final int UNIVERSAL_STRING    = 0x1c; // decimal 28
+    public static final int UNRESTRICTED_STRING = 0x1d; // decimal 29
     public static final int BMP_STRING          = 0x1e; // decimal 30
-    public static final int UTF8_STRING         = 0x0c; // decimal 12
-    
+    public static final int DATE                = 0x1f;
+    public static final int TIME_OF_DAY         = 0x20;
+    public static final int DATE_TIME           = 0x21;
+    public static final int DURATION            = 0x22;
+    public static final int OBJECT_IDENTIFIER_IRI = 0x23;
+    public static final int RELATIVE_OID_IRI    = 0x24;
+    // 0x25..: Reserved for addenda to this Recommendation | International Standard
+
     public static final int CONSTRUCTED         = 0x20; // decimal 32
+
+    public static final int UNIVERSAL           = 0x00; // decimal 32
     public static final int APPLICATION         = 0x40; // decimal 64
-    public static final int TAGGED              = 0x80; // decimal 128
+    public static final int TAGGED              = 0x80; // decimal 128 - maybe should deprecate this.
+    public static final int CONTEXT_SPECIFIC    = 0x80; // decimal 128
+    public static final int PRIVATE             = 0xC0; // decimal 192
+
+    public static final int FLAGS               = 0xE0;
 }
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ConstructedBitStream.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ConstructedBitStream.java
new file mode 100644
index 0000000..20b9c68
--- /dev/null
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ConstructedBitStream.java
@@ -0,0 +1,146 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.asn1;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+class ConstructedBitStream
+    extends InputStream
+{
+    private final ASN1StreamParser _parser;
+    private final boolean _octetAligned;
+
+    private boolean                _first = true;
+    private int                    _padBits = 0;
+
+    private ASN1BitStringParser    _currentParser;
+    private InputStream            _currentStream;
+
+    ConstructedBitStream(ASN1StreamParser parser, boolean octetAligned)
+    {
+        _parser = parser;
+        _octetAligned = octetAligned;
+    }
+
+    int getPadBits()
+    {
+        return _padBits;
+    }
+
+    public int read(byte[] b, int off, int len) throws IOException
+    {
+        if (_currentStream == null)
+        {
+            if (!_first)
+            {
+                return -1;
+            }
+
+            _currentParser = getNextParser();
+            if (_currentParser == null)
+            {
+                return -1;
+            }
+
+            _first = false;
+            _currentStream = _currentParser.getBitStream();
+            
+        }
+
+        int totalRead = 0;
+
+        for (;;)
+        {
+            int numRead = _currentStream.read(b, off + totalRead, len - totalRead);
+
+            if (numRead >= 0)
+            {
+                totalRead += numRead;
+
+                if (totalRead == len)
+                {
+                    return totalRead;
+                }
+            }
+            else
+            {
+                _padBits = _currentParser.getPadBits();
+                _currentParser = getNextParser();
+                if (_currentParser == null)
+                {
+                    _currentStream = null;
+                    return totalRead < 1 ? -1 : totalRead;
+                }
+
+                _currentStream = _currentParser.getBitStream();
+            }
+        }
+    }
+
+    public int read()
+        throws IOException
+    {
+        if (_currentStream == null)
+        {
+            if (!_first)
+            {
+                return -1;
+            }
+
+            _currentParser = getNextParser();
+            if (_currentParser == null)
+            {
+                return -1;
+            }
+
+            _first = false;
+            _currentStream = _currentParser.getBitStream();
+        }
+
+        for (;;)
+        {
+            int b = _currentStream.read();
+
+            if (b >= 0)
+            {
+                return b;
+            }
+
+            _padBits = _currentParser.getPadBits();
+            _currentParser = getNextParser();
+            if (_currentParser == null)
+            {
+                _currentStream = null;
+                return -1;
+            }
+
+            _currentStream = _currentParser.getBitStream();
+        }
+    }
+
+    private ASN1BitStringParser getNextParser() throws IOException
+    {
+        ASN1Encodable asn1Obj = _parser.readObject();
+        if (asn1Obj == null)
+        {
+            if (_octetAligned && _padBits != 0)
+            {
+                throw new IOException("expected octet-aligned bitstring, but found padBits: " + _padBits);
+            }
+
+            return null;
+        }
+
+        if (asn1Obj instanceof ASN1BitStringParser)
+        {
+            if (_padBits != 0)
+            {
+                throw new IOException("only the last nested bitstring can have padding");
+            }
+
+            return (ASN1BitStringParser)asn1Obj;
+        }
+
+        throw new IOException("unknown object encountered: " + asn1Obj.getClass());
+    }
+}
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DERApplicationSpecific.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DERApplicationSpecific.java
deleted file mode 100644
index f3e4589..0000000
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DERApplicationSpecific.java
+++ /dev/null
@@ -1,126 +0,0 @@
-/* GENERATED SOURCE. DO NOT MODIFY. */
-package com.android.internal.org.bouncycastle.asn1;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-
-/**
- * A DER encoding version of an application specific object.
- * @hide This class is not part of the Android public SDK API
- */
-public class DERApplicationSpecific 
-    extends ASN1ApplicationSpecific
-{
-    DERApplicationSpecific(
-        boolean isConstructed,
-        int     tag,
-        byte[]  octets)
-    {
-        super(isConstructed, tag, octets);
-    }
-
-    /**
-     * Create an application specific object from the passed in data. This will assume
-     * the data does not represent a constructed object.
-     *
-     * @param tag the tag number for this object.
-     * @param octets the encoding of the object's body.
-     */
-    public DERApplicationSpecific(
-        int    tag,
-        byte[] octets)
-    {
-        this(false, tag, octets);
-    }
-
-    /**
-     * Create an application specific object with a tagging of explicit/constructed.
-     *
-     * @param tag the tag number for this object.
-     * @param object the object to be contained.
-     */
-    public DERApplicationSpecific(
-        int           tag,
-        ASN1Encodable object)
-        throws IOException 
-    {
-        this(true, tag, object);
-    }
-
-    /**
-     * Create an application specific object with the tagging style given by the value of constructed.
-     *
-     * @param constructed true if the object is constructed.
-     * @param tag the tag number for this object.
-     * @param object the object to be contained.
-     */
-    public DERApplicationSpecific(
-        boolean      constructed,
-        int          tag,
-        ASN1Encodable object)
-        throws IOException
-    {
-        super(constructed || object.toASN1Primitive().isConstructed(), tag, getEncoding(constructed, object));
-    }
-
-    private static byte[] getEncoding(boolean explicit, ASN1Encodable object)
-        throws IOException
-    {
-        byte[] data = object.toASN1Primitive().getEncoded(ASN1Encoding.DER);
-
-        if (explicit)
-        {
-            return data;
-        }
-        else
-        {
-            int lenBytes = getLengthOfHeader(data);
-            byte[] tmp = new byte[data.length - lenBytes];
-            System.arraycopy(data, lenBytes, tmp, 0, tmp.length);
-            return tmp;
-        }
-    }
-
-    /**
-     * Create an application specific object which is marked as constructed
-     *
-     * @param tagNo the tag number for this object.
-     * @param vec the objects making up the application specific object.
-     */
-    public DERApplicationSpecific(int tagNo, ASN1EncodableVector vec)
-    {
-        super(true, tagNo, getEncodedVector(vec));
-    }
-
-    private static byte[] getEncodedVector(ASN1EncodableVector vec)
-    {
-        ByteArrayOutputStream bOut = new ByteArrayOutputStream();
-
-        for (int i = 0; i != vec.size(); i++)
-        {
-            try
-            {
-                bOut.write(((ASN1Object)vec.get(i)).getEncoded(ASN1Encoding.DER));
-            }
-            catch (IOException e)
-            {
-                throw new ASN1ParsingException("malformed object: " + e, e);
-            }
-        }
-        return bOut.toByteArray();
-    }
-
-    /* (non-Javadoc)
-     * @see org.bouncycastle.asn1.ASN1Primitive#encode(org.bouncycastle.asn1.DEROutputStream)
-     */
-    void encode(ASN1OutputStream out, boolean withTag) throws IOException
-    {
-        int flags = BERTags.APPLICATION;
-        if (isConstructed)
-        {
-            flags |= BERTags.CONSTRUCTED;
-        }
-
-        out.writeEncoded(withTag, flags, tag, octets);
-    }
-}
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DERBMPString.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DERBMPString.java
index 9ffb1be..d55b353 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DERBMPString.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DERBMPString.java
@@ -1,10 +1,6 @@
 /* GENERATED SOURCE. DO NOT MODIFY. */
 package com.android.internal.org.bouncycastle.asn1;
 
-import java.io.IOException;
-
-import com.android.internal.org.bouncycastle.util.Arrays;
-
 /**
  * DER BMPString object encodes BMP (<i>Basic Multilingual Plane</i>) subset
  * (aka UCS-2) of UNICODE (ISO 10646) characters in codepoints 0 to 65535.
@@ -15,203 +11,28 @@
  * @hide This class is not part of the Android public SDK API
  */
 public class DERBMPString
-    extends ASN1Primitive
-    implements ASN1String
+    extends ASN1BMPString
 {
-    private final char[]  string;
-
-    /**
-     * Return a BMP String from the given object.
-     *
-     * @param obj the object we want converted.
-     * @exception IllegalArgumentException if the object cannot be converted.
-     * @return a DERBMPString instance, or null.
-     */
-    public static DERBMPString getInstance(
-        Object  obj)
-    {
-        if (obj == null || obj instanceof DERBMPString)
-        {
-            return (DERBMPString)obj;
-        }
-
-        if (obj instanceof byte[])
-        {
-            try
-            {
-                return (DERBMPString)fromByteArray((byte[])obj);
-            }
-            catch (Exception e)
-            {
-                throw new IllegalArgumentException("encoding error in getInstance: " + e.toString());
-            }
-        }
-
-        throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
-    }
-
-    /**
-     * Return a BMP String from a tagged object.
-     *
-     * @param obj the tagged object holding the object we want
-     * @param explicit true if the object is meant to be explicitly
-     *              tagged false otherwise.
-     * @exception IllegalArgumentException if the tagged object cannot
-     *              be converted.
-     * @return a DERBMPString instance.
-     */
-    public static DERBMPString getInstance(
-        ASN1TaggedObject obj,
-        boolean          explicit)
-    {
-        ASN1Primitive o = obj.getObject();
-
-        if (explicit || o instanceof DERBMPString)
-        {
-            return getInstance(o);
-        }
-        else
-        {
-            return new DERBMPString(ASN1OctetString.getInstance(o).getOctets());
-        }
-    }
-
-    /**
-     * Basic constructor - byte encoded string.
-     * @param string the encoded BMP STRING to wrap.
-     */
-    DERBMPString(
-        byte[]   string)
-    {
-        if (string == null)
-        {
-            throw new NullPointerException("'string' cannot be null");
-        }
-
-        int byteLen = string.length;
-        if (0 != (byteLen & 1))
-        {
-            throw new IllegalArgumentException("malformed BMPString encoding encountered");
-        }
-
-        int charLen = byteLen / 2;
-        char[] cs = new char[charLen];
-
-        for (int i = 0; i != charLen; i++)
-        {
-            cs[i] = (char)((string[2 * i] << 8) | (string[2 * i + 1] & 0xff));
-        }
-
-        this.string = cs;
-    }
-
-    DERBMPString(char[] string)
-    {
-        if (string == null)
-        {
-            throw new NullPointerException("'string' cannot be null");
-        }
-
-        this.string = string;
-    }
-
     /**
      * Basic constructor
      * @param string a String to wrap as a BMP STRING.
      */
-    public DERBMPString(
-        String   string)
+    public DERBMPString(String string)
     {
-        if (string == null)
-        {
-            throw new NullPointerException("'string' cannot be null");
-        }
-
-        this.string = string.toCharArray();
+        super(string);
     }
 
-    public String getString()
+    /**
+     * Basic constructor - byte encoded string.
+     * @param contents the encoded BMP STRING to wrap.
+     */
+    DERBMPString(byte[] contents)
     {
-        return new String(string);
+        super(contents);
     }
 
-    public String toString()
+    DERBMPString(char[] string)
     {
-        return getString();
-    }
-
-    public int hashCode()
-    {
-        return Arrays.hashCode(string);
-    }
-
-    protected boolean asn1Equals(
-        ASN1Primitive o)
-    {
-        if (!(o instanceof DERBMPString))
-        {
-            return false;
-        }
-
-        DERBMPString  s = (DERBMPString)o;
-
-        return Arrays.areEqual(string, s.string);
-    }
-
-    boolean isConstructed()
-    {
-        return false;
-    }
-
-    int encodedLength()
-    {
-        return 1 + StreamUtil.calculateBodyLength(string.length * 2) + (string.length * 2);
-    }
-
-    void encode(
-        ASN1OutputStream out, boolean withTag)
-        throws IOException
-    {
-        int count = string.length;
-        if (withTag)
-        {
-            out.write(BERTags.BMP_STRING);
-        }
-        out.writeLength(count * 2);
-
-        byte[] buf = new byte[8];
-
-        int i = 0, limit = count & -4;
-        while (i < limit)
-        {
-            char c0 = string[i], c1 = string[i + 1], c2 = string[i + 2], c3 = string[i + 3];
-            i += 4;
-
-            buf[0] = (byte)(c0 >> 8);
-            buf[1] = (byte)c0;
-            buf[2] = (byte)(c1 >> 8);
-            buf[3] = (byte)c1;
-            buf[4] = (byte)(c2 >> 8);
-            buf[5] = (byte)c2;
-            buf[6] = (byte)(c3 >> 8);
-            buf[7] = (byte)c3;
-
-            out.write(buf, 0, 8);
-        }
-        if (i < count)
-        {
-            int bufPos = 0;
-            do
-            {
-                char c0 = string[i];
-                i += 1;
-
-                buf[bufPos++] = (byte)(c0 >> 8);
-                buf[bufPos++] = (byte)c0;
-            }
-            while (i < count);
-
-            out.write(buf, 0, bufPos);
-        }
+        super(string);
     }
 }
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DERBitString.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DERBitString.java
index 40d010a..983d862 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DERBitString.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DERBitString.java
@@ -10,123 +10,69 @@
 public class DERBitString
     extends ASN1BitString
 {
-    /**
-     * return a Bit String from the passed in object
-     *
-     * @param obj a DERBitString or an object that can be converted into one.
-     * @exception IllegalArgumentException if the object cannot be converted.
-     * @return a DERBitString instance, or null.
-     */
-    public static DERBitString getInstance(
-        Object  obj)
+    public static DERBitString convert(ASN1BitString bitString)
     {
-        if (obj == null || obj instanceof DERBitString)
-        {
-            return (DERBitString)obj;
-        }
-        if (obj instanceof DLBitString)
-        {
-            return new DERBitString(((DLBitString)obj).data, ((DLBitString)obj).padBits);
-        }
-        if (obj instanceof byte[])
-        {
-            try
-            {
-                return (DERBitString)fromByteArray((byte[])obj);
-            }
-            catch (Exception e)
-            {
-                throw new IllegalArgumentException("encoding error in getInstance: " + e.toString());
-            }
-        }
-
-        throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
+        return (DERBitString)bitString.toDERObject();
     }
 
-    /**
-     * return a Bit String from a tagged object.
-     *
-     * @param obj the tagged object holding the object we want
-     * @param explicit true if the object is meant to be explicitly
-     *              tagged false otherwise.
-     * @exception IllegalArgumentException if the tagged object cannot
-     *               be converted.
-     * @return a DERBitString instance, or null.
-     */
-    public static DERBitString getInstance(
-        ASN1TaggedObject obj,
-        boolean          explicit)
-    {
-        ASN1Primitive o = obj.getObject();
-
-        if (explicit || o instanceof DERBitString)
-        {
-            return getInstance(o);
-        }
-        else
-        {
-            return fromOctetString(ASN1OctetString.getInstance(o).getOctets());
-        }
-    }
-
-    protected DERBitString(byte data, int padBits)
-    {
-        super(data, padBits);
-    }
-
-    /**
-     * @param data the octets making up the bit string.
-     * @param padBits the number of extra bits at the end of the string.
-     */
-    public DERBitString(
-        byte[]  data,
-        int     padBits)
-    {
-        super(data, padBits);
-    }
-
-    public DERBitString(
-        byte[]  data)
+    public DERBitString(byte[] data)
     {
         this(data, 0);
     }
 
-    public DERBitString(
-        int value)
+    public DERBitString(byte data, int padBits)
     {
+        super(data, padBits);
+    }
+
+    public DERBitString(byte[] data, int padBits)
+    {
+        super(data, padBits);
+    }
+
+    public DERBitString(int value)
+    {
+        // TODO[asn1] Unify in single allocation of 'contents'
         super(getBytes(value), getPadBits(value));
     }
 
-    public DERBitString(
-        ASN1Encodable obj)
-        throws IOException
+    public DERBitString(ASN1Encodable obj) throws IOException
     {
+        // TODO[asn1] Unify in single allocation of 'contents'
         super(obj.toASN1Primitive().getEncoded(ASN1Encoding.DER), 0);
     }
 
-    boolean isConstructed()
+    DERBitString(byte[] contents, boolean check)
+    {
+        super(contents, check);
+    }
+
+    boolean encodeConstructed()
     {
         return false;
     }
 
-    int encodedLength()
+    int encodedLength(boolean withTag)
     {
-        return 1 + StreamUtil.calculateBodyLength(data.length + 1) + data.length + 1;
+        return ASN1OutputStream.getLengthOfEncodingDL(withTag, contents.length);
     }
 
     void encode(ASN1OutputStream out, boolean withTag) throws IOException
     {
-        int len = data.length;
-        if (0 == len
-            || 0 == padBits
-            || (data[len - 1] == (byte)(data[len - 1] & (0xFF << padBits))))
+        int padBits = contents[0] & 0xFF;
+        int length = contents.length;
+        int last = length - 1;
+
+        byte lastOctet = contents[last];
+        byte lastOctetDER = (byte)(contents[last] & (0xFF << padBits));
+
+        if (lastOctet == lastOctetDER)
         {
-            out.writeEncoded(withTag, BERTags.BIT_STRING, (byte)padBits, data);
+            out.writeEncodingDL(withTag, BERTags.BIT_STRING, contents);
         }
         else
         {
-            byte der = (byte)(data[len - 1] & (0xFF << padBits));
-            out.writeEncoded(withTag, BERTags.BIT_STRING, (byte)padBits, data, 0, len - 1, der);
+            out.writeEncodingDL(withTag, BERTags.BIT_STRING, contents, 0, last, lastOctetDER);
         }
     }
 
@@ -140,21 +86,8 @@
         return this;
     }
 
-    static DERBitString fromOctetString(byte[] bytes)
+    static DERBitString fromOctetString(ASN1OctetString octetString)
     {
-        if (bytes.length < 1)
-        {
-            throw new IllegalArgumentException("truncated BIT STRING detected");
-        }
-
-        int padBits = bytes[0];
-        byte[] data = new byte[bytes.length - 1];
-
-        if (data.length != 0)
-        {
-            System.arraycopy(bytes, 1, data, 0, bytes.length - 1);
-        }
-
-        return new DERBitString(data, padBits);
+        return new DERBitString(octetString.getOctets(), true);
     }
 }
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DEREncodableVector.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DEREncodableVector.java
deleted file mode 100644
index 2c53b69..0000000
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DEREncodableVector.java
+++ /dev/null
@@ -1,20 +0,0 @@
-/* GENERATED SOURCE. DO NOT MODIFY. */
-package com.android.internal.org.bouncycastle.asn1;
-
-/**
- * a general class for building up a vector of DER encodable objects -
- * this will eventually be superseded by ASN1EncodableVector so you should
- * use that class in preference.
- * @hide This class is not part of the Android public SDK API
- */
-public class DEREncodableVector
-    extends ASN1EncodableVector
-{
-    /**
-     * @deprecated use ASN1EncodableVector instead.
-     */
-    public DEREncodableVector()
-    {
-
-    }
-}
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DEREnumerated.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DEREnumerated.java
deleted file mode 100644
index 6069d1e..0000000
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DEREnumerated.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/* GENERATED SOURCE. DO NOT MODIFY. */
-package com.android.internal.org.bouncycastle.asn1;
-
-import java.math.BigInteger;
-
-/**
- * @deprecated Use ASN1Enumerated instead of this.
- * @hide This class is not part of the Android public SDK API
- */
-public class DEREnumerated
-    extends ASN1Enumerated
-{
-    /**
-     * @param bytes the value of this enumerated as an encoded BigInteger (signed).
-     * @deprecated use ASN1Enumerated
-     */
-    DEREnumerated(byte[] bytes)
-    {
-        super(bytes);
-    }
-
-    /**
-     * @param value the value of this enumerated.
-     * @deprecated use ASN1Enumerated
-     */
-    public DEREnumerated(BigInteger value)
-    {
-        super(value);
-    }
-
-    /**
-     * @param value the value of this enumerated.
-     * @deprecated use ASN1Enumerated
-     */
-    public DEREnumerated(int value)
-    {
-        super(value);
-    }
-}
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DERExternal.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DERExternal.java
index da29a77..d7614a9 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DERExternal.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DERExternal.java
@@ -1,9 +1,6 @@
 /* GENERATED SOURCE. DO NOT MODIFY. */
 package com.android.internal.org.bouncycastle.asn1;
 
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-
 /**
  * Class representing the DER-type External
  * @hide This class is not part of the Android public SDK API
@@ -21,11 +18,30 @@
      * <li> Anything but {@link DERTaggedObject} + data {@link DERTaggedObject} (data value form)</li>
      * </ul>
      *
-     * @throws IllegalArgumentException if input size is wrong, or
+     * @throws IllegalArgumentException if input size is wrong, or input is not an acceptable format
+     * 
+     * @deprecated Use {@link DERExternal#DERExternal(DERSequence)} instead.
      */
     public DERExternal(ASN1EncodableVector vector)
     {
-        super(vector);
+        this(DERFactory.createSequence(vector));
+    }
+
+    /**
+     * Construct a DER EXTERNAL object, the input sequence must have exactly two elements on it.
+     * <p>
+     * Acceptable input formats are:
+     * <ul>
+     * <li> {@link ASN1ObjectIdentifier} + data {@link DERTaggedObject} (direct reference form)</li>
+     * <li> {@link ASN1Integer} + data {@link DERTaggedObject} (indirect reference form)</li>
+     * <li> Anything but {@link DERTaggedObject} + data {@link DERTaggedObject} (data value form)</li>
+     * </ul>
+     *
+     * @throws IllegalArgumentException if input size is wrong, or input is not an acceptable format
+     */
+    public DERExternal(DERSequence sequence)
+    {
+        super(sequence);
     }
 
     /**
@@ -36,9 +52,10 @@
      * @param dataValueDescriptor The data value descriptor or <code>null</code> if not set.
      * @param externalData The external data in its encoded form.
      */
-    public DERExternal(ASN1ObjectIdentifier directReference, ASN1Integer indirectReference, ASN1Primitive dataValueDescriptor, DERTaggedObject externalData)
+    public DERExternal(ASN1ObjectIdentifier directReference, ASN1Integer indirectReference,
+        ASN1Primitive dataValueDescriptor, DERTaggedObject externalData)
     {
-        this(directReference, indirectReference, dataValueDescriptor, externalData.getTagNo(), externalData.toASN1Primitive());
+        super(directReference, indirectReference, dataValueDescriptor, externalData);
     }
 
     /**
@@ -50,11 +67,33 @@
      * @param encoding The encoding to be used for the external data
      * @param externalData The external data
      */
-    public DERExternal(ASN1ObjectIdentifier directReference, ASN1Integer indirectReference, ASN1Primitive dataValueDescriptor, int encoding, ASN1Primitive externalData)
+    public DERExternal(ASN1ObjectIdentifier directReference, ASN1Integer indirectReference,
+        ASN1Primitive dataValueDescriptor, int encoding, ASN1Primitive externalData)
     {
         super(directReference, indirectReference, dataValueDescriptor, encoding, externalData);
     }
 
+    ASN1Sequence buildSequence()
+    {
+        ASN1EncodableVector v = new ASN1EncodableVector(4);
+        if (directReference != null)
+        {
+            v.add(directReference);
+        }
+        if (indirectReference != null)
+        {
+            v.add(indirectReference);
+        }
+        if (dataValueDescriptor != null)
+        {
+            v.add(dataValueDescriptor.toDERObject());
+        }
+
+        v.add(new DERTaggedObject(0 == encoding, encoding, externalContent));
+
+        return new DERSequence(v);
+    }
+
     ASN1Primitive toDERObject()
     {
         return this;
@@ -64,34 +103,4 @@
     {
         return this;
     }
-
-    int encodedLength()
-        throws IOException
-    {
-        return this.getEncoded().length;
-    }
-
-    /* (non-Javadoc)
-     * @see org.bouncycastle.asn1.ASN1Primitive#encode(org.bouncycastle.asn1.DEROutputStream)
-     */
-    void encode(ASN1OutputStream out, boolean withTag) throws IOException
-    {
-        ByteArrayOutputStream baos = new ByteArrayOutputStream();
-        if (directReference != null)
-        {
-            baos.write(directReference.getEncoded(ASN1Encoding.DER));
-        }
-        if (indirectReference != null)
-        {
-            baos.write(indirectReference.getEncoded(ASN1Encoding.DER));
-        }
-        if (dataValueDescriptor != null)
-        {
-            baos.write(dataValueDescriptor.getEncoded(ASN1Encoding.DER));
-        }
-        DERTaggedObject obj = new DERTaggedObject(true, encoding, externalContent);
-        baos.write(obj.getEncoded(ASN1Encoding.DER));
-
-        out.writeEncoded(withTag, BERTags.CONSTRUCTED, BERTags.EXTERNAL, baos.toByteArray());
-    }
 }
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DERExternalParser.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DERExternalParser.java
index c400ab4..aed5dd8 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DERExternalParser.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DERExternalParser.java
@@ -8,7 +8,7 @@
  * @hide This class is not part of the Android public SDK API
  */
 public class DERExternalParser
-    implements ASN1Encodable, InMemoryRepresentable
+    implements ASN1ExternalParser
 {
     private ASN1StreamParser _parser;
 
@@ -37,14 +37,7 @@
     public ASN1Primitive getLoadedObject()
         throws IOException
     {
-        try
-        {
-            return new DLExternal(_parser.readVector());
-        }
-        catch (IllegalArgumentException e)
-        {
-            throw new ASN1Exception(e.getMessage(), e);
-        }
+        return parse(_parser);
     }
 
     /**
@@ -67,4 +60,16 @@
             throw new ASN1ParsingException("unable to get DER object", ioe);
         }
     }
+
+    static DLExternal parse(ASN1StreamParser sp) throws IOException
+    {
+        try
+        {
+            return new DLExternal(new DLSequence(sp.readVector()));
+        }
+        catch (IllegalArgumentException e)
+        {
+            throw new ASN1Exception(e.getMessage(), e);
+        }
+    }
 }
\ No newline at end of file
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DERFactory.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DERFactory.java
index e4dc5f6..291856e 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DERFactory.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DERFactory.java
@@ -3,10 +3,10 @@
 
 class DERFactory
 {
-    static final ASN1Sequence EMPTY_SEQUENCE = new DERSequence();
-    static final ASN1Set EMPTY_SET = new DERSet();
+    static final DERSequence EMPTY_SEQUENCE = new DERSequence();
+    static final DERSet EMPTY_SET = new DERSet();
 
-    static ASN1Sequence createSequence(ASN1EncodableVector v)
+    static DERSequence createSequence(ASN1EncodableVector v)
     {
         if (v.size() < 1)
         {
@@ -16,7 +16,7 @@
         return new DERSequence(v);
     }
 
-    static ASN1Set createSet(ASN1EncodableVector v)
+    static DERSet createSet(ASN1EncodableVector v)
     {
         if (v.size() < 1)
         {
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DERGeneralString.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DERGeneralString.java
index 660923d..628ac10 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DERGeneralString.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DERGeneralString.java
@@ -1,11 +1,6 @@
 /* GENERATED SOURCE. DO NOT MODIFY. */
 package com.android.internal.org.bouncycastle.asn1;
 
-import java.io.IOException;
-
-import com.android.internal.org.bouncycastle.util.Arrays;
-import com.android.internal.org.bouncycastle.util.Strings;
-
 /**
  * ASN.1 GENERAL-STRING data type.
  * <p>
@@ -15,136 +10,20 @@
  * @hide This class is not part of the Android public SDK API
  */
 public class DERGeneralString 
-    extends ASN1Primitive
-    implements ASN1String
+    extends ASN1GeneralString
 {
-    private final byte[] string;
-
-    /**
-     * Return a GeneralString from the given object.
-     *
-     * @param obj the object we want converted.
-     * @exception IllegalArgumentException if the object cannot be converted.
-     * @return a DERBMPString instance, or null.
-     */
-    public static DERGeneralString getInstance(
-        Object obj) 
-    {
-        if (obj == null || obj instanceof DERGeneralString) 
-        {
-            return (DERGeneralString) obj;
-        }
-
-        if (obj instanceof byte[])
-        {
-            try
-            {
-                return (DERGeneralString)fromByteArray((byte[])obj);
-            }
-            catch (Exception e)
-            {
-                throw new IllegalArgumentException("encoding error in getInstance: " + e.toString());
-            }
-        }
-
-        throw new IllegalArgumentException("illegal object in getInstance: "
-                + obj.getClass().getName());
-    }
-
-    /**
-     * Return a GeneralString from a tagged object.
-     *
-     * @param obj the tagged object holding the object we want
-     * @param explicit true if the object is meant to be explicitly
-     *              tagged false otherwise.
-     * @exception IllegalArgumentException if the tagged object cannot
-     *              be converted.
-     * @return a DERGeneralString instance.
-     */
-    public static DERGeneralString getInstance(
-        ASN1TaggedObject obj, 
-        boolean explicit) 
-    {
-        ASN1Primitive o = obj.getObject();
-
-        if (explicit || o instanceof DERGeneralString)
-        {
-            return getInstance(o);
-        }
-        else
-        {
-            return new DERGeneralString(ASN1OctetString.getInstance(o).getOctets());
-        }
-    }
-
-    DERGeneralString(byte[] string)
-    {
-        this.string = string;
-    }
-
     /**
      * Construct a GeneralString from the passed in String.
      *
      * @param string the string to be contained in this object.
      */
-    public DERGeneralString(String string) 
+    public DERGeneralString(String string)
     {
-        this.string = Strings.toByteArray(string);
+        super(string);
     }
 
-    /**
-     * Return a Java String representation of our contained String.
-     *
-     * @return a Java String representing our contents.
-     */
-    public String getString() 
+    DERGeneralString(byte[] contents, boolean clone)
     {
-        return Strings.fromByteArray(string);
-    }
-
-    public String toString()
-    {
-        return getString();
-    }
-
-    /**
-     * Return a byte array representation of our contained String.
-     *
-     * @return a byte array representing our contents.
-     */
-    public byte[] getOctets() 
-    {
-        return Arrays.clone(string);
-    }
-
-    boolean isConstructed()
-    {
-        return false;
-    }
-
-    int encodedLength()
-    {
-        return 1 + StreamUtil.calculateBodyLength(string.length) + string.length;
-    }
-
-    void encode(ASN1OutputStream out, boolean withTag) throws IOException
-    {
-        out.writeEncoded(withTag, BERTags.GENERAL_STRING, string);
-    }
-
-    public int hashCode() 
-    {
-        return Arrays.hashCode(string);
-    }
-    
-    boolean asn1Equals(ASN1Primitive o)
-    {
-        if (!(o instanceof DERGeneralString)) 
-        {
-            return false;
-        }
-        DERGeneralString s = (DERGeneralString)o;
-
-        return Arrays.areEqual(string, s.string);
+        super(contents, clone);
     }
 }
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DERGeneralizedTime.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DERGeneralizedTime.java
index 448f169..b78c51d 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DERGeneralizedTime.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DERGeneralizedTime.java
@@ -44,39 +44,39 @@
 
     private byte[] getDERTime()
     {
-        if (time[time.length - 1] == 'Z')
+        if (contents[contents.length - 1] == 'Z')
         {
             if (!hasMinutes())
             {
-                byte[] derTime = new byte[time.length + 4];
+                byte[] derTime = new byte[contents.length + 4];
 
-                System.arraycopy(time, 0, derTime, 0, time.length - 1);
-                System.arraycopy(Strings.toByteArray("0000Z"), 0, derTime, time.length - 1, 5);
+                System.arraycopy(contents, 0, derTime, 0, contents.length - 1);
+                System.arraycopy(Strings.toByteArray("0000Z"), 0, derTime, contents.length - 1, 5);
 
                 return derTime;
             }
             else if (!hasSeconds())
             {
-                byte[] derTime = new byte[time.length + 2];
+                byte[] derTime = new byte[contents.length + 2];
 
-                System.arraycopy(time, 0, derTime, 0, time.length - 1);
-                System.arraycopy(Strings.toByteArray("00Z"), 0, derTime, time.length - 1, 3);
+                System.arraycopy(contents, 0, derTime, 0, contents.length - 1);
+                System.arraycopy(Strings.toByteArray("00Z"), 0, derTime, contents.length - 1, 3);
 
                 return derTime;
             }
             else if (hasFractionalSeconds())
             {
-                int ind = time.length - 2;
-                while (ind > 0 && time[ind] == '0')
+                int ind = contents.length - 2;
+                while (ind > 0 && contents[ind] == '0')
                 {
                     ind--;
                 }
 
-                if (time[ind] == '.')
+                if (contents[ind] == '.')
                 {
                     byte[] derTime = new byte[ind + 1];
 
-                    System.arraycopy(time, 0, derTime, 0, ind);
+                    System.arraycopy(contents, 0, derTime, 0, ind);
                     derTime[ind] = (byte)'Z';
 
                     return derTime;
@@ -85,7 +85,7 @@
                 {
                     byte[] derTime = new byte[ind + 2];
 
-                    System.arraycopy(time, 0, derTime, 0, ind + 1);
+                    System.arraycopy(contents, 0, derTime, 0, ind + 1);
                     derTime[ind + 1] = (byte)'Z';
 
                     return derTime;
@@ -93,25 +93,23 @@
             }
             else
             {
-                return time;
+                return contents;
             }
         }
         else
         {
-            return time; // TODO: is there a better way?
+            return contents; // TODO: is there a better way?
         }
     }
 
-    int encodedLength()
+    int encodedLength(boolean withTag)
     {
-        int length = getDERTime().length;
-
-        return 1 + StreamUtil.calculateBodyLength(length) + length;
+        return ASN1OutputStream.getLengthOfEncodingDL(withTag, getDERTime().length);
     }
 
     void encode(ASN1OutputStream out, boolean withTag) throws IOException
     {
-        out.writeEncoded(withTag, BERTags.GENERALIZED_TIME, getDERTime());
+        out.writeEncodingDL(withTag, BERTags.GENERALIZED_TIME, getDERTime());
     }
 
     ASN1Primitive toDERObject()
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DERGraphicString.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DERGraphicString.java
index 779c899..acdc382 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DERGraphicString.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DERGraphicString.java
@@ -1,126 +1,19 @@
 /* GENERATED SOURCE. DO NOT MODIFY. */
 package com.android.internal.org.bouncycastle.asn1;
 
-import java.io.IOException;
-
-import com.android.internal.org.bouncycastle.util.Arrays;
-import com.android.internal.org.bouncycastle.util.Strings;
-
 /**
  * @hide This class is not part of the Android public SDK API
  */
 public class DERGraphicString
-    extends ASN1Primitive
-    implements ASN1String
+    extends ASN1GraphicString
 {
-    private final byte[] string;
-    
-    /**
-     * return a Graphic String from the passed in object
-     *
-     * @param obj a DERGraphicString or an object that can be converted into one.
-     * @exception IllegalArgumentException if the object cannot be converted.
-     * @return a DERGraphicString instance, or null.
-     */
-    public static DERGraphicString getInstance(
-        Object  obj)
+    public DERGraphicString(byte[] octets)
     {
-        if (obj == null || obj instanceof DERGraphicString)
-        {
-            return (DERGraphicString)obj;
-        }
-
-        if (obj instanceof byte[])
-        {
-            try
-            {
-                return (DERGraphicString)fromByteArray((byte[])obj);
-            }
-            catch (Exception e)
-            {
-                throw new IllegalArgumentException("encoding error in getInstance: " + e.toString());
-            }
-        }
-
-        throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
+        this(octets, true);
     }
 
-    /**
-     * return a Graphic String from a tagged object.
-     *
-     * @param obj the tagged object holding the object we want
-     * @param explicit true if the object is meant to be explicitly
-     *              tagged false otherwise.
-     * @exception IllegalArgumentException if the tagged object cannot
-     *               be converted.
-     * @return a DERGraphicString instance, or null.
-     */
-    public static DERGraphicString getInstance(
-        ASN1TaggedObject obj,
-        boolean          explicit)
+    DERGraphicString(byte[] contents, boolean clone)
     {
-        ASN1Primitive o = obj.getObject();
-
-        if (explicit || o instanceof DERGraphicString)
-        {
-            return getInstance(o);
-        }
-        else
-        {
-            return new DERGraphicString(ASN1OctetString.getInstance(o).getOctets());
-        }
-    }
-
-    /**
-     * basic constructor - with bytes.
-     * @param string the byte encoding of the characters making up the string.
-     */
-    public DERGraphicString(
-        byte[]   string)
-    {
-        this.string = Arrays.clone(string);
-    }
-    
-    public byte[] getOctets()
-    {
-        return Arrays.clone(string);
-    }
-
-    boolean isConstructed()
-    {
-        return false;
-    }
-
-    int encodedLength()
-    {
-        return 1 + StreamUtil.calculateBodyLength(string.length) + string.length;
-    }
-
-    void encode(ASN1OutputStream out, boolean withTag) throws IOException
-    {
-        out.writeEncoded(withTag, BERTags.GRAPHIC_STRING, string);
-    }
-
-    public int hashCode()
-    {
-        return Arrays.hashCode(string);
-    }
-
-    boolean asn1Equals(
-        ASN1Primitive o)
-    {
-        if (!(o instanceof DERGraphicString))
-        {
-            return false;
-        }
-
-        DERGraphicString  s = (DERGraphicString)o;
-
-        return Arrays.areEqual(string, s.string);
-    }
-
-    public String getString()
-    {
-        return Strings.fromByteArray(string);
+        super(contents, clone);
     }
 }
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DERIA5String.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DERIA5String.java
index 7cdb97b..4517496 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DERIA5String.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DERIA5String.java
@@ -1,11 +1,6 @@
 /* GENERATED SOURCE. DO NOT MODIFY. */
 package com.android.internal.org.bouncycastle.asn1;
 
-import java.io.IOException;
-
-import com.android.internal.org.bouncycastle.util.Arrays;
-import com.android.internal.org.bouncycastle.util.Strings;
-
 /**
  * DER IA5String object - this is a ISO 646 (ASCII) string encoding code points 0 to 127.
  * <p>
@@ -14,83 +9,13 @@
  * @hide This class is not part of the Android public SDK API
  */
 public class DERIA5String
-    extends ASN1Primitive
-    implements ASN1String
+    extends ASN1IA5String
 {
-    private final byte[]  string;
-
-    /**
-     * Return an IA5 string from the passed in object
-     *
-     * @param obj a DERIA5String or an object that can be converted into one.
-     * @exception IllegalArgumentException if the object cannot be converted.
-     * @return a DERIA5String instance, or null.
-     */
-    public static DERIA5String getInstance(
-        Object  obj)
-    {
-        if (obj == null || obj instanceof DERIA5String)
-        {
-            return (DERIA5String)obj;
-        }
-
-        if (obj instanceof byte[])
-        {
-            try
-            {
-                return (DERIA5String)fromByteArray((byte[])obj);
-            }
-            catch (Exception e)
-            {
-                throw new IllegalArgumentException("encoding error in getInstance: " + e.toString());
-            }
-        }
-
-        throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
-    }
-
-    /**
-     * Return an IA5 String from a tagged object.
-     *
-     * @param obj the tagged object holding the object we want
-     * @param explicit true if the object is meant to be explicitly
-     *              tagged false otherwise.
-     * @exception IllegalArgumentException if the tagged object cannot
-     *               be converted.
-     * @return a DERIA5String instance, or null.
-     */
-    public static DERIA5String getInstance(
-        ASN1TaggedObject obj,
-        boolean          explicit)
-    {
-        ASN1Primitive o = obj.getObject();
-
-        if (explicit || o instanceof DERIA5String)
-        {
-            return getInstance(o);
-        }
-        else
-        {
-            return new DERIA5String(ASN1OctetString.getInstance(o).getOctets());
-        }
-    }
-
-    /**
-     * Basic constructor - with bytes.
-     * @param string the byte encoding of the characters making up the string.
-     */
-    DERIA5String(
-        byte[]   string)
-    {
-        this.string = string;
-    }
-
     /**
      * Basic constructor - without validation.
      * @param string the base string to use..
      */
-    public DERIA5String(
-        String   string)
+    public DERIA5String(String string)
     {
         this(string, false);
     }
@@ -103,90 +28,13 @@
      * @throws IllegalArgumentException if validate is true and the string
      * contains characters that should not be in an IA5String.
      */
-    public DERIA5String(
-        String   string,
-        boolean  validate)
+    public DERIA5String(String string, boolean validate)
     {
-        if (string == null)
-        {
-            throw new NullPointerException("'string' cannot be null");
-        }
-        if (validate && !isIA5String(string))
-        {
-            throw new IllegalArgumentException("'string' contains illegal characters");
-        }
-
-        this.string = Strings.toByteArray(string);
+        super(string, validate);
     }
 
-    public String getString()
+    DERIA5String(byte[] contents, boolean clone)
     {
-        return Strings.fromByteArray(string);
-    }
-
-    public String toString()
-    {
-        return getString();
-    }
-
-    public byte[] getOctets()
-    {
-        return Arrays.clone(string);
-    }
-
-    boolean isConstructed()
-    {
-        return false;
-    }
-
-    int encodedLength()
-    {
-        return 1 + StreamUtil.calculateBodyLength(string.length) + string.length;
-    }
-
-    void encode(ASN1OutputStream out, boolean withTag) throws IOException
-    {
-        out.writeEncoded(withTag, BERTags.IA5_STRING, string);
-    }
-
-    public int hashCode()
-    {
-        return Arrays.hashCode(string);
-    }
-
-    boolean asn1Equals(
-        ASN1Primitive o)
-    {
-        if (!(o instanceof DERIA5String))
-        {
-            return false;
-        }
-
-        DERIA5String  s = (DERIA5String)o;
-
-        return Arrays.areEqual(string, s.string);
-    }
-
-    /**
-     * return true if the passed in String can be represented without
-     * loss as an IA5String, false otherwise.
-     *
-     * @param str the string to check.
-     * @return true if character set in IA5String set, false otherwise.
-     */
-    public static boolean isIA5String(
-        String  str)
-    {
-        for (int i = str.length() - 1; i >= 0; i--)
-        {
-            char    ch = str.charAt(i);
-
-            if (ch > 0x007f)
-            {
-                return false;
-            }
-        }
-
-        return true;
+        super(contents, clone);
     }
 }
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DERInteger.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DERInteger.java
deleted file mode 100644
index bc7d548..0000000
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DERInteger.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/* GENERATED SOURCE. DO NOT MODIFY. */
-package com.android.internal.org.bouncycastle.asn1;
-
-import java.math.BigInteger;
-
-/**
- * @deprecated  Use ASN1Integer instead of this,
- * @hide This class is not part of the Android public SDK API
- */
-public class DERInteger
-    extends ASN1Integer
-{
-    /**
-     * Constructor from a byte array containing a signed representation of the number.
-     *
-     * @param bytes a byte array containing the signed number.A copy is made of the byte array.
-     */
-    public DERInteger(byte[] bytes)
-    {
-        super(bytes, true);
-    }
-
-    public DERInteger(BigInteger value)
-    {
-        super(value);
-    }
-
-    public DERInteger(long value)
-    {
-        super(value);
-    }
-}
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DERNull.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DERNull.java
index e86c783..c6eaa91 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DERNull.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DERNull.java
@@ -20,18 +20,18 @@
     {
     }
 
-    boolean isConstructed()
+    boolean encodeConstructed()
     {
         return false;
     }
 
-    int encodedLength()
+    int encodedLength(boolean withTag)
     {
-        return 2;
+        return ASN1OutputStream.getLengthOfEncodingDL(withTag, 0);
     }
 
     void encode(ASN1OutputStream out, boolean withTag) throws IOException
     {
-        out.writeEncoded(withTag, BERTags.NULL, zeroBytes);
+        out.writeEncodingDL(withTag, BERTags.NULL, zeroBytes);
     }
 }
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DERNumericString.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DERNumericString.java
index a573f31..6c39623 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DERNumericString.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DERNumericString.java
@@ -1,11 +1,6 @@
 /* GENERATED SOURCE. DO NOT MODIFY. */
 package com.android.internal.org.bouncycastle.asn1;
 
-import java.io.IOException;
-
-import com.android.internal.org.bouncycastle.util.Arrays;
-import com.android.internal.org.bouncycastle.util.Strings;
-
 /**
  * DER NumericString object - this is an ascii string of characters {0,1,2,3,4,5,6,7,8,9, }.
  * ASN.1 NUMERIC-STRING object.
@@ -18,81 +13,12 @@
  * @hide This class is not part of the Android public SDK API
  */
 public class DERNumericString
-    extends ASN1Primitive
-    implements ASN1String
+    extends ASN1NumericString
 {
-    private final byte[]  string;
-
-    /**
-     * Return a Numeric string from the passed in object
-     *
-     * @param obj a DERNumericString or an object that can be converted into one.
-     * @exception IllegalArgumentException if the object cannot be converted.
-     * @return a DERNumericString instance, or null
-     */
-    public static DERNumericString getInstance(
-        Object  obj)
-    {
-        if (obj == null || obj instanceof DERNumericString)
-        {
-            return (DERNumericString)obj;
-        }
-
-        if (obj instanceof byte[])
-        {
-            try
-            {
-                return (DERNumericString)fromByteArray((byte[])obj);
-            }
-            catch (Exception e)
-            {
-                throw new IllegalArgumentException("encoding error in getInstance: " + e.toString());
-            }
-        }
-
-        throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
-    }
-
-    /**
-     * Return an Numeric String from a tagged object.
-     *
-     * @param obj the tagged object holding the object we want
-     * @param explicit true if the object is meant to be explicitly
-     *              tagged false otherwise.
-     * @exception IllegalArgumentException if the tagged object cannot
-     *               be converted.
-     * @return a DERNumericString instance, or null.
-     */
-    public static DERNumericString getInstance(
-        ASN1TaggedObject obj,
-        boolean          explicit)
-    {
-        ASN1Primitive o = obj.getObject();
-
-        if (explicit || o instanceof DERNumericString)
-        {
-            return getInstance(o);
-        }
-        else
-        {
-            return new DERNumericString(ASN1OctetString.getInstance(o).getOctets());
-        }
-    }
-
-    /**
-     * Basic constructor - with bytes.
-     */
-    DERNumericString(
-        byte[]   string)
-    {
-        this.string = string;
-    }
-
     /**
      * Basic constructor -  without validation..
      */
-    public DERNumericString(
-        String   string)
+    public DERNumericString(String string)
     {
         this(string, false);
     }
@@ -105,92 +31,13 @@
      * @throws IllegalArgumentException if validate is true and the string
      * contains characters that should not be in a NumericString.
      */
-    public DERNumericString(
-        String   string,
-        boolean  validate)
+    public DERNumericString(String string, boolean validate)
     {
-        if (validate && !isNumericString(string))
-        {
-            throw new IllegalArgumentException("string contains illegal characters");
-        }
-
-        this.string = Strings.toByteArray(string);
+        super(string, validate);
     }
 
-    public String getString()
+    DERNumericString(byte[] contents, boolean clone)
     {
-        return Strings.fromByteArray(string);
-    }
-
-    public String toString()
-    {
-        return getString();
-    }
-
-    public byte[] getOctets()
-    {
-        return Arrays.clone(string);
-    }
-
-    boolean isConstructed()
-    {
-        return false;
-    }
-
-    int encodedLength()
-    {
-        return 1 + StreamUtil.calculateBodyLength(string.length) + string.length;
-    }
-
-    void encode(ASN1OutputStream out, boolean withTag) throws IOException
-    {
-        out.writeEncoded(withTag, BERTags.NUMERIC_STRING, string);
-    }
-
-    public int hashCode()
-    {
-        return Arrays.hashCode(string);
-    }
-
-    boolean asn1Equals(
-        ASN1Primitive o)
-    {
-        if (!(o instanceof DERNumericString))
-        {
-            return false;
-        }
-
-        DERNumericString  s = (DERNumericString)o;
-
-        return Arrays.areEqual(string, s.string);
-    }
-
-    /**
-     * Return true if the string can be represented as a NumericString ('0'..'9', ' ')
-     *
-     * @param str string to validate.
-     * @return true if numeric, fale otherwise.
-     */
-    public static boolean isNumericString(
-        String  str)
-    {
-        for (int i = str.length() - 1; i >= 0; i--)
-        {
-            char    ch = str.charAt(i);
-
-            if (ch > 0x007f)
-            {
-                return false;
-            }
-
-            if (('0' <= ch && ch <= '9') || ch == ' ')
-            {
-                continue;
-            }
-
-            return false;
-        }
-
-        return true;
+        super(contents, clone);
     }
 }
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DERObjectIdentifier.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DERObjectIdentifier.java
deleted file mode 100644
index 1f497c5..0000000
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DERObjectIdentifier.java
+++ /dev/null
@@ -1,26 +0,0 @@
-/* GENERATED SOURCE. DO NOT MODIFY. */
-package com.android.internal.org.bouncycastle.asn1;
-
-/**
- *
- * @deprecated Use ASN1ObjectIdentifier instead of this,
- * @hide This class is not part of the Android public SDK API
- */
-public class DERObjectIdentifier
-    extends ASN1ObjectIdentifier
-{
-    public DERObjectIdentifier(String identifier)
-    {
-        super(identifier);
-    }
-
-    DERObjectIdentifier(byte[] bytes)
-    {
-        super(bytes);
-    }
-
-    DERObjectIdentifier(ASN1ObjectIdentifier oid, String branch)
-    {
-        super(oid, branch);
-    }
-}
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DEROctetString.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DEROctetString.java
index 7b2dc38..5d6aaea 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DEROctetString.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DEROctetString.java
@@ -33,19 +33,19 @@
         super(obj.toASN1Primitive().getEncoded(ASN1Encoding.DER));
     }
 
-    boolean isConstructed()
+    boolean encodeConstructed()
     {
         return false;
     }
 
-    int encodedLength()
+    int encodedLength(boolean withTag)
     {
-        return 1 + StreamUtil.calculateBodyLength(string.length) + string.length;
+        return ASN1OutputStream.getLengthOfEncodingDL(withTag, string.length);
     }
 
     void encode(ASN1OutputStream out, boolean withTag) throws IOException
     {
-        out.writeEncoded(withTag, BERTags.OCTET_STRING, string);
+        out.writeEncodingDL(withTag, BERTags.OCTET_STRING, string);
     }
 
     ASN1Primitive toDERObject()
@@ -58,8 +58,13 @@
         return this;
     }
 
-    static void encode(ASN1OutputStream derOut, boolean withTag, byte[] buf, int off, int len) throws IOException
+    static void encode(ASN1OutputStream out, boolean withTag, byte[] buf, int off, int len) throws IOException
     {
-        derOut.writeEncoded(withTag, BERTags.OCTET_STRING, buf, off, len);
+        out.writeEncodingDL(withTag, BERTags.OCTET_STRING, buf, off, len);
+    }
+
+    static int encodedLength(boolean withTag, int contentsLength)
+    {
+        return ASN1OutputStream.getLengthOfEncodingDL(withTag, contentsLength);
     }
 }
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DEROctetStringParser.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DEROctetStringParser.java
index db2da30..72b3b16 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DEROctetStringParser.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DEROctetStringParser.java
@@ -6,6 +6,8 @@
 
 /**
  * Parser for DER encoded OCTET STRINGS
+ * 
+ * @deprecated Check for 'ASN1OctetStringParser' instead 
  * @hide This class is not part of the Android public SDK API
  */
 public class DEROctetStringParser
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DEROutputStream.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DEROutputStream.java
index 6e08148..80c7e3a 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DEROutputStream.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DEROutputStream.java
@@ -18,18 +18,32 @@
         super(os);
     }
 
-    void writePrimitive(ASN1Primitive primitive, boolean withTag) throws IOException
-    {
-        primitive.toDERObject().encode(this, withTag);
-    }
-
     DEROutputStream getDERSubStream()
     {
         return this;
     }
 
-    ASN1OutputStream getDLSubStream()
+    void writeElements(ASN1Encodable[] elements)
+        throws IOException
     {
-        return this;
+        for (int i = 0, count = elements.length; i < count; ++i)
+        {
+            elements[i].toASN1Primitive().toDERObject().encode(this, true);
+        }
+    }
+
+    void writePrimitive(ASN1Primitive primitive, boolean withTag) throws IOException
+    {
+        primitive.toDERObject().encode(this, withTag);
+    }
+
+    void writePrimitives(ASN1Primitive[] primitives)
+        throws IOException
+    {
+        int count = primitives.length;
+        for (int i = 0; i < count; ++i)
+        {
+            primitives[i].toDERObject().encode(this, true);
+        }
     }
 }
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DERPrintableString.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DERPrintableString.java
index e885eb7..0a7df1a 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DERPrintableString.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DERPrintableString.java
@@ -1,11 +1,6 @@
 /* GENERATED SOURCE. DO NOT MODIFY. */
 package com.android.internal.org.bouncycastle.asn1;
 
-import java.io.IOException;
-
-import com.android.internal.org.bouncycastle.util.Arrays;
-import com.android.internal.org.bouncycastle.util.Strings;
-
 /**
  * DER PrintableString object.
  * <p>
@@ -34,76 +29,8 @@
  * @hide This class is not part of the Android public SDK API
  */
 public class DERPrintableString
-    extends ASN1Primitive
-    implements ASN1String
+    extends ASN1PrintableString
 {
-    private final byte[]  string;
-
-    /**
-     * Return a printable string from the passed in object.
-     *
-     * @param obj a DERPrintableString or an object that can be converted into one.
-     * @exception IllegalArgumentException if the object cannot be converted.
-     * @return a DERPrintableString instance, or null.
-     */
-    public static DERPrintableString getInstance(
-        Object  obj)
-    {
-        if (obj == null || obj instanceof DERPrintableString)
-        {
-            return (DERPrintableString)obj;
-        }
-
-        if (obj instanceof byte[])
-        {
-            try
-            {
-                return (DERPrintableString)fromByteArray((byte[])obj);
-            }
-            catch (Exception e)
-            {
-                throw new IllegalArgumentException("encoding error in getInstance: " + e.toString());
-            }
-        }
-
-        throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
-    }
-
-    /**
-     * Return a Printable String from a tagged object.
-     *
-     * @param obj the tagged object holding the object we want
-     * @param explicit true if the object is meant to be explicitly
-     *              tagged false otherwise.
-     * @exception IllegalArgumentException if the tagged object cannot
-     *               be converted.
-     * @return a DERPrintableString instance, or null.
-     */
-    public static DERPrintableString getInstance(
-        ASN1TaggedObject obj,
-        boolean          explicit)
-    {
-        ASN1Primitive o = obj.getObject();
-
-        if (explicit || o instanceof DERPrintableString)
-        {
-            return getInstance(o);
-        }
-        else
-        {
-            return new DERPrintableString(ASN1OctetString.getInstance(o).getOctets());
-        }
-    }
-
-    /**
-     * Basic constructor - byte encoded string.
-     */
-    DERPrintableString(
-        byte[]   string)
-    {
-        this.string = string;
-    }
-
     /**
      * Basic constructor - this does not validate the string
      */
@@ -125,115 +52,11 @@
         String   string,
         boolean  validate)
     {
-        if (validate && !isPrintableString(string))
-        {
-            throw new IllegalArgumentException("string contains illegal characters");
-        }
-
-        this.string = Strings.toByteArray(string);
+        super(string, validate);
     }
 
-    public String getString()
+    DERPrintableString(byte[] contents, boolean clone)
     {
-        return Strings.fromByteArray(string);
-    }
-
-    public byte[] getOctets()
-    {
-        return Arrays.clone(string);
-    }
-
-    boolean isConstructed()
-    {
-        return false;
-    }
-
-    int encodedLength()
-    {
-        return 1 + StreamUtil.calculateBodyLength(string.length) + string.length;
-    }
-
-    void encode(ASN1OutputStream out, boolean withTag) throws IOException
-    {
-        out.writeEncoded(withTag, BERTags.PRINTABLE_STRING, string);
-    }
-
-    public int hashCode()
-    {
-        return Arrays.hashCode(string);
-    }
-
-    boolean asn1Equals(
-        ASN1Primitive o)
-    {
-        if (!(o instanceof DERPrintableString))
-        {
-            return false;
-        }
-
-        DERPrintableString  s = (DERPrintableString)o;
-
-        return Arrays.areEqual(string, s.string);
-    }
-
-    public String toString()
-    {
-        return getString();
-    }
-
-    /**
-     * return true if the passed in String can be represented without
-     * loss as a PrintableString, false otherwise.
-     *
-     * @return true if in printable set, false otherwise.
-     */
-    public static boolean isPrintableString(
-        String  str)
-    {
-        for (int i = str.length() - 1; i >= 0; i--)
-        {
-            char    ch = str.charAt(i);
-
-            if (ch > 0x007f)
-            {
-                return false;
-            }
-
-            if ('a' <= ch && ch <= 'z')
-            {
-                continue;
-            }
-
-            if ('A' <= ch && ch <= 'Z')
-            {
-                continue;
-            }
-
-            if ('0' <= ch && ch <= '9')
-            {
-                continue;
-            }
-
-            switch (ch)
-            {
-            case ' ':
-            case '\'':
-            case '(':
-            case ')':
-            case '+':
-            case '-':
-            case '.':
-            case ':':
-            case '=':
-            case '?':
-            case '/':
-            case ',':
-                continue;
-            }
-
-            return false;
-        }
-
-        return true;
+        super(contents, clone);
     }
 }
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DERSequence.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DERSequence.java
index d1593ef..2fb5f49 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DERSequence.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DERSequence.java
@@ -18,7 +18,7 @@
         return (DERSequence)seq.toDERObject();
     }
 
-    private int bodyLength = -1;
+    private int contentsLength = -1;
 
     /**
      * Create an empty sequence
@@ -59,9 +59,9 @@
         super(elements, clone);
     }
 
-    private int getBodyLength() throws IOException
+    private int getContentsLength() throws IOException
     {
-        if (bodyLength < 0)
+        if (contentsLength < 0)
         {
             int count = elements.length;
             int totalLength = 0;
@@ -69,20 +69,18 @@
             for (int i = 0; i < count; ++i)
             {
                 ASN1Primitive derObject = elements[i].toASN1Primitive().toDERObject();
-                totalLength += derObject.encodedLength();
+                totalLength += derObject.encodedLength(true);
             }
 
-            this.bodyLength = totalLength;
+            this.contentsLength = totalLength;
         }
 
-        return bodyLength;
+        return contentsLength;
     }
 
-    int encodedLength() throws IOException
+    int encodedLength(boolean withTag) throws IOException
     {
-        int length = getBodyLength();
-
-        return 1 + StreamUtil.calculateBodyLength(length) + length;
+        return ASN1OutputStream.getLengthOfEncodingDL(withTag, getContentsLength());
     }
 
     /*
@@ -95,17 +93,14 @@
      */
     void encode(ASN1OutputStream out, boolean withTag) throws IOException
     {
-        if (withTag)
-        {
-            out.write(BERTags.SEQUENCE | BERTags.CONSTRUCTED);
-        }
+        out.writeIdentifier(withTag, BERTags.CONSTRUCTED | BERTags.SEQUENCE);
 
         DEROutputStream derOut = out.getDERSubStream();
 
         int count = elements.length;
-        if (bodyLength >= 0 || count > 16)
+        if (contentsLength >= 0 || count > 16)
         {
-            out.writeLength(getBodyLength());
+            out.writeDL(getContentsLength());
 
             for (int i = 0; i < count; ++i)
             {
@@ -122,11 +117,11 @@
             {
                 ASN1Primitive derObject = elements[i].toASN1Primitive().toDERObject();
                 derObjects[i] = derObject;
-                totalLength += derObject.encodedLength();
+                totalLength += derObject.encodedLength(true);
             }
 
-            this.bodyLength = totalLength;
-            out.writeLength(totalLength);
+            this.contentsLength = totalLength;
+            out.writeDL(totalLength);
 
             for (int i = 0; i < count; ++i)
             {
@@ -135,6 +130,27 @@
         }
     }
 
+    ASN1BitString toASN1BitString()
+    {
+        return new DERBitString(BERBitString.flattenBitStrings(getConstructedBitStrings()), false);
+    }
+
+    ASN1External toASN1External()
+    {
+        return new DERExternal(this);
+    }
+
+    ASN1OctetString toASN1OctetString()
+    {
+        return new DEROctetString(BEROctetString.flattenOctetStrings(getConstructedOctetStrings()));
+    }
+
+    ASN1Set toASN1Set()
+    {
+        // NOTE: DLSet is intentional, we don't want sorting
+        return new DLSet(false, toArrayInternal());
+    }
+
     ASN1Primitive toDERObject()
     {
         return this;
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DERSequenceParser.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DERSequenceParser.java
deleted file mode 100644
index a023592..0000000
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DERSequenceParser.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/* GENERATED SOURCE. DO NOT MODIFY. */
-package com.android.internal.org.bouncycastle.asn1;
-
-import java.io.IOException;
-
-/**
- * @deprecated Use DLSequenceParser instead
- * @hide This class is not part of the Android public SDK API
- */
-public class DERSequenceParser
-    implements ASN1SequenceParser
-{
-    private ASN1StreamParser _parser;
-
-    DERSequenceParser(ASN1StreamParser parser)
-    {
-        this._parser = parser;
-    }
-
-    /**
-     * Return the next object in the SEQUENCE.
-     *
-     * @return next object in SEQUENCE.
-     * @throws IOException if there is an issue loading the object.
-     */
-    public ASN1Encodable readObject()
-        throws IOException
-    {
-        return _parser.readObject();
-    }
-
-    /**
-     * Return an in memory, encodable, representation of the SEQUENCE.
-     *
-     * @return a DERSequence.
-     * @throws IOException if there is an issue loading the data.
-     */
-    public ASN1Primitive getLoadedObject()
-        throws IOException
-    {
-         return new DLSequence(_parser.readVector());
-    }
-
-    /**
-     * Return a DERSequence representing this parser and its contents.
-     *
-     * @return a DERSequence.
-     */
-    public ASN1Primitive toASN1Primitive()
-    {
-        try
-        {
-            return getLoadedObject();
-        }
-        catch (IOException e)
-        {
-            throw new IllegalStateException(e.getMessage());
-        }
-    }
-}
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DERSet.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DERSet.java
index d6914ad..42c8b44 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DERSet.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DERSet.java
@@ -22,7 +22,7 @@
         return (DERSet)set.toDERObject();
     }
 
-    private int bodyLength = -1;
+    private int contentsLength = -1;
 
     /**
      * create an empty set
@@ -63,9 +63,9 @@
         super(checkSorted(isSorted), elements);
     }
 
-    private int getBodyLength() throws IOException
+    private int getContentsLength() throws IOException
     {
-        if (bodyLength < 0)
+        if (contentsLength < 0)
         {
             int count = elements.length;
             int totalLength = 0;
@@ -73,20 +73,18 @@
             for (int i = 0; i < count; ++i)
             {
                 ASN1Primitive derObject = elements[i].toASN1Primitive().toDERObject();
-                totalLength += derObject.encodedLength();
+                totalLength += derObject.encodedLength(true);
             }
 
-            this.bodyLength = totalLength;
+            this.contentsLength = totalLength;
         }
 
-        return bodyLength;
+        return contentsLength;
     }
 
-    int encodedLength() throws IOException
+    int encodedLength(boolean withTag) throws IOException
     {
-        int length = getBodyLength();
-
-        return 1 + StreamUtil.calculateBodyLength(length) + length;
+        return ASN1OutputStream.getLengthOfEncodingDL(withTag, getContentsLength());
     }
 
     /*
@@ -99,17 +97,14 @@
      */
     void encode(ASN1OutputStream out, boolean withTag) throws IOException
     {
-        if (withTag)
-        {
-            out.write(BERTags.SET | BERTags.CONSTRUCTED);
-        }
+        out.writeIdentifier(withTag, BERTags.CONSTRUCTED | BERTags.SET);
 
         DEROutputStream derOut = out.getDERSubStream();
 
         int count = elements.length;
-        if (bodyLength >= 0 || count > 16)
+        if (contentsLength >= 0 || count > 16)
         {
-            out.writeLength(getBodyLength());
+            out.writeDL(getContentsLength());
 
             for (int i = 0; i < count; ++i)
             {
@@ -126,11 +121,11 @@
             {
                 ASN1Primitive derObject = elements[i].toASN1Primitive().toDERObject();
                 derObjects[i] = derObject;
-                totalLength += derObject.encodedLength();
+                totalLength += derObject.encodedLength(true);
             }
 
-            this.bodyLength = totalLength;
-            out.writeLength(totalLength);
+            this.contentsLength = totalLength;
+            out.writeDL(totalLength);
 
             for (int i = 0; i < count; ++i)
             {
@@ -141,7 +136,7 @@
 
     ASN1Primitive toDERObject()
     {
-        return isSorted ? this : super.toDERObject();
+        return (sortedElements != null) ? this : super.toDERObject();
     }
 
     ASN1Primitive toDLObject()
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DERSetParser.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DERSetParser.java
deleted file mode 100644
index f640c3c..0000000
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DERSetParser.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/* GENERATED SOURCE. DO NOT MODIFY. */
-package com.android.internal.org.bouncycastle.asn1;
-
-import java.io.IOException;
-
-/**
- * @deprecated Use DLSetParser instead
- * @hide This class is not part of the Android public SDK API
- */
-public class DERSetParser
-    implements ASN1SetParser
-{
-    private ASN1StreamParser _parser;
-
-    DERSetParser(ASN1StreamParser parser)
-    {
-        this._parser = parser;
-    }
-
-    /**
-     * Return the next object in the SET.
-     *
-     * @return next object in SET.
-     * @throws IOException if there is an issue loading the object.
-     */
-    public ASN1Encodable readObject()
-        throws IOException
-    {
-        return _parser.readObject();
-    }
-
-    /**
-     * Return an in memory, encodable, representation of the SET.
-     *
-     * @return a DERSet.
-     * @throws IOException if there is an issue loading the data.
-     */
-    public ASN1Primitive getLoadedObject()
-        throws IOException
-    {
-        return new DLSet(_parser.readVector());
-    }
-
-    /**
-     * Return a DERSet representing this parser and its contents.
-     *
-     * @return a DERSet
-     */
-    public ASN1Primitive toASN1Primitive()
-    {
-        try
-        {
-            return getLoadedObject();
-        }
-        catch (IOException e)
-        {
-            throw new ASN1ParsingException(e.getMessage(), e);
-        }
-    }
-}
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DERT61String.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DERT61String.java
index 2dad092..0f5dbc9 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DERT61String.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DERT61String.java
@@ -1,76 +1,22 @@
 /* GENERATED SOURCE. DO NOT MODIFY. */
 package com.android.internal.org.bouncycastle.asn1;
 
-import java.io.IOException;
-
-import com.android.internal.org.bouncycastle.util.Arrays;
-import com.android.internal.org.bouncycastle.util.Strings;
-
 /**
  * DER T61String (also the teletex string), try not to use this if you don't need to. The standard support the encoding for
  * this has been withdrawn.
  * @hide This class is not part of the Android public SDK API
  */
 public class DERT61String
-    extends ASN1Primitive
-    implements ASN1String
+    extends ASN1T61String
 {
-    private byte[] string;
-
     /**
-     * Return a T61 string from the passed in object.
+     * Basic constructor - with string 8 bit assumed.
      *
-     * @param obj a DERT61String or an object that can be converted into one.
-     * @exception IllegalArgumentException if the object cannot be converted.
-     * @return a DERT61String instance, or null
+     * @param string the string to be wrapped.
      */
-    public static DERT61String getInstance(
-        Object  obj)
+    public DERT61String(String string)
     {
-        if (obj == null || obj instanceof DERT61String)
-        {
-            return (DERT61String)obj;
-        }
-
-        if (obj instanceof byte[])
-        {
-            try
-            {
-                return (DERT61String)fromByteArray((byte[])obj);
-            }
-            catch (Exception e)
-            {
-                throw new IllegalArgumentException("encoding error in getInstance: " + e.toString());
-            }
-        }
-
-        throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
-    }
-
-    /**
-     * Return an T61 String from a tagged object.
-     *
-     * @param obj the tagged object holding the object we want
-     * @param explicit true if the object is meant to be explicitly
-     *              tagged false otherwise.
-     * @exception IllegalArgumentException if the tagged object cannot
-     *               be converted.
-     * @return a DERT61String instance, or null
-     */
-    public static DERT61String getInstance(
-        ASN1TaggedObject obj,
-        boolean          explicit)
-    {
-        ASN1Primitive o = obj.getObject();
-
-        if (explicit || o instanceof DERT61String)
-        {
-            return getInstance(o);
-        }
-        else
-        {
-            return new DERT61String(ASN1OctetString.getInstance(o).getOctets());
-        }
+        super(string);
     }
 
     /**
@@ -78,74 +24,13 @@
      *
      * @param string the byte encoding of the string to be wrapped.
      */
-    public DERT61String(
-        byte[]   string)
+    public DERT61String(byte[] string)
     {
-        this.string = Arrays.clone(string);
+        this(string, true);
     }
 
-    /**
-     * Basic constructor - with string 8 bit assumed.
-     *
-     * @param string the string to be wrapped.
-     */
-    public DERT61String(
-        String   string)
+    DERT61String(byte[] contents, boolean clone)
     {
-        this.string = Strings.toByteArray(string);
-    }
-
-    /**
-     * Decode the encoded string and return it, 8 bit encoding assumed.
-     * @return the decoded String
-     */
-    public String getString()
-    {
-        return Strings.fromByteArray(string);
-    }
-
-    public String toString()
-    {
-        return getString();
-    }
-
-    boolean isConstructed()
-    {
-        return false;
-    }
-
-    int encodedLength()
-    {
-        return 1 + StreamUtil.calculateBodyLength(string.length) + string.length;
-    }
-
-    void encode(ASN1OutputStream out, boolean withTag) throws IOException
-    {
-        out.writeEncoded(withTag, BERTags.T61_STRING, string);
-    }
-
-    /**
-     * Return the encoded string as a byte array.
-     * @return the actual bytes making up the encoded body of the T61 string.
-     */
-    public byte[] getOctets()
-    {
-        return Arrays.clone(string);
-    }
-
-    boolean asn1Equals(
-        ASN1Primitive o)
-    {
-        if (!(o instanceof DERT61String))
-        {
-            return false;
-        }
-
-        return Arrays.areEqual(string, ((DERT61String)o).string);
-    }
-    
-    public int hashCode()
-    {
-        return Arrays.hashCode(string);
+        super(contents, clone);
     }
 }
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DERTaggedObject.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DERTaggedObject.java
index 201a7ed..b43afae 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DERTaggedObject.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DERTaggedObject.java
@@ -12,68 +12,94 @@
 public class DERTaggedObject
     extends ASN1TaggedObject
 {
-    /**
-     * @param explicit true if an explicitly tagged object.
-     * @param tagNo the tag number for this object.
-     * @param obj the tagged object.
-     */
-    public DERTaggedObject(
-        boolean       explicit,
-        int           tagNo,
-        ASN1Encodable obj)
-    {
-        super(explicit, tagNo, obj);
-    }
-
     public DERTaggedObject(int tagNo, ASN1Encodable encodable)
     {
         super(true, tagNo, encodable);
     }
 
-    boolean isConstructed()
+    public DERTaggedObject(int tagClass, int tagNo, ASN1Encodable obj)
     {
-        return explicit || obj.toASN1Primitive().toDERObject().isConstructed();
+        super(true, tagClass, tagNo, obj);
     }
 
-    int encodedLength()
-        throws IOException
+    /**
+     * @param explicit true if an explicitly tagged object.
+     * @param tagNo the tag number for this object.
+     * @param obj the tagged object.
+     */
+    public DERTaggedObject(boolean explicit, int tagNo, ASN1Encodable obj)
+    {
+        super(explicit, tagNo, obj);
+    }
+
+    public DERTaggedObject(boolean explicit, int tagClass, int tagNo, ASN1Encodable obj)
+    {
+        super(explicit, tagClass, tagNo, obj);
+    }
+
+    DERTaggedObject(int explicitness, int tagClass, int tagNo, ASN1Encodable obj)
+    {
+        super(explicitness, tagClass, tagNo, obj);
+    }
+
+    boolean encodeConstructed()
+    {
+        return isExplicit() || obj.toASN1Primitive().toDERObject().encodeConstructed();
+    }
+
+    int encodedLength(boolean withTag) throws IOException
     {
         ASN1Primitive primitive = obj.toASN1Primitive().toDERObject();
-        int length = primitive.encodedLength();
+        boolean explicit = isExplicit();
+
+        int length = primitive.encodedLength(explicit);
 
         if (explicit)
         {
-            return StreamUtil.calculateTagLength(tagNo) + StreamUtil.calculateBodyLength(length) + length;
+            length += ASN1OutputStream.getLengthOfDL(length);
         }
-        else
-        {
-            // header length already in calculation
-            length = length - 1;
 
-            return StreamUtil.calculateTagLength(tagNo) + length;
-        }
+        length += withTag ? ASN1OutputStream.getLengthOfIdentifier(tagNo) : 0;
+
+        return length;
     }
 
     void encode(ASN1OutputStream out, boolean withTag) throws IOException
     {
+//      assert out.getClass().isAssignableFrom(DEROutputStream.class);
+
         ASN1Primitive primitive = obj.toASN1Primitive().toDERObject();
+        boolean explicit = isExplicit();
 
-        int flags = BERTags.TAGGED;
-        if (explicit || primitive.isConstructed())
+        if (withTag)
         {
-            flags |= BERTags.CONSTRUCTED;
-        }
+            int flags = tagClass;
+            if (explicit || primitive.encodeConstructed())
+            {
+                flags |= BERTags.CONSTRUCTED;
+            }
 
-        out.writeTag(withTag, flags, tagNo);
+            out.writeIdentifier(true, flags, tagNo);
+        }
 
         if (explicit)
         {
-            out.writeLength(primitive.encodedLength());
+            out.writeDL(primitive.encodedLength(true));
         }
 
         primitive.encode(out.getDERSubStream(), explicit);
     }
 
+    ASN1Sequence rebuildConstructed(ASN1Primitive primitive)
+    {
+        return new DERSequence(primitive);
+    }
+
+    ASN1TaggedObject replaceTag(int tagClass, int tagNo)
+    {
+        return new DERTaggedObject(explicitness, tagClass, tagNo, obj);
+    }
+
     ASN1Primitive toDERObject()
     {
         return this;
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DERTags.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DERTags.java
deleted file mode 100644
index fb68afe..0000000
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DERTags.java
+++ /dev/null
@@ -1,11 +0,0 @@
-/* GENERATED SOURCE. DO NOT MODIFY. */
-package com.android.internal.org.bouncycastle.asn1;
-
-/**
- * @deprecated use BERTags
- * @hide This class is not part of the Android public SDK API
- */
-public interface DERTags
-    extends BERTags
-{
-}
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DERUTF8String.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DERUTF8String.java
index d5f753a..ba25816 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DERUTF8String.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DERUTF8String.java
@@ -1,88 +1,13 @@
 /* GENERATED SOURCE. DO NOT MODIFY. */
 package com.android.internal.org.bouncycastle.asn1;
 
-import java.io.IOException;
-
-import com.android.internal.org.bouncycastle.util.Arrays;
-import com.android.internal.org.bouncycastle.util.Strings;
-
 /**
  * DER UTF8String object.
  * @hide This class is not part of the Android public SDK API
  */
 public class DERUTF8String
-    extends ASN1Primitive
-    implements ASN1String
+    extends ASN1UTF8String
 {
-    private final byte[]  string;
-
-    /**
-     * Return an UTF8 string from the passed in object.
-     *
-     * @param obj a DERUTF8String or an object that can be converted into one.
-     * @exception IllegalArgumentException
-     *                if the object cannot be converted.
-     * @return a DERUTF8String instance, or null
-     */
-    public static DERUTF8String getInstance(Object obj)
-    {
-        if (obj == null || obj instanceof DERUTF8String)
-        {
-            return (DERUTF8String)obj;
-        }
-
-        if (obj instanceof byte[])
-        {
-            try
-            {
-                return (DERUTF8String)fromByteArray((byte[])obj);
-            }
-            catch (Exception e)
-            {
-                throw new IllegalArgumentException("encoding error in getInstance: " + e.toString());
-            }
-        }
-
-        throw new IllegalArgumentException("illegal object in getInstance: "
-                + obj.getClass().getName());
-    }
-
-    /**
-     * Return an UTF8 String from a tagged object.
-     * 
-     * @param obj
-     *            the tagged object holding the object we want
-     * @param explicit
-     *            true if the object is meant to be explicitly tagged false
-     *            otherwise.
-     * @exception IllegalArgumentException
-     *                if the tagged object cannot be converted.
-     * @return a DERUTF8String instance, or null
-     */
-    public static DERUTF8String getInstance(
-        ASN1TaggedObject obj,
-        boolean explicit)
-    {
-        ASN1Primitive o = obj.getObject();
-
-        if (explicit || o instanceof DERUTF8String)
-        {
-            return getInstance(o);
-        }
-        else
-        {
-            return new DERUTF8String(ASN1OctetString.getInstance(o).getOctets());
-        }
-    }
-
-    /*
-     * Basic constructor - byte encoded string.
-     */
-    DERUTF8String(byte[] string)
-    {
-        this.string = string;
-    }
-
     /**
      * Basic constructor
      *
@@ -90,49 +15,11 @@
      */
     public DERUTF8String(String string)
     {
-        this.string = Strings.toUTF8ByteArray(string);
+        super(string);
     }
 
-    public String getString()
+    DERUTF8String(byte[] contents, boolean clone)
     {
-        return Strings.fromUTF8ByteArray(string);
-    }
-
-    public String toString()
-    {
-        return getString();
-    }
-
-    public int hashCode()
-    {
-        return Arrays.hashCode(string);
-    }
-
-    boolean asn1Equals(ASN1Primitive o)
-    {
-        if (!(o instanceof DERUTF8String))
-        {
-            return false;
-        }
-
-        DERUTF8String s = (DERUTF8String)o;
-
-        return Arrays.areEqual(string, s.string);
-    }
-
-    boolean isConstructed()
-    {
-        return false;
-    }
-
-    int encodedLength()
-        throws IOException
-    {
-        return 1 + StreamUtil.calculateBodyLength(string.length) + string.length;
-    }
-
-    void encode(ASN1OutputStream out, boolean withTag) throws IOException
-    {
-        out.writeEncoded(withTag, BERTags.UTF8_STRING, string);
+        super(contents, clone);
     }
 }
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DERUniversalString.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DERUniversalString.java
index b4c2a16..87a86ab 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DERUniversalString.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DERUniversalString.java
@@ -1,150 +1,26 @@
 /* GENERATED SOURCE. DO NOT MODIFY. */
 package com.android.internal.org.bouncycastle.asn1;
 
-import java.io.IOException;
-
-import com.android.internal.org.bouncycastle.util.Arrays;
-
 /**
  * DER UniversalString object - encodes UNICODE (ISO 10646) characters using 32-bit format. In Java we
  * have no way of representing this directly so we rely on byte arrays to carry these.
  * @hide This class is not part of the Android public SDK API
  */
 public class DERUniversalString
-    extends ASN1Primitive
-    implements ASN1String
+    extends ASN1UniversalString
 {
-    private static final char[]  table = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
-    private final byte[] string;
-    
-    /**
-     * Return a Universal String from the passed in object.
-     *
-     * @param obj a DERUniversalString or an object that can be converted into one.
-     * @exception IllegalArgumentException if the object cannot be converted.
-     * @return a DERUniversalString instance, or null
-     */
-    public static DERUniversalString getInstance(
-        Object  obj)
-    {
-        if (obj == null || obj instanceof DERUniversalString)
-        {
-            return (DERUniversalString)obj;
-        }
-
-        if (obj instanceof byte[])
-        {
-            try
-            {
-                return (DERUniversalString)fromByteArray((byte[])obj);
-            }
-            catch (Exception e)
-            {
-                throw new IllegalArgumentException("encoding error getInstance: " + e.toString());
-            }
-        }
-
-        throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
-    }
-
-    /**
-     * Return a Universal String from a tagged object.
-     *
-     * @param obj the tagged object holding the object we want
-     * @param explicit true if the object is meant to be explicitly
-     *              tagged false otherwise.
-     * @exception IllegalArgumentException if the tagged object cannot
-     *               be converted.
-     * @return a DERUniversalString instance, or null
-     */
-    public static DERUniversalString getInstance(
-        ASN1TaggedObject obj,
-        boolean          explicit)
-    {
-        ASN1Primitive o = obj.getObject();
-
-        if (explicit || o instanceof DERUniversalString)
-        {
-            return getInstance(o);
-        }
-        else
-        {
-            return new DERUniversalString(ASN1OctetString.getInstance(o).getOctets());
-        }
-    }
-
     /**
      * Basic constructor - byte encoded string.
      *
      * @param string the byte encoding of the string to be carried in the UniversalString object,
      */
-    public DERUniversalString(
-        byte[]   string)
+    public DERUniversalString(byte[] string)
     {
-        this.string = Arrays.clone(string);
+        this(string, true);
     }
 
-    public String getString()
+    DERUniversalString(byte[] contents, boolean clone)
     {
-        StringBuffer buf = new StringBuffer("#");
-
-        byte[] string;
-        try
-        {
-            string = getEncoded();
-        }
-        catch (IOException e)
-        {
-           throw new ASN1ParsingException("internal error encoding UniversalString");
-        }
-
-        for (int i = 0; i != string.length; i++)
-        {
-            buf.append(table[(string[i] >>> 4) & 0xf]);
-            buf.append(table[string[i] & 0xf]);
-        }
-        
-        return buf.toString();
-    }
-
-    public String toString()
-    {
-        return getString();
-    }
-
-    public byte[] getOctets()
-    {
-        return Arrays.clone(string);
-    }
-
-    boolean isConstructed()
-    {
-        return false;
-    }
-
-    int encodedLength()
-    {
-        return 1 + StreamUtil.calculateBodyLength(string.length) + string.length;
-    }
-
-    void encode(ASN1OutputStream out, boolean withTag) throws IOException
-    {
-        out.writeEncoded(withTag, BERTags.UNIVERSAL_STRING, string);
-    }
-
-    boolean asn1Equals(
-        ASN1Primitive o)
-    {
-        if (!(o instanceof DERUniversalString))
-        {
-            return false;
-        }
-
-        return Arrays.areEqual(string, ((DERUniversalString)o).string);
-    }
-    
-    public int hashCode()
-    {
-        return Arrays.hashCode(string);
+        super(contents, clone);
     }
 }
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DERVideotexString.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DERVideotexString.java
index 155d546..19d2a26 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DERVideotexString.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DERVideotexString.java
@@ -1,126 +1,19 @@
 /* GENERATED SOURCE. DO NOT MODIFY. */
 package com.android.internal.org.bouncycastle.asn1;
 
-import java.io.IOException;
-
-import com.android.internal.org.bouncycastle.util.Arrays;
-import com.android.internal.org.bouncycastle.util.Strings;
-
 /**
  * @hide This class is not part of the Android public SDK API
  */
 public class DERVideotexString
-    extends ASN1Primitive
-    implements ASN1String
+    extends ASN1VideotexString
 {
-    private final byte[] string;
-    
-    /**
-     * return a Videotex String from the passed in object
-     *
-     * @param obj a DERVideotexString or an object that can be converted into one.
-     * @exception IllegalArgumentException if the object cannot be converted.
-     * @return a DERVideotexString instance, or null.
-     */
-    public static DERVideotexString getInstance(
-        Object  obj)
+    public DERVideotexString(byte[] octets)
     {
-        if (obj == null || obj instanceof DERVideotexString)
-        {
-            return (DERVideotexString)obj;
-        }
-
-        if (obj instanceof byte[])
-        {
-            try
-            {
-                return (DERVideotexString)fromByteArray((byte[])obj);
-            }
-            catch (Exception e)
-            {
-                throw new IllegalArgumentException("encoding error in getInstance: " + e.toString());
-            }
-        }
-
-        throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
+        this(octets, true);
     }
 
-    /**
-     * return a Videotex String from a tagged object.
-     *
-     * @param obj the tagged object holding the object we want
-     * @param explicit true if the object is meant to be explicitly
-     *              tagged false otherwise.
-     * @exception IllegalArgumentException if the tagged object cannot
-     *               be converted.
-     * @return a DERVideotexString instance, or null.
-     */
-    public static DERVideotexString getInstance(
-        ASN1TaggedObject obj,
-        boolean          explicit)
+    DERVideotexString(byte[] contents, boolean clone)
     {
-        ASN1Primitive o = obj.getObject();
-
-        if (explicit || o instanceof DERVideotexString)
-        {
-            return getInstance(o);
-        }
-        else
-        {
-            return new DERVideotexString(ASN1OctetString.getInstance(o).getOctets());
-        }
-    }
-
-    /**
-     * basic constructor - with bytes.
-     * @param string the byte encoding of the characters making up the string.
-     */
-    public DERVideotexString(
-        byte[]   string)
-    {
-        this.string = Arrays.clone(string);
-    }
-    
-    public byte[] getOctets()
-    {
-        return Arrays.clone(string);
-    }
-
-    boolean isConstructed()
-    {
-        return false;
-    }
-
-    int encodedLength()
-    {
-        return 1 + StreamUtil.calculateBodyLength(string.length) + string.length;
-    }
-
-    void encode(ASN1OutputStream out, boolean withTag) throws IOException
-    {
-        out.writeEncoded(withTag, BERTags.VIDEOTEX_STRING, string);
-    }
-
-    public int hashCode()
-    {
-        return Arrays.hashCode(string);
-    }
-
-    boolean asn1Equals(
-        ASN1Primitive o)
-    {
-        if (!(o instanceof DERVideotexString))
-        {
-            return false;
-        }
-
-        DERVideotexString  s = (DERVideotexString)o;
-
-        return Arrays.areEqual(string, s.string);
-    }
-
-    public String getString()
-    {
-        return Strings.fromByteArray(string);
+        super(contents, clone);
     }
 }
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DERVisibleString.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DERVisibleString.java
index e402c13..aaa62a0 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DERVisibleString.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DERVisibleString.java
@@ -1,11 +1,6 @@
 /* GENERATED SOURCE. DO NOT MODIFY. */
 package com.android.internal.org.bouncycastle.asn1;
 
-import java.io.IOException;
-
-import com.android.internal.org.bouncycastle.util.Arrays;
-import com.android.internal.org.bouncycastle.util.Strings;
-
 /**
  * DER VisibleString object encoding ISO 646 (ASCII) character code points 32 to 126.
  * <p>
@@ -14,130 +9,20 @@
  * @hide This class is not part of the Android public SDK API
  */
 public class DERVisibleString
-    extends ASN1Primitive
-    implements ASN1String
+    extends ASN1VisibleString
 {
-    private final byte[]  string;
-
-    /**
-     * Return a Visible String from the passed in object.
-     *
-     * @param obj a DERVisibleString or an object that can be converted into one.
-     * @exception IllegalArgumentException if the object cannot be converted.
-     * @return a DERVisibleString instance, or null
-     */
-    public static DERVisibleString getInstance(
-        Object  obj)
-    {
-        if (obj == null || obj instanceof DERVisibleString)
-        {
-            return (DERVisibleString)obj;
-        }
-
-        if (obj instanceof byte[])
-        {
-            try
-            {
-                return (DERVisibleString)fromByteArray((byte[])obj);
-            }
-            catch (Exception e)
-            {
-                throw new IllegalArgumentException("encoding error in getInstance: " + e.toString());
-            }
-        }
-
-        throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
-    }
-
-    /**
-     * Return a Visible String from a tagged object.
-     *
-     * @param obj the tagged object holding the object we want
-     * @param explicit true if the object is meant to be explicitly
-     *              tagged false otherwise.
-     * @exception IllegalArgumentException if the tagged object cannot
-     *               be converted.
-     * @return a DERVisibleString instance, or null
-     */
-    public static DERVisibleString getInstance(
-        ASN1TaggedObject obj,
-        boolean          explicit)
-    {
-        ASN1Primitive o = obj.getObject();
-
-        if (explicit || o instanceof DERVisibleString)
-        {
-            return getInstance(o);
-        }
-        else
-        {
-            return new DERVisibleString(ASN1OctetString.getInstance(o).getOctets());
-        }
-    }
-
-    /*
-     * Basic constructor - byte encoded string.
-     */
-    DERVisibleString(
-        byte[]   string)
-    {
-        this.string = string;
-    }
-
     /**
      * Basic constructor
      *
      * @param string the string to be carried in the VisibleString object,
      */
-    public DERVisibleString(
-        String   string)
+    public DERVisibleString(String string)
     {
-        this.string = Strings.toByteArray(string);
+        super(string);
     }
 
-    public String getString()
+    DERVisibleString(byte[] contents, boolean clone)
     {
-        return Strings.fromByteArray(string);
-    }
-
-    public String toString()
-    {
-        return getString();
-    }
-
-    public byte[] getOctets()
-    {
-        return Arrays.clone(string);
-    }
-
-    boolean isConstructed()
-    {
-        return false;
-    }
-
-    int encodedLength()
-    {
-        return 1 + StreamUtil.calculateBodyLength(string.length) + string.length;
-    }
-
-    void encode(ASN1OutputStream out, boolean withTag) throws IOException
-    {
-        out.writeEncoded(withTag, BERTags.VISIBLE_STRING, this.string);
-    }
-
-    boolean asn1Equals(
-        ASN1Primitive o)
-    {
-        if (!(o instanceof DERVisibleString))
-        {
-            return false;
-        }
-
-        return Arrays.areEqual(string, ((DERVisibleString)o).string);
-    }
-    
-    public int hashCode()
-    {
-        return Arrays.hashCode(string);
+        super(contents, clone);
     }
 }
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DLApplicationSpecific.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DLApplicationSpecific.java
deleted file mode 100644
index 174ef82..0000000
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DLApplicationSpecific.java
+++ /dev/null
@@ -1,126 +0,0 @@
-/* GENERATED SOURCE. DO NOT MODIFY. */
-package com.android.internal.org.bouncycastle.asn1;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-
-/**
- * A DER encoding version of an application specific object.
- * @hide This class is not part of the Android public SDK API
- */
-public class DLApplicationSpecific
-    extends ASN1ApplicationSpecific
-{
-    DLApplicationSpecific(
-        boolean isConstructed,
-        int     tag,
-        byte[]  octets)
-    {
-        super(isConstructed, tag, octets);
-    }
-
-    /**
-     * Create an application specific object from the passed in data. This will assume
-     * the data does not represent a constructed object.
-     *
-     * @param tag the tag number for this object.
-     * @param octets the encoding of the object's body.
-     */
-    public DLApplicationSpecific(
-        int    tag,
-        byte[] octets)
-    {
-        this(false, tag, octets);
-    }
-
-    /**
-     * Create an application specific object with a tagging of explicit/constructed.
-     *
-     * @param tag the tag number for this object.
-     * @param object the object to be contained.
-     */
-    public DLApplicationSpecific(
-        int           tag,
-        ASN1Encodable object)
-        throws IOException
-    {
-        this(true, tag, object);
-    }
-
-    /**
-     * Create an application specific object with the tagging style given by the value of constructed.
-     *
-     * @param constructed true if the object is constructed.
-     * @param tag the tag number for this object.
-     * @param object the object to be contained.
-     */
-    public DLApplicationSpecific(
-        boolean      constructed,
-        int          tag,
-        ASN1Encodable object)
-        throws IOException
-    {
-        super(constructed || object.toASN1Primitive().isConstructed(), tag, getEncoding(constructed, object));
-    }
-
-    private static byte[] getEncoding(boolean explicit, ASN1Encodable object)
-        throws IOException
-    {
-        byte[] data = object.toASN1Primitive().getEncoded(ASN1Encoding.DL);
-
-        if (explicit)
-        {
-            return data;
-        }
-        else
-        {
-            int lenBytes = getLengthOfHeader(data);
-            byte[] tmp = new byte[data.length - lenBytes];
-            System.arraycopy(data, lenBytes, tmp, 0, tmp.length);
-            return tmp;
-        }
-    }
-
-    /**
-     * Create an application specific object which is marked as constructed
-     *
-     * @param tagNo the tag number for this object.
-     * @param vec the objects making up the application specific object.
-     */
-    public DLApplicationSpecific(int tagNo, ASN1EncodableVector vec)
-    {
-        super(true, tagNo, getEncodedVector(vec));
-    }
-
-    private static byte[] getEncodedVector(ASN1EncodableVector vec)
-    {
-        ByteArrayOutputStream bOut = new ByteArrayOutputStream();
-
-        for (int i = 0; i != vec.size(); i++)
-        {
-            try
-            {
-                bOut.write(((ASN1Object)vec.get(i)).getEncoded(ASN1Encoding.DL));
-            }
-            catch (IOException e)
-            {
-                throw new ASN1ParsingException("malformed object: " + e, e);
-            }
-        }
-        return bOut.toByteArray();
-    }
-
-    /* (non-Javadoc)
-     * @see org.bouncycastle.asn1.ASN1Primitive#encode(org.bouncycastle.asn1.DEROutputStream)
-     */
-    void encode(ASN1OutputStream out, boolean withTag) throws IOException
-    {
-        int flags = BERTags.APPLICATION;
-        if (isConstructed)
-        {
-            flags |= BERTags.CONSTRUCTED;
-        }
-
-        out.writeEncoded(withTag, flags, tag, octets);
-    }
-}
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DLBitString.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DLBitString.java
index cb72e95..b5fcbb5 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DLBitString.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DLBitString.java
@@ -10,113 +10,51 @@
 public class DLBitString
     extends ASN1BitString
 {
-    /**
-     * return a Bit String that can be definite-length encoded from the passed in object.
-     *
-     * @param obj a DL or DER BitString or an object that can be converted into one.
-     * @exception IllegalArgumentException if the object cannot be converted.
-     * @return an ASN1BitString instance, or null.
-     */
-    public static ASN1BitString getInstance(
-        Object  obj)
-    {
-        if (obj == null || obj instanceof DLBitString)
-        {
-            return (DLBitString)obj;
-        }
-        if (obj instanceof DERBitString)
-        {
-            return (DERBitString)obj;
-        }
-        if (obj instanceof byte[])
-        {
-            try
-            {
-                return (ASN1BitString)fromByteArray((byte[])obj);
-            }
-            catch (Exception e)
-            {
-                throw new IllegalArgumentException("encoding error in getInstance: " + e.toString());
-            }
-        }
-
-        throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
-    }
-
-    /**
-     * return a Bit String from a tagged object.
-     *
-     * @param obj the tagged object holding the object we want
-     * @param explicit true if the object is meant to be explicitly
-     *              tagged false otherwise.
-     * @exception IllegalArgumentException if the tagged object cannot
-     *               be converted.
-     * @return an ASN1BitString instance, or null.
-     */
-    public static ASN1BitString getInstance(
-        ASN1TaggedObject obj,
-        boolean          explicit)
-    {
-        ASN1Primitive o = obj.getObject();
-
-        if (explicit || o instanceof DLBitString)
-        {
-            return getInstance(o);
-        }
-        else
-        {
-            return fromOctetString(ASN1OctetString.getInstance(o).getOctets());
-        }
-    }
-
-    protected DLBitString(byte data, int padBits)
-    {
-        super(data, padBits);
-    }
-
-    /**
-     * @param data the octets making up the bit string.
-     * @param padBits the number of extra bits at the end of the string.
-     */
-    public DLBitString(
-        byte[]  data,
-        int     padBits)
-    {
-        super(data, padBits);
-    }
-
-    public DLBitString(
-        byte[]  data)
+    public DLBitString(byte[] data)
     {
         this(data, 0);
     }
 
-    public DLBitString(
-        int value)
+    public DLBitString(byte data, int padBits)
     {
+        super(data, padBits);
+    }
+
+    public DLBitString(byte[] data, int padBits)
+    {
+        super(data, padBits);
+    }
+
+    public DLBitString(int value)
+    {
+        // TODO[asn1] Unify in single allocation of 'contents'
         super(getBytes(value), getPadBits(value));
     }
 
-    public DLBitString(
-        ASN1Encodable obj)
-        throws IOException
+    public DLBitString(ASN1Encodable obj) throws IOException
     {
+        // TODO[asn1] Unify in single allocation of 'contents'
         super(obj.toASN1Primitive().getEncoded(ASN1Encoding.DER), 0);
     }
 
-    boolean isConstructed()
+    DLBitString(byte[] contents, boolean check)
+    {
+        super(contents, check);
+    }
+
+    boolean encodeConstructed()
     {
         return false;
     }
 
-    int encodedLength()
+    int encodedLength(boolean withTag)
     {
-        return 1 + StreamUtil.calculateBodyLength(data.length + 1) + data.length + 1;
+        return ASN1OutputStream.getLengthOfEncodingDL(withTag, contents.length);
     }
 
     void encode(ASN1OutputStream out, boolean withTag) throws IOException
     {
-        out.writeEncoded(withTag, BERTags.BIT_STRING, (byte)padBits, data);
+        out.writeEncodingDL(withTag, BERTags.BIT_STRING, contents);
     }
 
     ASN1Primitive toDLObject()
@@ -124,21 +62,19 @@
         return this;
     }
 
-    static DLBitString fromOctetString(byte[] bytes)
+    static int encodedLength(boolean withTag, int contentsLength)
     {
-        if (bytes.length < 1)
-        {
-            throw new IllegalArgumentException("truncated BIT STRING detected");
-        }
+        return ASN1OutputStream.getLengthOfEncodingDL(withTag, contentsLength);
+    }
 
-        int padBits = bytes[0];
-        byte[] data = new byte[bytes.length - 1];
+    static void encode(ASN1OutputStream out, boolean withTag, byte[] buf, int off, int len) throws IOException
+    {
+        out.writeEncodingDL(withTag, BERTags.BIT_STRING, buf, off, len);
+    }
 
-        if (data.length != 0)
-        {
-            System.arraycopy(bytes, 1, data, 0, bytes.length - 1);
-        }
-
-        return new DLBitString(data, padBits);
+    static void encode(ASN1OutputStream out, boolean withTag, byte pad, byte[] buf, int off, int len)
+        throws IOException
+    {
+        out.writeEncodingDL(withTag, BERTags.BIT_STRING, pad, buf, off, len);
     }
 }
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DLBitStringParser.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DLBitStringParser.java
new file mode 100644
index 0000000..24efac0
--- /dev/null
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DLBitStringParser.java
@@ -0,0 +1,85 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.asn1;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Parser for a DL encoded BIT STRING.
+ * 
+ * @deprecated Check for 'ASN1BitStringParser' instead 
+ * @hide This class is not part of the Android public SDK API
+ */
+public class DLBitStringParser
+    implements ASN1BitStringParser
+{
+    private final DefiniteLengthInputStream stream;
+    private int padBits = 0;
+
+    DLBitStringParser(
+        DefiniteLengthInputStream stream)
+    {
+        this.stream = stream;
+    }
+
+    public InputStream getBitStream() throws IOException
+    {
+        return getBitStream(false);
+    }
+
+    public InputStream getOctetStream() throws IOException
+    {
+        return getBitStream(true);
+    }
+
+    public int getPadBits()
+    {
+        return padBits;
+    }
+
+    public ASN1Primitive getLoadedObject()
+        throws IOException
+    {
+        return ASN1BitString.createPrimitive(stream.toByteArray());
+    }
+
+    public ASN1Primitive toASN1Primitive()
+    {
+        try
+        {
+            return getLoadedObject();
+        }
+        catch (IOException e)
+        {
+            throw new ASN1ParsingException("IOException converting stream to byte array: " + e.getMessage(), e);
+        }
+    }
+
+    private InputStream getBitStream(boolean octetAligned) throws IOException
+    {
+        int length = stream.getRemaining();
+        if (length < 1)
+        {
+            throw new IllegalStateException("content octets cannot be empty");
+        }
+
+        padBits = stream.read();
+        if (padBits > 0)
+        {
+            if (length < 2)
+            {
+                throw new IllegalStateException("zero length data with non-zero pad bits");
+            }
+            if (padBits > 7)
+            {
+                throw new IllegalStateException("pad bits cannot be greater than 7 or less than 0");
+            }
+            if (octetAligned)
+            {
+                throw new IOException("expected octet-aligned bitstring, but found padBits: " + padBits);
+            }
+        }
+
+        return stream;
+    }
+}
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DLExternal.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DLExternal.java
index 5d8c619..c3a35b9 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DLExternal.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DLExternal.java
@@ -1,9 +1,6 @@
 /* GENERATED SOURCE. DO NOT MODIFY. */
 package com.android.internal.org.bouncycastle.asn1;
 
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-
 /**
  * Class representing the Definite-Length-type External
  * @hide This class is not part of the Android public SDK API
@@ -21,11 +18,30 @@
      * <li> Anything but {@link DERTaggedObject} + data {@link DERTaggedObject} (data value form)</li>
      * </ul>
      *
-     * @throws IllegalArgumentException if input size is wrong, or
+     * @throws IllegalArgumentException if input size is wrong, or input is not an acceptable format
+     * 
+     * @deprecated Use {@link DLExternal#DLExternal(DLSequence)} instead.
      */
     public DLExternal(ASN1EncodableVector vector)
     {
-        super(vector);
+        this(DLFactory.createSequence(vector));
+    }
+
+    /**
+     * Construct a Definite-Length EXTERNAL object, the input sequence must have exactly two elements on it.
+     * <p>
+     * Acceptable input formats are:
+     * <ul>
+     * <li> {@link ASN1ObjectIdentifier} + data {@link DERTaggedObject} (direct reference form)</li>
+     * <li> {@link ASN1Integer} + data {@link DERTaggedObject} (indirect reference form)</li>
+     * <li> Anything but {@link DERTaggedObject} + data {@link DERTaggedObject} (data value form)</li>
+     * </ul>
+     *
+     * @throws IllegalArgumentException if input size is wrong, or input is not an acceptable format
+     */
+    public DLExternal(DLSequence sequence)
+    {
+        super(sequence);
     }
 
     /**
@@ -36,9 +52,10 @@
      * @param dataValueDescriptor The data value descriptor or <code>null</code> if not set.
      * @param externalData The external data in its encoded form.
      */
-    public DLExternal(ASN1ObjectIdentifier directReference, ASN1Integer indirectReference, ASN1Primitive dataValueDescriptor, DERTaggedObject externalData)
+    public DLExternal(ASN1ObjectIdentifier directReference, ASN1Integer indirectReference,
+        ASN1Primitive dataValueDescriptor, DERTaggedObject externalData)
     {
-        this(directReference, indirectReference, dataValueDescriptor, externalData.getTagNo(), externalData.toASN1Primitive());
+        super(directReference, indirectReference, dataValueDescriptor, externalData);
     }
 
     /**
@@ -50,43 +67,35 @@
      * @param encoding The encoding to be used for the external data
      * @param externalData The external data
      */
-    public DLExternal(ASN1ObjectIdentifier directReference, ASN1Integer indirectReference, ASN1Primitive dataValueDescriptor, int encoding, ASN1Primitive externalData)
+    public DLExternal(ASN1ObjectIdentifier directReference, ASN1Integer indirectReference,
+        ASN1Primitive dataValueDescriptor, int encoding, ASN1Primitive externalData)
     {
         super(directReference, indirectReference, dataValueDescriptor, encoding, externalData);
     }
 
+    ASN1Sequence buildSequence()
+    {
+        ASN1EncodableVector v = new ASN1EncodableVector(4);
+        if (directReference != null)
+        {
+            v.add(directReference);
+        }
+        if (indirectReference != null)
+        {
+            v.add(indirectReference);
+        }
+        if (dataValueDescriptor != null)
+        {
+            v.add(dataValueDescriptor.toDLObject());
+        }
+
+        v.add(new DLTaggedObject(0 == encoding, encoding, externalContent));
+
+        return new DLSequence(v);
+    }
+
     ASN1Primitive toDLObject()
     {
         return this;
     }
-
-    int encodedLength()
-        throws IOException
-    {
-        return this.getEncoded().length;
-    }
-
-    /* (non-Javadoc)
-     * @see org.bouncycastle.asn1.ASN1Primitive#encode(org.bouncycastle.asn1.DEROutputStream)
-     */
-    void encode(ASN1OutputStream out, boolean withTag) throws IOException
-    {
-        ByteArrayOutputStream baos = new ByteArrayOutputStream();
-        if (directReference != null)
-        {
-            baos.write(directReference.getEncoded(ASN1Encoding.DL));
-        }
-        if (indirectReference != null)
-        {
-            baos.write(indirectReference.getEncoded(ASN1Encoding.DL));
-        }
-        if (dataValueDescriptor != null)
-        {
-            baos.write(dataValueDescriptor.getEncoded(ASN1Encoding.DL));
-        }
-        ASN1TaggedObject obj = new DLTaggedObject(true, encoding, externalContent);
-        baos.write(obj.getEncoded(ASN1Encoding.DL));
-        
-        out.writeEncoded(withTag, BERTags.CONSTRUCTED, BERTags.EXTERNAL, baos.toByteArray());
-    }
 }
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DLFactory.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DLFactory.java
index 5f8d520..509eeb1 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DLFactory.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DLFactory.java
@@ -3,10 +3,10 @@
 
 class DLFactory
 {
-    static final ASN1Sequence EMPTY_SEQUENCE = new DLSequence();
-    static final ASN1Set EMPTY_SET = new DLSet();
+    static final DLSequence EMPTY_SEQUENCE = new DLSequence();
+    static final DLSet EMPTY_SET = new DLSet();
 
-    static ASN1Sequence createSequence(ASN1EncodableVector v)
+    static DLSequence createSequence(ASN1EncodableVector v)
     {
         if (v.size() < 1)
         {
@@ -16,7 +16,7 @@
         return new DLSequence(v);
     }
 
-    static ASN1Set createSet(ASN1EncodableVector v)
+    static DLSet createSet(ASN1EncodableVector v)
     {
         if (v.size() < 1)
         {
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DLOutputStream.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DLOutputStream.java
index 99a0abd..948abc4 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DLOutputStream.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DLOutputStream.java
@@ -15,13 +15,32 @@
         super(os);
     }
 
+    DLOutputStream getDLSubStream()
+    {
+        return this;
+    }
+
+    void writeElements(ASN1Encodable[] elements)
+        throws IOException
+    {
+        for (int i = 0, count = elements.length; i < count; ++i)
+        {
+            elements[i].toASN1Primitive().toDLObject().encode(this, true);
+        }
+    }
+
     void writePrimitive(ASN1Primitive primitive, boolean withTag) throws IOException
     {
         primitive.toDLObject().encode(this, withTag);
     }
 
-    ASN1OutputStream getDLSubStream()
+    void writePrimitives(ASN1Primitive[] primitives)
+        throws IOException
     {
-        return this;
+        int count = primitives.length;
+        for (int i = 0; i < count; ++i)
+        {
+            primitives[i].toDLObject().encode(this, true);
+        }
     }
 }
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DLSequence.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DLSequence.java
index b4a770f..0fd60c0 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DLSequence.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DLSequence.java
@@ -10,7 +10,7 @@
 public class DLSequence
     extends ASN1Sequence
 {
-    private int bodyLength = -1;
+    private int contentsLength = -1;
 
     /**
      * Create an empty sequence
@@ -51,9 +51,9 @@
         super(elements, clone);
     }
 
-    private int getBodyLength() throws IOException
+    private int getContentsLength() throws IOException
     {
-        if (bodyLength < 0)
+        if (contentsLength < 0)
         {
             int count = elements.length;
             int totalLength = 0;
@@ -61,20 +61,18 @@
             for (int i = 0; i < count; ++i)
             {
                 ASN1Primitive dlObject = elements[i].toASN1Primitive().toDLObject();
-                totalLength += dlObject.encodedLength();
+                totalLength += dlObject.encodedLength(true);
             }
 
-            this.bodyLength = totalLength;
+            this.contentsLength = totalLength;
         }
 
-        return bodyLength;
+        return contentsLength;
     }
 
-    int encodedLength() throws IOException
+    int encodedLength(boolean withTag) throws IOException
     {
-        int length = getBodyLength();
-
-        return 1 + StreamUtil.calculateBodyLength(length) + length;
+        return ASN1OutputStream.getLengthOfEncodingDL(withTag, getContentsLength());
     }
 
     /**
@@ -87,17 +85,14 @@
      */
     void encode(ASN1OutputStream out, boolean withTag) throws IOException
     {
-        if (withTag)
-        {
-            out.write(BERTags.SEQUENCE | BERTags.CONSTRUCTED);
-        }
+        out.writeIdentifier(withTag, BERTags.CONSTRUCTED | BERTags.SEQUENCE);
 
         ASN1OutputStream dlOut = out.getDLSubStream();
 
         int count = elements.length;
-        if (bodyLength >= 0 || count > 16)
+        if (contentsLength >= 0 || count > 16)
         {
-            out.writeLength(getBodyLength());
+            out.writeDL(getContentsLength());
 
             for (int i = 0; i < count; ++i)
             {
@@ -113,11 +108,11 @@
             {
                 ASN1Primitive dlObject = elements[i].toASN1Primitive().toDLObject();
                 dlObjects[i] = dlObject;
-                totalLength += dlObject.encodedLength();
+                totalLength += dlObject.encodedLength(true);
             }
 
-            this.bodyLength = totalLength;
-            out.writeLength(totalLength);
+            this.contentsLength = totalLength;
+            out.writeDL(totalLength);
 
             for (int i = 0; i < count; ++i)
             {
@@ -126,8 +121,29 @@
         }
     }
 
+    ASN1BitString toASN1BitString()
+    {
+        return new DLBitString(BERBitString.flattenBitStrings(getConstructedBitStrings()), false);
+    }
+
+    ASN1External toASN1External()
+    {
+        return new DLExternal(this);
+    }
+
+    ASN1OctetString toASN1OctetString()
+    {
+        // NOTE: There is no DLOctetString
+        return new DEROctetString(BEROctetString.flattenOctetStrings(getConstructedOctetStrings()));
+    }
+
+    ASN1Set toASN1Set()
+    {
+        return new DLSet(false, toArrayInternal());
+    }
+
     ASN1Primitive toDLObject()
     {
         return this;
     }
-}
\ No newline at end of file
+}
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DLSequenceParser.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DLSequenceParser.java
index 62158d0..4065398 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DLSequenceParser.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DLSequenceParser.java
@@ -6,7 +6,7 @@
 /**
  * Parser class for DL SEQUENCEs.
  *
- * TODO The class is only publicly visible to support 'instanceof' checks; provide an alternative
+ * @deprecated Check for 'ASN1SequenceParser' instead
  * @hide This class is not part of the Android public SDK API
  */
 public class DLSequenceParser
@@ -40,7 +40,7 @@
     public ASN1Primitive getLoadedObject()
         throws IOException
     {
-         return new DLSequence(_parser.readVector());
+         return DLFactory.createSequence(_parser.readVector());
     }
 
     /**
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DLSet.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DLSet.java
index 543b4f9..968ab84 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DLSet.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DLSet.java
@@ -55,7 +55,7 @@
 public class DLSet
     extends ASN1Set
 {
-    private int bodyLength = -1;
+    private int contentsLength = -1;
 
     /**
      * create an empty set
@@ -93,9 +93,14 @@
         super(isSorted, elements);
     }
 
-    private int getBodyLength() throws IOException
+    DLSet(ASN1Encodable[] elements, ASN1Encodable[] sortedElements)
     {
-        if (bodyLength < 0)
+        super(elements, sortedElements);
+    }
+
+    private int getContentsLength() throws IOException
+    {
+        if (contentsLength < 0)
         {
             int count = elements.length;
             int totalLength = 0;
@@ -103,20 +108,18 @@
             for (int i = 0; i < count; ++i)
             {
                 ASN1Primitive dlObject = elements[i].toASN1Primitive().toDLObject();
-                totalLength += dlObject.encodedLength();
+                totalLength += dlObject.encodedLength(true);
             }
 
-            this.bodyLength = totalLength;
+            this.contentsLength = totalLength;
         }
 
-        return bodyLength;
+        return contentsLength;
     }
 
-    int encodedLength() throws IOException
+    int encodedLength(boolean withTag) throws IOException
     {
-        int length = getBodyLength();
-
-        return 1 + StreamUtil.calculateBodyLength(length) + length;
+        return ASN1OutputStream.getLengthOfEncodingDL(withTag, getContentsLength());
     }
 
     /**
@@ -129,17 +132,14 @@
      */
     void encode(ASN1OutputStream out, boolean withTag) throws IOException
     {
-        if (withTag)
-        {
-            out.write(BERTags.SET | BERTags.CONSTRUCTED);
-        }
+        out.writeIdentifier(withTag, BERTags.CONSTRUCTED | BERTags.SET);
 
         ASN1OutputStream dlOut = out.getDLSubStream();
 
         int count = elements.length;
-        if (bodyLength >= 0 || count > 16)
+        if (contentsLength >= 0 || count > 16)
         {
-            out.writeLength(getBodyLength());
+            out.writeDL(getContentsLength());
 
             for (int i = 0; i < count; ++i)
             {
@@ -155,11 +155,11 @@
             {
                 ASN1Primitive dlObject = elements[i].toASN1Primitive().toDLObject();
                 dlObjects[i] = dlObject;
-                totalLength += dlObject.encodedLength();
+                totalLength += dlObject.encodedLength(true);
             }
 
-            this.bodyLength = totalLength;
-            out.writeLength(totalLength);
+            this.contentsLength = totalLength;
+            out.writeDL(totalLength);
 
             for (int i = 0; i < count; ++i)
             {
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DLSetParser.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DLSetParser.java
index 99d2ad9..f9f8161 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DLSetParser.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DLSetParser.java
@@ -6,7 +6,7 @@
 /**
  * Parser class for DL SETs.
  *
- * TODO The class is only publicly visible to support 'instanceof' checks; provide an alternative
+ * @deprecated Check for 'ASN1SetParser' instead
  * @hide This class is not part of the Android public SDK API
  */
 public class DLSetParser
@@ -40,7 +40,7 @@
     public ASN1Primitive getLoadedObject()
         throws IOException
     {
-        return new DLSet(_parser.readVector());
+        return DLFactory.createSet(_parser.readVector());
     }
 
     /**
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DLTaggedObject.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DLTaggedObject.java
index c6904a4..7ad0342 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DLTaggedObject.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DLTaggedObject.java
@@ -12,60 +12,92 @@
 public class DLTaggedObject
     extends ASN1TaggedObject
 {
+    public DLTaggedObject(int tagNo, ASN1Encodable encodable)
+    {
+        super(true, tagNo, encodable);
+    }
+
+    public DLTaggedObject(int tagClass, int tagNo, ASN1Encodable encodable)
+    {
+        super(true, tagClass, tagNo, encodable);
+    }
+
     /**
      * @param explicit true if an explicitly tagged object.
      * @param tagNo the tag number for this object.
      * @param obj the tagged object.
      */
-    public DLTaggedObject(
-        boolean explicit,
-        int tagNo,
-        ASN1Encodable obj)
+    public DLTaggedObject(boolean explicit, int tagNo, ASN1Encodable obj)
     {
         super(explicit, tagNo, obj);
     }
 
-    boolean isConstructed()
+    public DLTaggedObject(boolean explicit, int tagClass, int tagNo, ASN1Encodable obj)
     {
-        return explicit || obj.toASN1Primitive().toDLObject().isConstructed();
+        super(explicit, tagClass, tagNo, obj);
     }
 
-    int encodedLength()
-        throws IOException
+    DLTaggedObject(int explicitness, int tagClass, int tagNo, ASN1Encodable obj)
     {
-        int length = obj.toASN1Primitive().toDLObject().encodedLength();
+        super(explicitness, tagClass, tagNo, obj);
+    }
+
+    boolean encodeConstructed()
+    {
+        return isExplicit() || obj.toASN1Primitive().toDLObject().encodeConstructed();
+    }
+
+    int encodedLength(boolean withTag) throws IOException
+    {
+        ASN1Primitive primitive = obj.toASN1Primitive().toDLObject();
+        boolean explicit = isExplicit();
+
+        int length = primitive.encodedLength(explicit);
 
         if (explicit)
         {
-            return  StreamUtil.calculateTagLength(tagNo) + StreamUtil.calculateBodyLength(length) + length;
+            length += ASN1OutputStream.getLengthOfDL(length);
         }
-        else
-        {
-            // header length already in calculation
-            length = length - 1;
 
-            return StreamUtil.calculateTagLength(tagNo) + length;
-        }
+        length += withTag ? ASN1OutputStream.getLengthOfIdentifier(tagNo) : 0;
+
+        return length;
     }
 
     void encode(ASN1OutputStream out, boolean withTag) throws IOException
     {
+//        assert out.getClass().isAssignableFrom(DLOutputStream.class);
+
         ASN1Primitive primitive = obj.toASN1Primitive().toDLObject();
+        boolean explicit = isExplicit();
 
-        int flags = BERTags.TAGGED;
-        if (explicit || primitive.isConstructed())
+        if (withTag)
         {
-            flags |= BERTags.CONSTRUCTED;
-        }
+            int flags = tagClass;
+            if (explicit || primitive.encodeConstructed())
+            {
+                flags |= BERTags.CONSTRUCTED;
+            }
 
-        out.writeTag(withTag, flags, tagNo);
+            out.writeIdentifier(true, flags, tagNo);
+        }
 
         if (explicit)
         {
-            out.writeLength(primitive.encodedLength());
+            out.writeDL(primitive.encodedLength(true));
         }
 
-        out.getDLSubStream().writePrimitive(primitive, explicit);
+        primitive.encode(out.getDLSubStream(), explicit);
+    }
+
+    ASN1Sequence rebuildConstructed(ASN1Primitive primitive)
+    {
+        return new DLSequence(primitive);
+    }
+
+    ASN1TaggedObject replaceTag(int tagClass, int tagNo)
+    {
+        return new DLTaggedObject(explicitness, tagClass, tagNo, obj);
     }
 
     ASN1Primitive toDLObject()
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DLTaggedObjectParser.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DLTaggedObjectParser.java
new file mode 100644
index 0000000..8799f11
--- /dev/null
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DLTaggedObjectParser.java
@@ -0,0 +1,74 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.asn1;
+
+import java.io.IOException;
+
+/**
+ * Parser for definite-length tagged objects.
+ */
+class DLTaggedObjectParser
+    extends BERTaggedObjectParser
+{
+    private final boolean _constructed;
+
+    DLTaggedObjectParser(int tagClass, int tagNo, boolean constructed, ASN1StreamParser parser)
+    {
+        super(tagClass, tagNo, parser);
+
+        _constructed = constructed;
+    }
+
+    /**
+     * Return an in-memory, encodable, representation of the tagged object.
+     *
+     * @return an ASN1TaggedObject.
+     * @throws IOException if there is an issue loading the data.
+     */
+    public ASN1Primitive getLoadedObject()
+        throws IOException
+    {
+        return _parser.loadTaggedDL(_tagClass, _tagNo, _constructed);
+    }
+
+    public ASN1Encodable parseBaseUniversal(boolean declaredExplicit, int baseTagNo) throws IOException
+    {
+        if (declaredExplicit)
+        {
+            if (!_constructed)
+            {
+                throw new IOException("Explicit tags must be constructed (see X.690 8.14.2)");
+            }
+
+            return _parser.parseObject(baseTagNo);
+        }
+
+        return _constructed
+            ?  _parser.parseImplicitConstructedDL(baseTagNo)
+            :  _parser.parseImplicitPrimitive(baseTagNo);
+    }
+
+    public ASN1Encodable parseExplicitBaseObject() throws IOException
+    {
+        if (!_constructed)
+        {
+            throw new IOException("Explicit tags must be constructed (see X.690 8.14.2)");
+        }
+
+        return _parser.readObject();
+    }
+
+    public ASN1TaggedObjectParser parseExplicitBaseTagged() throws IOException
+    {
+        if (!_constructed)
+        {
+            throw new IOException("Explicit tags must be constructed (see X.690 8.14.2)");
+        }
+
+        return _parser.parseTaggedObject();
+    }
+
+    public ASN1TaggedObjectParser parseImplicitBaseTagged(int baseTagClass, int baseTagNo) throws IOException
+    {
+        return new DLTaggedObjectParser(baseTagClass, baseTagNo, _constructed, _parser);
+    }
+}
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DefiniteLengthInputStream.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DefiniteLengthInputStream.java
index f951b42..20fc479 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DefiniteLengthInputStream.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DefiniteLengthInputStream.java
@@ -26,18 +26,18 @@
     {
         super(in, limit);
 
-        if (length < 0)
+        if (length <= 0)
         {
-            throw new IllegalArgumentException("negative lengths not allowed");
+            if (length < 0)
+            {
+                throw new IllegalArgumentException("negative lengths not allowed");
+            }
+
+            setParentEofDetect(true);
         }
 
         this._originalLength = length;
         this._remaining = length;
-
-        if (length == 0)
-        {
-            setParentEofDetect(true);
-        }
     }
 
     int getRemaining()
@@ -112,7 +112,7 @@
             throw new IOException("corrupted stream - out of bounds length found: " + _remaining + " >= " + limit);
         }
 
-        if ((_remaining -= Streams.readFully(_in, buf)) != 0)
+        if ((_remaining -= Streams.readFully(_in, buf, 0, buf.length)) != 0)
         {
             throw new EOFException("DEF length " + _originalLength + " object truncated by " + _remaining);
         }
@@ -135,7 +135,7 @@
         }
 
         byte[] bytes = new byte[_remaining];
-        if ((_remaining -= Streams.readFully(_in, bytes)) != 0)
+        if ((_remaining -= Streams.readFully(_in, bytes, 0, bytes.length)) != 0)
         {
             throw new EOFException("DEF length " + _originalLength + " object truncated by " + _remaining);
         }
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/LazyConstructionEnumeration.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/LazyConstructionEnumeration.java
index 31ba897..a6d1a86 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/LazyConstructionEnumeration.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/LazyConstructionEnumeration.java
@@ -41,7 +41,7 @@
         }
         catch (IOException e)
         {
-            throw new ASN1ParsingException("malformed DER construction: " + e, e);
+            throw new ASN1ParsingException("malformed ASN.1: " + e, e);
         }
     }
 }
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/LazyEncodedSequence.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/LazyEncodedSequence.java
index cd914ee..66577bd 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/LazyEncodedSequence.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/LazyEncodedSequence.java
@@ -18,18 +18,24 @@
         // NOTE: Initially, the actual 'elements' will be empty
         super();
 
+        if (null == encoded)
+        {
+            throw new NullPointerException("'encoded' cannot be null");
+        }
+
         this.encoded = encoded;
     }
 
-    public synchronized ASN1Encodable getObjectAt(int index)
+    public ASN1Encodable getObjectAt(int index)
     {
         force();
 
         return super.getObjectAt(index);
     }
 
-    public synchronized Enumeration getObjects()
+    public Enumeration getObjects()
     {
+        byte[] encoded = getContents();
         if (null != encoded)
         {
             return new LazyConstructionEnumeration(encoded);
@@ -38,28 +44,28 @@
         return super.getObjects();
     }
 
-    public synchronized int hashCode()
+    public int hashCode()
     {
         force();
 
         return super.hashCode();
     }
 
-    public synchronized Iterator<ASN1Encodable> iterator()
+    public Iterator<ASN1Encodable> iterator()
     {
         force();
 
         return super.iterator();
     }
 
-    public synchronized int size()
+    public int size()
     {
         force();
 
         return super.size();
     }
 
-    public synchronized ASN1Encodable[] toArray()
+    public ASN1Encodable[] toArray()
     {
         force();
 
@@ -73,27 +79,48 @@
         return super.toArrayInternal();
     }
 
-    synchronized int encodedLength()
+    int encodedLength(boolean withTag)
         throws IOException
     {
+        byte[] encoded = getContents();
         if (null != encoded)
         {
-            return 1 + StreamUtil.calculateBodyLength(encoded.length) + encoded.length;
+            return ASN1OutputStream.getLengthOfEncodingDL(withTag, encoded.length);
         }
 
-        return super.toDLObject().encodedLength();
+        return super.toDLObject().encodedLength(withTag);
     }
 
-    synchronized void encode(ASN1OutputStream out, boolean withTag) throws IOException
+    void encode(ASN1OutputStream out, boolean withTag) throws IOException
     {
+        byte[] encoded = getContents();
         if (null != encoded)
         {
-            out.writeEncoded(withTag, BERTags.SEQUENCE | BERTags.CONSTRUCTED, encoded);
+            out.writeEncodingDL(withTag, BERTags.CONSTRUCTED | BERTags.SEQUENCE, encoded);
+            return;
         }
-        else
-        {
-            super.toDLObject().encode(out, withTag);
-        }
+
+        super.toDLObject().encode(out, withTag);
+    }
+
+    ASN1BitString toASN1BitString()
+    {
+        return ((ASN1Sequence)toDLObject()).toASN1BitString();
+    }
+
+    ASN1External toASN1External()
+    {
+        return ((ASN1Sequence)toDLObject()).toASN1External();
+    }
+
+    ASN1OctetString toASN1OctetString()
+    {
+        return ((ASN1Sequence)toDLObject()).toASN1OctetString();
+    }
+
+    ASN1Set toASN1Set()
+    {
+        return ((ASN1Sequence)toDLObject()).toASN1Set();
     }
 
     synchronized ASN1Primitive toDERObject()
@@ -110,20 +137,28 @@
         return super.toDLObject();
     }
 
-    private void force()
+    private synchronized void force()
     {
         if (null != encoded)
         {
-            ASN1EncodableVector v = new ASN1EncodableVector();
-
-            Enumeration en = new LazyConstructionEnumeration(encoded);
-            while (en.hasMoreElements())
+            ASN1InputStream aIn = new ASN1InputStream(encoded, true);
+            try
             {
-                v.add((ASN1Primitive)en.nextElement());
-            }
+                ASN1EncodableVector v = aIn.readVector();
+                aIn.close();
 
-            this.elements = v.takeElements();
-            this.encoded = null;
+                this.elements = v.takeElements();
+                this.encoded = null;
+            }
+            catch (IOException e)
+            {
+                throw new ASN1ParsingException("malformed ASN.1: " + e, e);
+            }
         }
     }
+
+    private synchronized byte[] getContents()
+    {
+        return encoded;
+    }
 }
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DateUtil.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/LocaleUtil.java
similarity index 76%
rename from repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DateUtil.java
rename to repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/LocaleUtil.java
index 8cecd07..9e832fc 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/DateUtil.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/LocaleUtil.java
@@ -8,13 +8,18 @@
 import java.util.Locale;
 import java.util.Map;
 
-class DateUtil
-{
-    private static Long ZERO = longValueOf(0);
+import com.android.internal.org.bouncycastle.util.Longs;
 
+/**
+ * ASN.1 uses an EN locale for its internal formatting. This class finds the nearest equivalent in the
+ * current JVM to ensure date formats are always respected.
+ * @hide This class is not part of the Android public SDK API
+ */
+public class LocaleUtil
+{
     private static final Map localeCache = new HashMap();
 
-    static Locale EN_Locale = forEN();
+    public static Locale EN_Locale = forEN();
 
     private static Locale forEN()
     {
@@ -53,19 +58,12 @@
                 SimpleDateFormat dateF = new SimpleDateFormat("yyyyMMddHHmmssz");
                 long v = dateF.parse("19700101000000GMT+00:00").getTime();
 
-                if (v == 0)
-                {
-                    adj = ZERO;
-                }
-                else
-                {
-                    adj = longValueOf(v);
-                }
+                adj = longValueOf(v);
 
                 localeCache.put(locale, adj);
             }
 
-            if (adj != ZERO)
+            if (adj.longValue() != 0L)
             {
                 return new Date(date.getTime() - adj.longValue());
             }
@@ -76,6 +74,6 @@
 
     private static Long longValueOf(long v)
     {
-        return Long.valueOf(v);
+        return Longs.valueOf(v);
     }
 }
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/bc/BCObjectIdentifiers.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/bc/BCObjectIdentifiers.java
index 4310f10..6154d33 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/bc/BCObjectIdentifiers.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/bc/BCObjectIdentifiers.java
@@ -4,75 +4,103 @@
 import com.android.internal.org.bouncycastle.asn1.ASN1ObjectIdentifier;
 
 /**
- *  Object Identifiers belonging to iso.org.dod.internet.private.enterprise.legion-of-the-bouncy-castle (1.3.6.1.4.1.22554)
+ * Object Identifiers belonging to iso.org.dod.internet.private.enterprise.legion-of-the-bouncy-castle (1.3.6.1.4.1.22554)
  * @hide This class is not part of the Android public SDK API
  */
 public interface BCObjectIdentifiers
 {
     /**
-     *  iso.org.dod.internet.private.enterprise.legion-of-the-bouncy-castle
-     *<p>
-     *  1.3.6.1.4.1.22554
+     * iso.org.dod.internet.private.enterprise.legion-of-the-bouncy-castle
+     * <p>
+     * 1.3.6.1.4.1.22554
      */
-    public static final ASN1ObjectIdentifier bc = new ASN1ObjectIdentifier("1.3.6.1.4.1.22554");
+    ASN1ObjectIdentifier bc = new ASN1ObjectIdentifier("1.3.6.1.4.1.22554");
 
     /**
      * pbe(1) algorithms
      * <p>
      * 1.3.6.1.4.1.22554.1
      */
-    public static final ASN1ObjectIdentifier bc_pbe        = bc.branch("1");
+    ASN1ObjectIdentifier bc_pbe = bc.branch("1");
 
     /**
      * SHA-1(1)
      * <p>
      * 1.3.6.1.4.1.22554.1.1
      */
-    public static final ASN1ObjectIdentifier bc_pbe_sha1   = bc_pbe.branch("1");
+    ASN1ObjectIdentifier bc_pbe_sha1 = bc_pbe.branch("1");
 
-    /** SHA-2.SHA-256; 1.3.6.1.4.1.22554.1.2.1 */
-    public static final ASN1ObjectIdentifier bc_pbe_sha256 = bc_pbe.branch("2.1");
-    /** SHA-2.SHA-384; 1.3.6.1.4.1.22554.1.2.2 */
-    public static final ASN1ObjectIdentifier bc_pbe_sha384 = bc_pbe.branch("2.2");
-    /** SHA-2.SHA-512; 1.3.6.1.4.1.22554.1.2.3 */
-    public static final ASN1ObjectIdentifier bc_pbe_sha512 = bc_pbe.branch("2.3");
-    /** SHA-2.SHA-224; 1.3.6.1.4.1.22554.1.2.4 */
-    public static final ASN1ObjectIdentifier bc_pbe_sha224 = bc_pbe.branch("2.4");
+    /**
+     * SHA-2.SHA-256; 1.3.6.1.4.1.22554.1.2.1
+     */
+    ASN1ObjectIdentifier bc_pbe_sha256 = bc_pbe.branch("2.1");
+    /**
+     * SHA-2.SHA-384; 1.3.6.1.4.1.22554.1.2.2
+     */
+    ASN1ObjectIdentifier bc_pbe_sha384 = bc_pbe.branch("2.2");
+    /**
+     * SHA-2.SHA-512; 1.3.6.1.4.1.22554.1.2.3
+     */
+    ASN1ObjectIdentifier bc_pbe_sha512 = bc_pbe.branch("2.3");
+    /**
+     * SHA-2.SHA-224; 1.3.6.1.4.1.22554.1.2.4
+     */
+    ASN1ObjectIdentifier bc_pbe_sha224 = bc_pbe.branch("2.4");
 
     /**
      * PKCS-5(1)|PKCS-12(2)
      */
-    /** SHA-1.PKCS5;  1.3.6.1.4.1.22554.1.1.1 */
-    public static final ASN1ObjectIdentifier bc_pbe_sha1_pkcs5    = bc_pbe_sha1.branch("1");
-    /** SHA-1.PKCS12; 1.3.6.1.4.1.22554.1.1.2 */
-    public static final ASN1ObjectIdentifier bc_pbe_sha1_pkcs12   = bc_pbe_sha1.branch("2");
+    /**
+     * SHA-1.PKCS5;  1.3.6.1.4.1.22554.1.1.1
+     */
+    ASN1ObjectIdentifier bc_pbe_sha1_pkcs5 = bc_pbe_sha1.branch("1");
+    /**
+     * SHA-1.PKCS12; 1.3.6.1.4.1.22554.1.1.2
+     */
+    ASN1ObjectIdentifier bc_pbe_sha1_pkcs12 = bc_pbe_sha1.branch("2");
 
-    /** SHA-256.PKCS12; 1.3.6.1.4.1.22554.1.2.1.1 */
-    public static final ASN1ObjectIdentifier bc_pbe_sha256_pkcs5  = bc_pbe_sha256.branch("1");
-    /** SHA-256.PKCS12; 1.3.6.1.4.1.22554.1.2.1.2 */
-    public static final ASN1ObjectIdentifier bc_pbe_sha256_pkcs12 = bc_pbe_sha256.branch("2");
+    /**
+     * SHA-256.PKCS5; 1.3.6.1.4.1.22554.1.2.1.1
+     */
+    ASN1ObjectIdentifier bc_pbe_sha256_pkcs5 = bc_pbe_sha256.branch("1");
+    /**
+     * SHA-256.PKCS12; 1.3.6.1.4.1.22554.1.2.1.2
+     */
+    ASN1ObjectIdentifier bc_pbe_sha256_pkcs12 = bc_pbe_sha256.branch("2");
 
     /**
      * AES(1) . (CBC-128(2)|CBC-192(22)|CBC-256(42))
      */
-    /** 1.3.6.1.4.1.22554.1.1.2.1.2 */
-    public static final ASN1ObjectIdentifier bc_pbe_sha1_pkcs12_aes128_cbc   = bc_pbe_sha1_pkcs12.branch("1.2");
-    /** 1.3.6.1.4.1.22554.1.1.2.1.22 */
-    public static final ASN1ObjectIdentifier bc_pbe_sha1_pkcs12_aes192_cbc   = bc_pbe_sha1_pkcs12.branch("1.22");
-    /** 1.3.6.1.4.1.22554.1.1.2.1.42 */
-    public static final ASN1ObjectIdentifier bc_pbe_sha1_pkcs12_aes256_cbc   = bc_pbe_sha1_pkcs12.branch("1.42");
+    /**
+     * 1.3.6.1.4.1.22554.1.1.2.1.2
+     */
+    ASN1ObjectIdentifier bc_pbe_sha1_pkcs12_aes128_cbc = bc_pbe_sha1_pkcs12.branch("1.2");
+    /**
+     * 1.3.6.1.4.1.22554.1.1.2.1.22
+     */
+    ASN1ObjectIdentifier bc_pbe_sha1_pkcs12_aes192_cbc = bc_pbe_sha1_pkcs12.branch("1.22");
+    /**
+     * 1.3.6.1.4.1.22554.1.1.2.1.42
+     */
+    ASN1ObjectIdentifier bc_pbe_sha1_pkcs12_aes256_cbc = bc_pbe_sha1_pkcs12.branch("1.42");
 
-    /** 1.3.6.1.4.1.22554.1.1.2.2.2 */
-    public static final ASN1ObjectIdentifier bc_pbe_sha256_pkcs12_aes128_cbc = bc_pbe_sha256_pkcs12.branch("1.2");
-    /** 1.3.6.1.4.1.22554.1.1.2.2.22 */
-    public static final ASN1ObjectIdentifier bc_pbe_sha256_pkcs12_aes192_cbc = bc_pbe_sha256_pkcs12.branch("1.22");
-    /** 1.3.6.1.4.1.22554.1.1.2.2.42 */
-    public static final ASN1ObjectIdentifier bc_pbe_sha256_pkcs12_aes256_cbc = bc_pbe_sha256_pkcs12.branch("1.42");
+    /**
+     * 1.3.6.1.4.1.22554.1.1.2.2.2
+     */
+    ASN1ObjectIdentifier bc_pbe_sha256_pkcs12_aes128_cbc = bc_pbe_sha256_pkcs12.branch("1.2");
+    /**
+     * 1.3.6.1.4.1.22554.1.1.2.2.22
+     */
+    ASN1ObjectIdentifier bc_pbe_sha256_pkcs12_aes192_cbc = bc_pbe_sha256_pkcs12.branch("1.22");
+    /**
+     * 1.3.6.1.4.1.22554.1.1.2.2.42
+     */
+    ASN1ObjectIdentifier bc_pbe_sha256_pkcs12_aes256_cbc = bc_pbe_sha256_pkcs12.branch("1.42");
 
     /**
      * signature(2) algorithms
      */
-    public static final ASN1ObjectIdentifier bc_sig        = bc.branch("2");
+    ASN1ObjectIdentifier bc_sig = bc.branch("2");
 
     // BEGIN Android-removed: Unsupported algorithms
     /*
@@ -83,101 +111,329 @@
     public static final ASN1ObjectIdentifier sphincs256_with_BLAKE512        = sphincs256.branch("1");
     public static final ASN1ObjectIdentifier sphincs256_with_SHA512          = sphincs256.branch("2");
     public static final ASN1ObjectIdentifier sphincs256_with_SHA3_512        = sphincs256.branch("3");
+    ASN1ObjectIdentifier sphincs256 = bc_sig.branch("1");
+    ASN1ObjectIdentifier sphincs256_with_BLAKE512 = sphincs256.branch("1");
+    ASN1ObjectIdentifier sphincs256_with_SHA512 = sphincs256.branch("2");
+    ASN1ObjectIdentifier sphincs256_with_SHA3_512 = sphincs256.branch("3");
+     */
 
     /**
      * XMSS
      */
-    public static final ASN1ObjectIdentifier xmss = bc_sig.branch("2");
-    public static final ASN1ObjectIdentifier xmss_SHA256ph = xmss.branch("1");
-    public static final ASN1ObjectIdentifier xmss_SHA512ph = xmss.branch("2");
-    public static final ASN1ObjectIdentifier xmss_SHAKE128ph = xmss.branch("3");
-    public static final ASN1ObjectIdentifier xmss_SHAKE256ph = xmss.branch("4");
-    public static final ASN1ObjectIdentifier xmss_SHA256 = xmss.branch("5");
-    public static final ASN1ObjectIdentifier xmss_SHA512 = xmss.branch("6");
-    public static final ASN1ObjectIdentifier xmss_SHAKE128 = xmss.branch("7");
-    public static final ASN1ObjectIdentifier xmss_SHAKE256 = xmss.branch("8");
+    ASN1ObjectIdentifier xmss = bc_sig.branch("2");
+    ASN1ObjectIdentifier xmss_SHA256ph = xmss.branch("1");
+    ASN1ObjectIdentifier xmss_SHA512ph = xmss.branch("2");
+    ASN1ObjectIdentifier xmss_SHAKE128_512ph = xmss.branch("3");
+    ASN1ObjectIdentifier xmss_SHAKE256_1024ph = xmss.branch("4");
+    ASN1ObjectIdentifier xmss_SHA256 = xmss.branch("5");
+    ASN1ObjectIdentifier xmss_SHA512 = xmss.branch("6");
+    ASN1ObjectIdentifier xmss_SHAKE128 = xmss.branch("7");
+    ASN1ObjectIdentifier xmss_SHAKE256 = xmss.branch("8");
+    ASN1ObjectIdentifier xmss_SHAKE128ph = xmss.branch("9");
+    ASN1ObjectIdentifier xmss_SHAKE256ph = xmss.branch("10");
 
     /**
      * XMSS^MT
      */
-    public static final ASN1ObjectIdentifier xmss_mt = bc_sig.branch("3");
-    public static final ASN1ObjectIdentifier xmss_mt_SHA256ph = xmss_mt.branch("1");
-    public static final ASN1ObjectIdentifier xmss_mt_SHA512ph = xmss_mt.branch("2");
-    public static final ASN1ObjectIdentifier xmss_mt_SHAKE128ph = xmss_mt.branch("3");
-    public static final ASN1ObjectIdentifier xmss_mt_SHAKE256ph = xmss_mt.branch("4");
-    public static final ASN1ObjectIdentifier xmss_mt_SHA256 = xmss_mt.branch("5");
-    public static final ASN1ObjectIdentifier xmss_mt_SHA512 = xmss_mt.branch("6");
-    public static final ASN1ObjectIdentifier xmss_mt_SHAKE128 = xmss_mt.branch("7");
-    public static final ASN1ObjectIdentifier xmss_mt_SHAKE256 = xmss_mt.branch("8");
-
-    // old OIDs.
-    /**
-     * @deprecated use xmss_SHA256ph
-     */
-    public static final ASN1ObjectIdentifier xmss_with_SHA256          = xmss_SHA256ph;
-    /**
-     * @deprecated use xmss_SHA512ph
-     */
-    public static final ASN1ObjectIdentifier xmss_with_SHA512 = xmss_SHA512ph;
-    /**
-     * @deprecated use xmss_SHAKE128ph
-     */
-    public static final ASN1ObjectIdentifier xmss_with_SHAKE128 = xmss_SHAKE128ph;
-    /**
-     * @deprecated use xmss_SHAKE256ph
-     */
-    public static final ASN1ObjectIdentifier xmss_with_SHAKE256        = xmss_SHAKE256ph;
-
-    /**
-     * @deprecated use xmss_mt_SHA256ph
-     */
-    public static final ASN1ObjectIdentifier xmss_mt_with_SHA256          = xmss_mt_SHA256ph;
-    /**
-     * @deprecated use xmss_mt_SHA512ph
-     */
-    public static final ASN1ObjectIdentifier xmss_mt_with_SHA512          = xmss_mt_SHA512ph;
-    /**
-     * @deprecated use xmss_mt_SHAKE128ph
-     */
-    public static final ASN1ObjectIdentifier xmss_mt_with_SHAKE128        = xmss_mt_SHAKE128;
-    /**
-     * @deprecated use xmss_mt_SHAKE256ph
-     */
-    public static final ASN1ObjectIdentifier xmss_mt_with_SHAKE256        = xmss_mt_SHAKE256;
+    ASN1ObjectIdentifier xmss_mt = bc_sig.branch("3");
+    ASN1ObjectIdentifier xmss_mt_SHA256ph = xmss_mt.branch("1");
+    ASN1ObjectIdentifier xmss_mt_SHA512ph = xmss_mt.branch("2");
+    ASN1ObjectIdentifier xmss_mt_SHAKE128_512ph = xmss_mt.branch("3");
+    ASN1ObjectIdentifier xmss_mt_SHAKE256_1024ph = xmss_mt.branch("4");
+    ASN1ObjectIdentifier xmss_mt_SHA256 = xmss_mt.branch("5");
+    ASN1ObjectIdentifier xmss_mt_SHA512 = xmss_mt.branch("6");
+    ASN1ObjectIdentifier xmss_mt_SHAKE128 = xmss_mt.branch("7");
+    ASN1ObjectIdentifier xmss_mt_SHAKE256 = xmss_mt.branch("8");
+    ASN1ObjectIdentifier xmss_mt_SHAKE128ph = xmss_mt.branch("9");
+    ASN1ObjectIdentifier xmss_mt_SHAKE256ph = xmss_mt.branch("10");
 
     /**
      * qTESLA
      */
-    public static final ASN1ObjectIdentifier qTESLA = bc_sig.branch("4");
+    ASN1ObjectIdentifier qTESLA = bc_sig.branch("4");
 
-    public static final ASN1ObjectIdentifier qTESLA_Rnd1_I = qTESLA.branch("1");
-    public static final ASN1ObjectIdentifier qTESLA_Rnd1_III_size = qTESLA.branch("2");
-    public static final ASN1ObjectIdentifier qTESLA_Rnd1_III_speed = qTESLA.branch("3");
-    public static final ASN1ObjectIdentifier qTESLA_Rnd1_p_I = qTESLA.branch("4");
-    public static final ASN1ObjectIdentifier qTESLA_Rnd1_p_III = qTESLA.branch("5");
+    ASN1ObjectIdentifier qTESLA_Rnd1_I = qTESLA.branch("1");
+    ASN1ObjectIdentifier qTESLA_Rnd1_III_size = qTESLA.branch("2");
+    ASN1ObjectIdentifier qTESLA_Rnd1_III_speed = qTESLA.branch("3");
+    ASN1ObjectIdentifier qTESLA_Rnd1_p_I = qTESLA.branch("4");
+    ASN1ObjectIdentifier qTESLA_Rnd1_p_III = qTESLA.branch("5");
 
 
-    public static final ASN1ObjectIdentifier qTESLA_p_I = qTESLA.branch("11");
-    public static final ASN1ObjectIdentifier qTESLA_p_III = qTESLA.branch("12");
+    ASN1ObjectIdentifier qTESLA_p_I = qTESLA.branch("11");
+    ASN1ObjectIdentifier qTESLA_p_III = qTESLA.branch("12");
+
+    /**
+     * SPHINCS+
+     */
+    ASN1ObjectIdentifier sphincsPlus = bc_sig.branch("5");
+    ASN1ObjectIdentifier sphincsPlus_sha2_128s_r3 = sphincsPlus.branch("1");
+    ASN1ObjectIdentifier sphincsPlus_sha2_128f_r3 = sphincsPlus.branch("2");
+    ASN1ObjectIdentifier sphincsPlus_shake_128s_r3 = sphincsPlus.branch("3");
+    ASN1ObjectIdentifier sphincsPlus_shake_128f_r3 = sphincsPlus.branch("4");
+    ASN1ObjectIdentifier sphincsPlus_haraka_128s_r3 = sphincsPlus.branch("5");
+    ASN1ObjectIdentifier sphincsPlus_haraka_128f_r3 = sphincsPlus.branch("6");
+
+    ASN1ObjectIdentifier sphincsPlus_sha2_192s_r3 = sphincsPlus.branch("7");
+    ASN1ObjectIdentifier sphincsPlus_sha2_192f_r3 = sphincsPlus.branch("8");
+    ASN1ObjectIdentifier sphincsPlus_shake_192s_r3 = sphincsPlus.branch("9");
+    ASN1ObjectIdentifier sphincsPlus_shake_192f_r3 = sphincsPlus.branch("10");
+    ASN1ObjectIdentifier sphincsPlus_haraka_192s_r3 = sphincsPlus.branch("11");
+    ASN1ObjectIdentifier sphincsPlus_haraka_192f_r3 = sphincsPlus.branch("12");
+
+    ASN1ObjectIdentifier sphincsPlus_sha2_256s_r3 = sphincsPlus.branch("13");
+    ASN1ObjectIdentifier sphincsPlus_sha2_256f_r3 = sphincsPlus.branch("14");
+    ASN1ObjectIdentifier sphincsPlus_shake_256s_r3 = sphincsPlus.branch("15");
+    ASN1ObjectIdentifier sphincsPlus_shake_256f_r3 = sphincsPlus.branch("16");
+    ASN1ObjectIdentifier sphincsPlus_haraka_256s_r3 = sphincsPlus.branch("17");
+    ASN1ObjectIdentifier sphincsPlus_haraka_256f_r3 = sphincsPlus.branch("18");
+
+    ASN1ObjectIdentifier sphincsPlus_sha2_128s_r3_simple = sphincsPlus.branch("19");
+    ASN1ObjectIdentifier sphincsPlus_sha2_128f_r3_simple = sphincsPlus.branch("20");
+    ASN1ObjectIdentifier sphincsPlus_shake_128s_r3_simple = sphincsPlus.branch("21");
+    ASN1ObjectIdentifier sphincsPlus_shake_128f_r3_simple = sphincsPlus.branch("22");
+    ASN1ObjectIdentifier sphincsPlus_haraka_128s_r3_simple = sphincsPlus.branch("23");
+    ASN1ObjectIdentifier sphincsPlus_haraka_128f_r3_simple = sphincsPlus.branch("24");
+
+    ASN1ObjectIdentifier sphincsPlus_sha2_192s_r3_simple = sphincsPlus.branch("25");
+    ASN1ObjectIdentifier sphincsPlus_sha2_192f_r3_simple = sphincsPlus.branch("26");
+    ASN1ObjectIdentifier sphincsPlus_shake_192s_r3_simple = sphincsPlus.branch("27");
+    ASN1ObjectIdentifier sphincsPlus_shake_192f_r3_simple = sphincsPlus.branch("28");
+    ASN1ObjectIdentifier sphincsPlus_haraka_192s_r3_simple = sphincsPlus.branch("29");
+    ASN1ObjectIdentifier sphincsPlus_haraka_192f_r3_simple = sphincsPlus.branch("30");
+
+    ASN1ObjectIdentifier sphincsPlus_sha2_256s_r3_simple = sphincsPlus.branch("31");
+    ASN1ObjectIdentifier sphincsPlus_sha2_256f_r3_simple = sphincsPlus.branch("32");
+    ASN1ObjectIdentifier sphincsPlus_shake_256s_r3_simple = sphincsPlus.branch("33");
+    ASN1ObjectIdentifier sphincsPlus_shake_256f_r3_simple = sphincsPlus.branch("34");
+    ASN1ObjectIdentifier sphincsPlus_haraka_256s_r3_simple = sphincsPlus.branch("35");
+    ASN1ObjectIdentifier sphincsPlus_haraka_256f_r3_simple = sphincsPlus.branch("36");
+
+
+    ASN1ObjectIdentifier sphincsPlus_interop = new ASN1ObjectIdentifier("1.3.9999.6");
+
+    ASN1ObjectIdentifier sphincsPlus_sha2_128f = new ASN1ObjectIdentifier("1.3.9999.6.4.13");
+    ASN1ObjectIdentifier sphincsPlus_sha2_128s = new ASN1ObjectIdentifier("1.3.9999.6.4.16");
+    ASN1ObjectIdentifier sphincsPlus_sha2_192f = new ASN1ObjectIdentifier("1.3.9999.6.5.10");
+    ASN1ObjectIdentifier sphincsPlus_sha2_192s = new ASN1ObjectIdentifier("1.3.9999.6.5.12");
+    ASN1ObjectIdentifier sphincsPlus_sha2_256f = new ASN1ObjectIdentifier("1.3.9999.6.6.10");
+    ASN1ObjectIdentifier sphincsPlus_sha2_256s = new ASN1ObjectIdentifier("1.3.9999.6.6.12");
+
+    ASN1ObjectIdentifier sphincsPlus_shake_128f = new ASN1ObjectIdentifier("1.3.9999.6.7.13");
+    ASN1ObjectIdentifier sphincsPlus_shake_128s = new ASN1ObjectIdentifier("1.3.9999.6.7.16");
+    ASN1ObjectIdentifier sphincsPlus_shake_192f = new ASN1ObjectIdentifier("1.3.9999.6.8.10");
+    ASN1ObjectIdentifier sphincsPlus_shake_192s = new ASN1ObjectIdentifier("1.3.9999.6.8.12");
+    ASN1ObjectIdentifier sphincsPlus_shake_256f = new ASN1ObjectIdentifier("1.3.9999.6.9.10");
+    ASN1ObjectIdentifier sphincsPlus_shake_256s = new ASN1ObjectIdentifier("1.3.9999.6.9.12");
+
+    /**
+     * Picnic
+     */
+    ASN1ObjectIdentifier picnic = bc_sig.branch("6");
+
+    ASN1ObjectIdentifier picnic_key = picnic.branch("1");
+
+    ASN1ObjectIdentifier picnicl1fs = picnic_key.branch("1");
+    ASN1ObjectIdentifier picnicl1ur = picnic_key.branch("2");
+    ASN1ObjectIdentifier picnicl3fs = picnic_key.branch("3");
+    ASN1ObjectIdentifier picnicl3ur = picnic_key.branch("4");
+    ASN1ObjectIdentifier picnicl5fs = picnic_key.branch("5");
+    ASN1ObjectIdentifier picnicl5ur = picnic_key.branch("6");
+    ASN1ObjectIdentifier picnic3l1 = picnic_key.branch("7");
+    ASN1ObjectIdentifier picnic3l3 = picnic_key.branch("8");
+    ASN1ObjectIdentifier picnic3l5 = picnic_key.branch("9");
+    ASN1ObjectIdentifier picnicl1full = picnic_key.branch("10");
+    ASN1ObjectIdentifier picnicl3full = picnic_key.branch("11");
+    ASN1ObjectIdentifier picnicl5full = picnic_key.branch("12");
+
+    ASN1ObjectIdentifier picnic_signature = picnic.branch("2");
+    ASN1ObjectIdentifier picnic_with_sha512 = picnic_signature.branch("1");
+    ASN1ObjectIdentifier picnic_with_shake256 = picnic_signature.branch("2");
+    ASN1ObjectIdentifier picnic_with_sha3_512 = picnic_signature.branch("3");
+
+
+    /*
+     * Falcon
+     */
+    ASN1ObjectIdentifier falcon = bc_sig.branch("7");
+
+    ASN1ObjectIdentifier falcon_512 = new ASN1ObjectIdentifier("1.3.9999.3.6");  // falcon.branch("1");
+    ASN1ObjectIdentifier falcon_1024 =  new ASN1ObjectIdentifier("1.3.9999.3.9"); // falcon.branch("2");
+
+    /*
+     * Dilithium
+     */
+    ASN1ObjectIdentifier dilithium = bc_sig.branch("8");
+
+    // OpenSSL OIDs
+    ASN1ObjectIdentifier dilithium2 = new ASN1ObjectIdentifier("1.3.6.1.4.1.2.267.12.4.4"); // dilithium.branch("1");
+    ASN1ObjectIdentifier dilithium3 = new ASN1ObjectIdentifier("1.3.6.1.4.1.2.267.12.6.5"); // dilithium.branch("2");
+    ASN1ObjectIdentifier dilithium5 = new ASN1ObjectIdentifier("1.3.6.1.4.1.2.267.12.8.7"); // dilithium.branch("3");
+    ASN1ObjectIdentifier dilithium2_aes = new ASN1ObjectIdentifier("1.3.6.1.4.1.2.267.11.4.4"); // dilithium.branch("4");
+    ASN1ObjectIdentifier dilithium3_aes = new ASN1ObjectIdentifier("1.3.6.1.4.1.2.267.11.6.5"); // dilithium.branch("5");
+    ASN1ObjectIdentifier dilithium5_aes = new ASN1ObjectIdentifier("1.3.6.1.4.1.2.267.11.8.7"); // dilithium.branch("6");
+
+    /*
+     * Rainbow
+     */
+    ASN1ObjectIdentifier rainbow = bc_sig.branch("9");
+
+    ASN1ObjectIdentifier rainbow_III_classic = rainbow.branch("1");
+    ASN1ObjectIdentifier rainbow_III_circumzenithal = rainbow.branch("2");
+    ASN1ObjectIdentifier rainbow_III_compressed = rainbow.branch("3");
+    ASN1ObjectIdentifier rainbow_V_classic = rainbow.branch("4");
+    ASN1ObjectIdentifier rainbow_V_circumzenithal = rainbow.branch("5");
+    ASN1ObjectIdentifier rainbow_V_compressed = rainbow.branch("6");
 
     /**
      * key_exchange(3) algorithms
-     *
-    public static final ASN1ObjectIdentifier bc_exch = bc.branch("3");
+     */
+    ASN1ObjectIdentifier bc_exch = bc.branch("3");
 
     /**
      * NewHope
-     *
-    public static final ASN1ObjectIdentifier newHope = bc_exch.branch("1");
-    */
-    // END Android-removed: Unsupported algorithms
+     */
+    ASN1ObjectIdentifier newHope = bc_exch.branch("1");
 
     /**
-     * X.509 extension(4) values
+     * X.509 extension/certificate types
      * <p>
      * 1.3.6.1.4.1.22554.4
      */
-    public static final ASN1ObjectIdentifier bc_ext        = bc.branch("4");
+    ASN1ObjectIdentifier bc_ext = bc.branch("4");
 
-    public static final ASN1ObjectIdentifier linkedCertificate = bc_ext.branch("1");
+    ASN1ObjectIdentifier linkedCertificate = bc_ext.branch("1");
+    ASN1ObjectIdentifier external_value = bc_ext.branch("2");
+
+    /**
+     * KEM(5) algorithms
+     */
+    ASN1ObjectIdentifier bc_kem = bc.branch("5");
+
+    /**
+     * Classic McEliece
+     */
+    ASN1ObjectIdentifier pqc_kem_mceliece = bc_kem.branch("1");
+
+    ASN1ObjectIdentifier mceliece348864_r3 = pqc_kem_mceliece.branch("1");
+    ASN1ObjectIdentifier mceliece348864f_r3 = pqc_kem_mceliece.branch("2");
+    ASN1ObjectIdentifier mceliece460896_r3 = pqc_kem_mceliece.branch("3");
+    ASN1ObjectIdentifier mceliece460896f_r3 = pqc_kem_mceliece.branch("4");
+    ASN1ObjectIdentifier mceliece6688128_r3 = pqc_kem_mceliece.branch("5");
+    ASN1ObjectIdentifier mceliece6688128f_r3 = pqc_kem_mceliece.branch("6");
+    ASN1ObjectIdentifier mceliece6960119_r3 = pqc_kem_mceliece.branch("7");
+    ASN1ObjectIdentifier mceliece6960119f_r3 = pqc_kem_mceliece.branch("8");
+    ASN1ObjectIdentifier mceliece8192128_r3 = pqc_kem_mceliece.branch("9");
+    ASN1ObjectIdentifier mceliece8192128f_r3 = pqc_kem_mceliece.branch("10");
+
+
+    /**
+     * Frodo
+     */
+    ASN1ObjectIdentifier pqc_kem_frodo = bc_kem.branch("2");
+
+    ASN1ObjectIdentifier frodokem640aes = pqc_kem_frodo.branch("1");
+    ASN1ObjectIdentifier frodokem640shake = pqc_kem_frodo.branch("2");
+    ASN1ObjectIdentifier frodokem976aes = pqc_kem_frodo.branch("3");
+    ASN1ObjectIdentifier frodokem976shake = pqc_kem_frodo.branch("4");
+    ASN1ObjectIdentifier frodokem1344aes = pqc_kem_frodo.branch("5");
+    ASN1ObjectIdentifier frodokem1344shake = pqc_kem_frodo.branch("6");
+
+    /**
+     * SABER
+     */
+    ASN1ObjectIdentifier pqc_kem_saber = bc_kem.branch("3");
+
+    ASN1ObjectIdentifier lightsaberkem128r3 = pqc_kem_saber.branch("1");
+    ASN1ObjectIdentifier saberkem128r3 = pqc_kem_saber.branch("2");
+    ASN1ObjectIdentifier firesaberkem128r3 = pqc_kem_saber.branch("3");
+    ASN1ObjectIdentifier lightsaberkem192r3 = pqc_kem_saber.branch("4");
+    ASN1ObjectIdentifier saberkem192r3 = pqc_kem_saber.branch("5");
+    ASN1ObjectIdentifier firesaberkem192r3 = pqc_kem_saber.branch("6");
+    ASN1ObjectIdentifier lightsaberkem256r3 = pqc_kem_saber.branch("7");
+    ASN1ObjectIdentifier saberkem256r3 = pqc_kem_saber.branch("8");
+    ASN1ObjectIdentifier firesaberkem256r3 = pqc_kem_saber.branch("9");
+    ASN1ObjectIdentifier ulightsaberkemr3 = pqc_kem_saber.branch("10");
+    ASN1ObjectIdentifier usaberkemr3 = pqc_kem_saber.branch("11");
+    ASN1ObjectIdentifier ufiresaberkemr3 = pqc_kem_saber.branch("12");
+    ASN1ObjectIdentifier lightsaberkem90sr3 = pqc_kem_saber.branch("13");
+    ASN1ObjectIdentifier saberkem90sr3 = pqc_kem_saber.branch("14");
+    ASN1ObjectIdentifier firesaberkem90sr3 = pqc_kem_saber.branch("15");
+    ASN1ObjectIdentifier ulightsaberkem90sr3 = pqc_kem_saber.branch("16");
+    ASN1ObjectIdentifier usaberkem90sr3 = pqc_kem_saber.branch("17");
+    ASN1ObjectIdentifier ufiresaberkem90sr3 = pqc_kem_saber.branch("18");
+
+    /**
+     * SIKE
+     */
+    ASN1ObjectIdentifier pqc_kem_sike = bc_kem.branch("4");
+
+    ASN1ObjectIdentifier sikep434 = pqc_kem_sike.branch("1");
+    ASN1ObjectIdentifier sikep503 = pqc_kem_sike.branch("2");
+    ASN1ObjectIdentifier sikep610 = pqc_kem_sike.branch("3");
+    ASN1ObjectIdentifier sikep751 = pqc_kem_sike.branch("4");
+    ASN1ObjectIdentifier sikep434_compressed = pqc_kem_sike.branch("5");
+    ASN1ObjectIdentifier sikep503_compressed = pqc_kem_sike.branch("6");
+    ASN1ObjectIdentifier sikep610_compressed = pqc_kem_sike.branch("7");
+    ASN1ObjectIdentifier sikep751_compressed = pqc_kem_sike.branch("8");
+
+    /**
+     * NTRU
+     */
+    ASN1ObjectIdentifier pqc_kem_ntru = bc_kem.branch("5");
+
+    ASN1ObjectIdentifier ntruhps2048509 = pqc_kem_ntru.branch("1");
+    ASN1ObjectIdentifier ntruhps2048677 = pqc_kem_ntru.branch("2");
+    ASN1ObjectIdentifier ntruhps4096821 = pqc_kem_ntru.branch("3");
+    ASN1ObjectIdentifier ntruhrss701 = pqc_kem_ntru.branch("4");
+
+    /**
+     * Kyber
+     */
+    ASN1ObjectIdentifier pqc_kem_kyber = bc_kem.branch("6");
+
+    ASN1ObjectIdentifier kyber512 = pqc_kem_kyber.branch("1");
+    ASN1ObjectIdentifier kyber768 = pqc_kem_kyber.branch("2");
+    ASN1ObjectIdentifier kyber1024 = pqc_kem_kyber.branch("3");
+    ASN1ObjectIdentifier kyber512_aes = pqc_kem_kyber.branch("4");
+    ASN1ObjectIdentifier kyber768_aes = pqc_kem_kyber.branch("5");
+    ASN1ObjectIdentifier kyber1024_aes = pqc_kem_kyber.branch("6");
+
+    /**
+     * NTRUPrime
+     */
+    ASN1ObjectIdentifier pqc_kem_ntruprime = bc_kem.branch("7");
+
+    ASN1ObjectIdentifier pqc_kem_ntrulprime = pqc_kem_ntruprime.branch("1");
+    ASN1ObjectIdentifier ntrulpr653 = pqc_kem_ntrulprime.branch("1");
+    ASN1ObjectIdentifier ntrulpr761 = pqc_kem_ntrulprime.branch("2");
+    ASN1ObjectIdentifier ntrulpr857 = pqc_kem_ntrulprime.branch("3");
+    ASN1ObjectIdentifier ntrulpr953 = pqc_kem_ntrulprime.branch("4");
+    ASN1ObjectIdentifier ntrulpr1013 = pqc_kem_ntrulprime.branch("5");
+    ASN1ObjectIdentifier ntrulpr1277 = pqc_kem_ntrulprime.branch("6");
+    
+    ASN1ObjectIdentifier pqc_kem_sntruprime = pqc_kem_ntruprime.branch("2");
+    ASN1ObjectIdentifier sntrup653 = pqc_kem_sntruprime.branch("1");
+    ASN1ObjectIdentifier sntrup761 = pqc_kem_sntruprime.branch("2");
+    ASN1ObjectIdentifier sntrup857 = pqc_kem_sntruprime.branch("3");
+    ASN1ObjectIdentifier sntrup953 = pqc_kem_sntruprime.branch("4");
+    ASN1ObjectIdentifier sntrup1013 = pqc_kem_sntruprime.branch("5");
+    ASN1ObjectIdentifier sntrup1277 = pqc_kem_sntruprime.branch("6");
+    
+    /**
+     * BIKE
+     **/
+    ASN1ObjectIdentifier pqc_kem_bike = bc_kem.branch("8");
+
+    ASN1ObjectIdentifier bike128 = pqc_kem_bike.branch("1");
+    ASN1ObjectIdentifier bike192 = pqc_kem_bike.branch("2");
+    ASN1ObjectIdentifier bike256 = pqc_kem_bike.branch("3");
+
+    /**
+     * HQC
+     **/
+    ASN1ObjectIdentifier pqc_kem_hqc = bc_kem.branch("9");
+
+    ASN1ObjectIdentifier hqc128 = pqc_kem_hqc.branch("1");
+    ASN1ObjectIdentifier hqc192 = pqc_kem_hqc.branch("2");
+    ASN1ObjectIdentifier hqc256 = pqc_kem_hqc.branch("3");
 }
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/bc/ExternalValue.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/bc/ExternalValue.java
new file mode 100644
index 0000000..97c1eeb
--- /dev/null
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/bc/ExternalValue.java
@@ -0,0 +1,119 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.asn1.bc;
+
+import com.android.internal.org.bouncycastle.asn1.ASN1BitString;
+import com.android.internal.org.bouncycastle.asn1.ASN1EncodableVector;
+import com.android.internal.org.bouncycastle.asn1.ASN1Object;
+import com.android.internal.org.bouncycastle.asn1.ASN1OctetString;
+import com.android.internal.org.bouncycastle.asn1.ASN1Primitive;
+import com.android.internal.org.bouncycastle.asn1.ASN1Sequence;
+import com.android.internal.org.bouncycastle.asn1.DERBitString;
+import com.android.internal.org.bouncycastle.asn1.DEROctetString;
+import com.android.internal.org.bouncycastle.asn1.DERSequence;
+import com.android.internal.org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import com.android.internal.org.bouncycastle.asn1.x509.GeneralName;
+import com.android.internal.org.bouncycastle.asn1.x509.GeneralNames;
+import com.android.internal.org.bouncycastle.util.Arrays;
+
+/**
+ * Based on External Keys And Signatures For Use In Internet PKI
+ * draft-ounsworth-pq-external-pubkeys-00
+ * <pre>
+ *  ExternalValue ::= SEQUENCE {
+ *      location GeneralNames,    # MUST refer to a DER encoded SubjectPublicKeyInfo/Signature  (may be Base64)
+ *      hashAlg AlgorithmIdentifier,
+ *      hashVal OCTET STRING }
+ * </pre>
+ * @hide This class is not part of the Android public SDK API
+ */
+public class ExternalValue
+    extends ASN1Object
+{
+    private final GeneralNames location;
+    private final AlgorithmIdentifier hashAlg;
+    private final byte[] hashValue;
+
+    public ExternalValue(GeneralName location, AlgorithmIdentifier hashAlg, byte[] hashVal)
+    {
+        this.location = new GeneralNames(location);
+        this.hashAlg = hashAlg;
+        this.hashValue = Arrays.clone(hashVal);
+    }
+
+    private ExternalValue(ASN1Sequence seq)
+    {
+        if (seq.size() == 3)
+        {
+            location = GeneralNames.getInstance(seq.getObjectAt(0));
+            hashAlg = AlgorithmIdentifier.getInstance(seq.getObjectAt(1));
+            if (seq.getObjectAt(2) instanceof ASN1BitString)    // legacy implementation on 2021 draft
+            {
+                hashValue = ASN1BitString.getInstance(seq.getObjectAt(2)).getOctets();
+            }
+            else
+            {
+                hashValue = ASN1OctetString.getInstance(seq.getObjectAt(2)).getOctets();
+            }
+        }
+        else
+        {
+            throw new IllegalArgumentException("unknown sequence");
+        }
+    }
+
+    public static ExternalValue getInstance(Object o)
+    {
+        if (o instanceof ExternalValue)
+        {
+            return (ExternalValue)o;
+        }
+        else if (o != null)
+        {
+            return new ExternalValue(ASN1Sequence.getInstance(o));
+        }
+
+        return null;
+    }
+
+    public GeneralName getLocation()
+    {
+        return location.getNames()[0];
+    }
+
+    public GeneralName[] getLocations()
+    {
+        return location.getNames();
+    }
+
+    public AlgorithmIdentifier getHashAlg()
+    {
+        return hashAlg;
+    }
+
+    public byte[] getHashValue()
+    {
+        return Arrays.clone(hashValue);
+    }
+
+    /**
+     * Get the hash value as a BIT STRING.
+     *
+     * @return the hash value as a BIT STRING
+     * @deprecated use getHash(), the internal encoding is now an OCTET STRING
+     */
+    public ASN1BitString getHashVal()
+    {
+        return new DERBitString(hashValue);
+    }
+
+    public ASN1Primitive toASN1Primitive()
+    {
+        ASN1EncodableVector v = new ASN1EncodableVector();
+
+        v.add(location);
+        v.add(hashAlg);
+        v.add(new DEROctetString(hashValue));
+
+        return new DERSequence(v);
+    }
+}
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/bc/LinkedCertificate.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/bc/LinkedCertificate.java
new file mode 100644
index 0000000..2798bc8
--- /dev/null
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/bc/LinkedCertificate.java
@@ -0,0 +1,128 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.asn1.bc;
+
+import com.android.internal.org.bouncycastle.asn1.ASN1EncodableVector;
+import com.android.internal.org.bouncycastle.asn1.ASN1Object;
+import com.android.internal.org.bouncycastle.asn1.ASN1Primitive;
+import com.android.internal.org.bouncycastle.asn1.ASN1Sequence;
+import com.android.internal.org.bouncycastle.asn1.ASN1TaggedObject;
+import com.android.internal.org.bouncycastle.asn1.DERSequence;
+import com.android.internal.org.bouncycastle.asn1.DERTaggedObject;
+import com.android.internal.org.bouncycastle.asn1.x500.X500Name;
+import com.android.internal.org.bouncycastle.asn1.x509.DigestInfo;
+import com.android.internal.org.bouncycastle.asn1.x509.GeneralName;
+import com.android.internal.org.bouncycastle.asn1.x509.GeneralNames;
+
+/**
+ * Extension to tie an alternate certificate to the containing certificate.
+ * <pre>
+ *     LinkedCertificate := SEQUENCE {
+ *         digest        DigestInfo,                   -- digest of PQC certificate
+ *         certLocation  GeneralName,                  -- location of PQC certificate
+ *         certIssuer    [0] Name OPTIONAL,            -- issuer of PQC cert (if different from current certificate)
+ *         cACerts       [1] GeneralNames OPTIONAL,    -- CA certificates for PQC cert (one of more locations)
+ * }
+ * </pre>
+ * @hide This class is not part of the Android public SDK API
+ */
+public class LinkedCertificate
+    extends ASN1Object
+{
+    private final DigestInfo digest;
+    private final GeneralName certLocation;
+
+    private X500Name certIssuer;
+    private GeneralNames cACerts;
+
+    public LinkedCertificate(DigestInfo digest, GeneralName certLocation)
+    {
+        this(digest, certLocation, null, null);
+    }
+
+    public LinkedCertificate(DigestInfo digest, GeneralName certLocation, X500Name certIssuer, GeneralNames cACerts)
+    {
+        this.digest = digest;
+        this.certLocation = certLocation;
+        this.certIssuer = certIssuer;
+        this.cACerts = cACerts;
+    }
+
+    private LinkedCertificate(ASN1Sequence seq)
+    {
+        this.digest = DigestInfo.getInstance(seq.getObjectAt(0));
+        this.certLocation = GeneralName.getInstance(seq.getObjectAt(1));
+
+        if (seq.size() > 2)
+        {
+            for (int i = 2; i != seq.size(); i++)
+            {
+                ASN1TaggedObject tagged =  ASN1TaggedObject.getInstance(seq.getObjectAt(i));
+
+                switch (tagged.getTagNo())
+                {
+                case 0:
+                    certIssuer = X500Name.getInstance(tagged, false);
+                    break;
+                case 1:
+                    cACerts = GeneralNames.getInstance(tagged, false);
+                    break;
+                default:
+                    throw new IllegalArgumentException("unknown tag in tagged field");
+                }
+            }
+        }
+    }
+
+    public static LinkedCertificate getInstance(Object o)
+    {
+        if (o instanceof LinkedCertificate)
+        {
+            return (LinkedCertificate)o;
+        }
+        else if (o != null)
+        {
+            return new LinkedCertificate(ASN1Sequence.getInstance(o));
+        }
+
+        return null;
+    }
+
+    public DigestInfo getDigest()
+    {
+        return digest;
+    }
+
+    public GeneralName getCertLocation()
+    {
+        return certLocation;
+    }
+
+    public X500Name getCertIssuer()
+    {
+        return certIssuer;
+    }
+
+    public GeneralNames getCACerts()
+    {
+        return cACerts;
+    }
+
+    public ASN1Primitive toASN1Primitive()
+    {
+        ASN1EncodableVector v = new ASN1EncodableVector(4);
+
+        v.add(digest);
+        v.add(certLocation);
+
+        if (certIssuer != null)
+        {
+            v.add(new DERTaggedObject(false, 0, certIssuer));
+        }
+        if (cACerts != null)
+        {
+            v.add(new DERTaggedObject(false, 1, cACerts));
+        }
+
+        return new DERSequence(v);
+    }
+}
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/cms/ContentInfo.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/cms/ContentInfo.java
index 51bbd3e..632eace 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/cms/ContentInfo.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/cms/ContentInfo.java
@@ -130,3 +130,4 @@
         return new BERSequence(v);
     }
 }
+
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/eac/EACObjectIdentifiers.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/eac/EACObjectIdentifiers.java
deleted file mode 100644
index 798f8a1..0000000
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/eac/EACObjectIdentifiers.java
+++ /dev/null
@@ -1,112 +0,0 @@
-/* GENERATED SOURCE. DO NOT MODIFY. */
-package com.android.internal.org.bouncycastle.asn1.eac;
-
-import com.android.internal.org.bouncycastle.asn1.ASN1ObjectIdentifier;
-
-/**
- * German Federal Office for Information Security
- * (Bundesamt f&uuml;r Sicherheit in der Informationstechnik)
- * <a href="https://www.bsi.bund.de/">https://www.bsi.bund.de/</a>
- * <p>
- * <a href="https://www.bsi.bund.de/EN/Publications/TechnicalGuidelines/TR03110/BSITR03110.html">BSI TR-03110</a>
- * Technical Guideline Advanced Security Mechanisms for Machine Readable Travel Documents
- * <p>
- * <a href="https://www.bsi.bund.de/SharedDocs/Downloads/EN/BSI/Publications/TechGuidelines/TR03110/TR-03110_v2.1_P3pdf.pdf">
- * Technical Guideline TR-03110-3</a>
- * Advanced Security Mechanisms for Machine Readable Travel Documents;
- * Part 3: Common Specifications.
- * @hide This class is not part of the Android public SDK API
- */
-public interface EACObjectIdentifiers
-{
-    /**
-     * <pre>
-     * bsi-de OBJECT IDENTIFIER ::= {
-     *     itu-t(0) identified-organization(4) etsi(0)
-     *     reserved(127) etsi-identified-organization(0) 7
-     * }
-     * </pre>
-     * OID: 0.4.0.127.0.7
-     */
-    static final ASN1ObjectIdentifier    bsi_de      = new ASN1ObjectIdentifier("0.4.0.127.0.7");
-
-    /**
-     * <pre>
-     * id-PK OBJECT IDENTIFIER ::= {
-     *     bsi-de protocols(2) smartcard(2) 1
-     * }
-     * </pre>
-     * OID: 0.4.0.127.0.7.2.2.1
-     */
-    static final ASN1ObjectIdentifier    id_PK      = bsi_de.branch("2.2.1");
-
-    /** OID: 0.4.0.127.0.7.2.2.1.1 */
-    static final ASN1ObjectIdentifier    id_PK_DH   = id_PK.branch("1");
-    /** OID: 0.4.0.127.0.7.2.2.1.2 */
-    static final ASN1ObjectIdentifier    id_PK_ECDH = id_PK.branch("2");
-
-    /**
-     * <pre>
-     * id-CA OBJECT IDENTIFIER ::= {
-     *     bsi-de protocols(2) smartcard(2) 3
-     * }
-     * </pre>
-     * OID: 0.4.0.127.0.7.2.2.3
-     */
-    static final ASN1ObjectIdentifier    id_CA                   = bsi_de.branch("2.2.3");
-    /** OID: 0.4.0.127.0.7.2.2.3.1 */
-    static final ASN1ObjectIdentifier    id_CA_DH                = id_CA.branch("1");
-    /** OID: 0.4.0.127.0.7.2.2.3.1.1 */
-    static final ASN1ObjectIdentifier    id_CA_DH_3DES_CBC_CBC   = id_CA_DH.branch("1");
-    /** OID: 0.4.0.127.0.7.2.2.3.2 */
-    static final ASN1ObjectIdentifier    id_CA_ECDH              = id_CA.branch("2");
-    /** OID: 0.4.0.127.0.7.2.2.3.2.1 */
-    static final ASN1ObjectIdentifier    id_CA_ECDH_3DES_CBC_CBC = id_CA_ECDH.branch("1");
-
-    /**
-     * <pre>
-     * id-TA OBJECT IDENTIFIER ::= {
-     *     bsi-de protocols(2) smartcard(2) 2
-     * }
-     * </pre>
-     * OID: 0.4.0.127.0.7.2.2.2
-     */
-    static final ASN1ObjectIdentifier    id_TA = bsi_de.branch("2.2.2");
-
-    /** OID: 0.4.0.127.0.7.2.2.2.1 */
-    static final ASN1ObjectIdentifier    id_TA_RSA              = id_TA.branch("1");
-    /** OID: 0.4.0.127.0.7.2.2.2.1.1 */
-    static final ASN1ObjectIdentifier    id_TA_RSA_v1_5_SHA_1   = id_TA_RSA.branch("1");
-    /** OID: 0.4.0.127.0.7.2.2.2.1.2 */
-    static final ASN1ObjectIdentifier    id_TA_RSA_v1_5_SHA_256 = id_TA_RSA.branch("2");
-    /** OID: 0.4.0.127.0.7.2.2.2.1.3 */
-    static final ASN1ObjectIdentifier    id_TA_RSA_PSS_SHA_1    = id_TA_RSA.branch("3");
-    /** OID: 0.4.0.127.0.7.2.2.2.1.4 */
-    static final ASN1ObjectIdentifier    id_TA_RSA_PSS_SHA_256  = id_TA_RSA.branch("4");
-    /** OID: 0.4.0.127.0.7.2.2.2.1.5 */
-    static final ASN1ObjectIdentifier    id_TA_RSA_v1_5_SHA_512 = id_TA_RSA.branch("5");
-    /** OID: 0.4.0.127.0.7.2.2.2.1.6 */
-    static final ASN1ObjectIdentifier    id_TA_RSA_PSS_SHA_512  = id_TA_RSA.branch("6");
-    /** OID: 0.4.0.127.0.7.2.2.2.2 */
-    static final ASN1ObjectIdentifier    id_TA_ECDSA            = id_TA.branch("2");
-    /** OID: 0.4.0.127.0.7.2.2.2.2.1 */
-    static final ASN1ObjectIdentifier    id_TA_ECDSA_SHA_1      = id_TA_ECDSA.branch("1");
-    /** OID: 0.4.0.127.0.7.2.2.2.2.2 */
-    static final ASN1ObjectIdentifier    id_TA_ECDSA_SHA_224    = id_TA_ECDSA.branch("2");
-    /** OID: 0.4.0.127.0.7.2.2.2.2.3 */
-    static final ASN1ObjectIdentifier    id_TA_ECDSA_SHA_256    = id_TA_ECDSA.branch("3");
-    /** OID: 0.4.0.127.0.7.2.2.2.2.4 */
-    static final ASN1ObjectIdentifier    id_TA_ECDSA_SHA_384    = id_TA_ECDSA.branch("4");
-    /** OID: 0.4.0.127.0.7.2.2.2.2.5 */
-    static final ASN1ObjectIdentifier    id_TA_ECDSA_SHA_512    = id_TA_ECDSA.branch("5");
-
-    /**
-     * <pre>
-     * id-EAC-ePassport OBJECT IDENTIFIER ::= {
-     *     bsi-de applications(3) mrtd(1) roles(2) 1
-     * }
-     * </pre>
-     * OID: 0.4.0.127.0.7.3.1.2.1
-     */
-    static final ASN1ObjectIdentifier id_EAC_ePassport = bsi_de.branch("3.1.2.1");
-}
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/gm/GMObjectIdentifiers.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/gm/GMObjectIdentifiers.java
index 52ed008..ed5fdaf 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/gm/GMObjectIdentifiers.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/gm/GMObjectIdentifiers.java
@@ -8,6 +8,8 @@
  */
 public interface GMObjectIdentifiers
 {
+    //1.2.156.10197: Chinese Cryptography Standardization Technology Committee (CCSTC)
+    //1.2.156.11235: China Broadband Wireless IP Standard Group
     ASN1ObjectIdentifier sm_scheme = new ASN1ObjectIdentifier("1.2.156.10197.1");
 
     ASN1ObjectIdentifier sm6_ecb = sm_scheme.branch("101.1");
@@ -50,7 +52,20 @@
     ASN1ObjectIdentifier sm2exchange = sm_scheme.branch("301.2");
     ASN1ObjectIdentifier sm2encrypt = sm_scheme.branch("301.3");
 
-    ASN1ObjectIdentifier wapip192v1 = sm_scheme.branch("301.101");
+    /**
+     * <Information security technology — Cryptographic application identifier criterion specification>
+     * <url>http&#058;//c.gb688.cn/bzgk/gb/showGb&#63;type=online&hcno=252CF0F72A7BE339A56DEA7D774E8994</url>,
+     * Page 21 only cover from 301.1 to 301.3
+     * */
+    ASN1ObjectIdentifier wapip192v1 =  sm_scheme.branch("301.101");
+    /**
+     * <WAPI certificate management—Part 5: Example of certificate format (draft)>
+     * <url>http&#058;//www.chinabwips.org.cn/zqyjgs1.htm</url> and
+     * <url>http&#058;//www.chinabwips.org.cn/doc/101.pdf</url>,
+     * Page 9 and page 10 states the OID of ECDSA-192 algorithm based on SHA-256 is 1.2.156.11235.1.1.1
+     * */
+    ASN1ObjectIdentifier wapi192v1 =  new ASN1ObjectIdentifier("1.2.156.11235.1.1.1");
+    ASN1ObjectIdentifier wapi192v1_parameters =  new ASN1ObjectIdentifier("1.2.156.11235.1.1.2.1");
 
     ASN1ObjectIdentifier sm2encrypt_recommendedParameters = sm2encrypt.branch("1");
     ASN1ObjectIdentifier sm2encrypt_specifiedParameters = sm2encrypt.branch("2");
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/isara/IsaraObjectIdentifiers.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/isara/IsaraObjectIdentifiers.java
new file mode 100644
index 0000000..2c30ba0
--- /dev/null
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/isara/IsaraObjectIdentifiers.java
@@ -0,0 +1,26 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.asn1.isara;
+
+import com.android.internal.org.bouncycastle.asn1.ASN1ObjectIdentifier;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+public interface IsaraObjectIdentifiers
+{
+    /*
+    id-alg-xmss  OBJECT IDENTIFIER ::= { itu-t(0)
+             identified-organization(4) etsi(0) reserved(127)
+             etsi-identified-organization(0) isara(15) algorithms(1)
+             asymmetric(1) xmss(13) 0 }
+     */
+    static ASN1ObjectIdentifier id_alg_xmss = new ASN1ObjectIdentifier("0.4.0.127.0.15.1.1.13.0");
+
+    /*
+      id-alg-xmssmt  OBJECT IDENTIFIER ::= { itu-t(0)
+         identified-organization(4) etsi(0) reserved(127)
+         etsi-identified-organization(0) isara(15) algorithms(1)
+         asymmetric(1) xmssmt(14) 0 }
+     */
+    static ASN1ObjectIdentifier id_alg_xmssmt = new ASN1ObjectIdentifier("0.4.0.127.0.15.1.1.14.0");
+}
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/misc/MiscObjectIdentifiers.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/misc/MiscObjectIdentifiers.java
index 2f69dde..ddf540d 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/misc/MiscObjectIdentifiers.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/misc/MiscObjectIdentifiers.java
@@ -128,7 +128,7 @@
     ASN1ObjectIdentifier cryptlib_algorithm_blowfish_OFB = cryptlib_algorithm.branch("1.4");
 
     //
-    // Blake2b
+    // Blake2b/Blake2s
     //
     ASN1ObjectIdentifier blake2 = new ASN1ObjectIdentifier("1.3.6.1.4.1.1722.12.2");
 
@@ -142,6 +142,10 @@
     ASN1ObjectIdentifier id_blake2s224 = blake2.branch("2.7");
     ASN1ObjectIdentifier id_blake2s256 = blake2.branch("2.8");
 
+    ASN1ObjectIdentifier blake3 = blake2.branch("3");
+
+    ASN1ObjectIdentifier blake3_256 = blake3.branch("8");
+
     //
     // Scrypt
     ASN1ObjectIdentifier id_scrypt = new ASN1ObjectIdentifier("1.3.6.1.4.1.11591.4.11");
@@ -152,4 +156,15 @@
     //        iso(1)  identified-organization(3) dod(6) internet(1) private(4)
     //        enterprise(1) OpenCA(18227) Algorithms(2) id-alg-composite(1) }
     ASN1ObjectIdentifier id_alg_composite = new ASN1ObjectIdentifier("1.3.6.1.4.1.18227.2.1");
+
+    // -- To be replaced by IANA
+    //
+    //id-composite-key OBJECT IDENTIFIER ::= {
+    //
+    //    joint-iso-itu-t(2) country(16) us(840) organization(1) entrust(114027)
+    //
+    //    Algorithm(80) Composite(4) CompositeKey(1)
+    ASN1ObjectIdentifier id_composite_key = new ASN1ObjectIdentifier("2.16.840.1.114027.80.4.1");
+
+    ASN1ObjectIdentifier id_oracle_pkcs12_trusted_key_usage = new ASN1ObjectIdentifier("2.16.840.1.113894.746875.1.1");
 }
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/misc/NetscapeCertType.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/misc/NetscapeCertType.java
index 2fdac51..4e6d53a 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/misc/NetscapeCertType.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/misc/NetscapeCertType.java
@@ -1,6 +1,7 @@
 /* GENERATED SOURCE. DO NOT MODIFY. */
 package com.android.internal.org.bouncycastle.asn1.misc;
 
+import com.android.internal.org.bouncycastle.asn1.ASN1BitString;
 import com.android.internal.org.bouncycastle.asn1.DERBitString;
 
 /**
@@ -44,13 +45,18 @@
     }
 
     public NetscapeCertType(
-        DERBitString usage)
+        ASN1BitString usage)
     {
         super(usage.getBytes(), usage.getPadBits());
     }
 
+    public boolean hasUsages(int usages)
+    {
+        return (intValue() & usages) == usages;
+    }
+
     public String toString()
     {
-        return "NetscapeCertType: 0x" + Integer.toHexString(data[0] & 0xff);
+        return "NetscapeCertType: 0x" + Integer.toHexString(intValue());
     }
 }
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/misc/NetscapeRevocationURL.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/misc/NetscapeRevocationURL.java
index ed0f5e2..52b1912 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/misc/NetscapeRevocationURL.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/misc/NetscapeRevocationURL.java
@@ -1,6 +1,7 @@
 /* GENERATED SOURCE. DO NOT MODIFY. */
 package com.android.internal.org.bouncycastle.asn1.misc;
 
+import com.android.internal.org.bouncycastle.asn1.ASN1IA5String;
 import com.android.internal.org.bouncycastle.asn1.DERIA5String;
 
 /**
@@ -10,7 +11,7 @@
     extends DERIA5String
 {
     public NetscapeRevocationURL(
-        DERIA5String str)
+        ASN1IA5String str)
     {
         super(str.getString());
     }
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/misc/VerisignCzagExtension.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/misc/VerisignCzagExtension.java
index a570d70..1b6cf2a 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/misc/VerisignCzagExtension.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/misc/VerisignCzagExtension.java
@@ -1,6 +1,7 @@
 /* GENERATED SOURCE. DO NOT MODIFY. */
 package com.android.internal.org.bouncycastle.asn1.misc;
 
+import com.android.internal.org.bouncycastle.asn1.ASN1IA5String;
 import com.android.internal.org.bouncycastle.asn1.DERIA5String;
 
 /**
@@ -10,7 +11,7 @@
     extends DERIA5String
 {
     public VerisignCzagExtension(
-        DERIA5String str)
+        ASN1IA5String str)
     {
         super(str.getString());
     }
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/nist/KMACwithSHAKE128_params.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/nist/KMACwithSHAKE128_params.java
new file mode 100644
index 0000000..d1c84bb
--- /dev/null
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/nist/KMACwithSHAKE128_params.java
@@ -0,0 +1,116 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.asn1.nist;
+
+import com.android.internal.org.bouncycastle.asn1.ASN1EncodableVector;
+import com.android.internal.org.bouncycastle.asn1.ASN1Integer;
+import com.android.internal.org.bouncycastle.asn1.ASN1Object;
+import com.android.internal.org.bouncycastle.asn1.ASN1OctetString;
+import com.android.internal.org.bouncycastle.asn1.ASN1Primitive;
+import com.android.internal.org.bouncycastle.asn1.ASN1Sequence;
+import com.android.internal.org.bouncycastle.asn1.DEROctetString;
+import com.android.internal.org.bouncycastle.asn1.DERSequence;
+import com.android.internal.org.bouncycastle.util.Arrays;
+
+/**
+ * <pre>
+ *   KMACwithSHAKE128-params ::= SEQUENCE {
+ *      kMACOutputLength     INTEGER DEFAULT 256, -- Output length in bits
+ *      customizationString  OCTET STRING DEFAULT ''H
+ *    }
+ * </pre>
+ * @hide This class is not part of the Android public SDK API
+ */
+public class KMACwithSHAKE128_params
+    extends ASN1Object
+{
+    private static final byte[] EMPTY_STRING = new byte[0];
+    private static final int DEF_LENGTH = 256;
+
+    private final int outputLength;
+    private final byte[] customizationString;
+
+    public KMACwithSHAKE128_params(int outputLength)
+    {
+        this.outputLength = outputLength;
+        this.customizationString = EMPTY_STRING;
+    }
+
+    public KMACwithSHAKE128_params(int outputLength, byte[] customizationString)
+    {
+        this.outputLength = outputLength;
+        this.customizationString = Arrays.clone(customizationString);
+    }
+
+    public static KMACwithSHAKE128_params getInstance(Object o)
+    {
+        if (o instanceof KMACwithSHAKE128_params)
+        {
+            return (KMACwithSHAKE128_params)o;
+        }
+        else if (o != null)
+        {
+            return new KMACwithSHAKE128_params(ASN1Sequence.getInstance(o));
+        }
+
+        return null;
+    }
+
+    private KMACwithSHAKE128_params(ASN1Sequence seq)
+    {
+        if (seq.size() > 2)
+        {
+            throw new IllegalArgumentException("sequence size greater than 2");
+        }
+
+        if (seq.size() == 2)
+        {
+            this.outputLength = ASN1Integer.getInstance(seq.getObjectAt(0)).intValueExact();
+            this.customizationString = Arrays.clone(ASN1OctetString.getInstance(seq.getObjectAt(1)).getOctets());
+        }
+        else if (seq.size() == 1)
+        {
+            if (seq.getObjectAt(0) instanceof ASN1Integer)
+            {
+                this.outputLength = ASN1Integer.getInstance(seq.getObjectAt(0)).intValueExact();
+                this.customizationString = EMPTY_STRING;
+            }
+            else
+            {
+                this.outputLength = DEF_LENGTH;
+                this.customizationString = Arrays.clone(ASN1OctetString.getInstance(seq.getObjectAt(0)).getOctets());
+            }
+        }
+        else
+        {
+            this.outputLength = DEF_LENGTH;
+            this.customizationString = EMPTY_STRING;
+        }
+    }
+
+    public int getOutputLength()
+    {
+        return outputLength;
+    }
+
+    public byte[] getCustomizationString()
+    {
+        return Arrays.clone(customizationString);
+    }
+
+    public ASN1Primitive toASN1Primitive()
+    {
+        ASN1EncodableVector v = new ASN1EncodableVector();
+
+        if (outputLength != DEF_LENGTH)
+        {
+            v.add(new ASN1Integer(outputLength));
+        }
+
+        if (customizationString.length != 0)
+        {
+            v.add(new DEROctetString(getCustomizationString()));
+        }
+
+        return new DERSequence(v);
+    }
+}
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/nist/KMACwithSHAKE256_params.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/nist/KMACwithSHAKE256_params.java
new file mode 100644
index 0000000..926c0f7
--- /dev/null
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/nist/KMACwithSHAKE256_params.java
@@ -0,0 +1,116 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.asn1.nist;
+
+import com.android.internal.org.bouncycastle.asn1.ASN1EncodableVector;
+import com.android.internal.org.bouncycastle.asn1.ASN1Integer;
+import com.android.internal.org.bouncycastle.asn1.ASN1Object;
+import com.android.internal.org.bouncycastle.asn1.ASN1OctetString;
+import com.android.internal.org.bouncycastle.asn1.ASN1Primitive;
+import com.android.internal.org.bouncycastle.asn1.ASN1Sequence;
+import com.android.internal.org.bouncycastle.asn1.DEROctetString;
+import com.android.internal.org.bouncycastle.asn1.DERSequence;
+import com.android.internal.org.bouncycastle.util.Arrays;
+
+/**
+ * <pre>
+ *   KMACwithSHAKE256-params ::= SEQUENCE {
+ *      kMACOutputLength     INTEGER DEFAULT 512, -- Output length in bits
+ *      customizationString  OCTET STRING DEFAULT ''H
+ *    }
+ * </pre>
+ * @hide This class is not part of the Android public SDK API
+ */
+public class KMACwithSHAKE256_params
+    extends ASN1Object
+{
+    private static final byte[] EMPTY_STRING = new byte[0];
+    private static final int DEF_LENGTH = 512;
+
+    private final int outputLength;
+    private final byte[] customizationString;
+
+    public KMACwithSHAKE256_params(int outputLength)
+    {
+        this.outputLength = outputLength;
+        this.customizationString = EMPTY_STRING;
+    }
+
+    public KMACwithSHAKE256_params(int outputLength, byte[] customizationString)
+    {
+        this.outputLength = outputLength;
+        this.customizationString = Arrays.clone(customizationString);
+    }
+
+    public static KMACwithSHAKE256_params getInstance(Object o)
+    {
+        if (o instanceof KMACwithSHAKE256_params)
+        {
+            return (KMACwithSHAKE256_params)o;
+        }
+        else if (o != null)
+        {
+            return new KMACwithSHAKE256_params(ASN1Sequence.getInstance(o));
+        }
+
+        return null;
+    }
+
+    private KMACwithSHAKE256_params(ASN1Sequence seq)
+    {
+        if (seq.size() > 2)
+        {
+            throw new IllegalArgumentException("sequence size greater than 2");
+        }
+
+        if (seq.size() == 2)
+        {
+            this.outputLength = ASN1Integer.getInstance(seq.getObjectAt(0)).intValueExact();
+            this.customizationString = Arrays.clone(ASN1OctetString.getInstance(seq.getObjectAt(1)).getOctets());
+        }
+        else if (seq.size() == 1)
+        {
+            if (seq.getObjectAt(0) instanceof ASN1Integer)
+            {
+                this.outputLength = ASN1Integer.getInstance(seq.getObjectAt(0)).intValueExact();
+                this.customizationString = EMPTY_STRING;
+            }
+            else
+            {
+                this.outputLength = DEF_LENGTH;
+                this.customizationString = Arrays.clone(ASN1OctetString.getInstance(seq.getObjectAt(0)).getOctets());
+            }
+        }
+        else
+        {
+            this.outputLength = DEF_LENGTH;
+            this.customizationString = EMPTY_STRING;
+        }
+    }
+
+    public int getOutputLength()
+    {
+        return outputLength;
+    }
+
+    public byte[] getCustomizationString()
+    {
+        return Arrays.clone(customizationString);
+    }
+    
+    public ASN1Primitive toASN1Primitive()
+    {
+        ASN1EncodableVector v = new ASN1EncodableVector();
+
+        if (outputLength != DEF_LENGTH)
+        {
+            v.add(new ASN1Integer(outputLength));
+        }
+
+        if (customizationString.length != 0)
+        {
+            v.add(new DEROctetString(getCustomizationString()));
+        }
+
+        return new DERSequence(v);
+    }
+}
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/nist/NISTNamedCurves.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/nist/NISTNamedCurves.java
index fe9c18b..eba9e2b 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/nist/NISTNamedCurves.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/nist/NISTNamedCurves.java
@@ -8,6 +8,7 @@
 import com.android.internal.org.bouncycastle.asn1.sec.SECNamedCurves;
 import com.android.internal.org.bouncycastle.asn1.sec.SECObjectIdentifiers;
 import com.android.internal.org.bouncycastle.asn1.x9.X9ECParameters;
+import com.android.internal.org.bouncycastle.asn1.x9.X9ECParametersHolder;
 import com.android.internal.org.bouncycastle.util.Strings;
 
 /**
@@ -44,17 +45,16 @@
         defineCurve("P-192", SECObjectIdentifiers.secp192r1);
     }
 
-    public static X9ECParameters getByName(
-        String  name)
+    public static X9ECParameters getByName(String name)
     {
-        ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier)objIds.get(Strings.toUpperCase(name));
+        ASN1ObjectIdentifier oid = getOID(name);
+        return null != oid ? SECNamedCurves.getByOID(oid) : null;
+    }
 
-        if (oid != null)
-        {
-            return getByOID(oid);
-        }
-
-        return null;
+    public static X9ECParametersHolder getByNameLazy(String name)
+    {
+        ASN1ObjectIdentifier oid = getOID(name);
+        return null != oid ? SECNamedCurves.getByOIDLazy(oid) : null;
     }
 
     /**
@@ -63,10 +63,14 @@
      *
      * @param oid an object identifier representing a named curve, if present.
      */
-    public static X9ECParameters getByOID(
-        ASN1ObjectIdentifier  oid)
+    public static X9ECParameters getByOID(ASN1ObjectIdentifier oid)
     {
-        return SECNamedCurves.getByOID(oid);
+        return names.containsKey(oid) ? SECNamedCurves.getByOID(oid) : null;
+    }
+
+    public static X9ECParametersHolder getByOIDLazy(ASN1ObjectIdentifier oid)
+    {
+        return names.containsKey(oid) ? SECNamedCurves.getByOIDLazy(oid) : null;
     }
 
     /**
@@ -75,8 +79,7 @@
      *
      * @return the object identifier associated with name, if present.
      */
-    public static ASN1ObjectIdentifier getOID(
-        String  name)
+    public static ASN1ObjectIdentifier getOID(String name)
     {
         return (ASN1ObjectIdentifier)objIds.get(Strings.toUpperCase(name));
     }
@@ -84,8 +87,7 @@
     /**
      * return the named curve name represented by the given object identifier.
      */
-    public static String getName(
-        ASN1ObjectIdentifier  oid)
+    public static String getName(ASN1ObjectIdentifier oid)
     {
         return (String)names.get(oid);
     }
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/nist/NISTObjectIdentifiers.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/nist/NISTObjectIdentifiers.java
index db60cf0..208b9f9 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/nist/NISTObjectIdentifiers.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/nist/NISTObjectIdentifiers.java
@@ -60,69 +60,137 @@
     /** 2.16.840.1.101.3.4.2.19 */
     static final ASN1ObjectIdentifier    id_KmacWithSHAKE128 = hashAlgs.branch("19");
     /** 2.16.840.1.101.3.4.2.20 */
-    static final ASN1ObjectIdentifier    id_KmacWithSHAKE256 = hashAlgs.branch("20");
+    static final ASN1ObjectIdentifier id_KmacWithSHAKE256 = hashAlgs.branch("20");
 
-    /** 2.16.840.1.101.3.4.1 */
-    static final ASN1ObjectIdentifier    aes                     = nistAlgorithm.branch("1");
-    
-    /** 2.16.840.1.101.3.4.1.1 */
-    static final ASN1ObjectIdentifier    id_aes128_ECB           = aes.branch("1"); 
-    /** 2.16.840.1.101.3.4.1.2 */
-    static final ASN1ObjectIdentifier    id_aes128_CBC           = aes.branch("2");
-    /** 2.16.840.1.101.3.4.1.3 */
-    static final ASN1ObjectIdentifier    id_aes128_OFB           = aes.branch("3"); 
-    /** 2.16.840.1.101.3.4.1.4 */
-    static final ASN1ObjectIdentifier    id_aes128_CFB           = aes.branch("4"); 
-    /** 2.16.840.1.101.3.4.1.5 */
-    static final ASN1ObjectIdentifier    id_aes128_wrap          = aes.branch("5");
-    /** 2.16.840.1.101.3.4.1.6 */
-    static final ASN1ObjectIdentifier    id_aes128_GCM           = aes.branch("6");
-    /** 2.16.840.1.101.3.4.1.7 */
-    static final ASN1ObjectIdentifier    id_aes128_CCM           = aes.branch("7");
-    /** 2.16.840.1.101.3.4.1.28 */
-    static final ASN1ObjectIdentifier    id_aes128_wrap_pad      = aes.branch("8");
+    /**
+     * 2.16.840.1.101.3.4.1
+     */
+    static final ASN1ObjectIdentifier aes = nistAlgorithm.branch("1");
 
-    /** 2.16.840.1.101.3.4.1.21 */
-    static final ASN1ObjectIdentifier    id_aes192_ECB           = aes.branch("21"); 
-    /** 2.16.840.1.101.3.4.1.22 */
-    static final ASN1ObjectIdentifier    id_aes192_CBC           = aes.branch("22"); 
-    /** 2.16.840.1.101.3.4.1.23 */
-    static final ASN1ObjectIdentifier    id_aes192_OFB           = aes.branch("23"); 
-    /** 2.16.840.1.101.3.4.1.24 */
-    static final ASN1ObjectIdentifier    id_aes192_CFB           = aes.branch("24"); 
-    /** 2.16.840.1.101.3.4.1.25 */
-    static final ASN1ObjectIdentifier    id_aes192_wrap          = aes.branch("25");
-    /** 2.16.840.1.101.3.4.1.26 */
-    static final ASN1ObjectIdentifier    id_aes192_GCM           = aes.branch("26");
-    /** 2.16.840.1.101.3.4.1.27 */
-    static final ASN1ObjectIdentifier    id_aes192_CCM           = aes.branch("27");
-    /** 2.16.840.1.101.3.4.1.28 */
-    static final ASN1ObjectIdentifier    id_aes192_wrap_pad      = aes.branch("28");
+    /**
+     * 2.16.840.1.101.3.4.1.1
+     */
+    static final ASN1ObjectIdentifier id_aes128_ECB = aes.branch("1");
+    /**
+     * 2.16.840.1.101.3.4.1.2
+     */
+    static final ASN1ObjectIdentifier id_aes128_CBC = aes.branch("2");
+    /**
+     * 2.16.840.1.101.3.4.1.3
+     */
+    static final ASN1ObjectIdentifier id_aes128_OFB = aes.branch("3");
+    /**
+     * 2.16.840.1.101.3.4.1.4
+     */
+    static final ASN1ObjectIdentifier id_aes128_CFB = aes.branch("4");
+    /**
+     * 2.16.840.1.101.3.4.1.5
+     */
+    static final ASN1ObjectIdentifier id_aes128_wrap = aes.branch("5");
+    /**
+     * 2.16.840.1.101.3.4.1.6
+     */
+    static final ASN1ObjectIdentifier id_aes128_GCM = aes.branch("6");
+    /**
+     * 2.16.840.1.101.3.4.1.7
+     */
+    static final ASN1ObjectIdentifier id_aes128_CCM = aes.branch("7");
+    /**
+     * 2.16.840.1.101.3.4.1.8
+     */
+    static final ASN1ObjectIdentifier id_aes128_wrap_pad = aes.branch("8");
+    /**
+     * 2.16.840.1.101.3.4.1.9
+     */
+    static final ASN1ObjectIdentifier id_aes128_GMAC = aes.branch("9");
 
-    /** 2.16.840.1.101.3.4.1.41 */
-    static final ASN1ObjectIdentifier    id_aes256_ECB           = aes.branch("41"); 
-    /** 2.16.840.1.101.3.4.1.42 */
-    static final ASN1ObjectIdentifier    id_aes256_CBC           = aes.branch("42");
-    /** 2.16.840.1.101.3.4.1.43 */
-    static final ASN1ObjectIdentifier    id_aes256_OFB           = aes.branch("43"); 
-    /** 2.16.840.1.101.3.4.1.44 */
-    static final ASN1ObjectIdentifier    id_aes256_CFB           = aes.branch("44"); 
-    /** 2.16.840.1.101.3.4.1.45 */
-    static final ASN1ObjectIdentifier    id_aes256_wrap          = aes.branch("45"); 
-    /** 2.16.840.1.101.3.4.1.46 */
-    static final ASN1ObjectIdentifier    id_aes256_GCM           = aes.branch("46");
-    /** 2.16.840.1.101.3.4.1.47 */
-    static final ASN1ObjectIdentifier    id_aes256_CCM           = aes.branch("47");
-    /** 2.16.840.1.101.3.4.1.48 */
-    static final ASN1ObjectIdentifier    id_aes256_wrap_pad      = aes.branch("48");
+
+    /**
+     * 2.16.840.1.101.3.4.1.21
+     */
+    static final ASN1ObjectIdentifier id_aes192_ECB = aes.branch("21");
+    /**
+     * 2.16.840.1.101.3.4.1.22
+     */
+    static final ASN1ObjectIdentifier id_aes192_CBC = aes.branch("22");
+    /**
+     * 2.16.840.1.101.3.4.1.23
+     */
+    static final ASN1ObjectIdentifier id_aes192_OFB = aes.branch("23");
+    /**
+     * 2.16.840.1.101.3.4.1.24
+     */
+    static final ASN1ObjectIdentifier id_aes192_CFB = aes.branch("24");
+    /**
+     * 2.16.840.1.101.3.4.1.25
+     */
+    static final ASN1ObjectIdentifier id_aes192_wrap = aes.branch("25");
+    /**
+     * 2.16.840.1.101.3.4.1.26
+     */
+    static final ASN1ObjectIdentifier id_aes192_GCM = aes.branch("26");
+    /**
+     * 2.16.840.1.101.3.4.1.27
+     */
+    static final ASN1ObjectIdentifier id_aes192_CCM = aes.branch("27");
+    /**
+     * 2.16.840.1.101.3.4.1.28
+     */
+    static final ASN1ObjectIdentifier id_aes192_wrap_pad = aes.branch("28");
+
+    /**
+     * 2.16.840.1.101.3.4.1.29
+     */
+    static final ASN1ObjectIdentifier id_aes192_GMAC = aes.branch("29");
+
+
+    /**
+     * 2.16.840.1.101.3.4.1.41
+     */
+    static final ASN1ObjectIdentifier id_aes256_ECB = aes.branch("41");
+    /**
+     * 2.16.840.1.101.3.4.1.42
+     */
+    static final ASN1ObjectIdentifier id_aes256_CBC = aes.branch("42");
+    /**
+     * 2.16.840.1.101.3.4.1.43
+     */
+    static final ASN1ObjectIdentifier id_aes256_OFB = aes.branch("43");
+    /**
+     * 2.16.840.1.101.3.4.1.44
+     */
+    static final ASN1ObjectIdentifier id_aes256_CFB = aes.branch("44");
+    /**
+     * 2.16.840.1.101.3.4.1.45
+     */
+    static final ASN1ObjectIdentifier id_aes256_wrap = aes.branch("45");
+    /**
+     * 2.16.840.1.101.3.4.1.46
+     */
+    static final ASN1ObjectIdentifier id_aes256_GCM = aes.branch("46");
+    /**
+     * 2.16.840.1.101.3.4.1.47
+     */
+    static final ASN1ObjectIdentifier id_aes256_CCM = aes.branch("47");
+    /**
+     * 2.16.840.1.101.3.4.1.48
+     */
+    static final ASN1ObjectIdentifier id_aes256_wrap_pad = aes.branch("48");
+    /**
+     * 2.16.840.1.101.3.4.1.49
+     */
+    static final ASN1ObjectIdentifier id_aes256_GMAC = aes.branch("49");
+
 
     //
     // signatures
     //
-    /** 2.16.840.1.101.3.4.3 */
-    static final ASN1ObjectIdentifier    sigAlgs        = nistAlgorithm.branch("3");
+    /**
+     * 2.16.840.1.101.3.4.3
+     */
+    static final ASN1ObjectIdentifier sigAlgs = nistAlgorithm.branch("3");
 
-    static final ASN1ObjectIdentifier    id_dsa_with_sha2        = sigAlgs;
+    static final ASN1ObjectIdentifier id_dsa_with_sha2 = sigAlgs;
 
     /** 2.16.840.1.101.3.4.3.1 */
     static final ASN1ObjectIdentifier    dsa_with_sha224         = sigAlgs.branch("1");
@@ -152,12 +220,12 @@
     static final ASN1ObjectIdentifier    id_ecdsa_with_sha3_512       = sigAlgs.branch("12");
 
     // RSA PKCS #1 v1.5 Signature with SHA-3 family.
-    /** 2.16.840.1.101.3.4.3.9 */
+    /** 2.16.840.1.101.3.4.3.13 */
     static final ASN1ObjectIdentifier    id_rsassa_pkcs1_v1_5_with_sha3_224       = sigAlgs.branch("13");
-    /** 2.16.840.1.101.3.4.3.10 */
+    /** 2.16.840.1.101.3.4.3.14 */
     static final ASN1ObjectIdentifier    id_rsassa_pkcs1_v1_5_with_sha3_256       = sigAlgs.branch("14");
-    /** 2.16.840.1.101.3.4.3.11 */
+    /** 2.16.840.1.101.3.4.3.15 */
     static final ASN1ObjectIdentifier    id_rsassa_pkcs1_v1_5_with_sha3_384       = sigAlgs.branch("15");
-    /** 2.16.840.1.101.3.4.3.12 */
+    /** 2.16.840.1.101.3.4.3.16 */
     static final ASN1ObjectIdentifier    id_rsassa_pkcs1_v1_5_with_sha3_512       = sigAlgs.branch("16");
 }
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ocsp/CertStatus.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ocsp/CertStatus.java
index 25ba415..286022c 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ocsp/CertStatus.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ocsp/CertStatus.java
@@ -3,9 +3,11 @@
 
 import com.android.internal.org.bouncycastle.asn1.ASN1Choice;
 import com.android.internal.org.bouncycastle.asn1.ASN1Encodable;
+import com.android.internal.org.bouncycastle.asn1.ASN1Null;
 import com.android.internal.org.bouncycastle.asn1.ASN1Object;
 import com.android.internal.org.bouncycastle.asn1.ASN1Primitive;
 import com.android.internal.org.bouncycastle.asn1.ASN1TaggedObject;
+import com.android.internal.org.bouncycastle.asn1.ASN1Util;
 import com.android.internal.org.bouncycastle.asn1.DERNull;
 import com.android.internal.org.bouncycastle.asn1.DERTaggedObject;
 
@@ -46,22 +48,25 @@
     private CertStatus(
         ASN1TaggedObject    choice)
     {
-        this.tagNo = choice.getTagNo();
+        int tagNo = choice.getTagNo();
 
-        switch (choice.getTagNo())
+        switch (tagNo)
         {
         case 0:
-            value = DERNull.INSTANCE;
+            value = ASN1Null.getInstance(choice, false);
             break;
         case 1:
             value = RevokedInfo.getInstance(choice, false);
             break;
         case 2:
-            value = DERNull.INSTANCE;
+            // UnknownInfo ::= NULL
+            value = ASN1Null.getInstance(choice, false);
             break;
         default:
-            throw new IllegalArgumentException("Unknown tag encountered: " + choice.getTagNo());
+            throw new IllegalArgumentException("Unknown tag encountered: " + ASN1Util.getTagText(choice));
         }
+
+        this.tagNo = tagNo;
     }
 
     public static CertStatus getInstance(
@@ -83,7 +88,7 @@
         ASN1TaggedObject obj,
         boolean          explicit)
     {
-        return getInstance(obj.getObject()); // must be explicitly tagged
+        return getInstance(obj.getExplicitBaseTagged()); // must be explicitly tagged
     }
     
     public int getTagNo()
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ocsp/CrlID.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ocsp/CrlID.java
index 403a4ac..4247fed 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ocsp/CrlID.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ocsp/CrlID.java
@@ -5,6 +5,7 @@
 
 import com.android.internal.org.bouncycastle.asn1.ASN1EncodableVector;
 import com.android.internal.org.bouncycastle.asn1.ASN1GeneralizedTime;
+import com.android.internal.org.bouncycastle.asn1.ASN1IA5String;
 import com.android.internal.org.bouncycastle.asn1.ASN1Integer;
 import com.android.internal.org.bouncycastle.asn1.ASN1Object;
 import com.android.internal.org.bouncycastle.asn1.ASN1Primitive;
@@ -20,7 +21,7 @@
 public class CrlID
     extends ASN1Object
 {
-    private DERIA5String         crlUrl;
+    private ASN1IA5String        crlUrl;
     private ASN1Integer          crlNum;
     private ASN1GeneralizedTime  crlTime;
 
@@ -36,7 +37,7 @@
             switch (o.getTagNo())
             {
             case 0:
-                crlUrl = DERIA5String.getInstance(o, true);
+                crlUrl = ASN1IA5String.getInstance(o, true);
                 break;
             case 1:
                 crlNum = ASN1Integer.getInstance(o, true);
@@ -66,8 +67,18 @@
         return null;
     }
 
+    /**
+     * @deprecated Use {@link #getCrlUrlIA5()} instead.
+     */
     public DERIA5String getCrlUrl()
     {
+        return null == crlUrl || crlUrl instanceof DERIA5String
+            ?   (DERIA5String)crlUrl
+            :   new DERIA5String(crlUrl.getString(), false);
+    }
+
+    public ASN1IA5String getCrlUrlIA5()
+    {
         return crlUrl;
     }
 
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ocsp/ResponderID.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ocsp/ResponderID.java
index ad42120..5bb17f8 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ocsp/ResponderID.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ocsp/ResponderID.java
@@ -64,7 +64,12 @@
         ASN1TaggedObject obj,
         boolean          explicit)
     {
-        return getInstance(obj.getObject()); // must be explicitly tagged
+        if (!explicit)
+        {
+            throw new IllegalArgumentException("choice item must be explicitly tagged");
+        }
+
+        return getInstance(obj.getExplicitBaseObject());
     }
 
     public byte[] getKeyHash()
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ocsp/ResponseBytes.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ocsp/ResponseBytes.java
index 2480e2f..a0d1ca0 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ocsp/ResponseBytes.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ocsp/ResponseBytes.java
@@ -33,10 +33,7 @@
         this.response = response;
     }
 
-    /**
-     * @deprecated use getInstance()
-     */
-    public ResponseBytes(
+    private ResponseBytes(
         ASN1Sequence    seq)
     {
         responseType = (ASN1ObjectIdentifier)seq.getObjectAt(0);
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ocsp/RevokedInfo.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ocsp/RevokedInfo.java
index 7c6b7b9..9ddb123 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ocsp/RevokedInfo.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/ocsp/RevokedInfo.java
@@ -21,6 +21,11 @@
     private ASN1GeneralizedTime  revocationTime;
     private CRLReason           revocationReason;
 
+    public RevokedInfo(ASN1GeneralizedTime revocationTime)
+    {
+        this(revocationTime, null);
+    }
+
     public RevokedInfo(
         ASN1GeneralizedTime  revocationTime,
         CRLReason           revocationReason)
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/pkcs/CRLBag.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/pkcs/CRLBag.java
index a12a870..1a048e0 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/pkcs/CRLBag.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/pkcs/CRLBag.java
@@ -24,8 +24,8 @@
     private CRLBag(
         ASN1Sequence seq)
     {
-        this.crlId = (ASN1ObjectIdentifier)seq.getObjectAt(0);
-        this.crlValue = ((ASN1TaggedObject)seq.getObjectAt(1)).getObject();
+        this.crlId = ASN1ObjectIdentifier.getInstance(seq.getObjectAt(0));
+        this.crlValue = ASN1TaggedObject.getInstance(seq.getObjectAt(1)).getExplicitBaseObject();
     }
 
     public static CRLBag getInstance(Object o)
@@ -69,7 +69,7 @@
      *
      * x509CRL BAG-TYPE ::= {OCTET STRING IDENTIFIED BY {certTypes 1}
      * -- DER-encoded X.509 CRL stored in OCTET STRING
-	 *
+     *
      * CRLTypes BAG-TYPE ::= {
      * x509CRL,
      * ... -- For future extensions
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/pkcs/CertBag.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/pkcs/CertBag.java
index 60f7506..abaae9a 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/pkcs/CertBag.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/pkcs/CertBag.java
@@ -24,7 +24,7 @@
         ASN1Sequence    seq)
     {
         this.certId = ASN1ObjectIdentifier.getInstance(seq.getObjectAt(0));
-        this.certValue = ASN1TaggedObject.getInstance(seq.getObjectAt(1)).getObject();
+        this.certValue = ASN1TaggedObject.getInstance(seq.getObjectAt(1)).getExplicitBaseObject();
     }
 
     public static CertBag getInstance(Object o)
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/pkcs/CertificationRequest.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/pkcs/CertificationRequest.java
index b3ee00e..a3ecffc 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/pkcs/CertificationRequest.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/pkcs/CertificationRequest.java
@@ -1,6 +1,7 @@
 /* GENERATED SOURCE. DO NOT MODIFY. */
 package com.android.internal.org.bouncycastle.asn1.pkcs;
 
+import com.android.internal.org.bouncycastle.asn1.ASN1BitString;
 import com.android.internal.org.bouncycastle.asn1.ASN1EncodableVector;
 import com.android.internal.org.bouncycastle.asn1.ASN1Object;
 import com.android.internal.org.bouncycastle.asn1.ASN1Primitive;
@@ -25,7 +26,7 @@
 {
     protected CertificationRequestInfo reqInfo = null;
     protected AlgorithmIdentifier sigAlgId = null;
-    protected DERBitString sigBits = null;
+    protected ASN1BitString sigBits = null;
 
     public static CertificationRequest getInstance(Object o)
     {
@@ -49,7 +50,7 @@
     public CertificationRequest(
         CertificationRequestInfo requestInfo,
         AlgorithmIdentifier     algorithm,
-        DERBitString            signature)
+        ASN1BitString            signature)
     {
         this.reqInfo = requestInfo;
         this.sigAlgId = algorithm;
@@ -77,7 +78,7 @@
         return sigAlgId;
     }
 
-    public DERBitString getSignature()
+    public ASN1BitString getSignature()
     {
         return sigBits;
     }
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/pkcs/CertificationRequestInfo.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/pkcs/CertificationRequestInfo.java
index bce5f0e..0c140bc 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/pkcs/CertificationRequestInfo.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/pkcs/CertificationRequestInfo.java
@@ -99,10 +99,7 @@
         this(X500Name.getInstance(subject.toASN1Primitive()), pkInfo, attributes);
     }
 
-    /**
-     * @deprecated use getInstance().
-     */
-    public CertificationRequestInfo(
+    private CertificationRequestInfo(
         ASN1Sequence  seq)
     {
         version = (ASN1Integer)seq.getObjectAt(0);
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/pkcs/ContentInfo.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/pkcs/ContentInfo.java
index ed6988d..7ee5f3b 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/pkcs/ContentInfo.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/pkcs/ContentInfo.java
@@ -50,7 +50,7 @@
 
         if (e.hasMoreElements())
         {
-            content = ((ASN1TaggedObject)e.nextElement()).getObject();
+            content = ((ASN1TaggedObject)e.nextElement()).getExplicitBaseObject();
         }
 
         isBer = seq instanceof BERSequence;
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/pkcs/EncryptedData.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/pkcs/EncryptedData.java
index 600b9d2..dc41097 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/pkcs/EncryptedData.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/pkcs/EncryptedData.java
@@ -57,9 +57,8 @@
     private EncryptedData(
         ASN1Sequence seq)
     {
-        int version = ((ASN1Integer)seq.getObjectAt(0)).intValueExact();
-
-        if (version != 0)
+        ASN1Integer version = (ASN1Integer)seq.getObjectAt(0);
+        if (!version.hasValue(0))
         {
             throw new IllegalArgumentException("sequence not version 0");
         }
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/pkcs/EncryptedPrivateKeyInfo.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/pkcs/EncryptedPrivateKeyInfo.java
index 47c6100..6507d4f 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/pkcs/EncryptedPrivateKeyInfo.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/pkcs/EncryptedPrivateKeyInfo.java
@@ -11,6 +11,7 @@
 import com.android.internal.org.bouncycastle.asn1.DEROctetString;
 import com.android.internal.org.bouncycastle.asn1.DERSequence;
 import com.android.internal.org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import com.android.internal.org.bouncycastle.util.Arrays;
 
 /**
  * @hide This class is not part of the Android public SDK API
@@ -35,7 +36,7 @@
         byte[]              encoding)
     {
         this.algId = algId;
-        this.data = new DEROctetString(encoding);
+        this.data = new DEROctetString(Arrays.clone(encoding));
     }
 
     public static EncryptedPrivateKeyInfo getInstance(
@@ -60,7 +61,7 @@
 
     public byte[] getEncryptedData()
     {
-        return data.getOctets();
+        return Arrays.clone(data.getOctets());
     }
 
     /**
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/pkcs/PBKDF2Params.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/pkcs/PBKDF2Params.java
index 82a22ec..c77a91e 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/pkcs/PBKDF2Params.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/pkcs/PBKDF2Params.java
@@ -185,7 +185,7 @@
      */
     public byte[] getSalt()
     {
-        return octStr.getOctets();
+        return Arrays.clone(octStr.getOctets());
     }
 
     /**
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/pkcs/PBMAC1Params.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/pkcs/PBMAC1Params.java
new file mode 100644
index 0000000..3927b45
--- /dev/null
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/pkcs/PBMAC1Params.java
@@ -0,0 +1,90 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.asn1.pkcs;
+
+import java.util.Enumeration;
+
+import com.android.internal.org.bouncycastle.asn1.ASN1Encodable;
+import com.android.internal.org.bouncycastle.asn1.ASN1EncodableVector;
+import com.android.internal.org.bouncycastle.asn1.ASN1Object;
+import com.android.internal.org.bouncycastle.asn1.ASN1Primitive;
+import com.android.internal.org.bouncycastle.asn1.ASN1Sequence;
+import com.android.internal.org.bouncycastle.asn1.DERSequence;
+import com.android.internal.org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+
+
+/**
+ * From https://datatracker.ietf.org/doc/html/rfc8018
+ *
+ * <pre>
+ * PBMAC1-params ::= SEQUENCE {
+ *     keyDerivationFunc AlgorithmIdentifier {{PBMAC1-KDFs}},
+ *     messageAuthScheme AlgorithmIdentifier {{PBMAC1-MACs}} }
+ * </pre>
+ * @hide This class is not part of the Android public SDK API
+ */
+public class PBMAC1Params
+    extends ASN1Object
+    implements PKCSObjectIdentifiers
+{
+    private AlgorithmIdentifier func;
+    private AlgorithmIdentifier scheme;
+
+    public static PBMAC1Params getInstance(
+        Object  obj)
+    {
+        if (obj instanceof PBMAC1Params)
+        {
+            return (PBMAC1Params)obj;
+        }
+        if (obj != null)
+        {
+            return new PBMAC1Params(ASN1Sequence.getInstance(obj));
+        }
+
+        return null;
+    }
+
+    public PBMAC1Params(AlgorithmIdentifier keyDevFunc, AlgorithmIdentifier encScheme)
+    {
+        this.func = keyDevFunc;
+        this.scheme = encScheme;
+    }
+
+    private PBMAC1Params(
+        ASN1Sequence  obj)
+    {
+        Enumeration e = obj.getObjects();
+        ASN1Sequence  funcSeq = ASN1Sequence.getInstance(((ASN1Encodable)e.nextElement()).toASN1Primitive());
+
+        if (funcSeq.getObjectAt(0).equals(id_PBKDF2))
+        {
+            func = new AlgorithmIdentifier(id_PBKDF2, PBKDF2Params.getInstance(funcSeq.getObjectAt(1)));
+        }
+        else
+        {
+            func = AlgorithmIdentifier.getInstance(funcSeq);
+        }
+
+        scheme = AlgorithmIdentifier.getInstance(e.nextElement());
+    }
+
+    public AlgorithmIdentifier getKeyDerivationFunc()
+    {
+        return func;
+    }
+
+    public AlgorithmIdentifier getMessageAuthScheme()
+    {
+        return scheme;
+    }
+
+    public ASN1Primitive toASN1Primitive()
+    {
+        ASN1EncodableVector v = new ASN1EncodableVector(2);
+
+        v.add(func);
+        v.add(scheme);
+
+        return new DERSequence(v);
+    }
+}
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/pkcs/PKCSObjectIdentifiers.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/pkcs/PKCSObjectIdentifiers.java
index 040a1c2..8207102 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/pkcs/PKCSObjectIdentifiers.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/pkcs/PKCSObjectIdentifiers.java
@@ -78,10 +78,12 @@
     ASN1ObjectIdentifier    pbeWithSHA1AndDES_CBC   = pkcs_5.branch("10");
     /** PKCS#5: 1.2.840.113549.1.5.11 */
     ASN1ObjectIdentifier    pbeWithSHA1AndRC2_CBC   = pkcs_5.branch("11");
-    /** PKCS#5: 1.2.840.113549.1.5.13 */
-    ASN1ObjectIdentifier    id_PBES2                = pkcs_5.branch("13");
     /** PKCS#5: 1.2.840.113549.1.5.12 */
     ASN1ObjectIdentifier    id_PBKDF2               = pkcs_5.branch("12");
+    /** PKCS#5: 1.2.840.113549.1.5.13 */
+    ASN1ObjectIdentifier    id_PBES2                = pkcs_5.branch("13");
+    /** PKCS#5: 1.2.840.113549.1.5.14 */
+    ASN1ObjectIdentifier    id_PBMAC1               = pkcs_5.branch("14");
 
     //
     // encryptionAlgorithm OBJECT IDENTIFIER ::= {
@@ -137,6 +139,10 @@
     ASN1ObjectIdentifier    id_hmacWithSHA384      = digestAlgorithm.branch("10").intern();
     /**  1.2.840.113549.2.11 */
     ASN1ObjectIdentifier    id_hmacWithSHA512      = digestAlgorithm.branch("11").intern();
+    /**  1.2.840.113549.2.12 */
+    ASN1ObjectIdentifier    id_hmacWithSHA512_224  = digestAlgorithm.branch("12").intern();
+    /**  1.2.840.113549.2.13 */
+    ASN1ObjectIdentifier    id_hmacWithSHA512_256  = digestAlgorithm.branch("13").intern();
 
     //
     // pkcs-7 OBJECT IDENTIFIER ::= {
@@ -191,6 +197,8 @@
     ASN1ObjectIdentifier    pkcs_9_at_smimeCapabilities  = pkcs_9.branch("15").intern();
     /** PKCS#9: 1.2.840.113549.1.9.16 */
     ASN1ObjectIdentifier    id_smime                     = pkcs_9.branch("16").intern();
+    /** PKCS#9: 1.2.840.113549.1.9.16.2.46 */
+    ASN1ObjectIdentifier    pkcs_9_at_binarySigningTime  = pkcs_9.branch("16.2.46").intern();
 
     /** PKCS#9: 1.2.840.113549.1.9.20 */
     ASN1ObjectIdentifier    pkcs_9_at_friendlyName  = pkcs_9.branch("20").intern();
@@ -249,8 +257,20 @@
 
     /** S/MIME: Algorithm Identifiers ; 1.2.840.113549.1.9.16.3 */
     ASN1ObjectIdentifier id_alg                  = id_smime.branch("3");
+
+    /** PKCS#9: 1.2.840.113549.1.9.16.3.5 */
+    ASN1ObjectIdentifier    id_alg_ESDH             = id_alg.branch("5");
+    /** PKCS#9: 1.2.840.113549.1.9.16.3.6 */
+    ASN1ObjectIdentifier    id_alg_CMS3DESwrap      = id_alg.branch("6");
+    /** PKCS#9: 1.2.840.113549.1.9.16.3.7 */
+    ASN1ObjectIdentifier    id_alg_CMSRC2wrap       = id_alg.branch("7");
+    /** PKCS#9: 1.2.840.113549.1.9.16.3.8 */
+    ASN1ObjectIdentifier id_alg_zlibCompress        = id_alg.branch("8");
     /** PKCS#9: 1.2.840.113549.1.9.16.3.9 */
-    ASN1ObjectIdentifier id_alg_PWRI_KEK         = id_alg.branch("9");
+    ASN1ObjectIdentifier id_alg_PWRI_KEK            = id_alg.branch("9");
+    /** PKCS#9: 1.2.840.113549.1.9.16.3.10 */
+    ASN1ObjectIdentifier    id_alg_SSDH             = id_alg.branch("10");
+
     /**
      * <pre>
      * -- RSA-KEM Key Transport Algorithm  RFC 5990
@@ -468,14 +488,5 @@
      * @deprecated use pbeWithSHAAnd40BitRC2_CBC
      */
     ASN1ObjectIdentifier    pbewithSHAAnd40BitRC2_CBC = pkcs_12PbeIds.branch("6");
-
-    /** PKCS#9: 1.2.840.113549.1.9.16.3.6 */
-    ASN1ObjectIdentifier    id_alg_CMS3DESwrap = new ASN1ObjectIdentifier("1.2.840.113549.1.9.16.3.6");
-    /** PKCS#9: 1.2.840.113549.1.9.16.3.7 */
-    ASN1ObjectIdentifier    id_alg_CMSRC2wrap  = new ASN1ObjectIdentifier("1.2.840.113549.1.9.16.3.7");
-    /** PKCS#9: 1.2.840.113549.1.9.16.3.5 */
-    ASN1ObjectIdentifier    id_alg_ESDH  = new ASN1ObjectIdentifier("1.2.840.113549.1.9.16.3.5");
-    /** PKCS#9: 1.2.840.113549.1.9.16.3.10 */
-    ASN1ObjectIdentifier    id_alg_SSDH  = new ASN1ObjectIdentifier("1.2.840.113549.1.9.16.3.10");
 }
 
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/pkcs/Pfx.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/pkcs/Pfx.java
index a782500..509dc17 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/pkcs/Pfx.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/pkcs/Pfx.java
@@ -23,7 +23,7 @@
         ASN1Sequence   seq)
     {
         ASN1Integer version = ASN1Integer.getInstance(seq.getObjectAt(0));
-        if (version.intValueExact() != 3)
+        if (!version.hasValue(3))
         {
             throw new IllegalArgumentException("wrong version for PFX PDU");
         }
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/pkcs/PrivateKeyInfo.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/pkcs/PrivateKeyInfo.java
index 9d8e99d..92e6162 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/pkcs/PrivateKeyInfo.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/pkcs/PrivateKeyInfo.java
@@ -166,7 +166,7 @@
                     throw new IllegalArgumentException("'publicKey' requires version v2(1) or later");
                 }
 
-                this.publicKey = DERBitString.getInstance(tagged, false);
+                this.publicKey = ASN1BitString.getInstance(tagged, false);
                 break;
             }
             default:
@@ -197,6 +197,11 @@
         return new DEROctetString(privateKey.getOctets());
     }
 
+    public int getPrivateKeyLength()
+    {
+        return privateKey.getOctetsLength();
+    }
+
     public ASN1Encodable parsePrivateKey()
         throws IOException
     {
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/pkcs/RSAESOAEPparams.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/pkcs/RSAESOAEPparams.java
index 6cd1615..b105459 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/pkcs/RSAESOAEPparams.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/pkcs/RSAESOAEPparams.java
@@ -62,11 +62,7 @@
         this.pSourceAlgorithm = pSourceAlgorithm;
     }
 
-    /**
-     * @deprecated use getInstance()
-     * @param seq
-     */
-    public RSAESOAEPparams(
+    private RSAESOAEPparams(
         ASN1Sequence seq)
     {
         hashAlgorithm = DEFAULT_HASH_ALGORITHM;
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/pkcs/SafeBag.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/pkcs/SafeBag.java
index 93fec39..6a3fb56 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/pkcs/SafeBag.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/pkcs/SafeBag.java
@@ -61,7 +61,7 @@
         ASN1Sequence    seq)
     {
         this.bagId = (ASN1ObjectIdentifier)seq.getObjectAt(0);
-        this.bagValue = ((ASN1TaggedObject)seq.getObjectAt(1)).getObject();
+        this.bagValue = ((ASN1TaggedObject)seq.getObjectAt(1)).getExplicitBaseObject();
         if (seq.size() == 3)
         {
             this.bagAttributes = (ASN1Set)seq.getObjectAt(2);
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/sec/ECPrivateKey.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/sec/ECPrivateKey.java
index e8b76a7..c0090ff 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/sec/ECPrivateKey.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/sec/ECPrivateKey.java
@@ -4,6 +4,7 @@
 import java.math.BigInteger;
 import java.util.Enumeration;
 
+import com.android.internal.org.bouncycastle.asn1.ASN1BitString;
 import com.android.internal.org.bouncycastle.asn1.ASN1Encodable;
 import com.android.internal.org.bouncycastle.asn1.ASN1EncodableVector;
 import com.android.internal.org.bouncycastle.asn1.ASN1Integer;
@@ -12,7 +13,7 @@
 import com.android.internal.org.bouncycastle.asn1.ASN1Primitive;
 import com.android.internal.org.bouncycastle.asn1.ASN1Sequence;
 import com.android.internal.org.bouncycastle.asn1.ASN1TaggedObject;
-import com.android.internal.org.bouncycastle.asn1.DERBitString;
+import com.android.internal.org.bouncycastle.asn1.BERTags;
 import com.android.internal.org.bouncycastle.asn1.DEROctetString;
 import com.android.internal.org.bouncycastle.asn1.DERSequence;
 import com.android.internal.org.bouncycastle.asn1.DERTaggedObject;
@@ -93,7 +94,7 @@
      */
     public ECPrivateKey(
         BigInteger key,
-        DERBitString publicKey,
+        ASN1BitString publicKey,
         ASN1Encodable parameters)
     {
         this(key.bitLength(), key, publicKey, parameters);
@@ -110,7 +111,7 @@
     public ECPrivateKey(
         int orderBitLength,
         BigInteger key,
-        DERBitString publicKey,
+        ASN1BitString publicKey,
         ASN1Encodable parameters)
     {
         byte[] bytes = BigIntegers.asUnsignedByteArray((orderBitLength + 7) / 8, key);
@@ -139,18 +140,27 @@
 
         return new BigInteger(1, octs.getOctets());
     }
-
-    public DERBitString getPublicKey()
+    
+    public ASN1BitString getPublicKey()
     {
-        return (DERBitString)getObjectInTag(1);
+        return (ASN1BitString)getObjectInTag(1, BERTags.BIT_STRING);
     }
 
+    /**
+     * @deprecated Use {@link #getParametersObject()} instead and getInstance
+     *             methods or similar to get the object at the desired type.
+     */
     public ASN1Primitive getParameters()
     {
-        return getObjectInTag(0);
+        return getParametersObject().toASN1Primitive();
     }
 
-    private ASN1Primitive getObjectInTag(int tagNo)
+    public ASN1Object getParametersObject()
+    {
+        return getObjectInTag(0, -1);
+    }
+
+    private ASN1Object getObjectInTag(int tagNo, int baseTagNo)
     {
         Enumeration e = seq.getObjects();
 
@@ -161,9 +171,11 @@
             if (obj instanceof ASN1TaggedObject)
             {
                 ASN1TaggedObject tag = (ASN1TaggedObject)obj;
-                if (tag.getTagNo() == tagNo)
+                if (tag.hasContextTag(tagNo))
                 {
-                    return tag.getObject().toASN1Primitive();
+                    return baseTagNo < 0
+                        ?   tag.getExplicitBaseObject().toASN1Primitive()
+                        :   tag.getBaseUniversal(true, baseTagNo);
                 }
             }
         }
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/sec/ECPrivateKeyStructure.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/sec/ECPrivateKeyStructure.java
index 4cb9eab..13edd6d 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/sec/ECPrivateKeyStructure.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/sec/ECPrivateKeyStructure.java
@@ -4,6 +4,7 @@
 import java.math.BigInteger;
 import java.util.Enumeration;
 
+import com.android.internal.org.bouncycastle.asn1.ASN1BitString;
 import com.android.internal.org.bouncycastle.asn1.ASN1Encodable;
 import com.android.internal.org.bouncycastle.asn1.ASN1EncodableVector;
 import com.android.internal.org.bouncycastle.asn1.ASN1Integer;
@@ -12,7 +13,6 @@
 import com.android.internal.org.bouncycastle.asn1.ASN1Primitive;
 import com.android.internal.org.bouncycastle.asn1.ASN1Sequence;
 import com.android.internal.org.bouncycastle.asn1.ASN1TaggedObject;
-import com.android.internal.org.bouncycastle.asn1.DERBitString;
 import com.android.internal.org.bouncycastle.asn1.DEROctetString;
 import com.android.internal.org.bouncycastle.asn1.DERSequence;
 import com.android.internal.org.bouncycastle.asn1.DERTaggedObject;
@@ -56,7 +56,7 @@
 
     public ECPrivateKeyStructure(
         BigInteger    key,
-        DERBitString  publicKey,
+        ASN1BitString  publicKey,
         ASN1Encodable parameters)
     {
         byte[] bytes = BigIntegers.asUnsignedByteArray(key);
@@ -86,9 +86,9 @@
         return new BigInteger(1, octs.getOctets());
     }
 
-    public DERBitString getPublicKey()
+    public ASN1BitString getPublicKey()
     {
-        return (DERBitString)getObjectInTag(1);
+        return (ASN1BitString)getObjectInTag(1);
     }
 
     public ASN1Primitive getParameters()
@@ -109,7 +109,7 @@
                 ASN1TaggedObject tag = (ASN1TaggedObject)obj;
                 if (tag.getTagNo() == tagNo)
                 {
-                    return (ASN1Primitive)((ASN1Encodable)tag.getObject()).toASN1Primitive();
+                    return (ASN1Primitive)((ASN1Encodable)tag.getExplicitBaseObject()).toASN1Primitive();
                 }
             }
         }
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/sec/SECNamedCurves.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/sec/SECNamedCurves.java
index 31cbd7f..bc40585 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/sec/SECNamedCurves.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/sec/SECNamedCurves.java
@@ -50,22 +50,27 @@
      */
     static X9ECParametersHolder secp112r1 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             // p = (2^128 - 3) / 76439
             BigInteger p = fromHex("DB7C2ABF62E35E668076BEAD208B");
             BigInteger a = fromHex("DB7C2ABF62E35E668076BEAD2088");
             BigInteger b = fromHex("659EF8BA043916EEDE8911702B22");
-            byte[] S = Hex.decodeStrict("00F50B028E4D696E676875615175290472783FB1");
             BigInteger n = fromHex("DB7C2ABF62E35E7628DFAC6561C5");
             BigInteger h = BigInteger.valueOf(1);
 
-            ECCurve curve = configureCurve(new ECCurve.Fp(p, a, b, n, h));
+            return configureCurve(new ECCurve.Fp(p, a, b, n, h, true));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = Hex.decodeStrict("00F50B028E4D696E676875615175290472783FB1");
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "0409487239995A5EE76B55F9C2F098A89CE5AF8724C0A23E0E0FF77500");
 
-            return new X9ECParameters(curve, G, n, h, S);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
@@ -74,22 +79,27 @@
      */
     static X9ECParametersHolder secp112r2 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             // p = (2^128 - 3) / 76439
             BigInteger p = fromHex("DB7C2ABF62E35E668076BEAD208B");
             BigInteger a = fromHex("6127C24C05F38A0AAAF65C0EF02C");
             BigInteger b = fromHex("51DEF1815DB5ED74FCC34C85D709");
-            byte[] S = Hex.decodeStrict("002757A1114D696E6768756151755316C05E0BD4");
             BigInteger n = fromHex("36DF0AAFD8B8D7597CA10520D04B");
             BigInteger h = BigInteger.valueOf(4);
 
-            ECCurve curve = configureCurve(new ECCurve.Fp(p, a, b, n, h));
+            return configureCurve(new ECCurve.Fp(p, a, b, n, h, true));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = Hex.decodeStrict("002757A1114D696E6768756151755316C05E0BD4");
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "044BA30AB5E892B4E1649DD0928643ADCD46F5882E3747DEF36E956E97");
 
-            return new X9ECParameters(curve, G, n, h, S);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
@@ -98,22 +108,27 @@
      */
     static X9ECParametersHolder secp128r1 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             // p = 2^128 - 2^97 - 1
             BigInteger p = fromHex("FFFFFFFDFFFFFFFFFFFFFFFFFFFFFFFF");
             BigInteger a = fromHex("FFFFFFFDFFFFFFFFFFFFFFFFFFFFFFFC");
             BigInteger b = fromHex("E87579C11079F43DD824993C2CEE5ED3");
-            byte[] S = Hex.decodeStrict("000E0D4D696E6768756151750CC03A4473D03679");
             BigInteger n = fromHex("FFFFFFFE0000000075A30D1B9038A115");
             BigInteger h = BigInteger.valueOf(1);
 
-            ECCurve curve = configureCurve(new ECCurve.Fp(p, a, b, n, h));
+            return configureCurve(new ECCurve.Fp(p, a, b, n, h, true));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = Hex.decodeStrict("000E0D4D696E6768756151750CC03A4473D03679");
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "04161FF7528B899B2D0C28607CA52C5B86CF5AC8395BAFEB13C02DA292DDED7A83");
 
-            return new X9ECParameters(curve, G, n, h, S);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
@@ -122,22 +137,27 @@
      */
     static X9ECParametersHolder secp128r2 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             // p = 2^128 - 2^97 - 1
             BigInteger p = fromHex("FFFFFFFDFFFFFFFFFFFFFFFFFFFFFFFF");
             BigInteger a = fromHex("D6031998D1B3BBFEBF59CC9BBFF9AEE1");
             BigInteger b = fromHex("5EEEFCA380D02919DC2C6558BB6D8A5D");
-            byte[] S = Hex.decodeStrict("004D696E67687561517512D8F03431FCE63B88F4");
             BigInteger n = fromHex("3FFFFFFF7FFFFFFFBE0024720613B5A3");
             BigInteger h = BigInteger.valueOf(4);
 
-            ECCurve curve = configureCurve(new ECCurve.Fp(p, a, b, n, h));
+            return configureCurve(new ECCurve.Fp(p, a, b, n, h, true));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = Hex.decodeStrict("004D696E67687561517512D8F03431FCE63B88F4");
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "047B6AA5D85E572983E6FB32A7CDEBC14027B6916A894D3AEE7106FE805FC34B44");
 
-            return new X9ECParameters(curve, G, n, h, S);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
@@ -146,13 +166,12 @@
      */
     static X9ECParametersHolder secp160k1 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             // p = 2^160 - 2^32 - 2^14 - 2^12 - 2^9 - 2^8 - 2^7 - 2^3 - 2^2 - 1
             BigInteger p = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFAC73");
             BigInteger a = ECConstants.ZERO;
             BigInteger b = BigInteger.valueOf(7);
-            byte[] S = null;
             BigInteger n = fromHex("0100000000000000000001B8FA16DFAB9ACA16B6B3");
             BigInteger h = BigInteger.valueOf(1);
 
@@ -170,12 +189,18 @@
                     new BigInteger("96341f1138933bc2f503fd44", 16),
                     176));
 
-            ECCurve curve = configureCurveGLV(new ECCurve.Fp(p, a, b, n, h), glv);
+            return configureCurveGLV(new ECCurve.Fp(p, a, b, n, h, true), glv);
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = null;
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "043B4C382CE37AA192A4019E763036F4F5DD4D7EBB938CF935318FDCED6BC28286531733C3F03C4FEE");
 
-            return new X9ECParameters(curve, G, n, h, S);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
@@ -184,22 +209,27 @@
      */
     static X9ECParametersHolder secp160r1 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             // p = 2^160 - 2^31 - 1
             BigInteger p = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7FFFFFFF");
             BigInteger a = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7FFFFFFC");
             BigInteger b = fromHex("1C97BEFC54BD7A8B65ACF89F81D4D4ADC565FA45");
-            byte[] S = Hex.decodeStrict("1053CDE42C14D696E67687561517533BF3F83345");
             BigInteger n = fromHex("0100000000000000000001F4C8F927AED3CA752257");
             BigInteger h = BigInteger.valueOf(1);
 
-            ECCurve curve = configureCurve(new ECCurve.Fp(p, a, b, n, h));
+            return configureCurve(new ECCurve.Fp(p, a, b, n, h, true));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = Hex.decodeStrict("1053CDE42C14D696E67687561517533BF3F83345");
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "044A96B5688EF573284664698968C38BB913CBFC8223A628553168947D59DCC912042351377AC5FB32");
 
-            return new X9ECParameters(curve, G, n, h, S);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
@@ -208,22 +238,27 @@
      */
     static X9ECParametersHolder secp160r2 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             // p = 2^160 - 2^32 - 2^14 - 2^12 - 2^9 - 2^8 - 2^7 - 2^3 - 2^2 - 1
             BigInteger p = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFAC73");
             BigInteger a = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFAC70");
             BigInteger b = fromHex("B4E134D3FB59EB8BAB57274904664D5AF50388BA");
-            byte[] S = Hex.decodeStrict("B99B99B099B323E02709A4D696E6768756151751");
             BigInteger n = fromHex("0100000000000000000000351EE786A818F3A1A16B");
             BigInteger h = BigInteger.valueOf(1);
 
-            ECCurve curve = configureCurve(new ECCurve.Fp(p, a, b, n, h));
+            return configureCurve(new ECCurve.Fp(p, a, b, n, h, true));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = Hex.decodeStrict("B99B99B099B323E02709A4D696E6768756151751");
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "0452DCB034293A117E1F4FF11B30F7199D3144CE6DFEAFFEF2E331F296E071FA0DF9982CFEA7D43F2E");
 
-            return new X9ECParameters(curve, G, n, h, S);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
@@ -232,13 +267,12 @@
      */
     static X9ECParametersHolder secp192k1 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             // p = 2^192 - 2^32 - 2^12 - 2^8 - 2^7 - 2^6 - 2^3 - 1
             BigInteger p = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFEE37");
             BigInteger a = ECConstants.ZERO;
             BigInteger b = BigInteger.valueOf(3);
-            byte[] S = null;
             BigInteger n = fromHex("FFFFFFFFFFFFFFFFFFFFFFFE26F2FC170F69466A74DEFD8D");
             BigInteger h = BigInteger.valueOf(1);
 
@@ -256,12 +290,18 @@
                     new BigInteger("b3fb3400dec5c4adceb8655d4c94", 16),
                     208));
 
-            ECCurve curve = configureCurveGLV(new ECCurve.Fp(p, a, b, n, h), glv);
+            return configureCurveGLV(new ECCurve.Fp(p, a, b, n, h, true), glv);
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = null;
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "04DB4FF10EC057E9AE26B07D0280B7F4341DA5D1B1EAE06C7D9B2F2F6D9C5628A7844163D015BE86344082AA88D95E2F9D");
 
-            return new X9ECParameters(curve, G, n, h, S);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
@@ -270,22 +310,27 @@
      */
     static X9ECParametersHolder secp192r1 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             // p = 2^192 - 2^64 - 1
             BigInteger p = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFF");
             BigInteger a = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFC");
             BigInteger b = fromHex("64210519E59C80E70FA7E9AB72243049FEB8DEECC146B9B1");
-            byte[] S = Hex.decodeStrict("3045AE6FC8422F64ED579528D38120EAE12196D5");
             BigInteger n = fromHex("FFFFFFFFFFFFFFFFFFFFFFFF99DEF836146BC9B1B4D22831");
             BigInteger h = BigInteger.valueOf(1);
 
-            ECCurve curve = configureCurve(new ECCurve.Fp(p, a, b, n, h));
+            return configureCurve(new ECCurve.Fp(p, a, b, n, h, true));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = Hex.decodeStrict("3045AE6FC8422F64ED579528D38120EAE12196D5");
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "04188DA80EB03090F67CBF20EB43A18800F4FF0AFD82FF101207192B95FFC8DA78631011ED6B24CDD573F977A11E794811");
 
-            return new X9ECParameters(curve, G, n, h, S);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
@@ -294,13 +339,12 @@
      */
     static X9ECParametersHolder secp224k1 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             // p = 2^224 - 2^32 - 2^12 - 2^11 - 2^9 - 2^7 - 2^4 - 2 - 1
             BigInteger p = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFE56D");
             BigInteger a = ECConstants.ZERO;
             BigInteger b = BigInteger.valueOf(5);
-            byte[] S = null;
             BigInteger n = fromHex("010000000000000000000000000001DCE8D2EC6184CAF0A971769FB1F7");
             BigInteger h = BigInteger.valueOf(1);
 
@@ -318,12 +362,18 @@
                     new BigInteger("b8adf1378a6eb73409fa6c9c637ba7f5", 16),
                     240));
 
-            ECCurve curve = configureCurveGLV(new ECCurve.Fp(p, a, b, n, h), glv);
+            return configureCurveGLV(new ECCurve.Fp(p, a, b, n, h, true), glv);
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = null;
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "04A1455B334DF099DF30FC28A169A467E9E47075A90F7E650EB6B7A45C7E089FED7FBA344282CAFBD6F7E319F7C0B0BD59E2CA4BDB556D61A5");
 
-            return new X9ECParameters(curve, G, n, h, S);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
@@ -332,22 +382,27 @@
      */
     static X9ECParametersHolder secp224r1 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             // p = 2^224 - 2^96 + 1
             BigInteger p = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000000000000001");
             BigInteger a = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFE");
             BigInteger b = fromHex("B4050A850C04B3ABF54132565044B0B7D7BFD8BA270B39432355FFB4");
-            byte[] S = Hex.decodeStrict("BD71344799D5C7FCDC45B59FA3B9AB8F6A948BC5");
             BigInteger n = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFF16A2E0B8F03E13DD29455C5C2A3D");
             BigInteger h = BigInteger.valueOf(1);
 
-            ECCurve curve = configureCurve(new ECCurve.Fp(p, a, b, n, h));
+            return configureCurve(new ECCurve.Fp(p, a, b, n, h, true));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = Hex.decodeStrict("BD71344799D5C7FCDC45B59FA3B9AB8F6A948BC5");
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "04B70E0CBD6BB4BF7F321390B94A03C1D356C21122343280D6115C1D21BD376388B5F723FB4C22DFE6CD4375A05A07476444D5819985007E34");
 
-            return new X9ECParameters(curve, G, n, h, S);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
@@ -356,13 +411,12 @@
      */
     static X9ECParametersHolder secp256k1 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             // p = 2^256 - 2^32 - 2^9 - 2^8 - 2^7 - 2^6 - 2^4 - 1
             BigInteger p = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F");
             BigInteger a = ECConstants.ZERO;
             BigInteger b = BigInteger.valueOf(7);
-            byte[] S = null;
             BigInteger n = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141");
             BigInteger h = BigInteger.valueOf(1);
 
@@ -380,12 +434,18 @@
                     new BigInteger("e4437ed6010e88286f547fa90abfe4c42212", 16),
                     272));
 
-            ECCurve curve = configureCurveGLV(new ECCurve.Fp(p, a, b, n, h), glv);
+            return configureCurveGLV(new ECCurve.Fp(p, a, b, n, h, true), glv);
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = null;
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "0479BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8");
 
-            return new X9ECParameters(curve, G, n, h, S);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
@@ -394,22 +454,27 @@
      */
     static X9ECParametersHolder secp256r1 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             // p = 2^224 (2^32 - 1) + 2^192 + 2^96 - 1
             BigInteger p = fromHex("FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF");
             BigInteger a = fromHex("FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC");
             BigInteger b = fromHex("5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B");
-            byte[] S = Hex.decodeStrict("C49D360886E704936A6678E1139D26B7819F7E90");
             BigInteger n = fromHex("FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551");
             BigInteger h = BigInteger.valueOf(1);
 
-            ECCurve curve = configureCurve(new ECCurve.Fp(p, a, b, n, h));
+            return configureCurve(new ECCurve.Fp(p, a, b, n, h, true));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = Hex.decodeStrict("C49D360886E704936A6678E1139D26B7819F7E90");
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "046B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C2964FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5");
 
-            return new X9ECParameters(curve, G, n, h, S);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
@@ -418,23 +483,28 @@
      */
     static X9ECParametersHolder secp384r1 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             // p = 2^384 - 2^128 - 2^96 + 2^32 - 1
             BigInteger p = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFF");
             BigInteger a = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFC");
             BigInteger b = fromHex("B3312FA7E23EE7E4988E056BE3F82D19181D9C6EFE8141120314088F5013875AC656398D8A2ED19D2A85C8EDD3EC2AEF");
-            byte[] S = Hex.decodeStrict("A335926AA319A27A1D00896A6773A4827ACDAC73");
             BigInteger n = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC7634D81F4372DDF581A0DB248B0A77AECEC196ACCC52973");
             BigInteger h = BigInteger.valueOf(1);
 
-            ECCurve curve = configureCurve(new ECCurve.Fp(p, a, b, n, h));
+            return configureCurve(new ECCurve.Fp(p, a, b, n, h, true));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = Hex.decodeStrict("A335926AA319A27A1D00896A6773A4827ACDAC73");
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve, "04"
                 + "AA87CA22BE8B05378EB1C71EF320AD746E1D3B628BA79B9859F741E082542A385502F25DBF55296C3A545E3872760AB7"
                 + "3617DE4A96262C6F5D9E98BF9292DC29F8F41DBD289A147CE9DA3113B5F0B8C00A60B1CE1D7E819D7A431D7C90EA0E5F");
 
-            return new X9ECParameters(curve, G, n, h, S);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
@@ -443,23 +513,28 @@
      */
     static X9ECParametersHolder secp521r1 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             // p = 2^521 - 1
             BigInteger p = fromHex("01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF");
             BigInteger a = fromHex("01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC");
             BigInteger b = fromHex("0051953EB9618E1C9A1F929A21A0B68540EEA2DA725B99B315F3B8B489918EF109E156193951EC7E937B1652C0BD3BB1BF073573DF883D2C34F1EF451FD46B503F00");
-            byte[] S = Hex.decodeStrict("D09E8800291CB85396CC6717393284AAA0DA64BA");
             BigInteger n = fromHex("01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA51868783BF2F966B7FCC0148F709A5D03BB5C9B8899C47AEBB6FB71E91386409");
             BigInteger h = BigInteger.valueOf(1);
 
-            ECCurve curve = configureCurve(new ECCurve.Fp(p, a, b, n, h));
+            return configureCurve(new ECCurve.Fp(p, a, b, n, h, true));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = Hex.decodeStrict("D09E8800291CB85396CC6717393284AAA0DA64BA");
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve, "04"
                 + "00C6858E06B70404E9CD9E3ECB662395B4429C648139053FB521F828AF606B4D3DBAA14B5E77EFE75928FE1DC127A2FFA8DE3348B3C1856A429BF97E7E31C2E5BD66"
                 + "011839296A789A3BC0045C8A5FB42C7D1BD998F54449579B446817AFBD17273E662C97EE72995EF42640C550B9013FAD0761353C7086A272C24088BE94769FD16650");
 
-            return new X9ECParameters(curve, G, n, h, S);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
@@ -468,23 +543,28 @@
      */
     static X9ECParametersHolder sect113r1 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             int m = 113;
             int k = 9;
 
             BigInteger a = fromHex("003088250CA6E7C7FE649CE85820F7");
             BigInteger b = fromHex("00E8BEE4D3E2260744188BE0E9C723");
-            byte[] S = Hex.decodeStrict("10E723AB14D696E6768756151756FEBF8FCB49A9");
             BigInteger n = fromHex("0100000000000000D9CCEC8A39E56F");
             BigInteger h = BigInteger.valueOf(2);
 
-            ECCurve curve = configureCurve(new ECCurve.F2m(m, k, a, b, n, h));
+            return configureCurve(new ECCurve.F2m(m, k, a, b, n, h));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = Hex.decodeStrict("10E723AB14D696E6768756151756FEBF8FCB49A9");
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "04009D73616F35F4AB1407D73562C10F00A52830277958EE84D1315ED31886");
 
-            return new X9ECParameters(curve, G, n, h, S);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
@@ -493,23 +573,28 @@
      */
     static X9ECParametersHolder sect113r2 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             int m = 113;
             int k = 9;
 
             BigInteger a = fromHex("00689918DBEC7E5A0DD6DFC0AA55C7");
             BigInteger b = fromHex("0095E9A9EC9B297BD4BF36E059184F");
-            byte[] S = Hex.decodeStrict("10C0FB15760860DEF1EEF4D696E676875615175D");
             BigInteger n = fromHex("010000000000000108789B2496AF93");
             BigInteger h = BigInteger.valueOf(2);
 
-            ECCurve curve = configureCurve(new ECCurve.F2m(m, k, a, b, n, h));
+            return configureCurve(new ECCurve.F2m(m, k, a, b, n, h));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = Hex.decodeStrict("10C0FB15760860DEF1EEF4D696E676875615175D");
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "0401A57A6A7B26CA5EF52FCDB816479700B3ADC94ED1FE674C06E695BABA1D");
 
-            return new X9ECParameters(curve, G, n, h, S);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
@@ -518,7 +603,7 @@
      */
     static X9ECParametersHolder sect131r1 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             int m = 131;
             int k1 = 2;
@@ -527,16 +612,21 @@
 
             BigInteger a = fromHex("07A11B09A76B562144418FF3FF8C2570B8");
             BigInteger b = fromHex("0217C05610884B63B9C6C7291678F9D341");
-            byte[] S = Hex.decodeStrict("4D696E676875615175985BD3ADBADA21B43A97E2");
             BigInteger n = fromHex("0400000000000000023123953A9464B54D");
             BigInteger h = BigInteger.valueOf(2);
 
-            ECCurve curve = configureCurve(new ECCurve.F2m(m, k1, k2, k3, a, b, n, h));
+            return configureCurve(new ECCurve.F2m(m, k1, k2, k3, a, b, n, h));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = Hex.decodeStrict("4D696E676875615175985BD3ADBADA21B43A97E2");
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "040081BAF91FDF9833C40F9C181343638399078C6E7EA38C001F73C8134B1B4EF9E150");
 
-            return new X9ECParameters(curve, G, n, h, S);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
@@ -545,7 +635,7 @@
      */
     static X9ECParametersHolder sect131r2 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             int m = 131;
             int k1 = 2;
@@ -554,16 +644,21 @@
 
             BigInteger a = fromHex("03E5A88919D7CAFCBF415F07C2176573B2");
             BigInteger b = fromHex("04B8266A46C55657AC734CE38F018F2192");
-            byte[] S = Hex.decodeStrict("985BD3ADBAD4D696E676875615175A21B43A97E3");
             BigInteger n = fromHex("0400000000000000016954A233049BA98F");
             BigInteger h = BigInteger.valueOf(2);
 
-            ECCurve curve = configureCurve(new ECCurve.F2m(m, k1, k2, k3, a, b, n, h));
+            return configureCurve(new ECCurve.F2m(m, k1, k2, k3, a, b, n, h));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = Hex.decodeStrict("985BD3ADBAD4D696E676875615175A21B43A97E3");
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "040356DCD8F2F95031AD652D23951BB366A80648F06D867940A5366D9E265DE9EB240F");
 
-            return new X9ECParameters(curve, G, n, h, S);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
@@ -572,7 +667,7 @@
      */
     static X9ECParametersHolder sect163k1 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             int m = 163;
             int k1 = 3;
@@ -581,16 +676,21 @@
 
             BigInteger a = BigInteger.valueOf(1);
             BigInteger b = BigInteger.valueOf(1);
-            byte[] S = null;
             BigInteger n = fromHex("04000000000000000000020108A2E0CC0D99F8A5EF");
             BigInteger h = BigInteger.valueOf(2);
 
-            ECCurve curve = configureCurve(new ECCurve.F2m(m, k1, k2, k3, a, b, n, h));
+            return configureCurve(new ECCurve.F2m(m, k1, k2, k3, a, b, n, h));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = null;
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "0402FE13C0537BBC11ACAA07D793DE4E6D5E5C94EEE80289070FB05D38FF58321F2E800536D538CCDAA3D9");
 
-            return new X9ECParameters(curve, G, n, h, S);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
@@ -599,7 +699,7 @@
      */
     static X9ECParametersHolder sect163r1 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             int m = 163;
             int k1 = 3;
@@ -608,16 +708,21 @@
 
             BigInteger a = fromHex("07B6882CAAEFA84F9554FF8428BD88E246D2782AE2");
             BigInteger b = fromHex("0713612DCDDCB40AAB946BDA29CA91F73AF958AFD9");
-            byte[] S = Hex.decodeStrict("24B7B137C8A14D696E6768756151756FD0DA2E5C");
             BigInteger n = fromHex("03FFFFFFFFFFFFFFFFFFFF48AAB689C29CA710279B");
             BigInteger h = BigInteger.valueOf(2);
 
-            ECCurve curve = configureCurve(new ECCurve.F2m(m, k1, k2, k3, a, b, n, h));
+            return configureCurve(new ECCurve.F2m(m, k1, k2, k3, a, b, n, h));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = Hex.decodeStrict("24B7B137C8A14D696E6768756151756FD0DA2E5C");
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "040369979697AB43897789566789567F787A7876A65400435EDB42EFAFB2989D51FEFCE3C80988F41FF883");
 
-            return new X9ECParameters(curve, G, n, h, S);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
@@ -626,7 +731,7 @@
      */
     static X9ECParametersHolder sect163r2 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             int m = 163;
             int k1 = 3;
@@ -635,16 +740,21 @@
 
             BigInteger a = BigInteger.valueOf(1);
             BigInteger b = fromHex("020A601907B8C953CA1481EB10512F78744A3205FD");
-            byte[] S = Hex.decodeStrict("85E25BFE5C86226CDB12016F7553F9D0E693A268");
             BigInteger n = fromHex("040000000000000000000292FE77E70C12A4234C33");
             BigInteger h = BigInteger.valueOf(2);
 
-            ECCurve curve = configureCurve(new ECCurve.F2m(m, k1, k2, k3, a, b, n, h));
+            return configureCurve(new ECCurve.F2m(m, k1, k2, k3, a, b, n, h));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = Hex.decodeStrict("85E25BFE5C86226CDB12016F7553F9D0E693A268");
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "0403F0EBA16286A2D57EA0991168D4994637E8343E3600D51FBC6C71A0094FA2CDD545B11C5C0C797324F1");
 
-            return new X9ECParameters(curve, G, n, h, S);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
@@ -653,23 +763,28 @@
      */
     static X9ECParametersHolder sect193r1 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             int m = 193;
             int k = 15;
 
             BigInteger a = fromHex("0017858FEB7A98975169E171F77B4087DE098AC8A911DF7B01");
             BigInteger b = fromHex("00FDFB49BFE6C3A89FACADAA7A1E5BBC7CC1C2E5D831478814");
-            byte[] S = Hex.decodeStrict("103FAEC74D696E676875615175777FC5B191EF30");
             BigInteger n = fromHex("01000000000000000000000000C7F34A778F443ACC920EBA49");
             BigInteger h = BigInteger.valueOf(2);
 
-            ECCurve curve = configureCurve(new ECCurve.F2m(m, k, a, b, n, h));
+            return configureCurve(new ECCurve.F2m(m, k, a, b, n, h));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = Hex.decodeStrict("103FAEC74D696E676875615175777FC5B191EF30");
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "0401F481BC5F0FF84A74AD6CDF6FDEF4BF6179625372D8C0C5E10025E399F2903712CCF3EA9E3A1AD17FB0B3201B6AF7CE1B05");
 
-            return new X9ECParameters(curve, G, n, h, S);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
@@ -678,23 +793,28 @@
      */
     static X9ECParametersHolder sect193r2 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             int m = 193;
             int k = 15;
 
             BigInteger a = fromHex("0163F35A5137C2CE3EA6ED8667190B0BC43ECD69977702709B");
             BigInteger b = fromHex("00C9BB9E8927D4D64C377E2AB2856A5B16E3EFB7F61D4316AE");
-            byte[] S = Hex.decodeStrict("10B7B4D696E676875615175137C8A16FD0DA2211");
             BigInteger n = fromHex("010000000000000000000000015AAB561B005413CCD4EE99D5");
             BigInteger h = BigInteger.valueOf(2);
 
-            ECCurve curve = configureCurve(new ECCurve.F2m(m, k, a, b, n, h));
+            return configureCurve(new ECCurve.F2m(m, k, a, b, n, h));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = Hex.decodeStrict("10B7B4D696E676875615175137C8A16FD0DA2211");
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "0400D9B67D192E0367C803F39E1A7E82CA14A651350AAE617E8F01CE94335607C304AC29E7DEFBD9CA01F596F927224CDECF6C");
 
-            return new X9ECParameters(curve, G, n, h, S);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
@@ -703,23 +823,28 @@
      */
     static X9ECParametersHolder sect233k1 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             int m = 233;
             int k = 74;
 
             BigInteger a = ECConstants.ZERO;
             BigInteger b = BigInteger.valueOf(1);
-            byte[] S = null;
             BigInteger n = fromHex("8000000000000000000000000000069D5BB915BCD46EFB1AD5F173ABDF");
             BigInteger h = BigInteger.valueOf(4);
 
-            ECCurve curve = configureCurve(new ECCurve.F2m(m, k, a, b, n, h));
+            return configureCurve(new ECCurve.F2m(m, k, a, b, n, h));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = null;
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "04017232BA853A7E731AF129F22FF4149563A419C26BF50A4C9D6EEFAD612601DB537DECE819B7F70F555A67C427A8CD9BF18AEB9B56E0C11056FAE6A3");
 
-            return new X9ECParameters(curve, G, n, h, S);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
@@ -728,23 +853,28 @@
      */
     static X9ECParametersHolder sect233r1 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             int m = 233;
             int k = 74;
 
             BigInteger a = BigInteger.valueOf(1);
             BigInteger b = fromHex("0066647EDE6C332C7F8C0923BB58213B333B20E9CE4281FE115F7D8F90AD");
-            byte[] S = Hex.decodeStrict("74D59FF07F6B413D0EA14B344B20A2DB049B50C3");
             BigInteger n = fromHex("01000000000000000000000000000013E974E72F8A6922031D2603CFE0D7");
             BigInteger h = BigInteger.valueOf(2);
 
-            ECCurve curve = configureCurve(new ECCurve.F2m(m, k, a, b, n, h));
+            return configureCurve(new ECCurve.F2m(m, k, a, b, n, h));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = Hex.decodeStrict("74D59FF07F6B413D0EA14B344B20A2DB049B50C3");
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "0400FAC9DFCBAC8313BB2139F1BB755FEF65BC391F8B36F8F8EB7371FD558B01006A08A41903350678E58528BEBF8A0BEFF867A7CA36716F7E01F81052");
 
-            return new X9ECParameters(curve, G, n, h, S);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
@@ -753,23 +883,28 @@
      */
     static X9ECParametersHolder sect239k1 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             int m = 239;
             int k = 158;
 
             BigInteger a = ECConstants.ZERO;
             BigInteger b = BigInteger.valueOf(1);
-            byte[] S = null;
             BigInteger n = fromHex("2000000000000000000000000000005A79FEC67CB6E91F1C1DA800E478A5");
             BigInteger h = BigInteger.valueOf(4);
 
-            ECCurve curve = configureCurve(new ECCurve.F2m(m, k, a, b, n, h));
+            return configureCurve(new ECCurve.F2m(m, k, a, b, n, h));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = null;
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "0429A0B6A887A983E9730988A68727A8B2D126C44CC2CC7B2A6555193035DC76310804F12E549BDB011C103089E73510ACB275FC312A5DC6B76553F0CA");
 
-            return new X9ECParameters(curve, G, n, h, S);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
@@ -778,7 +913,7 @@
      */
     static X9ECParametersHolder sect283k1 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             int m = 283;
             int k1 = 5;
@@ -787,17 +922,22 @@
 
             BigInteger a = ECConstants.ZERO;
             BigInteger b = BigInteger.valueOf(1);
-            byte[] S = null;
             BigInteger n = fromHex("01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE9AE2ED07577265DFF7F94451E061E163C61");
             BigInteger h = BigInteger.valueOf(4);
 
-            ECCurve curve = configureCurve(new ECCurve.F2m(m, k1, k2, k3, a, b, n, h));
+            return configureCurve(new ECCurve.F2m(m, k1, k2, k3, a, b, n, h));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = null;
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve, "04"
                 + "0503213F78CA44883F1A3B8162F188E553CD265F23C1567A16876913B0C2AC2458492836"
                 + "01CCDA380F1C9E318D90F95D07E5426FE87E45C0E8184698E45962364E34116177DD2259");
 
-            return new X9ECParameters(curve, G, n, h, S);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
@@ -806,7 +946,7 @@
      */
     static X9ECParametersHolder sect283r1 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             int m = 283;
             int k1 = 5;
@@ -815,17 +955,22 @@
 
             BigInteger a = BigInteger.valueOf(1);
             BigInteger b = fromHex("027B680AC8B8596DA5A4AF8A19A0303FCA97FD7645309FA2A581485AF6263E313B79A2F5");
-            byte[] S = Hex.decodeStrict("77E2B07370EB0F832A6DD5B62DFC88CD06BB84BE");
             BigInteger n = fromHex("03FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEF90399660FC938A90165B042A7CEFADB307");
             BigInteger h = BigInteger.valueOf(2);
 
-            ECCurve curve = configureCurve(new ECCurve.F2m(m, k1, k2, k3, a, b, n, h));
+            return configureCurve(new ECCurve.F2m(m, k1, k2, k3, a, b, n, h));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = Hex.decodeStrict("77E2B07370EB0F832A6DD5B62DFC88CD06BB84BE");
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve, "04"
                 + "05F939258DB7DD90E1934F8C70B0DFEC2EED25B8557EAC9C80E2E198F8CDBECD86B12053"
                 + "03676854FE24141CB98FE6D4B20D02B4516FF702350EDDB0826779C813F0DF45BE8112F4");
 
-            return new X9ECParameters(curve, G, n, h, S);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
@@ -834,24 +979,29 @@
      */
     static X9ECParametersHolder sect409k1 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             int m = 409;
             int k = 87;
 
             BigInteger a = ECConstants.ZERO;
             BigInteger b = BigInteger.valueOf(1);
-            byte[] S = null;
             BigInteger n = fromHex("7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE5F83B2D4EA20400EC4557D5ED3E3E7CA5B4B5C83B8E01E5FCF");
             BigInteger h = BigInteger.valueOf(4);
 
-            ECCurve curve = configureCurve(new ECCurve.F2m(m, k, a, b, n, h));
+            return configureCurve(new ECCurve.F2m(m, k, a, b, n, h));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = null;
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve, "04"
                 + "0060F05F658F49C1AD3AB1890F7184210EFD0987E307C84C27ACCFB8F9F67CC2C460189EB5AAAA62EE222EB1B35540CFE9023746"
                 + "01E369050B7C4E42ACBA1DACBF04299C3460782F918EA427E6325165E9EA10E3DA5F6C42E9C55215AA9CA27A5863EC48D8E0286B");
 
-            return new X9ECParameters(curve, G, n, h, S);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
@@ -860,24 +1010,29 @@
      */
     static X9ECParametersHolder sect409r1 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             int m = 409;
             int k = 87;
 
             BigInteger a = BigInteger.valueOf(1);
             BigInteger b = fromHex("0021A5C2C8EE9FEB5C4B9A753B7B476B7FD6422EF1F3DD674761FA99D6AC27C8A9A197B272822F6CD57A55AA4F50AE317B13545F");
-            byte[] S = Hex.decodeStrict("4099B5A457F9D69F79213D094C4BCD4D4262210B");
             BigInteger n = fromHex("010000000000000000000000000000000000000000000000000001E2AAD6A612F33307BE5FA47C3C9E052F838164CD37D9A21173");
             BigInteger h = BigInteger.valueOf(2);
 
-            ECCurve curve = configureCurve(new ECCurve.F2m(m, k, a, b, n, h));
+            return configureCurve(new ECCurve.F2m(m, k, a, b, n, h));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = Hex.decodeStrict("4099B5A457F9D69F79213D094C4BCD4D4262210B");
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve, "04"
                 + "015D4860D088DDB3496B0C6064756260441CDE4AF1771D4DB01FFE5B34E59703DC255A868A1180515603AEAB60794E54BB7996A7"
                 + "0061B1CFAB6BE5F32BBFA78324ED106A7636B9C5A7BD198D0158AA4F5488D08F38514F1FDF4B4F40D2181B3681C364BA0273C706");
 
-            return new X9ECParameters(curve, G, n, h, S);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
@@ -886,7 +1041,7 @@
      */
     static X9ECParametersHolder sect571k1 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             int m = 571;
             int k1 = 2;
@@ -895,17 +1050,22 @@
 
             BigInteger a = ECConstants.ZERO;
             BigInteger b = BigInteger.valueOf(1);
-            byte[] S = null;
             BigInteger n = fromHex("020000000000000000000000000000000000000000000000000000000000000000000000131850E1F19A63E4B391A8DB917F4138B630D84BE5D639381E91DEB45CFE778F637C1001");
             BigInteger h = BigInteger.valueOf(4);
 
-            ECCurve curve = configureCurve(new ECCurve.F2m(m, k1, k2, k3, a, b, n, h));
+            return configureCurve(new ECCurve.F2m(m, k1, k2, k3, a, b, n, h));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = null;
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve, "04"
                 + "026EB7A859923FBC82189631F8103FE4AC9CA2970012D5D46024804801841CA44370958493B205E647DA304DB4CEB08CBBD1BA39494776FB988B47174DCA88C7E2945283A01C8972"
                 + "0349DC807F4FBF374F4AEADE3BCA95314DD58CEC9F307A54FFC61EFC006D8A2C9D4979C0AC44AEA74FBEBBB9F772AEDCB620B01A7BA7AF1B320430C8591984F601CD4C143EF1C7A3");
 
-            return new X9ECParameters(curve, G, n, h, S);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
@@ -914,7 +1074,7 @@
      */
     static X9ECParametersHolder sect571r1 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             int m = 571;
             int k1 = 2;
@@ -923,17 +1083,22 @@
 
             BigInteger a = BigInteger.valueOf(1);
             BigInteger b = fromHex("02F40E7E2221F295DE297117B7F3D62F5C6A97FFCB8CEFF1CD6BA8CE4A9A18AD84FFABBD8EFA59332BE7AD6756A66E294AFD185A78FF12AA520E4DE739BACA0C7FFEFF7F2955727A");
-            byte[] S = Hex.decodeStrict("2AA058F73A0E33AB486B0F610410C53A7F132310");
             BigInteger n = fromHex("03FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE661CE18FF55987308059B186823851EC7DD9CA1161DE93D5174D66E8382E9BB2FE84E47");
             BigInteger h = BigInteger.valueOf(2);
 
-            ECCurve curve = configureCurve(new ECCurve.F2m(m, k1, k2, k3, a, b, n, h));
+            return configureCurve(new ECCurve.F2m(m, k1, k2, k3, a, b, n, h));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = Hex.decodeStrict("2AA058F73A0E33AB486B0F610410C53A7F132310");
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve, "04"
                 + "0303001D34B856296C16C0D40D3CD7750A93D1D2955FA80AA5F40FC8DB7B2ABDBDE53950F4C0D293CDD711A35B67FB1499AE60038614F1394ABFA3B4C850D927E1E7769C8EEC2D19"
                 + "037BF27342DA639B6DCCFFFEB73D69D78C6C27A6009CBBCA1980F8533921E8A684423E43BAB08A576291AF8F461BB2A8B3531D2F0485C19B16E2F1516E23DD3C1A4827AF1B8AC15B");
 
-            return new X9ECParameters(curve, G, n, h, S);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
@@ -987,26 +1152,35 @@
         defineCurve("sect571r1", SECObjectIdentifiers.sect571r1, sect571r1); 
     }
 
-    public static X9ECParameters getByName(
-        String name)
+    public static X9ECParameters getByName(String name)
     {
         ASN1ObjectIdentifier oid = getOID(name);
         return oid == null ? null : getByOID(oid);
     }
 
+    public static X9ECParametersHolder getByNameLazy(String name)
+    {
+        ASN1ObjectIdentifier oid = getOID(name);
+        return oid == null ? null : getByOIDLazy(oid);
+    }
+
     /**
      * return the X9ECParameters object for the named curve represented by
      * the passed in object identifier. Null if the curve isn't present.
      *
      * @param oid an object identifier representing a named curve, if present.
      */
-    public static X9ECParameters getByOID(
-        ASN1ObjectIdentifier oid)
+    public static X9ECParameters getByOID(ASN1ObjectIdentifier oid)
     {
-        X9ECParametersHolder holder = (X9ECParametersHolder)curves.get(oid);
+        X9ECParametersHolder holder = getByOIDLazy(oid);
         return holder == null ? null : holder.getParameters();
     }
 
+    public static X9ECParametersHolder getByOIDLazy(ASN1ObjectIdentifier oid)
+    {
+        return (X9ECParametersHolder)curves.get(oid);
+    }
+
     /**
      * return the object identifier signified by the passed in name. Null
      * if there is no object identifier associated with name.
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/util/ASN1Dump.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/util/ASN1Dump.java
index 786d484..99a0d76 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/util/ASN1Dump.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/util/ASN1Dump.java
@@ -1,43 +1,42 @@
 /* GENERATED SOURCE. DO NOT MODIFY. */
 package com.android.internal.org.bouncycastle.asn1.util;
 
-import java.io.IOException;
-import java.util.Enumeration;
-
-import com.android.internal.org.bouncycastle.asn1.ASN1ApplicationSpecific;
+import com.android.internal.org.bouncycastle.asn1.ASN1BMPString;
+import com.android.internal.org.bouncycastle.asn1.ASN1BitString;
 import com.android.internal.org.bouncycastle.asn1.ASN1Boolean;
 import com.android.internal.org.bouncycastle.asn1.ASN1Encodable;
 import com.android.internal.org.bouncycastle.asn1.ASN1Enumerated;
 import com.android.internal.org.bouncycastle.asn1.ASN1External;
 import com.android.internal.org.bouncycastle.asn1.ASN1GeneralizedTime;
+import com.android.internal.org.bouncycastle.asn1.ASN1GraphicString;
+import com.android.internal.org.bouncycastle.asn1.ASN1IA5String;
 import com.android.internal.org.bouncycastle.asn1.ASN1Integer;
+import com.android.internal.org.bouncycastle.asn1.ASN1Null;
+import com.android.internal.org.bouncycastle.asn1.ASN1NumericString;
+import com.android.internal.org.bouncycastle.asn1.ASN1ObjectDescriptor;
 import com.android.internal.org.bouncycastle.asn1.ASN1ObjectIdentifier;
 import com.android.internal.org.bouncycastle.asn1.ASN1OctetString;
 import com.android.internal.org.bouncycastle.asn1.ASN1Primitive;
+import com.android.internal.org.bouncycastle.asn1.ASN1PrintableString;
+import com.android.internal.org.bouncycastle.asn1.ASN1RelativeOID;
 import com.android.internal.org.bouncycastle.asn1.ASN1Sequence;
 import com.android.internal.org.bouncycastle.asn1.ASN1Set;
+import com.android.internal.org.bouncycastle.asn1.ASN1T61String;
 import com.android.internal.org.bouncycastle.asn1.ASN1TaggedObject;
 import com.android.internal.org.bouncycastle.asn1.ASN1UTCTime;
-import com.android.internal.org.bouncycastle.asn1.BERApplicationSpecific;
+import com.android.internal.org.bouncycastle.asn1.ASN1UTF8String;
+import com.android.internal.org.bouncycastle.asn1.ASN1Util;
+import com.android.internal.org.bouncycastle.asn1.ASN1VideotexString;
+import com.android.internal.org.bouncycastle.asn1.ASN1VisibleString;
 import com.android.internal.org.bouncycastle.asn1.BEROctetString;
 import com.android.internal.org.bouncycastle.asn1.BERSequence;
 import com.android.internal.org.bouncycastle.asn1.BERSet;
 import com.android.internal.org.bouncycastle.asn1.BERTaggedObject;
-import com.android.internal.org.bouncycastle.asn1.BERTags;
-import com.android.internal.org.bouncycastle.asn1.DERApplicationSpecific;
-import com.android.internal.org.bouncycastle.asn1.DERBMPString;
 import com.android.internal.org.bouncycastle.asn1.DERBitString;
-import com.android.internal.org.bouncycastle.asn1.DERGraphicString;
-import com.android.internal.org.bouncycastle.asn1.DERIA5String;
-import com.android.internal.org.bouncycastle.asn1.DERNull;
-import com.android.internal.org.bouncycastle.asn1.DERPrintableString;
 import com.android.internal.org.bouncycastle.asn1.DERSequence;
 import com.android.internal.org.bouncycastle.asn1.DERSet;
-import com.android.internal.org.bouncycastle.asn1.DERT61String;
-import com.android.internal.org.bouncycastle.asn1.DERUTF8String;
-import com.android.internal.org.bouncycastle.asn1.DERVideotexString;
-import com.android.internal.org.bouncycastle.asn1.DERVisibleString;
-import com.android.internal.org.bouncycastle.asn1.DLApplicationSpecific;
+import com.android.internal.org.bouncycastle.asn1.DERTaggedObject;
+import com.android.internal.org.bouncycastle.asn1.DLBitString;
 import com.android.internal.org.bouncycastle.util.Strings;
 import com.android.internal.org.bouncycastle.util.encoders.Hex;
 
@@ -62,11 +61,14 @@
         StringBuffer    buf)
     {
         String nl = Strings.lineSeparator();
-        if (obj instanceof ASN1Sequence)
+        if (obj instanceof ASN1Null)
         {
-            Enumeration     e = ((ASN1Sequence)obj).getObjects();
-            String          tab = indent + TAB;
-
+            buf.append(indent);
+            buf.append("NULL");
+            buf.append(nl);
+        }
+        else if (obj instanceof ASN1Sequence)
+        {
             buf.append(indent);
             if (obj instanceof BERSequence)
             {
@@ -80,64 +82,19 @@
             {
                 buf.append("Sequence");
             }
-
             buf.append(nl);
 
-            while (e.hasMoreElements())
+            ASN1Sequence sequence = (ASN1Sequence)obj;
+            String elementsIndent = indent + TAB;
+
+            for (int i = 0, count = sequence.size(); i < count; ++i)
             {
-                Object  o = e.nextElement();
-
-                if (o == null || o.equals(DERNull.INSTANCE))
-                {
-                    buf.append(tab);
-                    buf.append("NULL");
-                    buf.append(nl);
-                }
-                else if (o instanceof ASN1Primitive)
-                {
-                    _dumpAsString(tab, verbose, (ASN1Primitive)o, buf);
-                }
-                else
-                {
-                    _dumpAsString(tab, verbose, ((ASN1Encodable)o).toASN1Primitive(), buf);
-                }
+                _dumpAsString(elementsIndent, verbose, sequence.getObjectAt(i).toASN1Primitive(), buf);
             }
         }
-        else if (obj instanceof ASN1TaggedObject)
-        {
-            String          tab = indent + TAB;
-
-            buf.append(indent);
-            if (obj instanceof BERTaggedObject)
-            {
-                buf.append("BER Tagged [");
-            }
-            else
-            {
-                buf.append("Tagged [");
-            }
-
-            ASN1TaggedObject o = (ASN1TaggedObject)obj;
-
-            buf.append(Integer.toString(o.getTagNo()));
-            buf.append(']');
-
-            if (!o.isExplicit())
-            {
-                buf.append(" IMPLICIT ");
-            }
-
-            buf.append(nl);
-
-            _dumpAsString(tab, verbose, o.getObject(), buf);
-        }
         else if (obj instanceof ASN1Set)
         {
-            Enumeration     e = ((ASN1Set)obj).getObjects();
-            String          tab = indent + TAB;
-
             buf.append(indent);
-
             if (obj instanceof BERSet)
             {
                 buf.append("BER Set");
@@ -152,26 +109,45 @@
             }
             buf.append(nl);
 
-            while (e.hasMoreElements())
-            {
-                Object  o = e.nextElement();
+            ASN1Set set = (ASN1Set)obj;
+            String elementsIndent = indent + TAB;
 
-                if (o == null)
-                {
-                    buf.append(tab);
-                    buf.append("NULL");
-                    buf.append(nl);
-                }
-                else if (o instanceof ASN1Primitive)
-                {
-                    _dumpAsString(tab, verbose, (ASN1Primitive)o, buf);
-                }
-                else
-                {
-                    _dumpAsString(tab, verbose, ((ASN1Encodable)o).toASN1Primitive(), buf);
-                }
+            for (int i = 0, count = set.size(); i < count; ++i)
+            {
+                _dumpAsString(elementsIndent, verbose, set.getObjectAt(i).toASN1Primitive(), buf);
             }
         }
+        else if (obj instanceof ASN1TaggedObject)
+        {
+            buf.append(indent);
+            if (obj instanceof BERTaggedObject)
+            {
+                buf.append("BER Tagged ");
+            }
+            else if (obj instanceof DERTaggedObject)
+            {
+                buf.append("DER Tagged ");
+            }
+            else
+            {
+                buf.append("Tagged ");
+            }
+
+            ASN1TaggedObject o = (ASN1TaggedObject)obj;
+
+            buf.append(ASN1Util.getTagText(o));
+
+            if (!o.isExplicit())
+            {
+                buf.append(" IMPLICIT ");
+            }
+
+            buf.append(nl);
+
+            String baseIndent = indent + TAB;
+
+            _dumpAsString(baseIndent, verbose, o.getBaseObject().toASN1Primitive(), buf);
+        }
         else if (obj instanceof ASN1OctetString)
         {
             ASN1OctetString oct = (ASN1OctetString)obj;
@@ -197,6 +173,10 @@
         {
             buf.append(indent + "ObjectIdentifier(" + ((ASN1ObjectIdentifier)obj).getId() + ")" + nl);
         }
+        else if (obj instanceof ASN1RelativeOID)
+        {
+            buf.append(indent + "RelativeOID(" + ((ASN1RelativeOID)obj).getId() + ")" + nl);
+        }
         else if (obj instanceof ASN1Boolean)
         {
             buf.append(indent + "Boolean(" + ((ASN1Boolean)obj).isTrue() + ")" + nl);
@@ -205,50 +185,70 @@
         {
             buf.append(indent + "Integer(" + ((ASN1Integer)obj).getValue() + ")" + nl);
         }
-        else if (obj instanceof DERBitString)
+        else if (obj instanceof ASN1BitString)
         {
-            DERBitString bt = (DERBitString)obj;
-            buf.append(indent + "DER Bit String" + "[" + bt.getBytes().length + ", " + bt.getPadBits() + "] ");
+            ASN1BitString bitString = (ASN1BitString)obj;
+
+            byte[] bytes = bitString.getBytes();
+            int padBits = bitString.getPadBits();
+
+            if (bitString instanceof DERBitString)
+            {
+                buf.append(indent + "DER Bit String" + "[" + bytes.length + ", " + padBits + "] ");
+            }
+            else if (bitString instanceof DLBitString)
+            {
+                buf.append(indent + "DL Bit String" + "[" + bytes.length + ", " + padBits + "] ");
+            }
+            else
+            {
+                buf.append(indent + "BER Bit String" + "[" + bytes.length + ", " + padBits + "] ");
+            }
+
             if (verbose)
             {
-                buf.append(dumpBinaryDataAsString(indent, bt.getBytes()));
+                buf.append(dumpBinaryDataAsString(indent, bytes));
             }
             else
             {
                 buf.append(nl);
             }
         }
-        else if (obj instanceof DERIA5String)
+        else if (obj instanceof ASN1IA5String)
         {
-            buf.append(indent + "IA5String(" + ((DERIA5String)obj).getString() + ") " + nl);
+            buf.append(indent + "IA5String(" + ((ASN1IA5String)obj).getString() + ") " + nl);
         }
-        else if (obj instanceof DERUTF8String)
+        else if (obj instanceof ASN1UTF8String)
         {
-            buf.append(indent + "UTF8String(" + ((DERUTF8String)obj).getString() + ") " + nl);
+            buf.append(indent + "UTF8String(" + ((ASN1UTF8String)obj).getString() + ") " + nl);
         }
-        else if (obj instanceof DERPrintableString)
+        else if (obj instanceof ASN1NumericString)
         {
-            buf.append(indent + "PrintableString(" + ((DERPrintableString)obj).getString() + ") " + nl);
+            buf.append(indent + "NumericString(" + ((ASN1NumericString)obj).getString() + ") " + nl);
         }
-        else if (obj instanceof DERVisibleString)
+        else if (obj instanceof ASN1PrintableString)
         {
-            buf.append(indent + "VisibleString(" + ((DERVisibleString)obj).getString() + ") " + nl);
+            buf.append(indent + "PrintableString(" + ((ASN1PrintableString)obj).getString() + ") " + nl);
         }
-        else if (obj instanceof DERBMPString)
+        else if (obj instanceof ASN1VisibleString)
         {
-            buf.append(indent + "BMPString(" + ((DERBMPString)obj).getString() + ") " + nl);
+            buf.append(indent + "VisibleString(" + ((ASN1VisibleString)obj).getString() + ") " + nl);
         }
-        else if (obj instanceof DERT61String)
+        else if (obj instanceof ASN1BMPString)
         {
-            buf.append(indent + "T61String(" + ((DERT61String)obj).getString() + ") " + nl);
+            buf.append(indent + "BMPString(" + ((ASN1BMPString)obj).getString() + ") " + nl);
         }
-        else if (obj instanceof DERGraphicString)
+        else if (obj instanceof ASN1T61String)
         {
-            buf.append(indent + "GraphicString(" + ((DERGraphicString)obj).getString() + ") " + nl);
+            buf.append(indent + "T61String(" + ((ASN1T61String)obj).getString() + ") " + nl);
         }
-        else if (obj instanceof DERVideotexString)
+        else if (obj instanceof ASN1GraphicString)
         {
-            buf.append(indent + "VideotexString(" + ((DERVideotexString)obj).getString() + ") " + nl);
+            buf.append(indent + "GraphicString(" + ((ASN1GraphicString)obj).getString() + ") " + nl);
+        }
+        else if (obj instanceof ASN1VideotexString)
+        {
+            buf.append(indent + "VideotexString(" + ((ASN1VideotexString)obj).getString() + ") " + nl);
         }
         else if (obj instanceof ASN1UTCTime)
         {
@@ -258,23 +258,16 @@
         {
             buf.append(indent + "GeneralizedTime(" + ((ASN1GeneralizedTime)obj).getTime() + ") " + nl);
         }
-        else if (obj instanceof BERApplicationSpecific)
-        {
-            buf.append(outputApplicationSpecific("BER", indent, verbose, obj, nl));
-        }
-        else if (obj instanceof DERApplicationSpecific)
-        {
-            buf.append(outputApplicationSpecific("DER", indent, verbose, obj, nl));
-        }
-        else if (obj instanceof DLApplicationSpecific)
-        {
-            buf.append(outputApplicationSpecific("", indent, verbose, obj, nl));
-        }
         else if (obj instanceof ASN1Enumerated)
         {
             ASN1Enumerated en = (ASN1Enumerated) obj;
             buf.append(indent + "DER Enumerated(" + en.getValue() + ")" + nl);
         }
+        else if (obj instanceof ASN1ObjectDescriptor)
+        {
+            ASN1ObjectDescriptor od = (ASN1ObjectDescriptor)obj;
+            buf.append(indent + "ObjectDescriptor(" + od.getBaseGraphicString().getString() + ") " + nl);
+        }
         else if (obj instanceof ASN1External)
         {
             ASN1External ext = (ASN1External) obj;
@@ -300,32 +293,6 @@
             buf.append(indent + obj.toString() + nl);
         }
     }
-    
-    private static String outputApplicationSpecific(String type, String indent, boolean verbose, ASN1Primitive obj, String nl)
-    {
-        ASN1ApplicationSpecific app = ASN1ApplicationSpecific.getInstance(obj);
-        StringBuffer buf = new StringBuffer();
-
-        if (app.isConstructed())
-        {
-            try
-            {
-                ASN1Sequence s = ASN1Sequence.getInstance(app.getObject(BERTags.SEQUENCE));
-                buf.append(indent + type + " ApplicationSpecific[" + app.getApplicationTag() + "]" + nl);
-                for (Enumeration e = s.getObjects(); e.hasMoreElements();)
-                {
-                    _dumpAsString(indent + TAB, verbose, (ASN1Primitive)e.nextElement(), buf);
-                }
-            }
-            catch (IOException e)
-            {
-                buf.append(e);
-            }
-            return buf.toString();
-        }
-
-        return indent + type + " ApplicationSpecific[" + app.getApplicationTag() + "] (" + Strings.fromByteArray(Hex.encode(app.getContents())) + ")" + nl;
-    }
 
     /**
      * dump out a DER object as a formatted string, in non-verbose mode.
@@ -350,21 +317,22 @@
         Object   obj,
         boolean  verbose)
     {
-        StringBuffer buf = new StringBuffer();
-
+        ASN1Primitive primitive;
         if (obj instanceof ASN1Primitive)
         {
-            _dumpAsString("", verbose, (ASN1Primitive)obj, buf);
+            primitive = (ASN1Primitive)obj;
         }
         else if (obj instanceof ASN1Encodable)
         {
-            _dumpAsString("", verbose, ((ASN1Encodable)obj).toASN1Primitive(), buf);
+            primitive = ((ASN1Encodable)obj).toASN1Primitive();
         }
         else
         {
             return "unknown object type " + obj.toString();
         }
 
+        StringBuffer buf = new StringBuffer();
+        _dumpAsString("", verbose, primitive, buf);
         return buf.toString();
     }
 
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x500/DirectoryString.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x500/DirectoryString.java
index d13ae8f..48e814d 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x500/DirectoryString.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x500/DirectoryString.java
@@ -1,17 +1,18 @@
 /* GENERATED SOURCE. DO NOT MODIFY. */
 package com.android.internal.org.bouncycastle.asn1.x500;
 
+import com.android.internal.org.bouncycastle.asn1.ASN1BMPString;
 import com.android.internal.org.bouncycastle.asn1.ASN1Choice;
 import com.android.internal.org.bouncycastle.asn1.ASN1Encodable;
 import com.android.internal.org.bouncycastle.asn1.ASN1Object;
 import com.android.internal.org.bouncycastle.asn1.ASN1Primitive;
+import com.android.internal.org.bouncycastle.asn1.ASN1PrintableString;
 import com.android.internal.org.bouncycastle.asn1.ASN1String;
+import com.android.internal.org.bouncycastle.asn1.ASN1T61String;
 import com.android.internal.org.bouncycastle.asn1.ASN1TaggedObject;
-import com.android.internal.org.bouncycastle.asn1.DERBMPString;
-import com.android.internal.org.bouncycastle.asn1.DERPrintableString;
-import com.android.internal.org.bouncycastle.asn1.DERT61String;
+import com.android.internal.org.bouncycastle.asn1.ASN1UTF8String;
+import com.android.internal.org.bouncycastle.asn1.ASN1UniversalString;
 import com.android.internal.org.bouncycastle.asn1.DERUTF8String;
-import com.android.internal.org.bouncycastle.asn1.DERUniversalString;
 
 /**
  * The DirectoryString CHOICE object.
@@ -30,29 +31,29 @@
             return (DirectoryString)o;
         }
 
-        if (o instanceof DERT61String)
+        if (o instanceof ASN1T61String)
         {
-            return new DirectoryString((DERT61String)o);
+            return new DirectoryString((ASN1T61String)o);
         }
 
-        if (o instanceof DERPrintableString)
+        if (o instanceof ASN1PrintableString)
         {
-            return new DirectoryString((DERPrintableString)o);
+            return new DirectoryString((ASN1PrintableString)o);
         }
 
-        if (o instanceof DERUniversalString)
+        if (o instanceof ASN1UniversalString)
         {
-            return new DirectoryString((DERUniversalString)o);
+            return new DirectoryString((ASN1UniversalString)o);
         }
 
-        if (o instanceof DERUTF8String)
+        if (o instanceof ASN1UTF8String)
         {
-            return new DirectoryString((DERUTF8String)o);
+            return new DirectoryString((ASN1UTF8String)o);
         }
 
-        if (o instanceof DERBMPString)
+        if (o instanceof ASN1BMPString)
         {
-            return new DirectoryString((DERBMPString)o);
+            return new DirectoryString((ASN1BMPString)o);
         }
 
         throw new IllegalArgumentException("illegal object in getInstance: " + o.getClass().getName());
@@ -65,35 +66,35 @@
             throw new IllegalArgumentException("choice item must be explicitly tagged");
         }
 
-        return getInstance(o.getObject());
+        return getInstance(o.getExplicitBaseObject());
     }
 
     private DirectoryString(
-        DERT61String string)
+        ASN1T61String string)
     {
         this.string = string;
     }
 
     private DirectoryString(
-        DERPrintableString string)
+        ASN1PrintableString string)
     {
         this.string = string;
     }
 
     private DirectoryString(
-        DERUniversalString string)
+        ASN1UniversalString string)
     {
         this.string = string;
     }
 
     private DirectoryString(
-        DERUTF8String string)
+        ASN1UTF8String string)
     {
         this.string = string;
     }
 
     private DirectoryString(
-        DERBMPString string)
+        ASN1BMPString string)
     {
         this.string = string;
     }
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x500/RDN.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x500/RDN.java
index fc07882..3cb1e00 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x500/RDN.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x500/RDN.java
@@ -2,12 +2,11 @@
 package com.android.internal.org.bouncycastle.asn1.x500;
 
 import com.android.internal.org.bouncycastle.asn1.ASN1Encodable;
-import com.android.internal.org.bouncycastle.asn1.ASN1EncodableVector;
 import com.android.internal.org.bouncycastle.asn1.ASN1Object;
 import com.android.internal.org.bouncycastle.asn1.ASN1ObjectIdentifier;
 import com.android.internal.org.bouncycastle.asn1.ASN1Primitive;
 import com.android.internal.org.bouncycastle.asn1.ASN1Set;
-import com.android.internal.org.bouncycastle.asn1.DERSequence;
+import com.android.internal.org.bouncycastle.asn1.ASN1TaggedObject;
 import com.android.internal.org.bouncycastle.asn1.DERSet;
 
 /**
@@ -21,6 +20,7 @@
 
     private RDN(ASN1Set values)
     {
+        // TODO Require minimum size of 1?
         this.values = values;
     }
 
@@ -38,6 +38,11 @@
         return null;
     }
 
+    public static RDN getInstance(ASN1TaggedObject taggedObject, boolean declaredExplicit)
+    {
+        return new RDN(ASN1Set.getInstance(taggedObject, declaredExplicit));
+    }
+
     /**
      * Create a single valued RDN.
      *
@@ -46,12 +51,7 @@
      */
     public RDN(ASN1ObjectIdentifier oid, ASN1Encodable value)
     {
-        ASN1EncodableVector v = new ASN1EncodableVector(2);
-
-        v.add(oid);
-        v.add(value);
-
-        this.values = new DERSet(new DERSequence(v));
+        this(new AttributeTypeAndValue(oid, value));
     }
 
     public RDN(AttributeTypeAndValue attrTAndV)
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x500/X500Name.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x500/X500Name.java
index ed3f378..d29fa9f 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x500/X500Name.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x500/X500Name.java
@@ -1,8 +1,6 @@
 /* GENERATED SOURCE. DO NOT MODIFY. */
 package com.android.internal.org.bouncycastle.asn1.x500;
 
-import java.util.Enumeration;
-
 import com.android.internal.org.bouncycastle.asn1.ASN1Choice;
 import com.android.internal.org.bouncycastle.asn1.ASN1Encodable;
 import com.android.internal.org.bouncycastle.asn1.ASN1Object;
@@ -113,18 +111,18 @@
         X500NameStyle style,
         ASN1Sequence  seq)
     {
+        int count = seq.size();
+
         this.style = style;
-        this.rdns = new RDN[seq.size()];
+        this.rdns = new RDN[count];
 
         boolean inPlace = true;
-
-        int index = 0;
-        for (Enumeration e = seq.getObjects(); e.hasMoreElements();)
+        for (int index = 0; index < count; ++index)
         {
-            Object element = e.nextElement();
+            ASN1Encodable element = seq.getObjectAt(index);
             RDN rdn = RDN.getInstance(element);
             inPlace &= (rdn == element);
-            rdns[index++] = rdn;
+            rdns[index] = rdn;
         }
 
         if (inPlace)
@@ -231,6 +229,11 @@
         return res;
     }
 
+    public int size()
+    {
+        return rdns.length;
+    }
+
     public ASN1Primitive toASN1Primitive()
     {
         return rdnSeq;
@@ -267,14 +270,14 @@
         
         ASN1Primitive derO = ((ASN1Encodable)obj).toASN1Primitive();
 
-        if (this.toASN1Primitive().equals(derO))
+        if (toASN1Primitive().equals(derO))
         {
             return true;
         }
 
         try
         {
-            return style.areEqual(this, new X500Name(ASN1Sequence.getInstance(((ASN1Encodable)obj).toASN1Primitive())));
+            return style.areEqual(this, getInstance(obj));
         }
         catch (Exception e)
         {
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x500/style/AbstractX500NameStyle.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x500/style/AbstractX500NameStyle.java
index 3580944..ae466ed 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x500/style/AbstractX500NameStyle.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x500/style/AbstractX500NameStyle.java
@@ -133,19 +133,22 @@
 
     public boolean areEqual(X500Name name1, X500Name name2)
     {
-        RDN[] rdns1 = name1.getRDNs();
-        RDN[] rdns2 = name2.getRDNs();
-
-        if (rdns1.length != rdns2.length)
+        if (name1.size() != name2.size())
         {
             return false;
         }
 
+        RDN[] rdns1 = name1.getRDNs();
+        RDN[] rdns2 = name2.getRDNs();
+
         boolean reverse = false;
 
-        if (rdns1[0].getFirst() != null && rdns2[0].getFirst() != null)
+        AttributeTypeAndValue first1 = rdns1[0].getFirst();
+        AttributeTypeAndValue first2 = rdns2[0].getFirst();
+
+        if (first1 != null && first2 != null)
         {
-            reverse = !rdns1[0].getFirst().getType().equals(rdns2[0].getFirst().getType());  // guess forward
+            reverse = !first1.getType().equals(first2.getType());  // guess forward
         }
 
         for (int i = 0; i != rdns1.length; i++)
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x500/style/BCStrictStyle.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x500/style/BCStrictStyle.java
index 86f124f..030d643 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x500/style/BCStrictStyle.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x500/style/BCStrictStyle.java
@@ -17,14 +17,14 @@
 
     public boolean areEqual(X500Name name1, X500Name name2)
     {
-        RDN[] rdns1 = name1.getRDNs();
-        RDN[] rdns2 = name2.getRDNs();
-
-        if (rdns1.length != rdns2.length)
+        if (name1.size() != name2.size())
         {
             return false;
         }
 
+        RDN[] rdns1 = name1.getRDNs();
+        RDN[] rdns2 = name2.getRDNs();
+
         for (int i = 0; i != rdns1.length; i++)
         {
             if (!rdnAreEqual(rdns1[i], rdns2[i]))
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x500/style/BCStyle.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x500/style/BCStyle.java
index e320f82..81f5ea0 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x500/style/BCStyle.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x500/style/BCStyle.java
@@ -293,9 +293,9 @@
         defaultLookUp = copyHashTable(DefaultLookUp);
     }
 
-    protected ASN1Encodable encodeStringValue(ASN1ObjectIdentifier oid,
-    		String value) {
-    	if (oid.equals(EmailAddress) || oid.equals(DC))
+    protected ASN1Encodable encodeStringValue(ASN1ObjectIdentifier oid, String value)
+    {
+        if (oid.equals(EmailAddress) || oid.equals(DC))
         {
             return new DERIA5String(value);
         }
@@ -303,18 +303,18 @@
         {
             return new ASN1GeneralizedTime(value);
         }
-        else if (oid.equals(C) || oid.equals(SN) || oid.equals(DN_QUALIFIER)
+        else if (oid.equals(C) || oid.equals(SERIALNUMBER) || oid.equals(DN_QUALIFIER)
             || oid.equals(TELEPHONE_NUMBER))
         {
             return new DERPrintableString(value);
         }
-    	
-    	return super.encodeStringValue(oid, value);
+        
+        return super.encodeStringValue(oid, value);
     }
 
     public String oidToDisplayName(ASN1ObjectIdentifier oid)
     {
-        return (String)DefaultSymbols.get(oid);
+        return (String)defaultSymbols.get(oid);
     }
 
     public String[] oidToAttrNames(ASN1ObjectIdentifier oid)
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x500/style/IETFUtils.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x500/style/IETFUtils.java
index 130a589..da849eb 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x500/style/IETFUtils.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x500/style/IETFUtils.java
@@ -11,7 +11,7 @@
 import com.android.internal.org.bouncycastle.asn1.ASN1ObjectIdentifier;
 import com.android.internal.org.bouncycastle.asn1.ASN1Primitive;
 import com.android.internal.org.bouncycastle.asn1.ASN1String;
-import com.android.internal.org.bouncycastle.asn1.DERUniversalString;
+import com.android.internal.org.bouncycastle.asn1.ASN1UniversalString;
 import com.android.internal.org.bouncycastle.asn1.x500.AttributeTypeAndValue;
 import com.android.internal.org.bouncycastle.asn1.x500.RDN;
 import com.android.internal.org.bouncycastle.asn1.x500.X500NameBuilder;
@@ -26,12 +26,15 @@
 {
     private static String unescape(String elt)
     {
-        if (elt.length() == 0 || (elt.indexOf('\\') < 0 && elt.indexOf('"') < 0))
+        if (elt.length() == 0)
+        {
+            return elt;
+        }
+        if (elt.indexOf('\\') < 0 && elt.indexOf('"') < 0)
         {
             return elt.trim();
         }
 
-        char[] elts = elt.toCharArray();
         boolean escaped = false;
         boolean quoted = false;
         StringBuffer buf = new StringBuffer(elt.length());
@@ -39,9 +42,9 @@
 
         // if it's an escaped hash string and not an actual encoding in string form
         // we need to leave it escaped.
-        if (elts[0] == '\\')
+        if (elt.charAt(0) == '\\')
         {
-            if (elts[1] == '#')
+            if (elt.charAt(1) == '#')
             {
                 start = 2;
                 buf.append("\\#");
@@ -52,9 +55,9 @@
         int     lastEscaped = 0;
         char    hex1 = 0;
 
-        for (int i = start; i != elts.length; i++)
+        for (int i = start; i != elt.length(); i++)
         {
-            char c = elts[i];
+            char c = elt.charAt(i);
 
             if (c != ' ')
             {
@@ -70,8 +73,8 @@
                 else
                 {
                     buf.append(c);
+                    escaped = false;
                 }
-                escaped = false;
             }
             else if (c == '\\' && !(escaped || quoted))
             {
@@ -132,81 +135,93 @@
 
     public static RDN[] rDNsFromString(String name, X500NameStyle x500Style)
     {
-        X500NameTokenizer nTok = new X500NameTokenizer(name);
+        X500NameTokenizer tokenizer = new X500NameTokenizer(name);
         X500NameBuilder builder = new X500NameBuilder(x500Style);
 
-        while (nTok.hasMoreTokens())
+        addRDNs(x500Style, builder, tokenizer);
+
+        // TODO There's an unnecessary clone of the RDNs array happening here
+        return builder.build().getRDNs();
+    }
+
+    private static void addRDNs(X500NameStyle style, X500NameBuilder builder, X500NameTokenizer tokenizer)
+    {
+        String token;
+        while ((token = tokenizer.nextToken()) != null)
         {
-            String  token = nTok.nextToken();
-
-            if (token.indexOf('+') > 0)
+            if (token.indexOf('+') >= 0)
             {
-                X500NameTokenizer   pTok = new X500NameTokenizer(token, '+');
-                X500NameTokenizer   vTok = new X500NameTokenizer(pTok.nextToken(), '=');
-
-                String              attr = vTok.nextToken();
-
-                if (!vTok.hasMoreTokens())
-                {
-                    throw new IllegalArgumentException("badly formatted directory string");
-                }
-
-                String               value = vTok.nextToken();
-                ASN1ObjectIdentifier oid = x500Style.attrNameToOID(attr.trim());
-
-                if (pTok.hasMoreTokens())
-                {
-                    Vector oids = new Vector();
-                    Vector values = new Vector();
-
-                    oids.addElement(oid);
-                    values.addElement(unescape(value));
-
-                    while (pTok.hasMoreTokens())
-                    {
-                        vTok = new X500NameTokenizer(pTok.nextToken(), '=');
-
-                        attr = vTok.nextToken();
-
-                        if (!vTok.hasMoreTokens())
-                        {
-                            throw new IllegalArgumentException("badly formatted directory string");
-                        }
-
-                        value = vTok.nextToken();
-                        oid = x500Style.attrNameToOID(attr.trim());
-
-
-                        oids.addElement(oid);
-                        values.addElement(unescape(value));
-                    }
-
-                    builder.addMultiValuedRDN(toOIDArray(oids), toValueArray(values));
-                }
-                else
-                {
-                    builder.addRDN(oid, unescape(value));
-                }
+                addMultiValuedRDN(style, builder, new X500NameTokenizer(token, '+'));
             }
             else
             {
-                X500NameTokenizer   vTok = new X500NameTokenizer(token, '=');
-
-                String              attr = vTok.nextToken();
-
-                if (!vTok.hasMoreTokens())
-                {
-                    throw new IllegalArgumentException("badly formatted directory string");
-                }
-
-                String               value = vTok.nextToken();
-                ASN1ObjectIdentifier oid = x500Style.attrNameToOID(attr.trim());
-
-                builder.addRDN(oid, unescape(value));
+                addRDN(style, builder, token);
             }
         }
+    }
 
-        return builder.build().getRDNs();
+    private static void addMultiValuedRDN(X500NameStyle style, X500NameBuilder builder, X500NameTokenizer tokenizer)
+    {
+        String token = tokenizer.nextToken();
+        if (token == null)
+        {
+            throw new IllegalArgumentException("badly formatted directory string");
+        }
+
+        if (!tokenizer.hasMoreTokens())
+        {
+            addRDN(style, builder, token);
+            return;
+        }
+
+        Vector oids = new Vector();
+        Vector values = new Vector();
+
+        do
+        {
+            collectAttributeTypeAndValue(style, oids, values, token);
+            token = tokenizer.nextToken();
+        }
+        while (token != null);
+
+        builder.addMultiValuedRDN(toOIDArray(oids), toValueArray(values));
+    }
+
+    private static void addRDN(X500NameStyle style, X500NameBuilder builder, String token)
+    {
+        X500NameTokenizer tokenizer = new X500NameTokenizer(token, '=');
+
+        String typeToken = nextToken(tokenizer, true);
+        String valueToken = nextToken(tokenizer, false);
+
+        ASN1ObjectIdentifier oid = style.attrNameToOID(typeToken.trim());
+        String value = unescape(valueToken);
+
+        builder.addRDN(oid, value);
+    }
+
+    private static void collectAttributeTypeAndValue(X500NameStyle style, Vector oids, Vector values, String token)
+    {
+        X500NameTokenizer tokenizer = new X500NameTokenizer(token, '=');
+
+        String typeToken = nextToken(tokenizer, true);
+        String valueToken = nextToken(tokenizer, false);
+
+        ASN1ObjectIdentifier oid = style.attrNameToOID(typeToken.trim());
+        String value = unescape(valueToken);
+
+        oids.addElement(oid);
+        values.addElement(value);
+    }
+
+    private static String nextToken(X500NameTokenizer tokenizer, boolean expectMoreTokens)
+    {
+        String token = tokenizer.nextToken();
+        if (token == null || tokenizer.hasMoreTokens() != expectMoreTokens)
+        {
+            throw new IllegalArgumentException("badly formatted directory string");
+        }
+        return token;
     }
 
     private static String[] toValueArray(Vector values)
@@ -358,7 +373,7 @@
     {
         StringBuffer vBuf = new StringBuffer();
 
-        if (value instanceof ASN1String && !(value instanceof DERUniversalString))
+        if (value instanceof ASN1String && !(value instanceof ASN1UniversalString))
         {
             String v = ((ASN1String)value).getString();
             if (v.length() > 0 && v.charAt(0) == '#')
@@ -373,6 +388,7 @@
             try
             {
                 vBuf.append('#');
+                // -DM Hex.toHexString
                 vBuf.append(Hex.toHexString(value.toASN1Primitive().getEncoded(ASN1Encoding.DER)));
             }
             catch (IOException e)
@@ -427,7 +443,7 @@
 
         int endBuf = vBuf.length() - 1;
 
-        while (endBuf >= 0 && vBuf.charAt(endBuf) == ' ')
+        while (endBuf >= start && vBuf.charAt(endBuf) == ' ')
         {
             vBuf.insert(endBuf, '\\');
             endBuf--;
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x500/style/RFC4519Style.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x500/style/RFC4519Style.java
index 7d8f676..5679f9d 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x500/style/RFC4519Style.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x500/style/RFC4519Style.java
@@ -15,7 +15,7 @@
  * @hide This class is not part of the Android public SDK API
  */
 public class RFC4519Style
-	extends AbstractX500NameStyle
+    extends AbstractX500NameStyle
 {
     public static final ASN1ObjectIdentifier businessCategory = new ASN1ObjectIdentifier("2.5.4.15").intern();
     public static final ASN1ObjectIdentifier c = new ASN1ObjectIdentifier("2.5.4.6").intern();
@@ -179,9 +179,9 @@
         defaultLookUp = copyHashTable(DefaultLookUp);
     }
 
-    protected ASN1Encodable encodeStringValue(ASN1ObjectIdentifier oid,
-    		String value) {
-    	if (oid.equals(dc))
+    protected ASN1Encodable encodeStringValue(ASN1ObjectIdentifier oid, String value)
+    {
+        if (oid.equals(dc))
         {
             return new DERIA5String(value);
         }
@@ -191,12 +191,12 @@
             return new DERPrintableString(value);
         }
 
-    	return super.encodeStringValue(oid, value);
+        return super.encodeStringValue(oid, value);
     }
 
     public String oidToDisplayName(ASN1ObjectIdentifier oid)
     {
-        return (String)DefaultSymbols.get(oid);
+        return (String)defaultSymbols.get(oid);
     }
 
     public String[] oidToAttrNames(ASN1ObjectIdentifier oid)
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x500/style/X500NameTokenizer.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x500/style/X500NameTokenizer.java
index 6622550..a45f4a8 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x500/style/X500NameTokenizer.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x500/style/X500NameTokenizer.java
@@ -10,83 +10,78 @@
  */
 public class X500NameTokenizer
 {
-    private String          value;
-    private int             index;
-    private char            separator;
-    private StringBuffer    buf = new StringBuffer();
+    private final String value;
+    private final char separator;
 
-    public X500NameTokenizer(
-        String  oid)
+    private int index;
+
+    public X500NameTokenizer(String oid)
     {
         this(oid, ',');
     }
-    
-    public X500NameTokenizer(
-        String  oid,
-        char    separator)
+
+    public X500NameTokenizer(String oid, char separator)
     {
+        if (oid == null)
+        {
+            throw new NullPointerException();
+        }
+        if (separator == '"' || separator == '\\')
+        {
+            throw new IllegalArgumentException("reserved separator character");
+        }
+
         this.value = oid;
-        this.index = -1;
         this.separator = separator;
+        this.index = oid.length() < 1 ? 0 : -1;
     }
 
     public boolean hasMoreTokens()
     {
-        return (index != value.length());
+        return index < value.length();
     }
 
     public String nextToken()
     {
-        if (index == value.length())
+        if (index >= value.length())
         {
             return null;
         }
 
-        int     end = index + 1;
         boolean quoted = false;
         boolean escaped = false;
 
-        buf.setLength(0);
-
-        while (end != value.length())
+        int beginIndex = index + 1;
+        while (++index < value.length())
         {
-            char    c = value.charAt(end);
+            char c = value.charAt(index);
 
-            if (c == '"')
+            if (escaped)
             {
-                if (!escaped)
-                {
-                    quoted = !quoted;
-                }
-                buf.append(c);
                 escaped = false;
             }
-            else
+            else if (c == '"')
             {
-                if (escaped || quoted)
-                {
-                    buf.append(c);
-                    escaped = false;
-                }
-                else if (c == '\\')
-                {
-                    buf.append(c);
-                    escaped = true;
-                }
-                else if (c == separator)
-                {
-                    break;
-                }
-                else
-                {
-                    buf.append(c);
-                }
+                quoted = !quoted;
             }
-            end++;
+            else if (quoted)
+            {
+            }
+            else if (c == '\\')
+            {
+                escaped = true;
+            }
+            else if (c == separator)
+            {
+                return value.substring(beginIndex, index);
+            }
         }
 
-        index = end;
+        if (escaped || quoted)
+        {
+            throw new IllegalArgumentException("badly formatted directory string");
+        }
 
-        return buf.toString();
+        return value.substring(beginIndex, index);
     }
 }
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/AltSignatureAlgorithm.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/AltSignatureAlgorithm.java
new file mode 100644
index 0000000..429a072
--- /dev/null
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/AltSignatureAlgorithm.java
@@ -0,0 +1,96 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.asn1.x509;
+
+import com.android.internal.org.bouncycastle.asn1.ASN1Encodable;
+import com.android.internal.org.bouncycastle.asn1.ASN1Object;
+import com.android.internal.org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import com.android.internal.org.bouncycastle.asn1.ASN1Primitive;
+import com.android.internal.org.bouncycastle.asn1.ASN1TaggedObject;
+
+/**
+ * X.509 Section 9.8.3.
+ * <br/>
+ * This extension may be used as a public-key certificate extension, a CRL extension or an AVL extension. It shall contain
+ * the algorithm identifier for the alternative digital signature algorithm used by the signer when creating an alternative
+ * digital signature and by the relying party when validating the alternative digital signature.
+ * <pre>
+ * altSignatureAlgorithm EXTENSION ::= {
+ *     SYNTAX AltSignatureAlgorithm
+ *     IDENTIFIED BY id-ce-altSignatureAlgorithm }
+ *
+ * AltSignatureAlgorithm ::= AlgorithmIdentifier{{SupportedAlgorithms}}
+ * </pre>
+ * When the altSignatureAlgorithm extension is included in a particular value that is an instance of a data type that
+ * supports extensions, the altSignatureValue extension shall also be included.
+ * <br/>
+ * NOTE 1 – By having a separate altSignatureAlgorithm extension, instead of having it combined with the
+ * altSignatureValue extension, the alternative digital signature algorithm is protected by the alternative signature.
+ * This extension may be flagged either as critical or as non-critical.
+ * <br/>
+ * NOTE 2 – It is recommended that it be flagged as non-critical. Flagging it as critical would require all relying parties to understand
+ * the extension and the alternative public-key algorithms
+ * @hide This class is not part of the Android public SDK API
+ */
+public class AltSignatureAlgorithm
+    extends ASN1Object
+{
+    private final AlgorithmIdentifier algorithm;
+
+    public static AltSignatureAlgorithm getInstance(
+        ASN1TaggedObject obj,
+        boolean          explicit)
+    {
+        return getInstance(AlgorithmIdentifier.getInstance(obj, explicit));
+    }
+
+    public static AltSignatureAlgorithm getInstance(
+        Object obj)
+    {
+        if (obj instanceof AltSignatureAlgorithm)
+        {
+            return (AltSignatureAlgorithm)obj;
+        }
+        else if (obj != null)
+        {
+            return new AltSignatureAlgorithm(AlgorithmIdentifier.getInstance(obj));
+        }
+
+        return null;
+    }
+
+    public static AltSignatureAlgorithm fromExtensions(Extensions extensions)
+    {
+        return getInstance(Extensions.getExtensionParsedValue(extensions, Extension.altSignatureAlgorithm));
+    }
+
+    public AltSignatureAlgorithm(AlgorithmIdentifier algorithm)
+    {
+        this.algorithm = algorithm;
+    }
+
+    public AltSignatureAlgorithm(ASN1ObjectIdentifier algorithm)
+    {
+        this(algorithm, null);
+    }
+
+    public AltSignatureAlgorithm(ASN1ObjectIdentifier algorithm, ASN1Encodable parameters)
+    {
+        this.algorithm = new AlgorithmIdentifier(algorithm, parameters);
+    }
+
+    /**
+     * Return the algorithm identifier representing the alternate signature algorithm
+     * used to generate the alternate signature algorithm value extension.
+     *
+     * @return alternate signature algorithm identifier.
+     */
+    public AlgorithmIdentifier getAlgorithm()
+    {
+        return algorithm;
+    }
+
+    public ASN1Primitive toASN1Primitive()
+    {
+        return algorithm.toASN1Primitive();
+    }
+}
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/AltSignatureValue.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/AltSignatureValue.java
new file mode 100644
index 0000000..080cb14
--- /dev/null
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/AltSignatureValue.java
@@ -0,0 +1,96 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.asn1.x509;
+
+import com.android.internal.org.bouncycastle.asn1.ASN1BitString;
+import com.android.internal.org.bouncycastle.asn1.ASN1Object;
+import com.android.internal.org.bouncycastle.asn1.ASN1Primitive;
+import com.android.internal.org.bouncycastle.asn1.ASN1TaggedObject;
+import com.android.internal.org.bouncycastle.asn1.DERBitString;
+
+/**
+ * X.509 Section 9.8.4.
+ * <br/>
+ * This extension may be used as a public-key certificate extension, a CRL extension or an AVL extension.
+ * This alternative signature shall be created by the issuer using its alternative private key, and it shall be verified using the
+ * alternative public key of the issuer.
+ * <pre>
+ * altSignatureValue EXTENSION ::= {
+ *     SYNTAX AltSignatureValue
+ *     IDENTIFIED BY id-ce-altSignatureValue }
+ *
+ * AltSignatureValue ::= BIT STRING
+ * </pre>
+ * This extension can only be created by a signer holding a multiple cryptographic algorithms public-key certificate. When
+ * creating the alternative digital signature on an issued public-key certificate or CRL, the signer shall use its alternative
+ * private key.
+ * <br/>
+ * The procedures for creating and validating alternative digital signatures are specified in:
+ * <ul>
+ * <li>clause 7.2.2 for public-key certificates;</li>
+ * <li>clause 7.10.3 for CRLs: and</li>
+ * <li>clause 11.4 for AVLs.</li>
+ * </ul>
+ * @hide This class is not part of the Android public SDK API
+ */
+public class AltSignatureValue
+    extends ASN1Object
+{
+    private final ASN1BitString signature;
+
+    public static AltSignatureValue getInstance(
+        ASN1TaggedObject obj,
+        boolean          explicit)
+    {
+        return getInstance(ASN1BitString.getInstance(obj, explicit));
+    }
+
+    public static AltSignatureValue getInstance(
+        Object obj)
+    {
+        if (obj instanceof AltSignatureValue)
+        {
+            return (AltSignatureValue)obj;
+        }
+        else if (obj != null)
+        {
+            return new AltSignatureValue(ASN1BitString.getInstance(obj));
+        }
+
+        return null;
+    }
+
+    public static AltSignatureValue fromExtensions(Extensions extensions)
+    {
+        return getInstance(Extensions.getExtensionParsedValue(extensions, Extension.altSignatureValue));
+    }
+
+    private AltSignatureValue(ASN1BitString signature)
+    {
+        this.signature = signature;
+    }
+
+    /**
+     * Base constructor.
+     *
+     * @param signature  a signature value, based on the enclosing certificate.
+     */
+    public AltSignatureValue(byte[] signature)
+    {
+        this.signature = new DERBitString(signature);
+    }
+
+    /**
+     * Return the alternate signature to verify the certificate.
+     *
+     * @return certificate's alternate signature.
+     */
+    public ASN1BitString getSignature()
+    {
+        return signature;
+    }
+
+    public ASN1Primitive toASN1Primitive()
+    {
+        return signature;
+    }
+}
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/AttCertIssuer.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/AttCertIssuer.java
index ea2103f..acf96bb 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/AttCertIssuer.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/AttCertIssuer.java
@@ -50,7 +50,7 @@
         ASN1TaggedObject obj,
         boolean          explicit)
     {
-        return getInstance(obj.getObject()); // must be explicitly tagged
+        return getInstance(obj.getExplicitBaseObject()); // must be explicitly tagged
     }
 
     /**
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/AttributeCertificate.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/AttributeCertificate.java
index 1148862..d0f07a6 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/AttributeCertificate.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/AttributeCertificate.java
@@ -1,6 +1,7 @@
 /* GENERATED SOURCE. DO NOT MODIFY. */
 package com.android.internal.org.bouncycastle.asn1.x509;
 
+import com.android.internal.org.bouncycastle.asn1.ASN1BitString;
 import com.android.internal.org.bouncycastle.asn1.ASN1EncodableVector;
 import com.android.internal.org.bouncycastle.asn1.ASN1Object;
 import com.android.internal.org.bouncycastle.asn1.ASN1Primitive;
@@ -16,7 +17,7 @@
 {
     AttributeCertificateInfo    acinfo;
     AlgorithmIdentifier         signatureAlgorithm;
-    DERBitString                signatureValue;
+    ASN1BitString               signatureValue;
 
     /**
      * @param obj
@@ -39,28 +40,23 @@
     public AttributeCertificate(
         AttributeCertificateInfo    acinfo,
         AlgorithmIdentifier         signatureAlgorithm,
-        DERBitString                signatureValue)
+        ASN1BitString               signatureValue)
     {
         this.acinfo = acinfo;
         this.signatureAlgorithm = signatureAlgorithm;
         this.signatureValue = signatureValue;
     }
 
-    /**
-     * @deprecated use getInstance() method.
-     */
-    public AttributeCertificate(
-        ASN1Sequence    seq)
+    private AttributeCertificate(ASN1Sequence seq)
     {
         if (seq.size() != 3)
         {
-            throw new IllegalArgumentException("Bad sequence size: "
-                    + seq.size());
+            throw new IllegalArgumentException("Bad sequence size: " + seq.size());
         }
 
         this.acinfo = AttributeCertificateInfo.getInstance(seq.getObjectAt(0));
         this.signatureAlgorithm = AlgorithmIdentifier.getInstance(seq.getObjectAt(1));
-        this.signatureValue = DERBitString.getInstance(seq.getObjectAt(2));
+        this.signatureValue = ASN1BitString.getInstance(seq.getObjectAt(2));
     }
     
     public AttributeCertificateInfo getAcinfo()
@@ -73,7 +69,7 @@
         return signatureAlgorithm;
     }
 
-    public DERBitString getSignatureValue()
+    public ASN1BitString getSignatureValue()
     {
         return signatureValue;
     }
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/AttributeCertificateInfo.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/AttributeCertificateInfo.java
index db94e6f..a631ea6 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/AttributeCertificateInfo.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/AttributeCertificateInfo.java
@@ -1,6 +1,7 @@
 /* GENERATED SOURCE. DO NOT MODIFY. */
 package com.android.internal.org.bouncycastle.asn1.x509;
 
+import com.android.internal.org.bouncycastle.asn1.ASN1BitString;
 import com.android.internal.org.bouncycastle.asn1.ASN1Encodable;
 import com.android.internal.org.bouncycastle.asn1.ASN1EncodableVector;
 import com.android.internal.org.bouncycastle.asn1.ASN1Integer;
@@ -8,7 +9,6 @@
 import com.android.internal.org.bouncycastle.asn1.ASN1Primitive;
 import com.android.internal.org.bouncycastle.asn1.ASN1Sequence;
 import com.android.internal.org.bouncycastle.asn1.ASN1TaggedObject;
-import com.android.internal.org.bouncycastle.asn1.DERBitString;
 import com.android.internal.org.bouncycastle.asn1.DERSequence;
 
 /**
@@ -21,10 +21,10 @@
     private Holder                  holder;
     private AttCertIssuer           issuer;
     private AlgorithmIdentifier     signature;
-    private ASN1Integer              serialNumber;
+    private ASN1Integer             serialNumber;
     private AttCertValidityPeriod   attrCertValidityPeriod;
     private ASN1Sequence            attributes;
-    private DERBitString            issuerUniqueID;
+    private ASN1BitString           issuerUniqueID;
     private Extensions              extensions;
 
     public static AttributeCertificateInfo getInstance(
@@ -80,9 +80,9 @@
         {
             ASN1Encodable    obj = seq.getObjectAt(i);
 
-            if (obj instanceof DERBitString)
+            if (obj instanceof ASN1BitString)
             {
-                this.issuerUniqueID = DERBitString.getInstance(seq.getObjectAt(i));
+                this.issuerUniqueID = ASN1BitString.getInstance(seq.getObjectAt(i));
             }
             else if (obj instanceof ASN1Sequence || obj instanceof Extensions)
             {
@@ -126,7 +126,7 @@
         return attributes;
     }
 
-    public DERBitString getIssuerUniqueID()
+    public ASN1BitString getIssuerUniqueID()
     {
         return issuerUniqueID;
     }
@@ -158,7 +158,7 @@
     {
         ASN1EncodableVector v = new ASN1EncodableVector(9);
 
-        if (version.intValueExact() != 0)
+        if (!version.hasValue(0))
         {
             v.add(version);
         }
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/AuthorityKeyIdentifier.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/AuthorityKeyIdentifier.java
index 23ae0c7..400da2e 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/AuthorityKeyIdentifier.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/AuthorityKeyIdentifier.java
@@ -18,6 +18,7 @@
 // Android-changed: Use Android digests
 // import org.bouncycastle.crypto.digests.SHA1Digest;
 import com.android.internal.org.bouncycastle.crypto.digests.AndroidDigestFactory;
+import com.android.internal.org.bouncycastle.util.Arrays;
 import com.android.internal.org.bouncycastle.util.encoders.Hex;
 
 /**
@@ -175,7 +176,7 @@
         GeneralNames            name,
         BigInteger              serialNumber)
     {
-        this.keyidentifier = (keyIdentifier != null) ? new DEROctetString(keyIdentifier) : null;
+        this.keyidentifier = (keyIdentifier != null) ? new DEROctetString(Arrays.clone(keyIdentifier)) : null;
         this.certissuer = name;
         this.certserno = (serialNumber != null) ? new ASN1Integer(serialNumber) : null;
     }
@@ -232,6 +233,7 @@
 
     public String toString()
     {
+        // -DM Hex.toHexString
         String keyID = (keyidentifier != null) ? Hex.toHexString(keyidentifier.getOctets()) : "null";
 
         return "AuthorityKeyIdentifier: KeyID(" + keyID + ")";
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/BasicConstraints.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/BasicConstraints.java
index 54cb6b7..538137c 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/BasicConstraints.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/BasicConstraints.java
@@ -126,6 +126,11 @@
         return null;
     }
 
+    public ASN1Integer getPathLenConstraintInteger()
+    {
+        return pathLenConstraint;
+    }
+
     /**
      * Produce an object suitable for an ASN1OutputStream.
      * <pre>
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/CRLNumber.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/CRLNumber.java
index 291bf6c..dde27f2 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/CRLNumber.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/CRLNumber.java
@@ -6,6 +6,7 @@
 import com.android.internal.org.bouncycastle.asn1.ASN1Integer;
 import com.android.internal.org.bouncycastle.asn1.ASN1Object;
 import com.android.internal.org.bouncycastle.asn1.ASN1Primitive;
+import com.android.internal.org.bouncycastle.util.BigIntegers;
 
 /**
  * The CRLNumber object.
@@ -22,6 +23,10 @@
     public CRLNumber(
         BigInteger number)
     {
+        if (BigIntegers.ZERO.compareTo(number) > 0)
+        {
+            throw new IllegalArgumentException("Invalid CRL number : not in (0..MAX)");
+        }
         this.number = number;
     }
 
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/CRLReason.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/CRLReason.java
index 1055281..73b9f72 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/CRLReason.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/CRLReason.java
@@ -53,7 +53,7 @@
     /**
      * @deprecated use lower case version
      */
-    public static final int CESSATION_OF_OPERATION  = 5;
+    public static final int CESSATION_OF_OPERATION = 5;
     /**
      * @deprecated use lower case version
      */
@@ -76,7 +76,7 @@
     public static final int cACompromise = 2;
     public static final int affiliationChanged = 3;
     public static final int superseded = 4;
-    public static final int cessationOfOperation  = 5;
+    public static final int cessationOfOperation = 5;
     public static final int certificateHold = 6;
     // 7 -> unknown
     public static final int removeFromCRL = 8;
@@ -84,11 +84,11 @@
     public static final int aACompromise = 10;
 
     private static final String[] reasonString =
-    {
-        "unspecified", "keyCompromise", "cACompromise", "affiliationChanged",
-        "superseded", "cessationOfOperation", "certificateHold", "unknown",
-        "removeFromCRL", "privilegeWithdrawn", "aACompromise"
-    };
+        {
+            "unspecified", "keyCompromise", "cACompromise", "affiliationChanged",
+            "superseded", "cessationOfOperation", "certificateHold", "unknown",
+            "removeFromCRL", "privilegeWithdrawn", "aACompromise"
+        };
 
     private static final Hashtable table = new Hashtable();
 
@@ -111,6 +111,10 @@
     private CRLReason(
         int reason)
     {
+        if (reason < 0)
+        {
+            throw new IllegalArgumentException("Invalid CRL reason : not in (0..MAX)");
+        }
         value = new ASN1Enumerated(reason);
     }
 
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/Certificate.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/Certificate.java
index 667c834..308ed94 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/Certificate.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/Certificate.java
@@ -1,12 +1,12 @@
 /* GENERATED SOURCE. DO NOT MODIFY. */
 package com.android.internal.org.bouncycastle.asn1.x509;
 
+import com.android.internal.org.bouncycastle.asn1.ASN1BitString;
 import com.android.internal.org.bouncycastle.asn1.ASN1Integer;
 import com.android.internal.org.bouncycastle.asn1.ASN1Object;
 import com.android.internal.org.bouncycastle.asn1.ASN1Primitive;
 import com.android.internal.org.bouncycastle.asn1.ASN1Sequence;
 import com.android.internal.org.bouncycastle.asn1.ASN1TaggedObject;
-import com.android.internal.org.bouncycastle.asn1.DERBitString;
 import com.android.internal.org.bouncycastle.asn1.x500.X500Name;
 
 /**
@@ -26,7 +26,7 @@
     ASN1Sequence  seq;
     TBSCertificate tbsCert;
     AlgorithmIdentifier     sigAlgId;
-    DERBitString            sig;
+    ASN1BitString            sig;
 
     public static Certificate getInstance(
         ASN1TaggedObject obj,
@@ -63,7 +63,7 @@
             tbsCert = TBSCertificate.getInstance(seq.getObjectAt(0));
             sigAlgId = AlgorithmIdentifier.getInstance(seq.getObjectAt(1));
 
-            sig = DERBitString.getInstance(seq.getObjectAt(2));
+            sig = ASN1BitString.getInstance(seq.getObjectAt(2));
         }
         else
         {
@@ -121,7 +121,7 @@
         return sigAlgId;
     }
 
-    public DERBitString getSignature()
+    public ASN1BitString getSignature()
     {
         return sig;
     }
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/CertificateList.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/CertificateList.java
index 218a623..2e1e0ed 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/CertificateList.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/CertificateList.java
@@ -4,6 +4,7 @@
 
 import java.util.Enumeration;
 
+import com.android.internal.org.bouncycastle.asn1.ASN1BitString;
 import com.android.internal.org.bouncycastle.asn1.ASN1EncodableVector;
 import com.android.internal.org.bouncycastle.asn1.ASN1Object;
 import com.android.internal.org.bouncycastle.asn1.ASN1Primitive;
@@ -32,7 +33,7 @@
 {
     TBSCertList            tbsCertList;
     AlgorithmIdentifier    sigAlgId;
-    DERBitString           sig;
+    ASN1BitString          sig;
     boolean                isHashCodeSet = false;
     int                    hashCodeValue;
 
@@ -58,18 +59,14 @@
         return null;
     }
 
-    /**
-     * @deprecated use getInstance() method.
-     * @param seq
-     */
-    public CertificateList(
+    private CertificateList(
         ASN1Sequence seq)
     {
         if (seq.size() == 3)
         {
             tbsCertList = TBSCertList.getInstance(seq.getObjectAt(0));
             sigAlgId = AlgorithmIdentifier.getInstance(seq.getObjectAt(1));
-            sig = DERBitString.getInstance(seq.getObjectAt(2));
+            sig = ASN1BitString.getInstance(seq.getObjectAt(2));
         }
         else
         {
@@ -97,7 +94,7 @@
         return sigAlgId;
     }
 
-    public DERBitString getSignature()
+    public ASN1BitString getSignature()
     {
         return sig;
     }
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/DeltaCertificateDescriptor.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/DeltaCertificateDescriptor.java
new file mode 100644
index 0000000..459da2d
--- /dev/null
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/DeltaCertificateDescriptor.java
@@ -0,0 +1,276 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.asn1.x509;
+
+import java.util.Enumeration;
+
+import com.android.internal.org.bouncycastle.asn1.ASN1BitString;
+import com.android.internal.org.bouncycastle.asn1.ASN1Encodable;
+import com.android.internal.org.bouncycastle.asn1.ASN1EncodableVector;
+import com.android.internal.org.bouncycastle.asn1.ASN1Integer;
+import com.android.internal.org.bouncycastle.asn1.ASN1Object;
+import com.android.internal.org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import com.android.internal.org.bouncycastle.asn1.ASN1Primitive;
+import com.android.internal.org.bouncycastle.asn1.ASN1Sequence;
+import com.android.internal.org.bouncycastle.asn1.ASN1TaggedObject;
+import com.android.internal.org.bouncycastle.asn1.DERSequence;
+import com.android.internal.org.bouncycastle.asn1.DERTaggedObject;
+import com.android.internal.org.bouncycastle.asn1.x500.X500Name;
+
+/**
+ * <pre>
+ *     DeltaCertificateDescriptor ::= SEQUENCE {
+ *      serialNumber          CertificateSerialNumber,
+ *      signature             [0] IMPLICIT AlgorithmIdentifier
+ *           {SIGNATURE_ALGORITHM, {...}} OPTIONAL,
+ *      issuer                [1] IMPLICIT Name OPTIONAL,
+ *      validity              [2] IMPLICIT Validity OPTIONAL,
+ *      subject               [3] IMPLICIT Name OPTIONAL,
+ *      subjectPublicKeyInfo  SubjectPublicKeyInfo,
+ *      extensions            [4] IMPLICIT Extensions{CertExtensions}
+ *           OPTIONAL,
+ *      signatureValue        BIT STRING
+ *    }
+ *    </pre>
+ * @hide This class is not part of the Android public SDK API
+ */
+public class DeltaCertificateDescriptor
+    extends ASN1Object
+{
+    private final ASN1Integer serialNumber;
+
+    private AlgorithmIdentifier signature;
+    private X500Name issuer;
+    private ASN1Sequence validity;
+    private X500Name subject;
+    private SubjectPublicKeyInfo subjectPublicKeyInfo;
+    private Extensions extensions;
+
+    private final ASN1BitString signatureValue;
+
+    public static DeltaCertificateDescriptor getInstance(
+        Object  obj)
+    {
+        if (obj instanceof DeltaCertificateDescriptor)
+        {
+            return (DeltaCertificateDescriptor)obj;
+        }
+        else if (obj != null)
+        {
+            return new DeltaCertificateDescriptor(ASN1Sequence.getInstance(obj));
+        }
+
+        return null;
+    }
+
+    /**
+     * Retrieve a DeltaCertificateDescriptor for a passed in Extensions object, if present.
+     *
+     * @param extensions the extensions object to be examined.
+     * @return  the DeltaCertificateDescriptor, null if the extension is not present.
+     */
+    public static DeltaCertificateDescriptor fromExtensions(Extensions extensions)
+    {
+        return getInstance(Extensions.getExtensionParsedValue(extensions, Extension.deltaCertificateDescriptor));
+    }
+
+    private DeltaCertificateDescriptor(ASN1Sequence seq)
+    {
+        this.serialNumber = ASN1Integer.getInstance(seq.getObjectAt(0));
+
+        int idx = 1;
+        ASN1Encodable next = seq.getObjectAt(idx);
+        while (next instanceof ASN1TaggedObject)
+        {
+            ASN1TaggedObject tagged = ASN1TaggedObject.getInstance(next);
+            switch (tagged.getTagNo())
+            {
+            case 0:
+                signature = AlgorithmIdentifier.getInstance(tagged, false);
+                break;
+            case 1:
+                issuer = X500Name.getInstance(tagged, true);   // issuer
+                break;
+            case 2:
+                validity = ASN1Sequence.getInstance(tagged, false);
+                break;
+            case 3:
+                subject = X500Name.getInstance(tagged, true);   // subject
+                break;
+            }
+            next = seq.getObjectAt(idx++);
+        }
+
+        subjectPublicKeyInfo = subjectPublicKeyInfo.getInstance(next);
+
+        next = seq.getObjectAt(idx);
+        while (next instanceof ASN1TaggedObject)
+        {
+            ASN1TaggedObject tagged = ASN1TaggedObject.getInstance(next);
+            switch (tagged.getTagNo())
+            {
+            case 4:
+                extensions = Extensions.getInstance(tagged, false); 
+                break;
+            }
+            next = seq.getObjectAt(idx++);
+        }
+
+        signatureValue = ASN1BitString.getInstance(next);
+    }
+
+    public ASN1Integer getSerialNumber()
+    {
+        return serialNumber;
+    }
+
+    public AlgorithmIdentifier getSignature()
+    {
+        return signature;
+    }
+
+    public X500Name getIssuer()
+    {
+        return issuer;
+    }
+
+    public ASN1Sequence getValidity()
+    {
+        return validity;
+    }
+
+    public X500Name getSubject()
+    {
+        return subject;
+    }
+
+    public SubjectPublicKeyInfo getSubjectPublicKeyInfo()
+    {
+        return subjectPublicKeyInfo;
+    }
+
+    public Extensions getExtensions()
+    {
+        return extensions;
+    }
+
+    public ASN1BitString getSignatureValue()
+    {
+        return signatureValue;
+    }
+
+    public DeltaCertificateDescriptor trimTo(TBSCertificate baseTbsCertificate, Extensions tbsExtensions)
+    {
+        AlgorithmIdentifier signature = baseTbsCertificate.signature;
+        X500Name issuer = baseTbsCertificate.issuer;
+        ASN1Sequence validity = new DERSequence(new ASN1Encodable[]
+        {
+            baseTbsCertificate.startDate, baseTbsCertificate.endDate
+        });
+        X500Name subject = baseTbsCertificate.subject;
+        ASN1Sequence s = ASN1Sequence.getInstance(toASN1Primitive());
+        ASN1EncodableVector v = new ASN1EncodableVector();
+
+        Enumeration en = s.getObjects();
+        v.add((ASN1Encodable)en.nextElement());
+
+        ASN1Encodable next = (ASN1Encodable)en.nextElement();
+        while (next instanceof ASN1TaggedObject)
+        {
+            ASN1TaggedObject tagged = ASN1TaggedObject.getInstance(next);
+            switch (tagged.getTagNo())
+            {
+            case 0:
+                AlgorithmIdentifier sig = AlgorithmIdentifier.getInstance(tagged, false);
+                if (!sig.equals(signature))
+                {
+                    v.add(next);
+                }
+                break;
+            case 1:
+                X500Name iss = X500Name.getInstance(tagged, true);   // issuer
+                if (!iss.equals(issuer))
+                {
+                    v.add(next);
+                }
+                break;
+            case 2:
+                ASN1Sequence val = ASN1Sequence.getInstance(tagged, false);
+                if (!val.equals(validity))
+                {
+                    v.add(next);
+                }
+                break;
+            case 3:
+                X500Name sub = X500Name.getInstance(tagged, true);   // subject
+                if (!sub.equals(subject))
+                {
+                    v.add(next);
+                }
+                break;
+            }
+            next = (ASN1Encodable)en.nextElement();
+        }
+
+        v.add(next);
+
+        next = (ASN1Encodable)en.nextElement();
+        while (next instanceof ASN1TaggedObject)
+        {
+            ASN1TaggedObject tagged = ASN1TaggedObject.getInstance(next);
+            switch (tagged.getTagNo())
+            {
+            case 4:
+                Extensions deltaExts = Extensions.getInstance(tagged, false);
+                ExtensionsGenerator deltaExtGen = new ExtensionsGenerator();
+                for (Enumeration extEn = deltaExts.oids(); extEn.hasMoreElements(); )
+                {
+                    Extension deltaExt = deltaExts.getExtension((ASN1ObjectIdentifier)extEn.nextElement());
+                    Extension primaryExt = tbsExtensions.getExtension(deltaExt.getExtnId());
+
+                    if (primaryExt != null)
+                    {
+                        if (!deltaExt.equals(primaryExt))
+                        {
+                            deltaExtGen.addExtension(deltaExt);
+                        }
+                    }
+                }
+
+                DeltaCertificateDescriptor trimmedDeltaCertDesc;
+                if (!deltaExtGen.isEmpty())
+                {
+                    v.add(new DERTaggedObject(false, 4, deltaExtGen.generate()));
+                }
+            }
+            next = (ASN1Encodable)en.nextElement();
+        }
+
+        v.add(next);
+
+        return new DeltaCertificateDescriptor(new DERSequence(v));
+    }
+
+    private void addOptional(ASN1EncodableVector v, int tag, boolean explicit, ASN1Object obj)
+    {
+        if (obj != null)
+        {
+             v.add(new DERTaggedObject(explicit, tag, obj));
+        }
+    }
+
+    public ASN1Primitive toASN1Primitive()
+    {
+        ASN1EncodableVector v = new ASN1EncodableVector(7);
+
+        v.add(serialNumber);
+        addOptional(v, 0, false, signature);
+        addOptional(v, 1, true, issuer); // CHOICE
+        addOptional(v, 2, false, validity);
+        addOptional(v, 3, true, subject);  // CHOICE
+        v.add(subjectPublicKeyInfo);
+        addOptional(v, 4, false, extensions);
+        v.add(signatureValue);
+
+        return new DERSequence(v);
+    }
+}
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/DistributionPoint.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/DistributionPoint.java
index 4d3b786..8e16403 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/DistributionPoint.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/DistributionPoint.java
@@ -1,12 +1,12 @@
 /* GENERATED SOURCE. DO NOT MODIFY. */
 package com.android.internal.org.bouncycastle.asn1.x509;
 
+import com.android.internal.org.bouncycastle.asn1.ASN1BitString;
 import com.android.internal.org.bouncycastle.asn1.ASN1EncodableVector;
 import com.android.internal.org.bouncycastle.asn1.ASN1Object;
 import com.android.internal.org.bouncycastle.asn1.ASN1Primitive;
 import com.android.internal.org.bouncycastle.asn1.ASN1Sequence;
 import com.android.internal.org.bouncycastle.asn1.ASN1TaggedObject;
-import com.android.internal.org.bouncycastle.asn1.DERBitString;
 import com.android.internal.org.bouncycastle.asn1.DERSequence;
 import com.android.internal.org.bouncycastle.asn1.DERTaggedObject;
 import com.android.internal.org.bouncycastle.util.Strings;
@@ -61,10 +61,11 @@
             switch (t.getTagNo())
             {
             case 0:
+                // CHOICE so explicit
                 distributionPoint = DistributionPointName.getInstance(t, true);
                 break;
             case 1:
-                reasons = new ReasonFlags(DERBitString.getInstance(t, false));
+                reasons = new ReasonFlags(ASN1BitString.getInstance(t, false));
                 break;
             case 2:
                 cRLIssuer = GeneralNames.getInstance(t, false);
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/Extension.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/Extension.java
index 548f930..487fc44 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/Extension.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/Extension.java
@@ -13,6 +13,7 @@
 import com.android.internal.org.bouncycastle.asn1.ASN1Sequence;
 import com.android.internal.org.bouncycastle.asn1.DEROctetString;
 import com.android.internal.org.bouncycastle.asn1.DERSequence;
+import com.android.internal.org.bouncycastle.util.Arrays;
 
 /**
  * an object for the elements in the X.509 V3 extension block.
@@ -181,6 +182,26 @@
      */
     public static final ASN1ObjectIdentifier expiredCertsOnCRL = new ASN1ObjectIdentifier("2.5.29.60").intern();
 
+    /**
+     * the subject’s alternative public key information
+     */
+    public static final ASN1ObjectIdentifier subjectAltPublicKeyInfo = new ASN1ObjectIdentifier("2.5.29.72").intern();
+
+    /**
+     * the algorithm identifier for the alternative digital signature algorithm.
+     */
+    public static final ASN1ObjectIdentifier altSignatureAlgorithm = new ASN1ObjectIdentifier("2.5.29.73").intern();
+
+    /**
+     * alternative signature shall be created by the issuer using its alternative private key.
+     */
+    public static final ASN1ObjectIdentifier altSignatureValue = new ASN1ObjectIdentifier("2.5.29.74").intern();
+
+    /**
+     * delta certificate extension - prototype value will change!
+     */
+    public static final ASN1ObjectIdentifier deltaCertificateDescriptor = new ASN1ObjectIdentifier("2.16.840.1.114027.80.6.1");
+
     private ASN1ObjectIdentifier extnId;
     private boolean             critical;
     private ASN1OctetString      value;
@@ -212,7 +233,7 @@
         boolean critical,
         byte[] value)
     {
-        this(extnId, critical, new DEROctetString(value));
+        this(extnId, critical, new DEROctetString(Arrays.clone(value)));
     }
 
     /**
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/Extensions.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/Extensions.java
index 0d5cec1..f715156 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/Extensions.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/Extensions.java
@@ -13,6 +13,7 @@
 import com.android.internal.org.bouncycastle.asn1.ASN1Sequence;
 import com.android.internal.org.bouncycastle.asn1.ASN1TaggedObject;
 import com.android.internal.org.bouncycastle.asn1.DERSequence;
+import com.android.internal.org.bouncycastle.util.Properties;
 
 /**
  * <pre>
@@ -72,6 +73,11 @@
     private Extensions(
         ASN1Sequence seq)
     {
+        if (seq.size() == 0)
+        {
+            throw new IllegalArgumentException("empty extension sequence found");
+        }
+
         Enumeration e = seq.getObjects();
 
         while (e.hasMoreElements())
@@ -80,7 +86,10 @@
 
             if (extensions.containsKey(ext.getExtnId()))
             {
-                throw new IllegalArgumentException("repeated extension found: " + ext.getExtnId());
+                if (!Properties.isOverrideSet("com.android.internal.org.bouncycastle.x509.ignore_repeated_extensions"))
+                {
+                    throw new IllegalArgumentException("repeated extension found: " + ext.getExtnId());
+                }
             }
             
             extensions.put(ext.getExtnId(), ext);
@@ -108,6 +117,11 @@
     public Extensions(
         Extension[] extensions)
     {
+        if (extensions == null || extensions.length == 0)
+        {
+            throw new IllegalArgumentException("extension array cannot be null or empty");
+        }
+
         for (int i = 0; i != extensions.length; i++)
         {
             Extension ext = extensions[i];
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/ExtensionsGenerator.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/ExtensionsGenerator.java
index 7f9db5f..27c261b 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/ExtensionsGenerator.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/ExtensionsGenerator.java
@@ -2,13 +2,22 @@
 package com.android.internal.org.bouncycastle.asn1.x509;
 
 import java.io.IOException;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashSet;
 import java.util.Hashtable;
+import java.util.Set;
 import java.util.Vector;
 
 import com.android.internal.org.bouncycastle.asn1.ASN1Encodable;
+import com.android.internal.org.bouncycastle.asn1.ASN1EncodableVector;
 import com.android.internal.org.bouncycastle.asn1.ASN1Encoding;
 import com.android.internal.org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import com.android.internal.org.bouncycastle.asn1.ASN1ParsingException;
+import com.android.internal.org.bouncycastle.asn1.ASN1Sequence;
 import com.android.internal.org.bouncycastle.asn1.DEROctetString;
+import com.android.internal.org.bouncycastle.asn1.DERSequence;
+import com.android.internal.org.bouncycastle.util.Arrays;
 
 /**
  * Generator for X.509 extensions
@@ -18,6 +27,18 @@
 {
     private Hashtable extensions = new Hashtable();
     private Vector extOrdering = new Vector();
+    private static final Set dupsAllowed;
+
+
+    static
+    {
+        Set dups = new HashSet();
+        dups.add(Extension.subjectAlternativeName);
+        dups.add(Extension.issuerAlternativeName);
+        dups.add(Extension.subjectDirectoryAttributes);
+        dups.add(Extension.certificateIssuer);
+        dupsAllowed = Collections.unmodifiableSet(dups);
+    }
 
     /**
      * Reset the generator
@@ -32,14 +53,14 @@
      * Add an extension with the given oid and the passed in value to be included
      * in the OCTET STRING associated with the extension.
      *
-     * @param oid  OID for the extension.
-     * @param critical  true if critical, false otherwise.
-     * @param value the ASN.1 object to be included in the extension.
+     * @param oid      OID for the extension.
+     * @param critical true if critical, false otherwise.
+     * @param value    the ASN.1 object to be included in the extension.
      */
     public void addExtension(
         ASN1ObjectIdentifier oid,
-        boolean              critical,
-        ASN1Encodable        value)
+        boolean critical,
+        ASN1Encodable value)
         throws IOException
     {
         this.addExtension(oid, critical, value.toASN1Primitive().getEncoded(ASN1Encoding.DER));
@@ -49,22 +70,52 @@
      * Add an extension with the given oid and the passed in byte array to be wrapped in the
      * OCTET STRING associated with the extension.
      *
-     * @param oid OID for the extension.
+     * @param oid      OID for the extension.
      * @param critical true if critical, false otherwise.
-     * @param value the byte array to be wrapped.
+     * @param value    the byte array to be wrapped.
      */
     public void addExtension(
         ASN1ObjectIdentifier oid,
-        boolean             critical,
-        byte[]              value)
+        boolean critical,
+        byte[] value)
     {
         if (extensions.containsKey(oid))
         {
-            throw new IllegalArgumentException("extension " + oid + " already added");
-        }
+            if (dupsAllowed.contains(oid))
+            {
+                Extension existingExtension = (Extension)extensions.get(oid);
+                ASN1Sequence seq1 = ASN1Sequence.getInstance(DEROctetString.getInstance(existingExtension.getExtnValue()).getOctets());
+                ASN1Sequence seq2 = ASN1Sequence.getInstance(value);
 
-        extOrdering.addElement(oid);
-        extensions.put(oid, new Extension(oid, critical, new DEROctetString(value)));
+                ASN1EncodableVector items = new ASN1EncodableVector(seq1.size() + seq2.size());
+                for (Enumeration en = seq1.getObjects(); en.hasMoreElements();)
+                {
+                    items.add((ASN1Encodable)en.nextElement());
+                }
+                for (Enumeration en = seq2.getObjects(); en.hasMoreElements();)
+                {
+                    items.add((ASN1Encodable)en.nextElement());
+                }
+                
+                try
+                {
+                    extensions.put(oid, new Extension(oid, critical, new DERSequence(items).getEncoded()));
+                }
+                catch (IOException e)
+                {
+                    throw new ASN1ParsingException(e.getMessage(), e);
+                }
+            }
+            else
+            {
+                throw new IllegalArgumentException("extension " + oid + " already added");
+            }
+        }
+        else
+        {
+            extOrdering.addElement(oid);
+            extensions.put(oid, new Extension(oid, critical, new DEROctetString(Arrays.clone(value))));
+        }
     }
 
     /**
@@ -88,14 +139,14 @@
      * Replace an extension with the given oid and the passed in value to be included
      * in the OCTET STRING associated with the extension.
      *
-     * @param oid  OID for the extension.
-     * @param critical  true if critical, false otherwise.
-     * @param value the ASN.1 object to be included in the extension.
+     * @param oid      OID for the extension.
+     * @param critical true if critical, false otherwise.
+     * @param value    the ASN.1 object to be included in the extension.
      */
     public void replaceExtension(
         ASN1ObjectIdentifier oid,
-        boolean              critical,
-        ASN1Encodable        value)
+        boolean critical,
+        ASN1Encodable value)
         throws IOException
     {
         this.replaceExtension(oid, critical, value.toASN1Primitive().getEncoded(ASN1Encoding.DER));
@@ -105,14 +156,14 @@
      * Replace an extension with the given oid and the passed in byte array to be wrapped in the
      * OCTET STRING associated with the extension.
      *
-     * @param oid OID for the extension.
+     * @param oid      OID for the extension.
      * @param critical true if critical, false otherwise.
-     * @param value the byte array to be wrapped.
+     * @param value    the byte array to be wrapped.
      */
     public void replaceExtension(
         ASN1ObjectIdentifier oid,
-        boolean             critical,
-        byte[]              value)
+        boolean critical,
+        byte[] value)
     {
         this.replaceExtension(new Extension(oid, critical, value));
     }
@@ -158,7 +209,7 @@
      */
     public boolean hasExtension(ASN1ObjectIdentifier oid)
     {
-         return extensions.containsKey(oid);
+        return extensions.containsKey(oid);
     }
 
     /**
@@ -169,7 +220,7 @@
      */
     public Extension getExtension(ASN1ObjectIdentifier oid)
     {
-         return (Extension)extensions.get(oid);
+        return (Extension)extensions.get(oid);
     }
 
     /**
@@ -185,7 +236,7 @@
     /**
      * Generate an Extensions object based on the current state of the generator.
      *
-     * @return  an X09Extensions object.
+     * @return an X09Extensions object.
      */
     public Extensions generate()
     {
@@ -198,4 +249,15 @@
 
         return new Extensions(exts);
     }
+
+    public void addExtension(Extensions extensions)
+    {
+        ASN1ObjectIdentifier[] oids = extensions.getExtensionOIDs();
+        for (int i = 0; i != oids.length; i++)
+        {
+            ASN1ObjectIdentifier ident = oids[i];
+            Extension ext = extensions.getExtension(ident);
+            addExtension(ASN1ObjectIdentifier.getInstance(ident), ext.isCritical(), ext.getExtnValue().getOctets());
+        }
+    }
 }
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/GeneralName.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/GeneralName.java
index 779009a..7ce143f 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/GeneralName.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/GeneralName.java
@@ -6,6 +6,7 @@
 
 import com.android.internal.org.bouncycastle.asn1.ASN1Choice;
 import com.android.internal.org.bouncycastle.asn1.ASN1Encodable;
+import com.android.internal.org.bouncycastle.asn1.ASN1IA5String;
 import com.android.internal.org.bouncycastle.asn1.ASN1Object;
 import com.android.internal.org.bouncycastle.asn1.ASN1ObjectIdentifier;
 import com.android.internal.org.bouncycastle.asn1.ASN1OctetString;
@@ -196,7 +197,7 @@
             case dNSName:
             case rfc822Name:
             case uniformResourceIdentifier:
-                return new GeneralName(tag, DERIA5String.getInstance(tagObj, false));
+                return new GeneralName(tag, ASN1IA5String.getInstance(tagObj, false));
 
             case directoryName:
                 return new GeneralName(tag, X500Name.getInstance(tagObj, true));
@@ -229,6 +230,11 @@
         ASN1TaggedObject tagObj,
         boolean          explicit)
     {
+        if (!explicit)
+        {
+            throw new IllegalArgumentException("choice item must be explicitly tagged");
+        }
+
         return GeneralName.getInstance(ASN1TaggedObject.getInstance(tagObj, true));
     }
 
@@ -253,7 +259,7 @@
         case rfc822Name:
         case dNSName:
         case uniformResourceIdentifier:
-            buf.append(DERIA5String.getInstance(obj).getString());
+            buf.append(ASN1IA5String.getInstance(obj).getString());
             break;
         case directoryName:
             buf.append(X500Name.getInstance(obj).toString());
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/GeneralSubtree.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/GeneralSubtree.java
index 64773c6..0c058ae 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/GeneralSubtree.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/GeneralSubtree.java
@@ -205,7 +205,7 @@
 
         v.add(base);
 
-        if (minimum != null && !minimum.hasValue(ZERO))
+        if (minimum != null && !minimum.hasValue(0))
         {
             v.add(new DERTaggedObject(false, 0, minimum));
         }
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/Holder.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/Holder.java
index 9a4eed1..8bb4e32 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/Holder.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/Holder.java
@@ -89,7 +89,7 @@
         default:
             throw new IllegalArgumentException("unknown tag in Holder");
         }
-        version = 0;
+        version = V1_CERTIFICATE_HOLDER;
     }
 
     /**
@@ -125,7 +125,7 @@
                 throw new IllegalArgumentException("unknown tag in Holder");
             }
         }
-        version = 1;
+        version = V2_CERTIFICATE_HOLDER;
     }
 
     public Holder(IssuerSerial baseCertificateID)
@@ -211,7 +211,7 @@
 
     public ASN1Primitive toASN1Primitive()
     {
-        if (version == 1)
+        if (version == V2_CERTIFICATE_HOLDER)
         {
             ASN1EncodableVector v = new ASN1EncodableVector(3);
 
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/IssuerSerial.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/IssuerSerial.java
index 0c5aba5..bc721d5 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/IssuerSerial.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/IssuerSerial.java
@@ -3,13 +3,13 @@
 
 import java.math.BigInteger;
 
+import com.android.internal.org.bouncycastle.asn1.ASN1BitString;
 import com.android.internal.org.bouncycastle.asn1.ASN1EncodableVector;
 import com.android.internal.org.bouncycastle.asn1.ASN1Integer;
 import com.android.internal.org.bouncycastle.asn1.ASN1Object;
 import com.android.internal.org.bouncycastle.asn1.ASN1Primitive;
 import com.android.internal.org.bouncycastle.asn1.ASN1Sequence;
 import com.android.internal.org.bouncycastle.asn1.ASN1TaggedObject;
-import com.android.internal.org.bouncycastle.asn1.DERBitString;
 import com.android.internal.org.bouncycastle.asn1.DERSequence;
 import com.android.internal.org.bouncycastle.asn1.x500.X500Name;
 
@@ -19,9 +19,9 @@
 public class IssuerSerial
     extends ASN1Object
 {
-    GeneralNames            issuer;
-    ASN1Integer              serial;
-    DERBitString            issuerUID;
+    GeneralNames  issuer;
+    ASN1Integer   serial;
+    ASN1BitString issuerUID;
 
     public static IssuerSerial getInstance(
             Object  obj)
@@ -59,7 +59,7 @@
 
         if (seq.size() == 3)
         {
-            issuerUID = DERBitString.getInstance(seq.getObjectAt(2));
+            issuerUID = ASN1BitString.getInstance(seq.getObjectAt(2));
         }
     }
 
@@ -95,7 +95,7 @@
         return serial;
     }
 
-    public DERBitString getIssuerUID()
+    public ASN1BitString getIssuerUID()
     {
         return issuerUID;
     }
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/IssuingDistributionPoint.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/IssuingDistributionPoint.java
index 43d1c23..571f78e 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/IssuingDistributionPoint.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/IssuingDistributionPoint.java
@@ -1,6 +1,7 @@
 /* GENERATED SOURCE. DO NOT MODIFY. */
 package com.android.internal.org.bouncycastle.asn1.x509;
 
+import com.android.internal.org.bouncycastle.asn1.ASN1BitString;
 import com.android.internal.org.bouncycastle.asn1.ASN1Boolean;
 import com.android.internal.org.bouncycastle.asn1.ASN1EncodableVector;
 import com.android.internal.org.bouncycastle.asn1.ASN1Object;
@@ -154,7 +155,7 @@
             switch (o.getTagNo())
             {
             case 0:
-                                                    // CHOICE so explicit
+                // CHOICE so explicit
                 distributionPoint = DistributionPointName.getInstance(o, true);
                 break;
             case 1:
@@ -164,7 +165,7 @@
                 onlyContainsCACerts = ASN1Boolean.getInstance(o, false).isTrue();
                 break;
             case 3:
-                onlySomeReasons = new ReasonFlags(ReasonFlags.getInstance(o, false));
+                onlySomeReasons = new ReasonFlags(ASN1BitString.getInstance(o, false));
                 break;
             case 4:
                 indirectCRL = ASN1Boolean.getInstance(o, false).isTrue();
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/KeyPurposeId.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/KeyPurposeId.java
index 0aac42b..ad1cf20 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/KeyPurposeId.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/KeyPurposeId.java
@@ -109,6 +109,30 @@
      */
     public static final KeyPurposeId id_kp_capwapWTP = new KeyPurposeId(id_kp.branch("19"));
 
+
+    /**
+     * id-kp-cmcCA OBJECT IDENTIFIER ::= {
+     *          iso(1) identified-organization(3) dod(6) internet(1)
+     *          security(5) mechanisms(5) pkix(7) kp(3) 27 }
+     */
+    public static final KeyPurposeId id_kp_cmcCA = new KeyPurposeId(id_kp.branch("27"));
+
+    /**
+     * id-kp-cmcRA OBJECT IDENTIFIER ::= {
+     *          iso(1) identified-organization(3) dod(6) internet(1)
+     *          security(5) mechanisms(5) pkix(7) kp(3) 28 }
+     */
+    public static final KeyPurposeId id_kp_cmcRA = new KeyPurposeId(id_kp.branch("28"));
+
+    /**
+     * id-kp-cmKGA OBJECT IDENTIFIER ::= {
+     *          iso(1) identified-organization(3) dod(6) internet(1)
+     *          security(5) mechanisms(5) pkix(7) kp(3) 32 }
+     */
+    public static final KeyPurposeId id_kp_cmKGA = new KeyPurposeId(id_kp.branch("32"));
+
+
+
     //
     // microsoft key purpose ids
     //
@@ -142,15 +166,6 @@
         this.id = id;
     }
 
-    /**
-     * @param id string representation of an OID.
-     * @deprecated use getInstance and an OID or one of the constants above.
-     */
-    public KeyPurposeId(String id)
-    {
-        this(new ASN1ObjectIdentifier(id));
-    }
-
     public static KeyPurposeId getInstance(Object o)
     {
         if (o instanceof KeyPurposeId)
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/KeyUsage.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/KeyUsage.java
index 94433bf..aa7bfcd 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/KeyUsage.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/KeyUsage.java
@@ -1,6 +1,7 @@
 /* GENERATED SOURCE. DO NOT MODIFY. */
 package com.android.internal.org.bouncycastle.asn1.x509;
 
+import com.android.internal.org.bouncycastle.asn1.ASN1BitString;
 import com.android.internal.org.bouncycastle.asn1.ASN1Object;
 import com.android.internal.org.bouncycastle.asn1.ASN1Primitive;
 import com.android.internal.org.bouncycastle.asn1.DERBitString;
@@ -36,7 +37,7 @@
     public static final int        encipherOnly     = (1 << 0);
     public static final int        decipherOnly     = (1 << 15);
 
-    private DERBitString bitString;
+    private ASN1BitString bitString;
 
     public static KeyUsage getInstance(Object obj)   // needs to be DERBitString for other VMs
     {
@@ -46,7 +47,7 @@
         }
         else if (obj != null)
         {
-            return new KeyUsage(DERBitString.getInstance(obj));
+            return new KeyUsage(ASN1BitString.getInstance(obj));
         }
 
         return null;
@@ -71,7 +72,7 @@
     }
 
     private KeyUsage(
-        DERBitString bitString)
+        ASN1BitString bitString)
     {
         this.bitString = bitString;
     }
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/ObjectDigestInfo.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/ObjectDigestInfo.java
index e174f4c..4e8d9ef 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/ObjectDigestInfo.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/ObjectDigestInfo.java
@@ -1,6 +1,7 @@
 /* GENERATED SOURCE. DO NOT MODIFY. */
 package com.android.internal.org.bouncycastle.asn1.x509;
 
+import com.android.internal.org.bouncycastle.asn1.ASN1BitString;
 import com.android.internal.org.bouncycastle.asn1.ASN1EncodableVector;
 import com.android.internal.org.bouncycastle.asn1.ASN1Enumerated;
 import com.android.internal.org.bouncycastle.asn1.ASN1Object;
@@ -56,7 +57,7 @@
 
     AlgorithmIdentifier digestAlgorithm;
 
-    DERBitString objectDigest;
+    ASN1BitString objectDigest;
 
     public static ObjectDigestInfo getInstance(
         Object obj)
@@ -131,7 +132,7 @@
 
         digestAlgorithm = AlgorithmIdentifier.getInstance(seq.getObjectAt(1 + offset));
 
-        objectDigest = DERBitString.getInstance(seq.getObjectAt(2 + offset));
+        objectDigest = ASN1BitString.getInstance(seq.getObjectAt(2 + offset));
     }
 
     public ASN1Enumerated getDigestedObjectType()
@@ -149,7 +150,7 @@
         return digestAlgorithm;
     }
 
-    public DERBitString getObjectDigest()
+    public ASN1BitString getObjectDigest()
     {
         return objectDigest;
     }
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/OtherName.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/OtherName.java
index 098b21f..5a45583 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/OtherName.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/OtherName.java
@@ -69,7 +69,7 @@
     private OtherName(ASN1Sequence seq)
     {
         this.typeID = ASN1ObjectIdentifier.getInstance(seq.getObjectAt(0));
-        this.value = ASN1TaggedObject.getInstance(seq.getObjectAt(1)).getObject(); // explicitly tagged
+        this.value = ASN1TaggedObject.getInstance(seq.getObjectAt(1)).getExplicitBaseObject();
     }
 
     public ASN1ObjectIdentifier getTypeID()
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/PKIXNameConstraintValidator.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/PKIXNameConstraintValidator.java
index 70c7a58..14e8434 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/PKIXNameConstraintValidator.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/PKIXNameConstraintValidator.java
@@ -10,9 +10,9 @@
 import java.util.Map;
 import java.util.Set;
 
+import com.android.internal.org.bouncycastle.asn1.ASN1IA5String;
 import com.android.internal.org.bouncycastle.asn1.ASN1OctetString;
 import com.android.internal.org.bouncycastle.asn1.ASN1Sequence;
-import com.android.internal.org.bouncycastle.asn1.DERIA5String;
 import com.android.internal.org.bouncycastle.asn1.x500.RDN;
 import com.android.internal.org.bouncycastle.asn1.x500.X500Name;
 import com.android.internal.org.bouncycastle.asn1.x500.style.IETFUtils;
@@ -1826,22 +1826,22 @@
     private static String extractHostFromURL(String url)
     {
         // see RFC 1738
-        // remove ':' after protocol, e.g. http:
+        // remove ':' after protocol, e.g. https:
         String sub = url.substring(url.indexOf(':') + 1);
-        // extract host from Common Internet Scheme Syntax, e.g. http://
+        // extract host from Common Internet Scheme Syntax, e.g. https://
         if (sub.indexOf("//") != -1)
         {
             sub = sub.substring(sub.indexOf("//") + 2);
         }
-        // first remove port, e.g. http://test.com:21
+        // first remove port, e.g. https://test.com:21
         if (sub.lastIndexOf(':') != -1)
         {
             sub = sub.substring(0, sub.lastIndexOf(':'));
         }
-        // remove user and password, e.g. http://john:[email protected]
+        // remove user and password, e.g. https://john:[email protected]
         sub = sub.substring(sub.indexOf(':') + 1);
         sub = sub.substring(sub.indexOf('@') + 1);
-        // remove local parts, e.g. http://test.com/bla
+        // remove local parts, e.g. https://test.com/bla
         if (sub.indexOf('/') != -1)
         {
             sub = sub.substring(0, sub.indexOf('/'));
@@ -1851,7 +1851,7 @@
 
     private String extractNameAsString(GeneralName name)
     {
-        return DERIA5String.getInstance(name.getName()).getString();
+        return ASN1IA5String.getInstance(name.getName()).getString();
     }
 
     /**
@@ -2080,6 +2080,7 @@
             temp.append(":");
             try
             {
+                // -DM Hex.toHexString
                 temp.append(Hex.toHexString(name.getValue().toASN1Primitive().getEncoded()));
             }
             catch (IOException e)
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/PolicyQualifierInfo.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/PolicyQualifierInfo.java
index 8b3fda3..e51d504 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/PolicyQualifierInfo.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/PolicyQualifierInfo.java
@@ -116,4 +116,9 @@
 
       return new DERSequence(dev);
    }
+
+    public String toString()
+    {
+        return "PolicyQualifierInfo[" + policyQualifierId + ", " + qualifier + "]";
+    }
 }
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/ReasonFlags.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/ReasonFlags.java
index ecb4416..f73a82e 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/ReasonFlags.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/ReasonFlags.java
@@ -1,6 +1,7 @@
 /* GENERATED SOURCE. DO NOT MODIFY. */
 package com.android.internal.org.bouncycastle.asn1.x509;
 
+import com.android.internal.org.bouncycastle.asn1.ASN1BitString;
 import com.android.internal.org.bouncycastle.asn1.DERBitString;
 
 /**
@@ -80,7 +81,7 @@
     }
 
     public ReasonFlags(
-        DERBitString reasons)
+        ASN1BitString reasons)
     {
         super(reasons.getBytes(), reasons.getPadBits());
     }
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/SubjectAltPublicKeyInfo.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/SubjectAltPublicKeyInfo.java
new file mode 100644
index 0000000..5c66d0d
--- /dev/null
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/SubjectAltPublicKeyInfo.java
@@ -0,0 +1,110 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.asn1.x509;
+
+import com.android.internal.org.bouncycastle.asn1.ASN1BitString;
+import com.android.internal.org.bouncycastle.asn1.ASN1EncodableVector;
+import com.android.internal.org.bouncycastle.asn1.ASN1Object;
+import com.android.internal.org.bouncycastle.asn1.ASN1Primitive;
+import com.android.internal.org.bouncycastle.asn1.ASN1Sequence;
+import com.android.internal.org.bouncycastle.asn1.ASN1TaggedObject;
+import com.android.internal.org.bouncycastle.asn1.DERSequence;
+
+/**
+ * X.509 Section 9.8.2.
+ * <br/>
+ * This public-key certificate extension, when present, shall contain the subject’s alternative public key information
+ * <pre>
+ * subjectAltPublicKeyInfo EXTENSION ::= {
+ *      SYNTAX SubjectAltPublicKeyInfo
+ *      IDENTIFIED BY id-ce-subjectAltPublicKeyInfo }
+ *
+ * SubjectAltPublicKeyInfo ::= SEQUENCE {
+ *     algorithm AlgorithmIdentifier{{SupportedAlgorithms}},
+ *     subjectAltPublicKey BIT STRING }
+ * </pre>
+ * The SubjectAltPublicKeyInfo data type has the following components:
+ * <ul>
+ * <li>the algorithm subcomponent, which shall hold the algorithm that this public key is an instance of</li>
+ * <li>the subjectAltPublicKey subcomponent, which shall hold the alternative public key</li>
+ * </ul>
+ * This extension may be flagged as critical or as non-critical.
+ * <br/>
+ * NOTE – It is recommended that it be flagged as non-critical. Flagging it as critical would require relying parties to understand this
+ * extension and the alternative public-key algorithm.
+ * @hide This class is not part of the Android public SDK API
+ */
+public class SubjectAltPublicKeyInfo
+    extends ASN1Object
+{
+    private AlgorithmIdentifier algorithm;
+    private ASN1BitString subjectAltPublicKey;
+
+    public static SubjectAltPublicKeyInfo getInstance(
+        ASN1TaggedObject obj,
+        boolean          explicit)
+    {
+        return getInstance(ASN1Sequence.getInstance(obj, explicit));
+    }
+
+    public static SubjectAltPublicKeyInfo getInstance(
+        Object obj)
+    {
+        if (obj instanceof SubjectAltPublicKeyInfo)
+        {
+            return (SubjectAltPublicKeyInfo)obj;
+        }
+        else if (obj != null)
+        {
+            return new SubjectAltPublicKeyInfo(ASN1Sequence.getInstance(obj));
+        }
+
+        return null;
+    }
+
+    public static SubjectAltPublicKeyInfo fromExtensions(Extensions extensions)
+    {
+        return getInstance(Extensions.getExtensionParsedValue(extensions, Extension.subjectAltPublicKeyInfo));
+    }
+
+    private SubjectAltPublicKeyInfo(ASN1Sequence s)
+    {
+        if (s.size() != 2)
+        {
+            throw new IllegalArgumentException("extension should contain only 2 elements");
+        }
+        algorithm = AlgorithmIdentifier.getInstance(s.getObjectAt(0));
+        subjectAltPublicKey = ASN1BitString.getInstance(s.getObjectAt(1));
+    }
+
+    public SubjectAltPublicKeyInfo(AlgorithmIdentifier algorithm, ASN1BitString subjectAltPublicKey)
+    {
+        this.algorithm = algorithm;
+        this.subjectAltPublicKey = subjectAltPublicKey;
+    }
+
+    public SubjectAltPublicKeyInfo(SubjectPublicKeyInfo subjectPublicKeyInfo)
+    {
+        this.algorithm = subjectPublicKeyInfo.getAlgorithm();
+        this.subjectAltPublicKey = subjectPublicKeyInfo.getPublicKeyData();
+    }
+
+    public AlgorithmIdentifier getAlgorithm()
+    {
+        return algorithm;
+    }
+
+    public ASN1BitString getSubjectAltPublicKey()
+    {
+        return subjectAltPublicKey;
+    }
+
+    public ASN1Primitive toASN1Primitive()
+    {
+        ASN1EncodableVector v = new ASN1EncodableVector();
+
+        v.add(algorithm);
+        v.add(subjectAltPublicKey);
+
+        return new DERSequence(v);
+    }
+}
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/SubjectPublicKeyInfo.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/SubjectPublicKeyInfo.java
index d26ac7b..8d97bd2 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/SubjectPublicKeyInfo.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/SubjectPublicKeyInfo.java
@@ -4,6 +4,7 @@
 import java.io.IOException;
 import java.util.Enumeration;
 
+import com.android.internal.org.bouncycastle.asn1.ASN1BitString;
 import com.android.internal.org.bouncycastle.asn1.ASN1Encodable;
 import com.android.internal.org.bouncycastle.asn1.ASN1EncodableVector;
 import com.android.internal.org.bouncycastle.asn1.ASN1Object;
@@ -24,7 +25,7 @@
     extends ASN1Object
 {
     private AlgorithmIdentifier     algId;
-    private DERBitString            keyData;
+    private ASN1BitString           keyData;
 
     public static SubjectPublicKeyInfo getInstance(
         ASN1TaggedObject obj,
@@ -50,6 +51,14 @@
 
     public SubjectPublicKeyInfo(
         AlgorithmIdentifier algId,
+        ASN1BitString publicKey)
+    {
+        this.keyData = publicKey;
+        this.algId = algId;
+    }
+
+    public SubjectPublicKeyInfo(
+        AlgorithmIdentifier algId,
         ASN1Encodable       publicKey)
         throws IOException
     {
@@ -77,10 +86,10 @@
                     + seq.size());
         }
 
-        Enumeration         e = seq.getObjects();
+        Enumeration e = seq.getObjects();
 
         this.algId = AlgorithmIdentifier.getInstance(e.nextElement());
-        this.keyData = DERBitString.getInstance(e.nextElement());
+        this.keyData = ASN1BitString.getInstance(e.nextElement());
     }
 
     public AlgorithmIdentifier getAlgorithm()
@@ -131,7 +140,7 @@
      *
      * @return the public key as the raw bit string...
      */
-    public DERBitString getPublicKeyData()
+    public ASN1BitString getPublicKeyData()
     {
         return keyData;
     }
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/TBSCertList.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/TBSCertList.java
index 49e763f..bb95f43 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/TBSCertList.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/TBSCertList.java
@@ -107,7 +107,7 @@
         }
     }
 
-    private class RevokedCertificatesEnumeration
+    private static class RevokedCertificatesEnumeration
         implements Enumeration
     {
         private final Enumeration en;
@@ -128,7 +128,7 @@
         }
     }
 
-    private class EmptyEnumeration
+    private static class EmptyEnumeration
         implements Enumeration
     {
         public boolean hasMoreElements()
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/TBSCertificate.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/TBSCertificate.java
index 74e2827..0c19019 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/TBSCertificate.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/TBSCertificate.java
@@ -1,19 +1,16 @@
 /* GENERATED SOURCE. DO NOT MODIFY. */
 package com.android.internal.org.bouncycastle.asn1.x509;
 
-import java.math.BigInteger;
-
+import com.android.internal.org.bouncycastle.asn1.ASN1BitString;
 import com.android.internal.org.bouncycastle.asn1.ASN1EncodableVector;
 import com.android.internal.org.bouncycastle.asn1.ASN1Integer;
 import com.android.internal.org.bouncycastle.asn1.ASN1Object;
 import com.android.internal.org.bouncycastle.asn1.ASN1Primitive;
 import com.android.internal.org.bouncycastle.asn1.ASN1Sequence;
 import com.android.internal.org.bouncycastle.asn1.ASN1TaggedObject;
-import com.android.internal.org.bouncycastle.asn1.DERBitString;
 import com.android.internal.org.bouncycastle.asn1.DERSequence;
 import com.android.internal.org.bouncycastle.asn1.DERTaggedObject;
 import com.android.internal.org.bouncycastle.asn1.x500.X500Name;
-import com.android.internal.org.bouncycastle.util.BigIntegers;
 import com.android.internal.org.bouncycastle.util.Properties;
 
 /**
@@ -49,8 +46,8 @@
     Time                    startDate, endDate;
     X500Name                subject;
     SubjectPublicKeyInfo    subjectPublicKeyInfo;
-    DERBitString            issuerUniqueId;
-    DERBitString            subjectUniqueId;
+    ASN1BitString           issuerUniqueId;
+    ASN1BitString           subjectUniqueId;
     Extensions              extensions;
 
     public static TBSCertificate getInstance(
@@ -98,15 +95,15 @@
         boolean isV1 = false;
         boolean isV2 = false;
  
-        if (version.hasValue(BigInteger.valueOf(0)))
+        if (version.hasValue(0))
         {
             isV1 = true;
         }
-        else if (version.hasValue(BigInteger.valueOf(1)))
+        else if (version.hasValue(1))
         {
             isV2 = true;
         }
-        else if (!version.hasValue(BigInteger.valueOf(2)))
+        else if (!version.hasValue(2))
         {
             throw new IllegalArgumentException("version number not recognised");
         }
@@ -144,10 +141,10 @@
             switch (extra.getTagNo())
             {
             case 1:
-                issuerUniqueId = DERBitString.getInstance(extra, false);
+                issuerUniqueId = ASN1BitString.getInstance(extra, false);
                 break;
             case 2:
-                subjectUniqueId = DERBitString.getInstance(extra, false);
+                subjectUniqueId = ASN1BitString.getInstance(extra, false);
                 break;
             case 3:
                 if (isV2)
@@ -208,12 +205,12 @@
         return subjectPublicKeyInfo;
     }
 
-    public DERBitString getIssuerUniqueId()
+    public ASN1BitString getIssuerUniqueId()
     {
         return issuerUniqueId;
     }
 
-    public DERBitString getSubjectUniqueId()
+    public ASN1BitString getSubjectUniqueId()
     {
         return subjectUniqueId;
     }
@@ -240,7 +237,7 @@
         ASN1EncodableVector v = new ASN1EncodableVector();
 
         // DEFAULT Zero
-        if (!version.hasValue(BigIntegers.ZERO))
+        if (!version.hasValue(0))
         {
             v.add(new DERTaggedObject(true, 0, version));
         }
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/TBSCertificateStructure.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/TBSCertificateStructure.java
index 5ae69df..c010192 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/TBSCertificateStructure.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/TBSCertificateStructure.java
@@ -1,12 +1,12 @@
 /* GENERATED SOURCE. DO NOT MODIFY. */
 package com.android.internal.org.bouncycastle.asn1.x509;
 
+import com.android.internal.org.bouncycastle.asn1.ASN1BitString;
 import com.android.internal.org.bouncycastle.asn1.ASN1Integer;
 import com.android.internal.org.bouncycastle.asn1.ASN1Object;
 import com.android.internal.org.bouncycastle.asn1.ASN1Primitive;
 import com.android.internal.org.bouncycastle.asn1.ASN1Sequence;
 import com.android.internal.org.bouncycastle.asn1.ASN1TaggedObject;
-import com.android.internal.org.bouncycastle.asn1.DERBitString;
 import com.android.internal.org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
 import com.android.internal.org.bouncycastle.asn1.x500.X500Name;
 
@@ -45,8 +45,8 @@
     Time                    startDate, endDate;
     X500Name                subject;
     SubjectPublicKeyInfo    subjectPublicKeyInfo;
-    DERBitString            issuerUniqueId;
-    DERBitString            subjectUniqueId;
+    ASN1BitString           issuerUniqueId;
+    ASN1BitString           subjectUniqueId;
     X509Extensions          extensions;
 
     public static TBSCertificateStructure getInstance(
@@ -118,10 +118,10 @@
             switch (extra.getTagNo())
             {
             case 1:
-                issuerUniqueId = DERBitString.getInstance(extra, false);
+                issuerUniqueId = ASN1BitString.getInstance(extra, false);
                 break;
             case 2:
-                subjectUniqueId = DERBitString.getInstance(extra, false);
+                subjectUniqueId = ASN1BitString.getInstance(extra, false);
                 break;
             case 3:
                 extensions = X509Extensions.getInstance(extra);
@@ -174,12 +174,12 @@
         return subjectPublicKeyInfo;
     }
 
-    public DERBitString getIssuerUniqueId()
+    public ASN1BitString getIssuerUniqueId()
     {
         return issuerUniqueId;
     }
 
-    public DERBitString getSubjectUniqueId()
+    public ASN1BitString getSubjectUniqueId()
     {
         return subjectUniqueId;
     }
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/Time.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/Time.java
index eb98713..35d5c44 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/Time.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/Time.java
@@ -17,6 +17,7 @@
 import com.android.internal.org.bouncycastle.asn1.ASN1UTCTime;
 import com.android.internal.org.bouncycastle.asn1.DERGeneralizedTime;
 import com.android.internal.org.bouncycastle.asn1.DERUTCTime;
+import com.android.internal.org.bouncycastle.asn1.LocaleUtil;
 
 /**
  * @hide This class is not part of the Android public SDK API
@@ -31,7 +32,12 @@
         ASN1TaggedObject obj,
         boolean          explicit)
     {
-        return getInstance(obj.getObject()); // must be explicitly tagged
+        if (!explicit)
+        {
+            throw new IllegalArgumentException("choice item must be explicitly tagged");
+        }
+
+        return getInstance(obj.getExplicitBaseObject());
     }
 
     public Time(
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/V2Form.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/V2Form.java
index 110b930..8d7d81f 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/V2Form.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/V2Form.java
@@ -71,10 +71,7 @@
         this.objectDigestInfo = objectDigestInfo;
     }
 
-    /**
-     * @deprecated use getInstance().
-     */
-    public V2Form(
+    private V2Form(
         ASN1Sequence seq)
     {
         if (seq.size() > 3)
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/V3TBSCertificateGenerator.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/V3TBSCertificateGenerator.java
index 2123bdc..ef6f55b 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/V3TBSCertificateGenerator.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/V3TBSCertificateGenerator.java
@@ -3,6 +3,8 @@
 
 import com.android.internal.org.bouncycastle.asn1.ASN1EncodableVector;
 import com.android.internal.org.bouncycastle.asn1.ASN1Integer;
+import com.android.internal.org.bouncycastle.asn1.ASN1Object;
+import com.android.internal.org.bouncycastle.asn1.ASN1Sequence;
 import com.android.internal.org.bouncycastle.asn1.ASN1UTCTime;
 import com.android.internal.org.bouncycastle.asn1.DERBitString;
 import com.android.internal.org.bouncycastle.asn1.DERSequence;
@@ -157,20 +159,34 @@
         }
     }
 
-    public TBSCertificate generateTBSCertificate()
+    public ASN1Sequence generatePreTBSCertificate()
     {
-        if ((serialNumber == null) || (signature == null)
+        if (signature != null)
+        {
+            throw new IllegalStateException("signature field should not be set in PreTBSCertificate");
+        }
+        if ((serialNumber == null)
             || (issuer == null) || (startDate == null) || (endDate == null)
             || (subject == null && !altNamePresentAndCritical) || (subjectPublicKeyInfo == null))
         {
             throw new IllegalStateException("not all mandatory fields set in V3 TBScertificate generator");
         }
 
+        return generateTBSStructure();
+    }
+
+    private ASN1Sequence generateTBSStructure()
+    {
         ASN1EncodableVector v = new ASN1EncodableVector(10);
 
         v.add(version);
         v.add(serialNumber);
-        v.add(signature);
+
+        if (signature != null)
+        {
+            v.add(signature);
+        }
+        
         v.add(issuer);
 
         //
@@ -210,6 +226,18 @@
             v.add(new DERTaggedObject(true, 3, extensions));
         }
 
-        return TBSCertificate.getInstance(new DERSequence(v));
+        return new DERSequence(v);
+    }
+
+    public TBSCertificate generateTBSCertificate()
+    {
+        if ((serialNumber == null) || (signature == null)
+            || (issuer == null) || (startDate == null) || (endDate == null)
+            || (subject == null && !altNamePresentAndCritical) || (subjectPublicKeyInfo == null))
+        {
+            throw new IllegalStateException("not all mandatory fields set in V3 TBScertificate generator");
+        }
+
+        return TBSCertificate.getInstance(generateTBSStructure());
     }
 }
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/X509CertificateStructure.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/X509CertificateStructure.java
index 34afe36..cffec83 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/X509CertificateStructure.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/X509CertificateStructure.java
@@ -1,12 +1,12 @@
 /* GENERATED SOURCE. DO NOT MODIFY. */
 package com.android.internal.org.bouncycastle.asn1.x509;
 
+import com.android.internal.org.bouncycastle.asn1.ASN1BitString;
 import com.android.internal.org.bouncycastle.asn1.ASN1Integer;
 import com.android.internal.org.bouncycastle.asn1.ASN1Object;
 import com.android.internal.org.bouncycastle.asn1.ASN1Primitive;
 import com.android.internal.org.bouncycastle.asn1.ASN1Sequence;
 import com.android.internal.org.bouncycastle.asn1.ASN1TaggedObject;
-import com.android.internal.org.bouncycastle.asn1.DERBitString;
 import com.android.internal.org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
 import com.android.internal.org.bouncycastle.asn1.x500.X500Name;
 
@@ -29,7 +29,7 @@
     ASN1Sequence  seq;
     TBSCertificateStructure tbsCert;
     AlgorithmIdentifier     sigAlgId;
-    DERBitString            sig;
+    ASN1BitString sig;
 
     public static X509CertificateStructure getInstance(
         ASN1TaggedObject obj,
@@ -66,7 +66,7 @@
             tbsCert = TBSCertificateStructure.getInstance(seq.getObjectAt(0));
             sigAlgId = AlgorithmIdentifier.getInstance(seq.getObjectAt(1));
 
-            sig = DERBitString.getInstance(seq.getObjectAt(2));
+            sig = ASN1BitString.getInstance(seq.getObjectAt(2));
         }
         else
         {
@@ -119,7 +119,7 @@
         return sigAlgId;
     }
 
-    public DERBitString getSignature()
+    public ASN1BitString getSignature()
     {
         return sig;
     }
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/X509DefaultEntryConverter.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/X509DefaultEntryConverter.java
index 582ebdd..e949d3e 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/X509DefaultEntryConverter.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/X509DefaultEntryConverter.java
@@ -9,6 +9,7 @@
 import com.android.internal.org.bouncycastle.asn1.DERIA5String;
 import com.android.internal.org.bouncycastle.asn1.DERPrintableString;
 import com.android.internal.org.bouncycastle.asn1.DERUTF8String;
+import com.android.internal.org.bouncycastle.asn1.x500.style.BCStyle;
 
 /**
  * The default converter for X509 DN entries when going from their
@@ -47,16 +48,16 @@
             {
                 value = value.substring(1);
             }
-            if (oid.equals(X509Name.EmailAddress) || oid.equals(X509Name.DC))
+            if (oid.equals(BCStyle.EmailAddress) || oid.equals(BCStyle.DC))
             {
                 return new DERIA5String(value);
             }
-            else if (oid.equals(X509Name.DATE_OF_BIRTH))  // accept time string as well as # (for compatibility)
+            else if (oid.equals(BCStyle.DATE_OF_BIRTH))  // accept time string as well as # (for compatibility)
             {
                 return new DERGeneralizedTime(value);
             }
-            else if (oid.equals(X509Name.C) || oid.equals(X509Name.SN) || oid.equals(X509Name.DN_QUALIFIER)
-                || oid.equals(X509Name.TELEPHONE_NUMBER))
+            else if (oid.equals(BCStyle.C) || oid.equals(BCStyle.SERIALNUMBER) || oid.equals(BCStyle.DN_QUALIFIER)
+                || oid.equals(BCStyle.TELEPHONE_NUMBER))
             {
                  return new DERPrintableString(value);
             }
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/X509Extensions.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/X509Extensions.java
index 2f3fbef..90adcc4 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/X509Extensions.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/X509Extensions.java
@@ -13,10 +13,11 @@
 import com.android.internal.org.bouncycastle.asn1.ASN1Primitive;
 import com.android.internal.org.bouncycastle.asn1.ASN1Sequence;
 import com.android.internal.org.bouncycastle.asn1.ASN1TaggedObject;
+import com.android.internal.org.bouncycastle.asn1.BERTags;
 import com.android.internal.org.bouncycastle.asn1.DERSequence;
 
 /**
- * @deprecated use {@link Extensions}
+ * @deprecated use {@link Extension} and  {@link Extensions}
  * @hide This class is not part of the Android public SDK API
  */
 public class X509Extensions
@@ -238,7 +239,9 @@
 
         if (obj instanceof ASN1TaggedObject)
         {
-            return getInstance(((ASN1TaggedObject)obj).getObject());
+            ASN1TaggedObject taggedObject = ASN1TaggedObject.getInstance(obj, BERTags.CONTEXT_SPECIFIC);
+
+            return getInstance(taggedObject.getBaseObject().toASN1Primitive());
         }
 
         throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/X509Name.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/X509Name.java
index 9065239..270577e 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/X509Name.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/X509Name.java
@@ -16,9 +16,9 @@
 import com.android.internal.org.bouncycastle.asn1.ASN1Set;
 import com.android.internal.org.bouncycastle.asn1.ASN1String;
 import com.android.internal.org.bouncycastle.asn1.ASN1TaggedObject;
+import com.android.internal.org.bouncycastle.asn1.ASN1UniversalString;
 import com.android.internal.org.bouncycastle.asn1.DERSequence;
 import com.android.internal.org.bouncycastle.asn1.DERSet;
-import com.android.internal.org.bouncycastle.asn1.DERUniversalString;
 import com.android.internal.org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
 import com.android.internal.org.bouncycastle.asn1.x500.X500Name;
 import com.android.internal.org.bouncycastle.util.Strings;
@@ -427,7 +427,7 @@
                    ordering.addElement(ASN1ObjectIdentifier.getInstance(s.getObjectAt(0)));
                    
                    ASN1Encodable value = s.getObjectAt(1);
-                   if (value instanceof ASN1String && !(value instanceof DERUniversalString))
+                   if (value instanceof ASN1String && !(value instanceof ASN1UniversalString))
                    {
                        String v = ((ASN1String)value).getString();
                        if (v.length() > 0 && v.charAt(0) == '#')
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/X509NameEntryConverter.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/X509NameEntryConverter.java
index 9329e67..80b071b 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/X509NameEntryConverter.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/X509NameEntryConverter.java
@@ -5,7 +5,7 @@
 
 import com.android.internal.org.bouncycastle.asn1.ASN1ObjectIdentifier;
 import com.android.internal.org.bouncycastle.asn1.ASN1Primitive;
-import com.android.internal.org.bouncycastle.asn1.DERPrintableString;
+import com.android.internal.org.bouncycastle.asn1.ASN1PrintableString;
 import com.android.internal.org.bouncycastle.util.encoders.Hex;
 
 /**
@@ -73,7 +73,7 @@
     protected boolean canBePrintable(
         String  str)
     {
-        return DERPrintableString.isPrintableString(str);
+        return ASN1PrintableString.isPrintableString(str);
     }
     
     /**
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/X509ObjectIdentifiers.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/X509ObjectIdentifiers.java
index 89e8a56..80c6b6d 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/X509ObjectIdentifiers.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x509/X509ObjectIdentifiers.java
@@ -111,4 +111,11 @@
     static final ASN1ObjectIdentifier ocspAccessMethod = id_ad_ocsp;
     /** OID for crl uri in AuthorityInformationAccess extension */
     static final ASN1ObjectIdentifier crlAccessMethod  = id_ad_caIssuers;
+
+
+    /**
+     *  id-PasswordBasedMac OBJECT IDENTIFIER ::= { iso(1) member-body(2)
+     *          us(840) nt(113533) nsn(7) algorithms(66) 13 }
+     */
+    static final ASN1ObjectIdentifier id_PasswordBasedMac = new ASN1ObjectIdentifier("1.2.840.113533.7.66.13");
 }
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x9/DHValidationParms.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x9/DHValidationParms.java
index a2d4c8a..5ca0dd6 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x9/DHValidationParms.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x9/DHValidationParms.java
@@ -1,13 +1,13 @@
 /* GENERATED SOURCE. DO NOT MODIFY. */
 package com.android.internal.org.bouncycastle.asn1.x9;
 
+import com.android.internal.org.bouncycastle.asn1.ASN1BitString;
 import com.android.internal.org.bouncycastle.asn1.ASN1EncodableVector;
 import com.android.internal.org.bouncycastle.asn1.ASN1Integer;
 import com.android.internal.org.bouncycastle.asn1.ASN1Object;
 import com.android.internal.org.bouncycastle.asn1.ASN1Primitive;
 import com.android.internal.org.bouncycastle.asn1.ASN1Sequence;
 import com.android.internal.org.bouncycastle.asn1.ASN1TaggedObject;
-import com.android.internal.org.bouncycastle.asn1.DERBitString;
 import com.android.internal.org.bouncycastle.asn1.DERSequence;
 
 /**
@@ -16,7 +16,7 @@
  */
 public class DHValidationParms extends ASN1Object
 {
-    private DERBitString seed;
+    private ASN1BitString seed;
     private ASN1Integer pgenCounter;
 
     public static DHValidationParms getInstance(ASN1TaggedObject obj, boolean explicit)
@@ -38,7 +38,7 @@
         return null;
     }
 
-    public DHValidationParms(DERBitString seed, ASN1Integer pgenCounter)
+    public DHValidationParms(ASN1BitString seed, ASN1Integer pgenCounter)
     {
         if (seed == null)
         {
@@ -60,11 +60,11 @@
             throw new IllegalArgumentException("Bad sequence size: " + seq.size());
         }
 
-        this.seed = DERBitString.getInstance(seq.getObjectAt(0));
+        this.seed = ASN1BitString.getInstance(seq.getObjectAt(0));
         this.pgenCounter = ASN1Integer.getInstance(seq.getObjectAt(1));
     }
 
-    public DERBitString getSeed()
+    public ASN1BitString getSeed()
     {
         return this.seed;
     }
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x9/ECNamedCurveTable.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x9/ECNamedCurveTable.java
index 08ada94..848834c 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x9/ECNamedCurveTable.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x9/ECNamedCurveTable.java
@@ -71,6 +71,47 @@
         return ecP;
     }
 
+    public static X9ECParametersHolder getByNameLazy(String name)
+    {
+        X9ECParametersHolder holder = X962NamedCurves.getByNameLazy(name);
+
+        if (null == holder)
+        {
+            holder = SECNamedCurves.getByNameLazy(name);
+        }
+
+        if (null == holder)
+        {
+            holder = NISTNamedCurves.getByNameLazy(name);
+        }
+
+        // BEGIN Android-removed: Unsupported curves
+        /*
+        if (null == holder)
+        {
+            holder = TeleTrusTNamedCurves.getByNameLazy(name);
+        }
+
+        if (null == holder)
+        {
+            holder = ANSSINamedCurves.getByNameLazy(name);
+        }
+
+        if (null == holder)
+        {
+            holder = ECGOST3410NamedCurves.getByNameLazy(name);
+        }
+
+        if (null == holder)
+        {
+            holder = GMNamedCurves.getByNameLazy(name);
+        }
+        */
+        // END Android-removed: Unsupported curves
+
+        return holder;
+    }
+
     /**
      * return the object identifier signified by the passed in name. Null
      * if there is no object identifier associated with name.
@@ -224,6 +265,44 @@
         return ecP;
     }
 
+    public static X9ECParametersHolder getByOIDLazy(ASN1ObjectIdentifier oid)
+    {
+        X9ECParametersHolder holder = X962NamedCurves.getByOIDLazy(oid);
+
+        if (null == holder)
+        {
+            holder = SECNamedCurves.getByOIDLazy(oid);
+        }
+
+        // NOTE: All the NIST curves are currently from SEC, so no point in redundant OID lookup
+
+        // BEGIN Android-removed: Unsupported curves
+        /*
+        if (null == holder)
+        {
+            holder = TeleTrusTNamedCurves.getByOIDLazy(oid);
+        }
+
+        if (null == holder)
+        {
+            holder = ANSSINamedCurves.getByOIDLazy(oid);
+        }
+
+        if (null == holder)
+        {
+            holder = ECGOST3410NamedCurves.getByOIDLazy(oid);
+        }
+
+        if (null == holder)
+        {
+            holder = GMNamedCurves.getByOIDLazy(oid);
+        }
+        */
+        // END Android-removed: Unsupported curves
+
+        return holder;
+    }
+
     /**
      * return an enumeration of the names of the available curves.
      *
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x9/ValidationParams.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x9/ValidationParams.java
index 0e2329c..29505b2 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x9/ValidationParams.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x9/ValidationParams.java
@@ -3,6 +3,7 @@
 
 import java.math.BigInteger;
 
+import com.android.internal.org.bouncycastle.asn1.ASN1BitString;
 import com.android.internal.org.bouncycastle.asn1.ASN1EncodableVector;
 import com.android.internal.org.bouncycastle.asn1.ASN1Integer;
 import com.android.internal.org.bouncycastle.asn1.ASN1Object;
@@ -25,7 +26,7 @@
 public class ValidationParams
     extends ASN1Object
 {
-    private DERBitString seed;
+    private ASN1BitString seed;
     private ASN1Integer pgenCounter;
 
     public static ValidationParams getInstance(ASN1TaggedObject obj, boolean explicit)
@@ -80,7 +81,7 @@
             throw new IllegalArgumentException("Bad sequence size: " + seq.size());
         }
 
-        this.seed = DERBitString.getInstance(seq.getObjectAt(0));
+        this.seed = ASN1BitString.getInstance(seq.getObjectAt(0));
         this.pgenCounter = ASN1Integer.getInstance(seq.getObjectAt(1));
     }
 
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x9/X962NamedCurves.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x9/X962NamedCurves.java
index 7b82cc0..b6046ea 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x9/X962NamedCurves.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x9/X962NamedCurves.java
@@ -37,141 +37,183 @@
 
     static X9ECParametersHolder prime192v1 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             BigInteger n = fromHex("ffffffffffffffffffffffff99def836146bc9b1b4d22831");
             BigInteger h = BigInteger.valueOf(1);
 
-            ECCurve curve = configureCurve(new ECCurve.Fp(
+            return configureCurve(new ECCurve.Fp(
                 fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFF"),
                 fromHex("fffffffffffffffffffffffffffffffefffffffffffffffc"),
                 fromHex("64210519e59c80e70fa7e9ab72243049feb8deecc146b9b1"),
-                n, h));
+                n, h, true));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = Hex.decodeStrict("3045AE6FC8422f64ED579528D38120EAE12196D5");
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "03188da80eb03090f67cbf20eb43a18800f4ff0afd82ff1012");
 
-            return new X9ECParameters(curve, G, n, h, Hex.decodeStrict("3045AE6FC8422f64ED579528D38120EAE12196D5"));
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
     static X9ECParametersHolder prime192v2 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             BigInteger n = fromHex("fffffffffffffffffffffffe5fb1a724dc80418648d8dd31");
             BigInteger h = BigInteger.valueOf(1);
 
-            ECCurve curve = configureCurve(new ECCurve.Fp(
+            return configureCurve(new ECCurve.Fp(
                 fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFF"),
                 fromHex("fffffffffffffffffffffffffffffffefffffffffffffffc"),
                 fromHex("cc22d6dfb95c6b25e49c0d6364a4e5980c393aa21668d953"),
-                n, h));
+                n, h, true));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = Hex.decodeStrict("31a92ee2029fd10d901b113e990710f0d21ac6b6");
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "03eea2bae7e1497842f2de7769cfe9c989c072ad696f48034a");
 
-            return new X9ECParameters(curve, G, n, h, Hex.decodeStrict("31a92ee2029fd10d901b113e990710f0d21ac6b6"));
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
     static X9ECParametersHolder prime192v3 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             BigInteger n = fromHex("ffffffffffffffffffffffff7a62d031c83f4294f640ec13");
             BigInteger h = BigInteger.valueOf(1);
 
-            ECCurve curve = configureCurve(new ECCurve.Fp(
+            return configureCurve(new ECCurve.Fp(
                 fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFF"),
                 fromHex("fffffffffffffffffffffffffffffffefffffffffffffffc"),
                 fromHex("22123dc2395a05caa7423daeccc94760a7d462256bd56916"),
-                n, h));
+                n, h, true));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = Hex.decodeStrict("c469684435deb378c4b65ca9591e2a5763059a2e");
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "027d29778100c65a1da1783716588dce2b8b4aee8e228f1896");
 
-            return new X9ECParameters(curve, G, n, h, Hex.decodeStrict("c469684435deb378c4b65ca9591e2a5763059a2e"));
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
     static X9ECParametersHolder prime239v1 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             BigInteger n = fromHex("7fffffffffffffffffffffff7fffff9e5e9a9f5d9071fbd1522688909d0b");
             BigInteger h = BigInteger.valueOf(1);
 
-            ECCurve curve = configureCurve(new ECCurve.Fp(
+            return configureCurve(new ECCurve.Fp(
                 new BigInteger("883423532389192164791648750360308885314476597252960362792450860609699839"),
                 fromHex("7fffffffffffffffffffffff7fffffffffff8000000000007ffffffffffc"),
                 fromHex("6b016c3bdcf18941d0d654921475ca71a9db2fb27d1d37796185c2942c0a"),
-                n, h));
+                n, h, true));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = Hex.decodeStrict("e43bb460f0b80cc0c0b075798e948060f8321b7d");
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "020ffa963cdca8816ccc33b8642bedf905c3d358573d3f27fbbd3b3cb9aaaf");
 
-            return new X9ECParameters(curve, G, n, h, Hex.decodeStrict("e43bb460f0b80cc0c0b075798e948060f8321b7d"));
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
     static X9ECParametersHolder prime239v2 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             BigInteger n = fromHex("7fffffffffffffffffffffff800000cfa7e8594377d414c03821bc582063");
             BigInteger h = BigInteger.valueOf(1);
 
-            ECCurve curve = configureCurve(new ECCurve.Fp(
+            return configureCurve(new ECCurve.Fp(
                 new BigInteger("883423532389192164791648750360308885314476597252960362792450860609699839"),
                 fromHex("7fffffffffffffffffffffff7fffffffffff8000000000007ffffffffffc"),
                 fromHex("617fab6832576cbbfed50d99f0249c3fee58b94ba0038c7ae84c8c832f2c"),
-                n, h));
+                n, h, true));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = Hex.decodeStrict("e8b4011604095303ca3b8099982be09fcb9ae616");
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "0238af09d98727705120c921bb5e9e26296a3cdcf2f35757a0eafd87b830e7");
 
-            return new X9ECParameters(curve, G, n, h, Hex.decodeStrict("e8b4011604095303ca3b8099982be09fcb9ae616"));
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
     static X9ECParametersHolder prime239v3 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             BigInteger n = fromHex("7fffffffffffffffffffffff7fffff975deb41b3a6057c3c432146526551");
             BigInteger h = BigInteger.valueOf(1);
 
-            ECCurve curve = configureCurve(new ECCurve.Fp(
+            return configureCurve(new ECCurve.Fp(
                 new BigInteger("883423532389192164791648750360308885314476597252960362792450860609699839"),
                 fromHex("7fffffffffffffffffffffff7fffffffffff8000000000007ffffffffffc"),
                 fromHex("255705fa2a306654b1f4cb03d6a750a30c250102d4988717d9ba15ab6d3e"),
-                n, h));
+                n, h, true));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = Hex.decodeStrict("7d7374168ffe3471b60a857686a19475d3bfa2ff");
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "036768ae8e18bb92cfcf005c949aa2c6d94853d0e660bbf854b1c9505fe95a");
 
-            return new X9ECParameters(curve, G, n, h, Hex.decodeStrict("7d7374168ffe3471b60a857686a19475d3bfa2ff"));
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
     static X9ECParametersHolder prime256v1 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             BigInteger n = fromHex("ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551");
             BigInteger h = BigInteger.valueOf(1);
 
-            ECCurve curve = configureCurve(new ECCurve.Fp(
+            return configureCurve(new ECCurve.Fp(
                 new BigInteger("115792089210356248762697446949407573530086143415290314195533631308867097853951"),
                 fromHex("ffffffff00000001000000000000000000000000fffffffffffffffffffffffc"),
                 fromHex("5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b"),
-                n, h));
+                n, h, true));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = Hex.decodeStrict("c49d360886e704936a6678e1139d26b7819f7e90");
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "036b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296");
 
-            return new X9ECParameters(curve, G, n, h, Hex.decodeStrict("c49d360886e704936a6678e1139d26b7819f7e90"));
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
@@ -180,337 +222,433 @@
      */
     static X9ECParametersHolder c2pnb163v1 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             BigInteger n = fromHex("0400000000000000000001E60FC8821CC74DAEAFC1");
             BigInteger h = BigInteger.valueOf(2);
 
-            ECCurve curve = configureCurve(new ECCurve.F2m(
+            return configureCurve(new ECCurve.F2m(
                 163,
                 1, 2, 8,
                 fromHex("072546B5435234A422E0789675F432C89435DE5242"),
                 fromHex("00C9517D06D5240D3CFF38C74B20B6CD4D6F9DD4D9"),
                 n, h));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = Hex.decodeStrict("D2C0FB15760860DEF1EEF4D696E6768756151754");
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "0307AF69989546103D79329FCC3D74880F33BBE803CB");
 
-            return new X9ECParameters(curve, G, n, h, Hex.decodeStrict("D2C0FB15760860DEF1EEF4D696E6768756151754"));
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
     static X9ECParametersHolder c2pnb163v2 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             BigInteger n = fromHex("03FFFFFFFFFFFFFFFFFFFDF64DE1151ADBB78F10A7");
             BigInteger h = BigInteger.valueOf(2);
 
-            ECCurve curve = configureCurve(new ECCurve.F2m(
+            return configureCurve(new ECCurve.F2m(
                 163,
                 1, 2, 8,
                 fromHex("0108B39E77C4B108BED981ED0E890E117C511CF072"),
                 fromHex("0667ACEB38AF4E488C407433FFAE4F1C811638DF20"),
                 n, h));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = null;
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "030024266E4EB5106D0A964D92C4860E2671DB9B6CC5");
 
-            return new X9ECParameters(curve, G, n, h);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
     static X9ECParametersHolder c2pnb163v3 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             BigInteger n = fromHex("03FFFFFFFFFFFFFFFFFFFE1AEE140F110AFF961309");
             BigInteger h = BigInteger.valueOf(2);
 
-            ECCurve curve = configureCurve(new ECCurve.F2m(
+            return configureCurve(new ECCurve.F2m(
                 163,
                 1, 2, 8,
                 fromHex("07A526C63D3E25A256A007699F5447E32AE456B50E"),
                 fromHex("03F7061798EB99E238FD6F1BF95B48FEEB4854252B"),
                 n, h));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = null;
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "0202F9F87B7C574D0BDECF8A22E6524775F98CDEBDCB");
 
-            return new X9ECParameters(curve, G, n, h);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
     static X9ECParametersHolder c2pnb176w1 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             BigInteger n = fromHex("010092537397ECA4F6145799D62B0A19CE06FE26AD");
             BigInteger h = BigInteger.valueOf(0xFF6E);
 
-            ECCurve curve = configureCurve(new ECCurve.F2m(
+            return configureCurve(new ECCurve.F2m(
                 176,
                 1, 2, 43,
                 fromHex("E4E6DB2995065C407D9D39B8D0967B96704BA8E9C90B"),
                 fromHex("5DDA470ABE6414DE8EC133AE28E9BBD7FCEC0AE0FFF2"),
                 n, h));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = null;
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "038D16C2866798B600F9F08BB4A8E860F3298CE04A5798");
 
-            return new X9ECParameters(curve, G, n, h);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
     static X9ECParametersHolder c2tnb191v1 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             BigInteger n = fromHex("40000000000000000000000004A20E90C39067C893BBB9A5");
             BigInteger h = BigInteger.valueOf(2);
 
-            ECCurve curve = configureCurve(new ECCurve.F2m(
+            return configureCurve(new ECCurve.F2m(
                 191,
                 9,
                 fromHex("2866537B676752636A68F56554E12640276B649EF7526267"),
                 fromHex("2E45EF571F00786F67B0081B9495A3D95462F5DE0AA185EC"),
                 n, h));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = Hex.decodeStrict("4E13CA542744D696E67687561517552F279A8C84");
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "0236B3DAF8A23206F9C4F299D7B21A9C369137F2C84AE1AA0D");
 
-            return new X9ECParameters(curve, G, n, h, Hex.decodeStrict("4E13CA542744D696E67687561517552F279A8C84"));
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
     static X9ECParametersHolder c2tnb191v2 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             BigInteger n = fromHex("20000000000000000000000050508CB89F652824E06B8173");
             BigInteger h = BigInteger.valueOf(4);
 
-            ECCurve curve = configureCurve(new ECCurve.F2m(
+            return configureCurve(new ECCurve.F2m(
                 191,
                 9,
                 fromHex("401028774D7777C7B7666D1366EA432071274F89FF01E718"),
                 fromHex("0620048D28BCBD03B6249C99182B7C8CD19700C362C46A01"),
                 n, h));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = null;
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "023809B2B7CC1B28CC5A87926AAD83FD28789E81E2C9E3BF10");
 
-            return new X9ECParameters(curve, G, n, h);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
     static X9ECParametersHolder c2tnb191v3 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             BigInteger n = fromHex("155555555555555555555555610C0B196812BFB6288A3EA3");
             BigInteger h = BigInteger.valueOf(6);
 
-            ECCurve curve = configureCurve(new ECCurve.F2m(
+            return configureCurve(new ECCurve.F2m(
                 191,
                 9,
                 fromHex("6C01074756099122221056911C77D77E77A777E7E7E77FCB"),
                 fromHex("71FE1AF926CF847989EFEF8DB459F66394D90F32AD3F15E8"),
                 n, h));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = null;
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "03375D4CE24FDE434489DE8746E71786015009E66E38A926DD");
 
-            return new X9ECParameters(curve, G, n, h);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
     static X9ECParametersHolder c2pnb208w1 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             BigInteger n = fromHex("0101BAF95C9723C57B6C21DA2EFF2D5ED588BDD5717E212F9D");
             BigInteger h = BigInteger.valueOf(0xFE48);
 
-            ECCurve curve = configureCurve(new ECCurve.F2m(
+            return configureCurve(new ECCurve.F2m(
                 208,
                 1, 2, 83,
                 BigInteger.valueOf(0),
                 fromHex("C8619ED45A62E6212E1160349E2BFA844439FAFC2A3FD1638F9E"),
                 n, h));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = null;
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "0289FDFBE4ABE193DF9559ECF07AC0CE78554E2784EB8C1ED1A57A");
 
-            return new X9ECParameters(curve, G, n, h);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
     static X9ECParametersHolder c2tnb239v1 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             BigInteger n = fromHex("2000000000000000000000000000000F4D42FFE1492A4993F1CAD666E447");
             BigInteger h = BigInteger.valueOf(4);
 
-            ECCurve curve = configureCurve(new ECCurve.F2m(
+            return configureCurve(new ECCurve.F2m(
                 239,
                 36,
                 fromHex("32010857077C5431123A46B808906756F543423E8D27877578125778AC76"),
                 fromHex("790408F2EEDAF392B012EDEFB3392F30F4327C0CA3F31FC383C422AA8C16"),
                 n, h));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = null;
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "0257927098FA932E7C0A96D3FD5B706EF7E5F5C156E16B7E7C86038552E91D");
 
-            return new X9ECParameters(curve, G, n, h);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
     static X9ECParametersHolder c2tnb239v2 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             BigInteger n = fromHex("1555555555555555555555555555553C6F2885259C31E3FCDF154624522D");
             BigInteger h = BigInteger.valueOf(6);
 
-            ECCurve curve = configureCurve(new ECCurve.F2m(
+            return configureCurve(new ECCurve.F2m(
                 239,
                 36,
                 fromHex("4230017757A767FAE42398569B746325D45313AF0766266479B75654E65F"),
                 fromHex("5037EA654196CFF0CD82B2C14A2FCF2E3FF8775285B545722F03EACDB74B"),
                 n, h));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = null;
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "0228F9D04E900069C8DC47A08534FE76D2B900B7D7EF31F5709F200C4CA205");
 
-            return new X9ECParameters(curve, G, n, h);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
     static X9ECParametersHolder c2tnb239v3 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             BigInteger n = fromHex("0CCCCCCCCCCCCCCCCCCCCCCCCCCCCCAC4912D2D9DF903EF9888B8A0E4CFF");
             BigInteger h = BigInteger.valueOf(10);
 
-            ECCurve curve = configureCurve(new ECCurve.F2m(
+            return configureCurve(new ECCurve.F2m(
                 239,
                 36,
                 fromHex("01238774666A67766D6676F778E676B66999176666E687666D8766C66A9F"),
                 fromHex("6A941977BA9F6A435199ACFC51067ED587F519C5ECB541B8E44111DE1D40"),
                 n, h));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = null;
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "0370F6E9D04D289C4E89913CE3530BFDE903977D42B146D539BF1BDE4E9C92");
 
-            return new X9ECParameters(curve, G, n, h);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
     static X9ECParametersHolder c2pnb272w1 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             BigInteger n = fromHex("0100FAF51354E0E39E4892DF6E319C72C8161603FA45AA7B998A167B8F1E629521");
             BigInteger h = BigInteger.valueOf(0xFF06);
 
-            ECCurve curve = configureCurve(new ECCurve.F2m(
+            return configureCurve(new ECCurve.F2m(
                 272,
                 1, 3, 56,
                 fromHex("91A091F03B5FBA4AB2CCF49C4EDD220FB028712D42BE752B2C40094DBACDB586FB20"),
                 fromHex("7167EFC92BB2E3CE7C8AAAFF34E12A9C557003D7C73A6FAF003F99F6CC8482E540F7"),
                 n, h));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = null;
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "026108BABB2CEEBCF787058A056CBE0CFE622D7723A289E08A07AE13EF0D10D171DD8D");
 
-            return new X9ECParameters(curve, G, n, h);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
     static X9ECParametersHolder c2pnb304w1 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             BigInteger n = fromHex("0101D556572AABAC800101D556572AABAC8001022D5C91DD173F8FB561DA6899164443051D");
             BigInteger h = BigInteger.valueOf(0xFE2E);
 
-            ECCurve curve = configureCurve(new ECCurve.F2m(
+            return configureCurve(new ECCurve.F2m(
                 304,
                 1, 2, 11,
                 fromHex("FD0D693149A118F651E6DCE6802085377E5F882D1B510B44160074C1288078365A0396C8E681"),
                 fromHex("BDDB97E555A50A908E43B01C798EA5DAA6788F1EA2794EFCF57166B8C14039601E55827340BE"),
                 n, h));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = null;
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "02197B07845E9BE2D96ADB0F5F3C7F2CFFBD7A3EB8B6FEC35C7FD67F26DDF6285A644F740A2614");
 
-            return new X9ECParameters(curve, G, n, h);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
     static X9ECParametersHolder c2tnb359v1 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             BigInteger n = fromHex("01AF286BCA1AF286BCA1AF286BCA1AF286BCA1AF286BC9FB8F6B85C556892C20A7EB964FE7719E74F490758D3B");
             BigInteger h = BigInteger.valueOf(0x4C);
 
-            ECCurve curve = configureCurve(new ECCurve.F2m(
+            return configureCurve(new ECCurve.F2m(
                 359,
                 68,
                 fromHex("5667676A654B20754F356EA92017D946567C46675556F19556A04616B567D223A5E05656FB549016A96656A557"),
                 fromHex("2472E2D0197C49363F1FE7F5B6DB075D52B6947D135D8CA445805D39BC345626089687742B6329E70680231988"),
                 n, h));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = null;
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "033C258EF3047767E7EDE0F1FDAA79DAEE3841366A132E163ACED4ED2401DF9C6BDCDE98E8E707C07A2239B1B097");
 
-            return new X9ECParameters(curve, G, n, h);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
     static X9ECParametersHolder c2pnb368w1 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             BigInteger n = fromHex("010090512DA9AF72B08349D98A5DD4C7B0532ECA51CE03E2D10F3B7AC579BD87E909AE40A6F131E9CFCE5BD967");
             BigInteger h = BigInteger.valueOf(0xFF70);
 
-            ECCurve curve = configureCurve(new ECCurve.F2m(
+            return configureCurve(new ECCurve.F2m(
                 368,
                 1, 2, 85,
                 fromHex("E0D2EE25095206F5E2A4F9ED229F1F256E79A0E2B455970D8D0D865BD94778C576D62F0AB7519CCD2A1A906AE30D"),
                 fromHex("FC1217D4320A90452C760A58EDCD30C8DD069B3C34453837A34ED50CB54917E1C2112D84D164F444F8F74786046A"),
                 n, h));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = null;
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "021085E2755381DCCCE3C1557AFA10C2F0C0C2825646C5B34A394CBCFA8BC16B22E7E789E927BE216F02E1FB136A5F");
 
-            return new X9ECParameters(curve, G, n, h);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
     static X9ECParametersHolder c2tnb431r1 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
             BigInteger n = fromHex("0340340340340340340340340340340340340340340340340340340323C313FAB50589703B5EC68D3587FEC60D161CC149C1AD4A91");
             BigInteger h = BigInteger.valueOf(0x2760);
 
-            ECCurve curve = configureCurve(new ECCurve.F2m(
+            return configureCurve(new ECCurve.F2m(
                 431,
                 120,
                 fromHex("1A827EF00DD6FC0E234CAF046C6A5D8A85395B236CC4AD2CF32A0CADBDC9DDF620B0EB9906D0957F6C6FEACD615468DF104DE296CD8F"),
                 fromHex("10D9B4A3D9047D8B154359ABFB1B7F5485B04CEB868237DDC9DEDA982A679A5A919B626D4E50A8DD731B107A9962381FB5D807BF2618"),
                 n, h));
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = null;
+            ECCurve curve = getCurve();
 
             X9ECPoint G = configureBasepoint(curve,
                 "02120FC05D3C67A99DE161D2F4092622FECA701BE4F50F4758714E8A87BBF2A658EF8C21E7C5EFE965361F6C2999C0C247B0DBD70CE6B7");
 
-            return new X9ECParameters(curve, G, n, h);
+            return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
         }
     };
 
@@ -553,17 +691,16 @@
         defineCurve("c2tnb431r1", X9ObjectIdentifiers.c2tnb431r1, c2tnb431r1);
     }
 
-    public static X9ECParameters getByName(
-        String name)
+    public static X9ECParameters getByName(String name)
     {
-        ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier)objIds.get(Strings.toLowerCase(name));
+        ASN1ObjectIdentifier oid = getOID(name);
+        return oid == null ? null : getByOID(oid);
+    }
 
-        if (oid != null)
-        {
-            return getByOID(oid);
-        }
-
-        return null;
+    public static X9ECParametersHolder getByNameLazy(String name)
+    {
+        ASN1ObjectIdentifier oid = getOID(name);
+        return oid == null ? null : getByOIDLazy(oid);
     }
 
     /**
@@ -572,17 +709,15 @@
      *
      * @param oid an object identifier representing a named curve, if present.
      */
-    public static X9ECParameters getByOID(
-        ASN1ObjectIdentifier oid)
+    public static X9ECParameters getByOID(ASN1ObjectIdentifier oid)
     {
-        X9ECParametersHolder holder = (X9ECParametersHolder)curves.get(oid);
+        X9ECParametersHolder holder = getByOIDLazy(oid);
+        return holder == null ? null : holder.getParameters();
+    }
 
-        if (holder != null)
-        {
-            return holder.getParameters();
-        }
-
-        return null;
+    public static X9ECParametersHolder getByOIDLazy(ASN1ObjectIdentifier oid)
+    {
+        return (X9ECParametersHolder)curves.get(oid);
     }
 
     /**
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x9/X962Parameters.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x9/X962Parameters.java
index 0780832..d8eac08 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x9/X962Parameters.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x9/X962Parameters.java
@@ -50,7 +50,12 @@
         ASN1TaggedObject obj,
         boolean          explicit)
     {
-        return getInstance(obj.getObject()); // must be explicitly tagged
+        if (!explicit)
+        {
+            throw new IllegalArgumentException("choice item must be explicitly tagged");
+        }
+
+        return getInstance(obj.getExplicitBaseObject());
     }
     
     public X962Parameters(
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x9/X9ECParameters.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x9/X9ECParameters.java
index 58ca88b..edbefdb 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x9/X9ECParameters.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x9/X9ECParameters.java
@@ -38,7 +38,7 @@
         ASN1Sequence  seq)
     {
         if (!(seq.getObjectAt(0) instanceof ASN1Integer)
-            || !((ASN1Integer)seq.getObjectAt(0)).hasValue(ONE))
+            || !((ASN1Integer)seq.getObjectAt(0)).hasValue(1))
         {
             throw new IllegalArgumentException("bad version in X9ECParameters");
         }
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x9/X9ECParametersHolder.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x9/X9ECParametersHolder.java
index 4db5dd1..b53213c 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x9/X9ECParametersHolder.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x9/X9ECParametersHolder.java
@@ -1,14 +1,27 @@
 /* GENERATED SOURCE. DO NOT MODIFY. */
 package com.android.internal.org.bouncycastle.asn1.x9;
 
+import com.android.internal.org.bouncycastle.math.ec.ECCurve;
+
 /**
  * A holding class that allows for X9ECParameters to be lazily constructed.
  * @hide This class is not part of the Android public SDK API
  */
 public abstract class X9ECParametersHolder
 {
+    private ECCurve curve;
     private X9ECParameters params;
 
+    public synchronized ECCurve getCurve()
+    {
+        if (curve == null)
+        {
+            curve = createCurve();
+        }
+
+        return curve;
+    }
+
     public synchronized X9ECParameters getParameters()
     {
         if (params == null)
@@ -19,5 +32,10 @@
         return params;
     }
 
+    protected ECCurve createCurve()
+    {
+        return createParameters().getCurve();
+    }
+
     protected abstract X9ECParameters createParameters();
 }
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x9/X9FieldElement.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x9/X9FieldElement.java
index e9124a2..a22b40b 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x9/X9FieldElement.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/x9/X9FieldElement.java
@@ -1,10 +1,7 @@
 /* GENERATED SOURCE. DO NOT MODIFY. */
 package com.android.internal.org.bouncycastle.asn1.x9;
 
-import java.math.BigInteger;
-
 import com.android.internal.org.bouncycastle.asn1.ASN1Object;
-import com.android.internal.org.bouncycastle.asn1.ASN1OctetString;
 import com.android.internal.org.bouncycastle.asn1.ASN1Primitive;
 import com.android.internal.org.bouncycastle.asn1.DEROctetString;
 import com.android.internal.org.bouncycastle.math.ec.ECFieldElement;
@@ -25,22 +22,6 @@
         this.f = f;
     }
 
-    /**
-     * @deprecated Will be removed
-     */
-    public X9FieldElement(BigInteger p, ASN1OctetString s)
-    {
-        this(new ECFieldElement.Fp(p, new BigInteger(1, s.getOctets())));
-    }
-
-    /**
-     * @deprecated Will be removed
-     */
-    public X9FieldElement(int m, int k1, int k2, int k3, ASN1OctetString s)
-    {
-        this(new ECFieldElement.F2m(m, k1, k2, k3, new BigInteger(1, s.getOctets())));
-    }
-
     public ECFieldElement getValue()
     {
         return f;
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/AlphabetMapper.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/AlphabetMapper.java
new file mode 100644
index 0000000..53e0341
--- /dev/null
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/AlphabetMapper.java
@@ -0,0 +1,34 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.crypto;
+
+/**
+ * Base interface for mapping from an alphabet to a set of indexes
+ * suitable for use with FPE.
+ * @hide This class is not part of the Android public SDK API
+ */
+public interface AlphabetMapper
+{
+    /**
+     * Return the number of characters in the alphabet.
+     *
+     * @return the radix for the alphabet.
+     */
+    int getRadix();
+
+    /**
+     * Return the passed in char[] as a byte array of indexes (indexes
+     * can be more than 1 byte)
+     *
+     * @param input characters to be mapped.
+     * @return an index array.
+     */
+    byte[] convertToIndexes(char[] input);
+
+    /**
+     * Return a char[] for this alphabet based on the indexes passed.
+     *
+     * @param input input array of indexes.
+     * @return an array of char corresponding to the index values.
+     */
+    char[] convertToChars(byte[] input);
+}
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/BlockCipher.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/BlockCipher.java
index 9fbfe10..3c8e44e 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/BlockCipher.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/BlockCipher.java
@@ -38,16 +38,16 @@
      * Process one block of input from the array in and write it to
      * the out array.
      *
-     * @param in the array containing the input data.
+     * @param input the array containing the input data.
      * @param inOff offset into the in array the data starts at.
-     * @param out the array the output data will be copied into.
+     * @param output the array the output data will be copied into.
      * @param outOff the offset into the out array the output will start at.
-     * @exception DataLengthException if there isn't enough data in in, or
+     * @exception DataLengthException if there isn't enough data in input , or
      * space in out.
      * @exception IllegalStateException if the cipher isn't initialised.
      * @return the number of bytes processed and produced.
      */
-    public int processBlock(byte[] in, int inOff, byte[] out, int outOff)
+    public int processBlock(byte[] input, int inOff, byte[] output, int outOff)
         throws DataLengthException, IllegalStateException;
 
     /**
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/BufferedBlockCipher.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/BufferedBlockCipher.java
index f9e8c6c..d0e6ad2 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/BufferedBlockCipher.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/BufferedBlockCipher.java
@@ -16,8 +16,9 @@
     protected byte[]        buf;
     protected int           bufOff;
 
-    protected boolean       forEncryption;
-    protected BlockCipher   cipher;
+    protected boolean          forEncryption;
+    protected BlockCipher      cipher;
+    protected MultiBlockCipher mbCipher;
 
     protected boolean       partialBlockOkay;
     protected boolean       pgpCFB;
@@ -25,7 +26,7 @@
     /**
      * constructor for subclasses
      */
-    protected BufferedBlockCipher()
+    BufferedBlockCipher()
     {
     }
 
@@ -33,13 +34,24 @@
      * Create a buffered block cipher without padding.
      *
      * @param cipher the underlying block cipher this buffering object wraps.
+     * @deprecated use the constructor on DefaultBufferedBlockCipher.
      */
     public BufferedBlockCipher(
         BlockCipher     cipher)
     {
         this.cipher = cipher;
 
-        buf = new byte[cipher.getBlockSize()];
+        if (cipher instanceof MultiBlockCipher)
+        {
+            this.mbCipher = (MultiBlockCipher)cipher;
+            buf = new byte[mbCipher.getMultiBlockSize()];
+        }
+        else
+        {
+            this.mbCipher = null;
+            buf = new byte[cipher.getBlockSize()];
+        }
+
         bufOff = 0;
 
         //
@@ -145,6 +157,11 @@
     public int getOutputSize(
         int length)
     {
+        if (pgpCFB && forEncryption)
+        {
+            return length + bufOff + (cipher.getBlockSize() + 2);
+        }
+
         // Note: Can assume partialBlockOkay is true for purposes of this calculation
         return length + bufOff;
     }
@@ -227,12 +244,29 @@
             len -= gapLen;
             inOff += gapLen;
 
-            while (len > buf.length)
+            if (mbCipher != null)
             {
-                resultLen += cipher.processBlock(in, inOff, out, outOff + resultLen);
+                int blockCount = len / mbCipher.getMultiBlockSize();
 
-                len -= blockSize;
-                inOff += blockSize;
+                if (blockCount > 0)
+                {
+                    resultLen += mbCipher.processBlocks(in, inOff, blockCount, out, outOff + resultLen);
+
+                    int processed = blockCount * mbCipher.getMultiBlockSize();
+
+                    len -= processed;
+                    inOff += processed;
+                }
+            }
+            else
+            {
+                while (len > buf.length)
+                {
+                    resultLen += cipher.processBlock(in, inOff, out, outOff + resultLen);
+
+                    len -= blockSize;
+                    inOff += blockSize;
+                }
             }
         }
 
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/CipherKeyGenerator.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/CipherKeyGenerator.java
index 2b26db6..d5fca73 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/CipherKeyGenerator.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/CipherKeyGenerator.java
@@ -3,6 +3,8 @@
 
 import java.security.SecureRandom;
 
+import com.android.internal.org.bouncycastle.crypto.constraints.DefaultServiceProperties;
+
 /**
  * The base class for symmetric, or secret, cipher key generators.
  * @hide This class is not part of the Android public SDK API
@@ -22,6 +24,8 @@
     {
         this.random = param.getRandom();
         this.strength = (param.getStrength() + 7) / 8;
+
+        CryptoServicesRegistrar.checkConstraints(new DefaultServiceProperties("SymKeyGen", param.getStrength()));
     }
 
     /**
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/CryptoServiceConstraintsException.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/CryptoServiceConstraintsException.java
new file mode 100644
index 0000000..9550f05
--- /dev/null
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/CryptoServiceConstraintsException.java
@@ -0,0 +1,14 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.crypto;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+public class CryptoServiceConstraintsException
+    extends RuntimeException
+{
+    public CryptoServiceConstraintsException(String msg)
+    {
+        super(msg);
+    }
+}
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/CryptoServiceProperties.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/CryptoServiceProperties.java
new file mode 100644
index 0000000..2e52488
--- /dev/null
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/CryptoServiceProperties.java
@@ -0,0 +1,16 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.crypto;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+public interface CryptoServiceProperties
+{
+    int bitsOfSecurity();
+
+    String getServiceName();
+
+    CryptoServicePurpose getPurpose();
+
+    Object getParams();
+}
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/CryptoServicePurpose.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/CryptoServicePurpose.java
new file mode 100644
index 0000000..80dd1c2
--- /dev/null
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/CryptoServicePurpose.java
@@ -0,0 +1,19 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.crypto;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+public enum CryptoServicePurpose
+{
+    AGREEMENT,
+    ENCRYPTION,
+    DECRYPTION,
+    KEYGEN,
+    SIGNING,         // for signatures (and digests)
+    VERIFYING,
+    AUTHENTICATION,  // for MACs (and digests)
+    VERIFICATION,
+    PRF,
+    ANY
+}
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/CryptoServicesConstraints.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/CryptoServicesConstraints.java
new file mode 100644
index 0000000..da7f1ec
--- /dev/null
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/CryptoServicesConstraints.java
@@ -0,0 +1,10 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.crypto;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+public interface CryptoServicesConstraints
+{
+    void check(CryptoServiceProperties service);
+}
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/CryptoServicesPermission.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/CryptoServicesPermission.java
index 6c1f44a..c82d785 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/CryptoServicesPermission.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/CryptoServicesPermission.java
@@ -27,6 +27,11 @@
      */
     public static final String DEFAULT_RANDOM = "defaultRandomConfig";
 
+    /**
+     * Enable the setting of the constraints.
+     */
+    public static final String CONSTRAINTS = "constraints";
+
     private final Set<String> actions = new HashSet<String>();
 
     public CryptoServicesPermission(String name)
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/CryptoServicesRegistrar.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/CryptoServicesRegistrar.java
index 7a337ce..daa59ef 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/CryptoServicesRegistrar.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/CryptoServicesRegistrar.java
@@ -9,12 +9,16 @@
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.Map;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.logging.Logger;
 
 import com.android.internal.org.bouncycastle.asn1.x9.X9ECParameters;
 import com.android.internal.org.bouncycastle.crypto.params.DHParameters;
 import com.android.internal.org.bouncycastle.crypto.params.DHValidationParameters;
 import com.android.internal.org.bouncycastle.crypto.params.DSAParameters;
 import com.android.internal.org.bouncycastle.crypto.params.DSAValidationParameters;
+import com.android.internal.org.bouncycastle.util.Properties;
+import com.android.internal.org.bouncycastle.util.Strings;
 import com.android.internal.org.bouncycastle.util.encoders.Hex;
 
 /**
@@ -23,15 +27,28 @@
  */
 public final class CryptoServicesRegistrar
 {
+    private static final Logger LOG = Logger.getLogger(CryptoServicesRegistrar.class.getName());
+
     private static final Permission CanSetDefaultProperty = new CryptoServicesPermission(CryptoServicesPermission.GLOBAL_CONFIG);
     private static final Permission CanSetThreadProperty = new CryptoServicesPermission(CryptoServicesPermission.THREAD_LOCAL_CONFIG);
     private static final Permission CanSetDefaultRandom = new CryptoServicesPermission(CryptoServicesPermission.DEFAULT_RANDOM);
+    private static final Permission CanSetConstraints = new CryptoServicesPermission(CryptoServicesPermission.CONSTRAINTS);
 
     private static final ThreadLocal<Map<String, Object[]>> threadProperties = new ThreadLocal<Map<String, Object[]>>();
     private static final Map<String, Object[]> globalProperties = Collections.synchronizedMap(new HashMap<String, Object[]>());
+    private static final SecureRandomProvider defaultRandomProviderImpl = new ThreadLocalSecureRandomProvider();
 
-    private static final Object cacheLock = new Object();
-    private static SecureRandom defaultSecureRandom;
+    private static final CryptoServicesConstraints noConstraintsImpl = new CryptoServicesConstraints()
+    {
+        public void check(CryptoServiceProperties service)
+        {
+            // anything goes.
+        }
+    };
+
+    private static final AtomicReference<SecureRandomProvider> defaultSecureRandomProvider = new AtomicReference<SecureRandomProvider>();
+    private static final boolean preconfiguredConstraints;
+    private static final AtomicReference<CryptoServicesConstraints> servicesConstraints = new AtomicReference<CryptoServicesConstraints>();
 
     static
     {
@@ -45,56 +62,59 @@
 
         DSAParameters def768Params = new DSAParameters(
             new BigInteger("e9e642599d355f37c97ffd3567120b8e25c9cd43e927b3a9670fbec5" +
-                           "d890141922d2c3b3ad2480093799869d1e846aab49fab0ad26d2ce6a" +
-                           "22219d470bce7d777d4a21fbe9c270b57f607002f3cef8393694cf45" +
-                           "ee3688c11a8c56ab127a3daf", 16),
+                "d890141922d2c3b3ad2480093799869d1e846aab49fab0ad26d2ce6a" +
+                "22219d470bce7d777d4a21fbe9c270b57f607002f3cef8393694cf45" +
+                "ee3688c11a8c56ab127a3daf", 16),
             new BigInteger("9cdbd84c9f1ac2f38d0f80f42ab952e7338bf511", 16),
             new BigInteger("30470ad5a005fb14ce2d9dcd87e38bc7d1b1c5facbaecbe95f190aa7" +
-                           "a31d23c4dbbcbe06174544401a5b2c020965d8c2bd2171d366844577" +
-                           "1f74ba084d2029d83c1c158547f3a9f1a2715be23d51ae4d3e5a1f6a" +
-                           "7064f316933a346d3f529252", 16),
+                "a31d23c4dbbcbe06174544401a5b2c020965d8c2bd2171d366844577" +
+                "1f74ba084d2029d83c1c158547f3a9f1a2715be23d51ae4d3e5a1f6a" +
+                "7064f316933a346d3f529252", 16),
             new DSAValidationParameters(Hex.decodeStrict("77d0f8c4dad15eb8c4f2f8d6726cefd96d5bb399"), 263));
 
         DSAParameters def1024Params = new DSAParameters(
             new BigInteger("fd7f53811d75122952df4a9c2eece4e7f611b7523cef4400c31e3f80" +
-                            "b6512669455d402251fb593d8d58fabfc5f5ba30f6cb9b556cd7813b" +
-                            "801d346ff26660b76b9950a5a49f9fe8047b1022c24fbba9d7feb7c6" +
-                            "1bf83b57e7c6a8a6150f04fb83f6d3c51ec3023554135a169132f675" +
-                            "f3ae2b61d72aeff22203199dd14801c7", 16),
+                "b6512669455d402251fb593d8d58fabfc5f5ba30f6cb9b556cd7813b" +
+                "801d346ff26660b76b9950a5a49f9fe8047b1022c24fbba9d7feb7c6" +
+                "1bf83b57e7c6a8a6150f04fb83f6d3c51ec3023554135a169132f675" +
+                "f3ae2b61d72aeff22203199dd14801c7", 16),
             new BigInteger("9760508f15230bccb292b982a2eb840bf0581cf5", 16),
             new BigInteger("f7e1a085d69b3ddecbbcab5c36b857b97994afbbfa3aea82f9574c0b" +
-                            "3d0782675159578ebad4594fe67107108180b449167123e84c281613" +
-                            "b7cf09328cc8a6e13c167a8b547c8d28e0a3ae1e2bb3a675916ea37f" +
-                            "0bfa213562f1fb627a01243bcca4f1bea8519089a883dfe15ae59f06" +
-                            "928b665e807b552564014c3bfecf492a", 16),
+                "3d0782675159578ebad4594fe67107108180b449167123e84c281613" +
+                "b7cf09328cc8a6e13c167a8b547c8d28e0a3ae1e2bb3a675916ea37f" +
+                "0bfa213562f1fb627a01243bcca4f1bea8519089a883dfe15ae59f06" +
+                "928b665e807b552564014c3bfecf492a", 16),
             new DSAValidationParameters(Hex.decodeStrict("8d5155894229d5e689ee01e6018a237e2cae64cd"), 92));
 
         DSAParameters def2048Params = new DSAParameters(
             new BigInteger("95475cf5d93e596c3fcd1d902add02f427f5f3c7210313bb45fb4d5b" +
-                            "b2e5fe1cbd678cd4bbdd84c9836be1f31c0777725aeb6c2fc38b85f4" +
-                            "8076fa76bcd8146cc89a6fb2f706dd719898c2083dc8d896f84062e2" +
-                            "c9c94d137b054a8d8096adb8d51952398eeca852a0af12df83e475aa" +
-                            "65d4ec0c38a9560d5661186ff98b9fc9eb60eee8b030376b236bc73b" +
-                            "e3acdbd74fd61c1d2475fa3077b8f080467881ff7e1ca56fee066d79" +
-                            "506ade51edbb5443a563927dbc4ba520086746175c8885925ebc64c6" +
-                            "147906773496990cb714ec667304e261faee33b3cbdf008e0c3fa906" +
-                            "50d97d3909c9275bf4ac86ffcb3d03e6dfc8ada5934242dd6d3bcca2" +
-                            "a406cb0b", 16),
+                "b2e5fe1cbd678cd4bbdd84c9836be1f31c0777725aeb6c2fc38b85f4" +
+                "8076fa76bcd8146cc89a6fb2f706dd719898c2083dc8d896f84062e2" +
+                "c9c94d137b054a8d8096adb8d51952398eeca852a0af12df83e475aa" +
+                "65d4ec0c38a9560d5661186ff98b9fc9eb60eee8b030376b236bc73b" +
+                "e3acdbd74fd61c1d2475fa3077b8f080467881ff7e1ca56fee066d79" +
+                "506ade51edbb5443a563927dbc4ba520086746175c8885925ebc64c6" +
+                "147906773496990cb714ec667304e261faee33b3cbdf008e0c3fa906" +
+                "50d97d3909c9275bf4ac86ffcb3d03e6dfc8ada5934242dd6d3bcca2" +
+                "a406cb0b", 16),
             new BigInteger("f8183668ba5fc5bb06b5981e6d8b795d30b8978d43ca0ec572e37e09939a9773", 16),
             new BigInteger("42debb9da5b3d88cc956e08787ec3f3a09bba5f48b889a74aaf53174" +
-                            "aa0fbe7e3c5b8fcd7a53bef563b0e98560328960a9517f4014d3325f" +
-                            "c7962bf1e049370d76d1314a76137e792f3f0db859d095e4a5b93202" +
-                            "4f079ecf2ef09c797452b0770e1350782ed57ddf794979dcef23cb96" +
-                            "f183061965c4ebc93c9c71c56b925955a75f94cccf1449ac43d586d0" +
-                            "beee43251b0b2287349d68de0d144403f13e802f4146d882e057af19" +
-                            "b6f6275c6676c8fa0e3ca2713a3257fd1b27d0639f695e347d8d1cf9" +
-                            "ac819a26ca9b04cb0eb9b7b035988d15bbac65212a55239cfc7e58fa" +
-                            "e38d7250ab9991ffbc97134025fe8ce04c4399ad96569be91a546f49" +
-                            "78693c7a", 16),
+                "aa0fbe7e3c5b8fcd7a53bef563b0e98560328960a9517f4014d3325f" +
+                "c7962bf1e049370d76d1314a76137e792f3f0db859d095e4a5b93202" +
+                "4f079ecf2ef09c797452b0770e1350782ed57ddf794979dcef23cb96" +
+                "f183061965c4ebc93c9c71c56b925955a75f94cccf1449ac43d586d0" +
+                "beee43251b0b2287349d68de0d144403f13e802f4146d882e057af19" +
+                "b6f6275c6676c8fa0e3ca2713a3257fd1b27d0639f695e347d8d1cf9" +
+                "ac819a26ca9b04cb0eb9b7b035988d15bbac65212a55239cfc7e58fa" +
+                "e38d7250ab9991ffbc97134025fe8ce04c4399ad96569be91a546f49" +
+                "78693c7a", 16),
             new DSAValidationParameters(Hex.decodeStrict("b0b4417601b59cbc9d8ac8f935cadaec4f5fbb2f23785609ae466748d9b5a536"), 497));
 
         localSetGlobalProperty(Property.DSA_DEFAULT_PARAMS, def512Params, def768Params, def1024Params, def2048Params);
         localSetGlobalProperty(Property.DH_DEFAULT_PARAMS, toDH(def512Params), toDH(def768Params), toDH(def1024Params), toDH(def2048Params));
+
+        servicesConstraints.set(getDefaultConstraints());
+        preconfiguredConstraints = (servicesConstraints.get() != noConstraintsImpl);
     }
 
     private CryptoServicesRegistrar()
@@ -109,25 +129,9 @@
      */
     public static SecureRandom getSecureRandom()
     {
-        synchronized (cacheLock)
-        {
-            if (null != defaultSecureRandom)
-            {
-                return defaultSecureRandom;
-            }
-        }
+        defaultSecureRandomProvider.compareAndSet(null, defaultRandomProviderImpl);
 
-        SecureRandom tmp = new SecureRandom();
-
-        synchronized (cacheLock)
-        {
-            if (null == defaultSecureRandom)
-            {
-                defaultSecureRandom = tmp;
-            }
-
-            return defaultSecureRandom;
-        }
+        return defaultSecureRandomProvider.get().get();
     }
 
     /**
@@ -146,13 +150,83 @@
      *
      * @param secureRandom the SecureRandom to use as the default.
      */
-    public static void setSecureRandom(SecureRandom secureRandom)
+    public static void setSecureRandom(final SecureRandom secureRandom)
     {
         checkPermission(CanSetDefaultRandom);
 
-        synchronized (cacheLock)
+        if (secureRandom == null)
         {
-            defaultSecureRandom = secureRandom;
+            defaultSecureRandomProvider.set(defaultRandomProviderImpl);
+        }
+        else
+        {
+            defaultSecureRandomProvider.set(new SecureRandomProvider()
+            {
+                public SecureRandom get()
+                {
+                    return secureRandom;
+                }
+            });
+        }
+    }
+
+    /**
+     * Set a default secure random provider to be used where none is otherwise provided.
+     *
+     * @param secureRandomProvider a provider SecureRandom to use when a default SecureRandom is requested.
+     */
+    public static void setSecureRandomProvider(SecureRandomProvider secureRandomProvider)
+    {
+        checkPermission(CanSetDefaultRandom);
+
+        defaultSecureRandomProvider.set(secureRandomProvider);
+    }
+
+    /**
+     * Return the current algorithm/services constraints.
+     *
+     * @return the algorithm/services constraints.
+     */
+    public static CryptoServicesConstraints getServicesConstraints()
+    {
+        return servicesConstraints.get();
+    }
+
+    /**
+     * Check a service to make sure it meets the current constraints.
+     *
+     * @param cryptoService the service to be checked.
+     * @throws CryptoServiceConstraintsException if the service violates the current constraints.
+     */
+    public static void checkConstraints(CryptoServiceProperties cryptoService)
+    {
+        servicesConstraints.get().check(cryptoService);
+    }
+
+    /**
+     * Set the current algorithm constraints.
+     */
+    public static void setServicesConstraints(CryptoServicesConstraints constraints)
+    {
+        checkPermission(CanSetConstraints);
+
+        CryptoServicesConstraints newConstraints = (constraints == null) ? noConstraintsImpl : constraints;
+
+        if (preconfiguredConstraints)
+        {
+            if (Properties.isOverrideSet("com.android.internal.org.bouncycastle.constraints.allow_override"))
+            {
+                servicesConstraints.set(newConstraints);
+            }
+            else
+            {
+                LOG.warning("attempt to override pre-configured constraints ignored");
+            }
+        }
+        else
+        {
+            // TODO: should this only be allowed once?
+            servicesConstraints.set(newConstraints);
         }
     }
 
@@ -161,7 +235,7 @@
      * configuration first and then on the global configuration in no local configuration exists.
      *
      * @param property the property to look up.
-     * @param <T> the type to be returned
+     * @param <T>      the type to be returned
      * @return null if the property is not set, the default value otherwise,
      */
     public static <T> T getProperty(Property property)
@@ -197,7 +271,7 @@
      * DSA_DEFAULT_PARAMS.
      *
      * @param property the name of the property to look up.
-     * @param <T> the base type of the array to be returned.
+     * @param <T>      the base type of the array to be returned.
      * @return null if the property is not set, an array of the current values otherwise.
      */
     public static <T> T[] getSizedProperty(Property property)
@@ -217,8 +291,8 @@
      * DSA_DEFAULT_PARAMS.
      *
      * @param property the name of the property to look up.
-     * @param size the size (in bits) of the defining value in the property type.
-     * @param <T> the type of the value to be returned.
+     * @param size     the size (in bits) of the defining value in the property type.
+     * @param <T>      the type of the value to be returned.
      * @return the current value for the size, null if there is no value set,
      */
     public static <T> T getSizedProperty(Property property, int size)
@@ -263,9 +337,9 @@
      * one value can be passed in for a sized property. If more than one value is provided the
      * first value in the argument list becomes the default value.
      *
-     * @param property the name of the property to set.
+     * @param property      the name of the property to set.
      * @param propertyValue the values to assign to the property.
-     * @param <T> the base type of the property value.
+     * @param <T>           the base type of the property value.
      */
     public static <T> void setThreadProperty(Property property, T... propertyValue)
     {
@@ -284,9 +358,9 @@
      * one value can be passed in for a sized property. If more than one value is provided the
      * first value in the argument list becomes the default value.
      *
-     * @param property the name of the property to set.
+     * @param property      the name of the property to set.
      * @param propertyValue the values to assign to the property.
-     * @param <T> the base type of the property value.
+     * @param <T>           the base type of the property value.
      */
     public static <T> void setGlobalProperty(Property property, T... propertyValue)
     {
@@ -325,7 +399,7 @@
      * Clear the global value for the passed in property.
      *
      * @param property the property to be cleared.
-     * @param <T> the base type of the property value
+     * @param <T>      the base type of the property value
      * @return an array of T if a value was previously set, null otherwise.
      */
     public static <T> T[] clearGlobalProperty(Property property)
@@ -342,7 +416,7 @@
      * Clear the thread local value for the passed in property.
      *
      * @param property the property to be cleared.
-     * @param <T> the base type of the property value
+     * @param <T>      the base type of the property value
      * @return an array of T if a value was previously set, null otherwise.
      */
     public static <T> T[] clearThreadProperty(Property property)
@@ -417,6 +491,13 @@
         return m;
     }
 
+    private static CryptoServicesConstraints getDefaultConstraints()
+    {
+        // TODO: return one based on system/security properties if set.
+
+        return noConstraintsImpl;
+    }
+
     /**
      * Available properties that can be set.
      * @hide This class is not part of the Android public SDK API
@@ -430,11 +511,11 @@
         /**
          * The default parameters for a particular size of Diffie-Hellman key.This is a sized property.
          */
-        public static final Property DH_DEFAULT_PARAMS= new Property("dhDefaultParams", DHParameters.class);
+        public static final Property DH_DEFAULT_PARAMS = new Property("dhDefaultParams", DHParameters.class);
         /**
          * The default parameters for a particular size of DSA key. This is a sized property.
          */
-        public static final Property DSA_DEFAULT_PARAMS= new Property("dsaDefaultParams", DSAParameters.class);
+        public static final Property DSA_DEFAULT_PARAMS = new Property("dsaDefaultParams", DSAParameters.class);
         private final String name;
         private final Class type;
 
@@ -444,4 +525,20 @@
             this.type = type;
         }
     }
+
+    private static class ThreadLocalSecureRandomProvider
+        implements SecureRandomProvider
+    {
+        final ThreadLocal<SecureRandom> defaultRandoms = new ThreadLocal<SecureRandom>();
+
+        public SecureRandom get()
+        {
+            if (defaultRandoms.get() == null)
+            {
+                defaultRandoms.set(new SecureRandom());
+            }
+
+            return defaultRandoms.get();
+        }
+    }
 }
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/DefaultBufferedBlockCipher.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/DefaultBufferedBlockCipher.java
new file mode 100644
index 0000000..6ff4992
--- /dev/null
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/DefaultBufferedBlockCipher.java
@@ -0,0 +1,358 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.crypto;
+
+
+/**
+ * A wrapper class that allows block ciphers to be used to process data in
+ * a piecemeal fashion. The BufferedBlockCipher outputs a block only when the
+ * buffer is full and more data is being added, or on a doFinal.
+ * <p>
+ * Note: in the case where the underlying cipher is either a CFB cipher or an
+ * OFB one the last block may not be a multiple of the block size. Use this class
+ * for construction rather than BufferedBlockCipher as BufferedBlockCipher will eventually
+ * turn into an interface.
+ * @hide This class is not part of the Android public SDK API
+ */
+public class DefaultBufferedBlockCipher
+    extends BufferedBlockCipher
+{
+    protected byte[]        buf;
+    protected int           bufOff;
+
+    protected boolean          forEncryption;
+    protected BlockCipher      cipher;
+    protected MultiBlockCipher mbCipher;
+
+    protected boolean       partialBlockOkay;
+    protected boolean       pgpCFB;
+
+    /**
+     * constructor for subclasses
+     */
+    protected DefaultBufferedBlockCipher()
+    {
+    }
+
+    /**
+     * Create a buffered block cipher without padding.
+     *
+     * @param cipher the underlying block cipher this buffering object wraps.
+     */
+    public DefaultBufferedBlockCipher(
+        BlockCipher     cipher)
+    {
+        this.cipher = cipher;
+
+        if (cipher instanceof MultiBlockCipher)
+        {
+            this.mbCipher = (MultiBlockCipher)cipher;
+            buf = new byte[mbCipher.getMultiBlockSize()];
+        }
+        else
+        {
+            this.mbCipher = null;
+            buf = new byte[cipher.getBlockSize()];
+        }
+
+        bufOff = 0;
+
+        //
+        // check if we can handle partial blocks on doFinal.
+        //
+        String  name = cipher.getAlgorithmName();
+        int     idx = name.indexOf('/') + 1;
+
+        pgpCFB = (idx > 0 && name.startsWith("PGP", idx));
+
+        if (pgpCFB || cipher instanceof StreamCipher)
+        {
+            partialBlockOkay = true;
+        }
+        else
+        {
+            partialBlockOkay = (idx > 0 && (name.startsWith("OpenPGP", idx)));
+        }
+    }
+
+    /**
+     * return the cipher this object wraps.
+     *
+     * @return the cipher this object wraps.
+     */
+    public BlockCipher getUnderlyingCipher()
+    {
+        return cipher;
+    }
+
+    /**
+     * initialise the cipher.
+     *
+     * @param forEncryption if true the cipher is initialised for
+     *  encryption, if false for decryption.
+     * @param params the key and other data required by the cipher.
+     * @exception IllegalArgumentException if the params argument is
+     * inappropriate.
+     */
+    public void init(
+        boolean             forEncryption,
+        CipherParameters    params)
+        throws IllegalArgumentException
+    {
+        this.forEncryption = forEncryption;
+
+        reset();
+
+        cipher.init(forEncryption, params);
+    }
+
+    /**
+     * return the blocksize for the underlying cipher.
+     *
+     * @return the blocksize for the underlying cipher.
+     */
+    public int getBlockSize()
+    {
+        return cipher.getBlockSize();
+    }
+
+    /**
+     * return the size of the output buffer required for an update 
+     * an input of len bytes.
+     *
+     * @param len the length of the input.
+     * @return the space required to accommodate a call to update
+     * with len bytes of input.
+     */
+    public int getUpdateOutputSize(
+        int len)
+    {
+        int total       = len + bufOff;
+        int leftOver;
+
+        if (pgpCFB)
+        {
+            if (forEncryption)
+            {
+                leftOver = total % buf.length - (cipher.getBlockSize() + 2);
+            }
+            else
+            {
+                leftOver = total % buf.length;
+            }
+        }
+        else
+        {
+            leftOver    = total % buf.length;
+        }
+
+        return total - leftOver;
+    }
+
+    /**
+     * return the size of the output buffer required for an update plus a
+     * doFinal with an input of 'length' bytes.
+     *
+     * @param length the length of the input.
+     * @return the space required to accommodate a call to update and doFinal
+     * with 'length' bytes of input.
+     */
+    public int getOutputSize(
+        int length)
+    {
+        if (pgpCFB && forEncryption)
+        {
+            return length + bufOff + (cipher.getBlockSize() + 2);
+        }
+
+        // Note: Can assume partialBlockOkay is true for purposes of this calculation
+        return length + bufOff;
+    }
+
+    /**
+     * process a single byte, producing an output block if necessary.
+     *
+     * @param in the input byte.
+     * @param out the space for any output that might be produced.
+     * @param outOff the offset from which the output will be copied.
+     * @return the number of output bytes copied to out.
+     * @exception DataLengthException if there isn't enough space in out.
+     * @exception IllegalStateException if the cipher isn't initialised.
+     */
+    public int processByte(
+        byte        in,
+        byte[]      out,
+        int         outOff)
+        throws DataLengthException, IllegalStateException
+    {
+        int         resultLen = 0;
+
+        buf[bufOff++] = in;
+
+        if (bufOff == buf.length)
+        {
+            resultLen = cipher.processBlock(buf, 0, out, outOff);
+            bufOff = 0;
+        }
+
+        return resultLen;
+    }
+
+    /**
+     * process an array of bytes, producing output if necessary.
+     *
+     * @param in the input byte array.
+     * @param inOff the offset at which the input data starts.
+     * @param len the number of bytes to be copied out of the input array.
+     * @param out the space for any output that might be produced.
+     * @param outOff the offset from which the output will be copied.
+     * @return the number of output bytes copied to out.
+     * @exception DataLengthException if there isn't enough space in out.
+     * @exception IllegalStateException if the cipher isn't initialised.
+     */
+    public int processBytes(
+        byte[]      in,
+        int         inOff,
+        int         len,
+        byte[]      out,
+        int         outOff)
+        throws DataLengthException, IllegalStateException
+    {
+        if (len < 0)
+        {
+            throw new IllegalArgumentException("Can't have a negative input length!");
+        }
+
+        int blockSize   = getBlockSize();
+        int length      = getUpdateOutputSize(len);
+        
+        if (length > 0)
+        {
+            if ((outOff + length) > out.length)
+            {
+                throw new OutputLengthException("output buffer too short");
+            }
+        }
+
+        int resultLen = 0;
+        int gapLen = buf.length - bufOff;
+
+        if (len > gapLen)
+        {
+            System.arraycopy(in, inOff, buf, bufOff, gapLen);
+
+            resultLen += cipher.processBlock(buf, 0, out, outOff);
+
+            bufOff = 0;
+            len -= gapLen;
+            inOff += gapLen;
+
+            if (mbCipher != null)
+            {
+                int blockCount = len / mbCipher.getMultiBlockSize();
+
+                if (blockCount > 0)
+                {
+                    resultLen += mbCipher.processBlocks(in, inOff, blockCount, out, outOff + resultLen);
+
+                    int processed = blockCount * mbCipher.getMultiBlockSize();
+
+                    len -= processed;
+                    inOff += processed;
+                }
+            }
+            else
+            {
+                while (len > buf.length)
+                {
+                    resultLen += cipher.processBlock(in, inOff, out, outOff + resultLen);
+
+                    len -= blockSize;
+                    inOff += blockSize;
+                }
+            }
+        }
+
+        System.arraycopy(in, inOff, buf, bufOff, len);
+
+        bufOff += len;
+
+        if (bufOff == buf.length)
+        {
+            resultLen += cipher.processBlock(buf, 0, out, outOff + resultLen);
+            bufOff = 0;
+        }
+
+        return resultLen;
+    }
+
+    /**
+     * Process the last block in the buffer.
+     *
+     * @param out the array the block currently being held is copied into.
+     * @param outOff the offset at which the copying starts.
+     * @return the number of output bytes copied to out.
+     * @exception DataLengthException if there is insufficient space in out for
+     * the output, or the input is not block size aligned and should be.
+     * @exception IllegalStateException if the underlying cipher is not
+     * initialised.
+     * @exception InvalidCipherTextException if padding is expected and not found.
+     * @exception DataLengthException if the input is not block size
+     * aligned.
+     */
+    public int doFinal(
+        byte[]  out,
+        int     outOff)
+        throws DataLengthException, IllegalStateException, InvalidCipherTextException
+    {
+        try
+        {
+            int resultLen = 0;
+
+            if (outOff + bufOff > out.length)
+            {
+                throw new OutputLengthException("output buffer too short for doFinal()");
+            }
+
+            if (bufOff != 0)
+            {
+                if (!partialBlockOkay)
+                {
+                    throw new DataLengthException("data not block size aligned");
+                }
+
+                cipher.processBlock(buf, 0, buf, 0);
+                resultLen = bufOff;
+                bufOff = 0;
+                System.arraycopy(buf, 0, out, outOff, resultLen);
+            }
+
+            return resultLen;
+        }
+        finally
+        {
+            reset();
+        }
+    }
+
+    /**
+     * Reset the buffer and cipher. After resetting the object is in the same
+     * state as it was after the last init (if there was one).
+     */
+    public void reset()
+    {
+        //
+        // clean the buffer.
+        //
+        for (int i = 0; i < buf.length; i++)
+        {
+            buf[i] = 0;
+        }
+
+        bufOff = 0;
+
+        //
+        // reset the underlying cipher.
+        //
+        cipher.reset();
+    }
+}
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/DefaultMultiBlockCipher.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/DefaultMultiBlockCipher.java
new file mode 100644
index 0000000..f1ca987
--- /dev/null
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/DefaultMultiBlockCipher.java
@@ -0,0 +1,37 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.crypto;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+public abstract class DefaultMultiBlockCipher
+    implements MultiBlockCipher
+{
+    protected DefaultMultiBlockCipher()
+    {
+    }
+
+    public int getMultiBlockSize()
+    {
+        return this.getBlockSize();
+    }
+
+    public int processBlocks(byte[] in, int inOff, int blockCount, byte[] out, int outOff)
+        throws DataLengthException, IllegalStateException
+    {
+
+        // TODO check if the underlying cipher supports the multiblock interface and call it directly?
+
+        int resultLen = 0;
+        int blockSize = this.getMultiBlockSize();
+        
+        for (int i = 0; i != blockCount; i++)
+        {
+            resultLen += this.processBlock(in, inOff, out, outOff + resultLen);
+
+            inOff += blockSize;
+        }
+
+        return resultLen;
+    }
+}
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/EncapsulatedSecretExtractor.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/EncapsulatedSecretExtractor.java
new file mode 100644
index 0000000..16c1394
--- /dev/null
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/EncapsulatedSecretExtractor.java
@@ -0,0 +1,22 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.crypto;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+public interface EncapsulatedSecretExtractor
+{
+    /**
+     * Extract the secret based on the recipient private key.
+     *
+     * @param encapsulation the encapsulated secret.
+     */
+    byte[] extractSecret(byte[] encapsulation);
+
+    /**
+     * Return the length in bytes of the encapsulation.
+     *
+     * @return length in bytes of an encapsulation for this parameter set.
+     */
+    int getEncapsulationLength();
+}
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/EncapsulatedSecretGenerator.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/EncapsulatedSecretGenerator.java
new file mode 100644
index 0000000..2b362ea
--- /dev/null
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/EncapsulatedSecretGenerator.java
@@ -0,0 +1,17 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.crypto;
+
+import com.android.internal.org.bouncycastle.crypto.params.AsymmetricKeyParameter;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+public interface EncapsulatedSecretGenerator
+{
+    /**
+     * Generate secret/encapsulation based on the recipient public key.
+     *
+     * @return An SecretWithEncapsulation derived from the recipient public key.
+     */
+    SecretWithEncapsulation generateEncapsulated(AsymmetricKeyParameter recipientKey);
+}
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/MultiBlockCipher.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/MultiBlockCipher.java
new file mode 100644
index 0000000..c0f386e
--- /dev/null
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/MultiBlockCipher.java
@@ -0,0 +1,33 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.crypto;
+
+/**
+ * Base interface for a cipher engine capable of processing multiple blocks at a time.
+ * @hide This class is not part of the Android public SDK API
+ */
+public interface MultiBlockCipher
+    extends BlockCipher
+{
+    /**
+     * Return the multi-block size for this cipher (in bytes).
+     *
+     * @return the multi-block size for this cipher in bytes.
+     */
+    int getMultiBlockSize();
+
+    /**
+     * Process blockCount blocks from input in offset inOff and place the output in
+     * out from offset outOff.
+     *
+     * @param in input data array.
+     * @param inOff start of input data in in.
+     * @param blockCount number of blocks to be processed.
+     * @param out output data array.
+     * @param outOff start position for output data.
+     * @return number of bytes written to out.
+     * @throws DataLengthException
+     * @throws IllegalStateException
+     */
+    int processBlocks(byte[] in, int inOff, int blockCount, byte[] out, int outOff)
+        throws DataLengthException, IllegalStateException;
+}
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/SavableDigest.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/SavableDigest.java
new file mode 100644
index 0000000..f018b89
--- /dev/null
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/SavableDigest.java
@@ -0,0 +1,15 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.crypto;
+
+import com.android.internal.org.bouncycastle.crypto.digests.EncodableDigest;
+import com.android.internal.org.bouncycastle.util.Memoable;
+
+/**
+ * Extended digest which provides the ability to store state and
+ * provide an encoding.
+ * @hide This class is not part of the Android public SDK API
+ */
+public interface SavableDigest
+    extends ExtendedDigest, EncodableDigest, Memoable
+{
+}
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/SecretWithEncapsulation.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/SecretWithEncapsulation.java
new file mode 100644
index 0000000..ff512eb
--- /dev/null
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/SecretWithEncapsulation.java
@@ -0,0 +1,26 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.crypto;
+
+import javax.security.auth.Destroyable;
+
+/**
+ * Interface describing secret with encapsulation details.
+ * @hide This class is not part of the Android public SDK API
+ */
+public interface SecretWithEncapsulation
+    extends Destroyable
+{
+    /**
+     * Return the secret associated with the encapsulation.
+     *
+     * @return the secret the encapsulation is for.
+     */
+    byte[] getSecret();
+
+    /**
+     * Return the data that carries the secret in its encapsulated form.
+     *
+     * @return the encapsulation of the secret.
+     */
+    byte[] getEncapsulation();
+}
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/SecureRandomProvider.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/SecureRandomProvider.java
new file mode 100644
index 0000000..5b7eb8a
--- /dev/null
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/SecureRandomProvider.java
@@ -0,0 +1,17 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.crypto;
+
+import java.security.SecureRandom;
+
+/**
+ * Source provider for SecureRandom implementations.
+ * @hide This class is not part of the Android public SDK API
+ */
+public interface SecureRandomProvider
+{
+    /**
+     * Return a SecureRandom instance.
+     * @return a SecureRandom
+     */
+    SecureRandom get();
+}
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/StreamBlockCipher.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/StreamBlockCipher.java
index b52b224..a3652a7 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/StreamBlockCipher.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/StreamBlockCipher.java
@@ -7,7 +7,8 @@
  * @hide This class is not part of the Android public SDK API
  */
 public abstract class StreamBlockCipher
-    implements BlockCipher, StreamCipher
+    extends DefaultMultiBlockCipher
+    implements StreamCipher
 {
     private final BlockCipher cipher;
 
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/agreement/DHBasicAgreement.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/agreement/DHBasicAgreement.java
index 718e105..04d5351 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/agreement/DHBasicAgreement.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/agreement/DHBasicAgreement.java
@@ -5,6 +5,7 @@
 
 import com.android.internal.org.bouncycastle.crypto.BasicAgreement;
 import com.android.internal.org.bouncycastle.crypto.CipherParameters;
+import com.android.internal.org.bouncycastle.crypto.CryptoServicesRegistrar;
 import com.android.internal.org.bouncycastle.crypto.params.AsymmetricKeyParameter;
 import com.android.internal.org.bouncycastle.crypto.params.DHParameters;
 import com.android.internal.org.bouncycastle.crypto.params.DHPrivateKeyParameters;
@@ -49,6 +50,8 @@
 
         this.key = (DHPrivateKeyParameters)kParam;
         this.dhParams = key.getParameters();
+
+        CryptoServicesRegistrar.checkConstraints(Utils.getDefaultProperties("DHB", key));
     }
 
     public int getFieldSize()
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/agreement/ECDHBasicAgreement.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/agreement/ECDHBasicAgreement.java
index 944a986..a0b2468 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/agreement/ECDHBasicAgreement.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/agreement/ECDHBasicAgreement.java
@@ -5,6 +5,7 @@
 
 import com.android.internal.org.bouncycastle.crypto.BasicAgreement;
 import com.android.internal.org.bouncycastle.crypto.CipherParameters;
+import com.android.internal.org.bouncycastle.crypto.CryptoServicesRegistrar;
 import com.android.internal.org.bouncycastle.crypto.params.ECDomainParameters;
 import com.android.internal.org.bouncycastle.crypto.params.ECPrivateKeyParameters;
 import com.android.internal.org.bouncycastle.crypto.params.ECPublicKeyParameters;
@@ -36,6 +37,8 @@
         CipherParameters key)
     {
         this.key = (ECPrivateKeyParameters)key;
+
+        CryptoServicesRegistrar.checkConstraints(Utils.getDefaultProperties("ECDH", this.key));
     }
 
     public int getFieldSize()
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/agreement/Utils.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/agreement/Utils.java
new file mode 100644
index 0000000..a8bed7e
--- /dev/null
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/agreement/Utils.java
@@ -0,0 +1,41 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.crypto.agreement;
+
+import com.android.internal.org.bouncycastle.crypto.CryptoServiceProperties;
+import com.android.internal.org.bouncycastle.crypto.CryptoServicePurpose;
+import com.android.internal.org.bouncycastle.crypto.constraints.ConstraintUtils;
+import com.android.internal.org.bouncycastle.crypto.constraints.DefaultServiceProperties;
+import com.android.internal.org.bouncycastle.crypto.params.DHKeyParameters;
+import com.android.internal.org.bouncycastle.crypto.params.ECKeyParameters;
+// Android-removed: unsupported algorithms
+// import org.bouncycastle.crypto.params.X25519PrivateKeyParameters;
+// import org.bouncycastle.crypto.params.X448PrivateKeyParameters;
+
+class Utils
+{
+    static CryptoServiceProperties getDefaultProperties(String algorithm, ECKeyParameters k)
+    {
+        return new DefaultServiceProperties(algorithm, ConstraintUtils.bitsOfSecurityFor(k.getParameters().getCurve()), k, CryptoServicePurpose.AGREEMENT);
+    }
+
+    static CryptoServiceProperties getDefaultProperties(String algorithm, DHKeyParameters k)
+    {
+        return new DefaultServiceProperties(algorithm, ConstraintUtils.bitsOfSecurityFor(k.getParameters().getP()), k, CryptoServicePurpose.AGREEMENT);
+    }
+
+    // BEGIN Android-removed: unsupported algorithms
+    /*
+    static CryptoServiceProperties getDefaultProperties(String algorithm, X448PrivateKeyParameters k)
+    {
+        return new DefaultServiceProperties(algorithm, 224, k, CryptoServicePurpose.AGREEMENT);
+    }
+
+    static CryptoServiceProperties getDefaultProperties(String algorithm, X25519PrivateKeyParameters k)
+    {
+        return new DefaultServiceProperties(algorithm, 128, k, CryptoServicePurpose.AGREEMENT);
+    }
+    */
+    // END Android-removed: unsupported algorithms
+}
+
+
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/constraints/ConstraintUtils.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/constraints/ConstraintUtils.java
new file mode 100644
index 0000000..7fdf02d
--- /dev/null
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/constraints/ConstraintUtils.java
@@ -0,0 +1,51 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.crypto.constraints;
+
+import java.math.BigInteger;
+
+import com.android.internal.org.bouncycastle.math.ec.ECCurve;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+public class ConstraintUtils
+{
+    /**
+     * Return the bits of security for the passed in RSA modulus or DH/DSA group value.
+     *
+     * @param p a modulus or group value
+     * @return the security strength in bits.
+     */
+    public static int bitsOfSecurityFor(BigInteger p)
+    {
+        return bitsOfSecurityForFF(p.bitLength());
+    }
+
+    /**
+     * Return the bits of security for the passed in Elliptic Curve.
+     *
+     * @param curve the ECCurve of interest.
+     * @return the security strength in bits.
+     */
+    public static int bitsOfSecurityFor(ECCurve curve)
+    {
+        int sBits = (curve.getFieldSize() + 1) / 2;
+
+        return (sBits > 256) ? 256 : sBits;
+    }
+
+    public static int bitsOfSecurityForFF(int strength)
+    {
+        if (strength >= 2048)
+        {
+            return (strength >= 3072) ?
+                        ((strength >= 7680) ?
+                            ((strength >= 15360) ? 256
+                            : 192)
+                        : 128)
+                   : 112;
+        }
+
+        return (strength >= 1024) ? 80 : 20;      // TODO: possibly a bit harsh...
+    }
+}
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/constraints/DefaultServiceProperties.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/constraints/DefaultServiceProperties.java
new file mode 100644
index 0000000..e9003a8
--- /dev/null
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/constraints/DefaultServiceProperties.java
@@ -0,0 +1,59 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.crypto.constraints;
+
+import com.android.internal.org.bouncycastle.crypto.CryptoServiceProperties;
+import com.android.internal.org.bouncycastle.crypto.CryptoServicePurpose;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+public class DefaultServiceProperties
+    implements CryptoServiceProperties
+{
+    private final String algorithm;
+    private final int bitsOfSecurity;
+    private final Object params;
+    private final CryptoServicePurpose purpose;
+
+    public DefaultServiceProperties(String algorithm, int bitsOfSecurity)
+    {
+        this(algorithm, bitsOfSecurity, null, CryptoServicePurpose.ANY);
+    }
+
+    public DefaultServiceProperties(String algorithm, int bitsOfSecurity, Object params)
+    {
+        this(algorithm, bitsOfSecurity, params, CryptoServicePurpose.ANY);
+    }
+
+    public DefaultServiceProperties(String algorithm, int bitsOfSecurity, Object params, CryptoServicePurpose purpose)
+    {
+        this.algorithm = algorithm;
+        this.bitsOfSecurity = bitsOfSecurity;
+        this.params = params;
+        if (params instanceof CryptoServicePurpose)
+        {
+            throw new IllegalArgumentException("params should not be CryptoServicePurpose");
+        }
+        this.purpose = purpose;
+    }
+
+    public int bitsOfSecurity()
+    {
+        return bitsOfSecurity;
+    }
+
+    public String getServiceName()
+    {
+        return algorithm;
+    }
+
+    public CryptoServicePurpose getPurpose()
+    {
+        return purpose;
+    }
+
+    public Object getParams()
+    {
+        return params;
+    }
+}
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/digests/AsconDigest.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/digests/AsconDigest.java
new file mode 100644
index 0000000..32ae62e
--- /dev/null
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/digests/AsconDigest.java
@@ -0,0 +1,222 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.crypto.digests;
+
+import java.io.ByteArrayOutputStream;
+
+import com.android.internal.org.bouncycastle.crypto.DataLengthException;
+import com.android.internal.org.bouncycastle.crypto.ExtendedDigest;
+import com.android.internal.org.bouncycastle.crypto.OutputLengthException;
+
+/* ASCON v1.2 Digest, https://ascon.iaik.tugraz.at/ .
+ * <p>
+ * https://csrc.nist.gov/CSRC/media/Projects/lightweight-cryptography/documents/finalist-round/updated-spec-doc/ascon-spec-final.pdf
+ * <p>
+ * ASCON v1.2 Digest with reference to C Reference Impl from: https://github.com/ascon/ascon-c .
+ */
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+public class AsconDigest
+    implements ExtendedDigest
+{
+    /**
+     * @hide This class is not part of the Android public SDK API
+     */
+    public enum AsconParameters
+    {
+        AsconHash,
+        AsconHashA,
+    }
+
+    AsconParameters asconParameters;
+
+    public AsconDigest(AsconParameters parameters)
+    {
+        this.asconParameters = parameters;
+        switch (parameters)
+        {
+        case AsconHash:
+            ASCON_PB_ROUNDS = 12;
+            algorithmName = "Ascon-Hash";
+            break;
+        case AsconHashA:
+            ASCON_PB_ROUNDS = 8;
+            algorithmName = "Ascon-HashA";
+            break;
+        default:
+            throw new IllegalArgumentException("Invalid parameter settings for Ascon Hash");
+        }
+        reset();
+    }
+
+    private final String algorithmName;
+    private final ByteArrayOutputStream buffer = new ByteArrayOutputStream();
+    private long x0;
+    private long x1;
+    private long x2;
+    private long x3;
+    private long x4;
+    private final int CRYPTO_BYTES = 32;
+    private final int ASCON_PB_ROUNDS;
+
+    private long ROR(long x, int n)
+    {
+        return x >>> n | x << (64 - n);
+    }
+
+    private void ROUND(long C)
+    {
+        long t0 = x0 ^ x1 ^ x2 ^ x3 ^ C ^ (x1 & (x0 ^ x2 ^ x4 ^ C));
+        long t1 = x0 ^ x2 ^ x3 ^ x4 ^ C ^ ((x1 ^ x2 ^ C) & (x1 ^ x3));
+        long t2 = x1 ^ x2 ^ x4 ^ C ^ (x3 & x4);
+        long t3 = x0 ^ x1 ^ x2 ^ C ^ ((~x0) & (x3 ^ x4));
+        long t4 = x1 ^ x3 ^ x4 ^ ((x0 ^ x4) & x1);
+        x0 = t0 ^ ROR(t0, 19) ^ ROR(t0, 28);
+        x1 = t1 ^ ROR(t1, 39) ^ ROR(t1, 61);
+        x2 = ~(t2 ^ ROR(t2, 1) ^ ROR(t2, 6));
+        x3 = t3 ^ ROR(t3, 10) ^ ROR(t3, 17);
+        x4 = t4 ^ ROR(t4, 7) ^ ROR(t4, 41);
+    }
+
+    private void P(int nr)
+    {
+        if (nr == 12)
+        {
+            ROUND(0xf0L);
+            ROUND(0xe1L);
+            ROUND(0xd2L);
+            ROUND(0xc3L);
+        }
+        if (nr >= 8)
+        {
+            ROUND(0xb4L);
+            ROUND(0xa5L);
+        }
+        ROUND(0x96L);
+        ROUND(0x87L);
+        ROUND(0x78L);
+        ROUND(0x69L);
+        ROUND(0x5aL);
+        ROUND(0x4bL);
+    }
+
+    private long PAD(int i)
+    {
+        return 0x80L << (56 - (i << 3));
+    }
+
+    private long LOADBYTES(final byte[] bytes, int inOff, int n)
+    {
+        long x = 0;
+        for (int i = 0; i < n; ++i)
+        {
+            x |= (bytes[i + inOff] & 0xFFL) << ((7 - i) << 3);
+        }
+        return x;
+    }
+
+    private void STOREBYTES(byte[] bytes, int inOff, long w, int n)
+    {
+        for (int i = 0; i < n; ++i)
+        {
+            bytes[i + inOff] = (byte)(w >>> ((7 - i) << 3));
+        }
+    }
+
+    @Override
+    public String getAlgorithmName()
+    {
+        return algorithmName;
+    }
+
+    @Override
+    public int getDigestSize()
+    {
+        return CRYPTO_BYTES;
+    }
+
+    @Override
+    public int getByteLength()
+    {
+        return 8;
+    }
+
+    @Override
+    public void update(byte in)
+    {
+        buffer.write(in);
+    }
+
+    @Override
+    public void update(byte[] input, int inOff, int len)
+    {
+        if ((inOff + len) > input.length)
+        {
+            throw new DataLengthException("input buffer too short");
+        }
+        buffer.write(input, inOff, len);
+    }
+
+    @Override
+    public int doFinal(byte[] output, int outOff)
+    {
+        if (CRYPTO_BYTES + outOff > output.length)
+        {
+            throw new OutputLengthException("output buffer is too short");
+        }
+        byte[] input = buffer.toByteArray();
+        int len = buffer.size();
+        int inOff = 0;
+        /* absorb full plaintext blocks */
+        int ASCON_HASH_RATE = 8;
+        while (len >= ASCON_HASH_RATE)
+        {
+            x0 ^= LOADBYTES(input, inOff, 8);
+            P(ASCON_PB_ROUNDS);
+            inOff += ASCON_HASH_RATE;
+            len -= ASCON_HASH_RATE;
+        }
+        /* absorb final plaintext block */
+        x0 ^= LOADBYTES(input, inOff, len);
+        x0 ^= PAD(len);
+        int ASCON_PA_ROUNDS = 12;
+        P(ASCON_PA_ROUNDS);
+        /* squeeze full output blocks */
+        len = CRYPTO_BYTES;
+        while (len > ASCON_HASH_RATE)
+        {
+            STOREBYTES(output, outOff, x0, 8);
+            P(ASCON_PB_ROUNDS);
+            outOff += ASCON_HASH_RATE;
+            len -= ASCON_HASH_RATE;
+        }
+        /* squeeze final output block */
+        STOREBYTES(output, outOff, x0, len);
+        reset();
+        return CRYPTO_BYTES;
+    }
+
+    @Override
+    public void reset()
+    {
+        buffer.reset();
+        /* initialize */
+        switch (asconParameters)
+        {
+        case AsconHashA:
+            x0 = 92044056785660070L;
+            x1 = 8326807761760157607L;
+            x2 = 3371194088139667532L;
+            x3 = -2956994353054992515L;
+            x4 = -6828509670848688761L;
+            break;
+        case AsconHash:
+            x0 = -1255492011513352131L;
+            x1 = -8380609354527731710L;
+            x2 = -5437372128236807582L;
+            x3 = 4834782570098516968L;
+            x4 = 3787428097924915520L;
+            break;
+        }
+    }
+}
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/digests/GeneralDigest.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/digests/GeneralDigest.java
index 75038bd..f62783e 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/digests/GeneralDigest.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/digests/GeneralDigest.java
@@ -1,4 +1,5 @@
 /* GENERATED SOURCE. DO NOT MODIFY. */
+// BEGIN Android-changed: maintain old behaviour
 package com.android.internal.org.bouncycastle.crypto.digests;
 
 import com.android.internal.org.bouncycastle.crypto.ExtendedDigest;
@@ -160,3 +161,4 @@
 
     protected abstract void processBlock();
 }
+// END Android-changed: maintain old behaviour
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/digests/Haraka256Digest.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/digests/Haraka256Digest.java
new file mode 100644
index 0000000..15000f5
--- /dev/null
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/digests/Haraka256Digest.java
@@ -0,0 +1,156 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.crypto.digests;
+
+import com.android.internal.org.bouncycastle.crypto.CryptoServicePurpose;
+import com.android.internal.org.bouncycastle.crypto.CryptoServicesRegistrar;
+import com.android.internal.org.bouncycastle.util.Arrays;
+import com.android.internal.org.bouncycastle.util.Bytes;
+
+/**
+ * Haraka-256 v2, https://eprint.iacr.org/2016/098.pdf
+ * <p>
+ * Haraka256-256 with reference to Python Reference Impl from: https://github.com/kste/haraka
+ * </p>
+ * @hide This class is not part of the Android public SDK API
+ */
+public class Haraka256Digest
+    extends HarakaBase
+{
+    private void mix256(byte[][] s1, byte[][] s2)
+    {
+        System.arraycopy(s1[0], 0, s2[0], 0, 4);
+        System.arraycopy(s1[1], 0, s2[0], 4, 4);
+        System.arraycopy(s1[0], 4, s2[0], 8, 4);
+        System.arraycopy(s1[1], 4, s2[0], 12, 4);
+
+        System.arraycopy(s1[0], 8, s2[1], 0, 4);
+        System.arraycopy(s1[1], 8, s2[1], 4, 4);
+        System.arraycopy(s1[0], 12, s2[1], 8, 4);
+        System.arraycopy(s1[1], 12, s2[1], 12, 4);
+    }
+
+    private int haraka256256(byte[] msg, byte[] out, int outOff)
+    {
+        byte[][] s1 = new byte[2][16];
+        byte[][] s2 = new byte[2][16];
+
+        System.arraycopy(msg, 0, s1[0], 0, 16);
+        System.arraycopy(msg, 16, s1[1], 0, 16);
+
+        s1[0] = aesEnc(s1[0], RC[0]);
+        s1[1] = aesEnc(s1[1], RC[1]);
+        s1[0] = aesEnc(s1[0], RC[2]);
+        s1[1] = aesEnc(s1[1], RC[3]);
+        mix256(s1, s2);
+
+        s1[0] = aesEnc(s2[0], RC[4]);
+        s1[1] = aesEnc(s2[1], RC[5]);
+        s1[0] = aesEnc(s1[0], RC[6]);
+        s1[1] = aesEnc(s1[1], RC[7]);
+        mix256(s1, s2);
+
+        s1[0] = aesEnc(s2[0], RC[8]);
+        s1[1] = aesEnc(s2[1], RC[9]);
+        s1[0] = aesEnc(s1[0], RC[10]);
+        s1[1] = aesEnc(s1[1], RC[11]);
+        mix256(s1, s2);
+
+        s1[0] = aesEnc(s2[0], RC[12]);
+        s1[1] = aesEnc(s2[1], RC[13]);
+        s1[0] = aesEnc(s1[0], RC[14]);
+        s1[1] = aesEnc(s1[1], RC[15]);
+        mix256(s1, s2);
+
+        s1[0] = aesEnc(s2[0], RC[16]);
+        s1[1] = aesEnc(s2[1], RC[17]);
+        s1[0] = aesEnc(s1[0], RC[18]);
+        s1[1] = aesEnc(s1[1], RC[19]);
+        mix256(s1, s2);
+
+        Bytes.xor(16, s2[0], 0, msg,  0, out, outOff);
+        Bytes.xor(16, s2[1], 0, msg, 16, out, outOff + 16);
+
+        return DIGEST_SIZE;
+    }
+
+    private final byte[] buffer;
+    private int off;
+
+    private final CryptoServicePurpose purpose;
+
+
+    public Haraka256Digest()
+    {
+        this(CryptoServicePurpose.ANY);
+    }
+
+    public Haraka256Digest(CryptoServicePurpose purpose)
+    {
+        this.purpose = purpose;
+
+        this.buffer = new byte[32];
+
+        CryptoServicesRegistrar.checkConstraints(Utils.getDefaultProperties(this, getDigestSize()*4, purpose));
+    }
+
+    public Haraka256Digest(Haraka256Digest digest)
+    {
+        this.purpose = digest.purpose;
+
+        this.buffer = Arrays.clone(digest.buffer);
+        this.off = digest.off;
+
+        CryptoServicesRegistrar.checkConstraints(Utils.getDefaultProperties(this, getDigestSize()*4, purpose));
+    }
+
+    public String getAlgorithmName()
+    {
+        return "Haraka-256";
+    }
+
+    public void update(byte in)
+    {
+        if (off > 32 - 1)
+        {
+            throw new IllegalArgumentException("total input cannot be more than 32 bytes");
+        }
+
+        buffer[off++] = in;
+    }
+
+    public void update(byte[] in, int inOff, int len)
+    {
+        if (off > 32 - len)
+        {
+            throw new IllegalArgumentException("total input cannot be more than 32 bytes");
+        }
+
+        System.arraycopy(in, inOff, buffer, off, len);
+        off += len;
+    }
+
+    public int doFinal(byte[] out, int outOff)
+    {
+        if (off != 32)
+        {
+            throw new IllegalStateException("input must be exactly 32 bytes");
+        }
+
+        if (out.length - outOff < 32)
+        {
+            throw new IllegalArgumentException("output too short to receive digest");
+        }
+
+        int rv = haraka256256(buffer, out, outOff);
+
+        reset();
+
+        return rv;
+    }
+
+    public void reset()
+    {
+        off = 0;
+        Arrays.clear(buffer);
+    }
+}
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/digests/Haraka512Digest.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/digests/Haraka512Digest.java
new file mode 100644
index 0000000..69a8654
--- /dev/null
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/digests/Haraka512Digest.java
@@ -0,0 +1,191 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.crypto.digests;
+
+import com.android.internal.org.bouncycastle.crypto.CryptoServicePurpose;
+import com.android.internal.org.bouncycastle.util.Arrays;
+import com.android.internal.org.bouncycastle.util.Bytes;
+
+/**
+ * Haraka-512 v2, https://eprint.iacr.org/2016/098.pdf
+ * <p>
+ * Haraka512-256 with reference to Python Reference Impl from: https://github.com/kste/haraka
+ * </p>
+ * @hide This class is not part of the Android public SDK API
+ */
+public class Haraka512Digest
+    extends HarakaBase
+{
+    private final byte[] buffer;
+    private int off;
+
+    private final CryptoServicePurpose purpose;
+
+
+    public Haraka512Digest()
+    {
+        this(CryptoServicePurpose.ANY);
+    }
+    public Haraka512Digest(CryptoServicePurpose purpose)
+    {
+        this.purpose = purpose;
+
+        this.buffer = new byte[64];
+    }
+
+    public Haraka512Digest(Haraka512Digest digest)
+    {
+        this.purpose = digest.purpose;
+
+        this.buffer = Arrays.clone(digest.buffer);
+        this.off = digest.off;
+    }
+
+    private void mix512(byte[][] s1, byte[][] s2)
+    {
+        System.arraycopy(s1[0], 12, s2[0], 0, 4);
+        System.arraycopy(s1[2], 12, s2[0], 4, 4);
+        System.arraycopy(s1[1], 12, s2[0], 8, 4);
+        System.arraycopy(s1[3], 12, s2[0], 12, 4);
+
+        System.arraycopy(s1[2], 0, s2[1], 0, 4);
+        System.arraycopy(s1[0], 0, s2[1], 4, 4);
+        System.arraycopy(s1[3], 0, s2[1], 8, 4);
+        System.arraycopy(s1[1], 0, s2[1], 12, 4);
+
+        System.arraycopy(s1[2], 4, s2[2], 0, 4);
+        System.arraycopy(s1[0], 4, s2[2], 4, 4);
+        System.arraycopy(s1[3], 4, s2[2], 8, 4);
+        System.arraycopy(s1[1], 4, s2[2], 12, 4);
+
+        System.arraycopy(s1[0], 8, s2[3], 0, 4);
+        System.arraycopy(s1[2], 8, s2[3], 4, 4);
+        System.arraycopy(s1[1], 8, s2[3], 8, 4);
+        System.arraycopy(s1[3], 8, s2[3], 12, 4);
+    }
+
+    private int haraka512256(byte[] msg, byte[] out, int outOff)
+    {
+        byte[][] s1 = new byte[4][16];
+        byte[][] s2 = new byte[4][16];
+
+        //-- Unrolled version of above.
+
+        System.arraycopy(msg, 0, s1[0], 0, 16);
+        System.arraycopy(msg, 16, s1[1], 0, 16);
+        System.arraycopy(msg, 32, s1[2], 0, 16);
+        System.arraycopy(msg, 48, s1[3], 0, 16);
+
+        s1[0] = aesEnc(s1[0], RC[0]);
+        s1[1] = aesEnc(s1[1], RC[1]);
+        s1[2] = aesEnc(s1[2], RC[2]);
+        s1[3] = aesEnc(s1[3], RC[3]);
+        s1[0] = aesEnc(s1[0], RC[4]);
+        s1[1] = aesEnc(s1[1], RC[5]);
+        s1[2] = aesEnc(s1[2], RC[6]);
+        s1[3] = aesEnc(s1[3], RC[7]);
+        mix512(s1, s2);
+
+        s1[0] = aesEnc(s2[0], RC[8]);
+        s1[1] = aesEnc(s2[1], RC[9]);
+        s1[2] = aesEnc(s2[2], RC[10]);
+        s1[3] = aesEnc(s2[3], RC[11]);
+        s1[0] = aesEnc(s1[0], RC[12]);
+        s1[1] = aesEnc(s1[1], RC[13]);
+        s1[2] = aesEnc(s1[2], RC[14]);
+        s1[3] = aesEnc(s1[3], RC[15]);
+        mix512(s1, s2);
+
+        s1[0] = aesEnc(s2[0], RC[16]);
+        s1[1] = aesEnc(s2[1], RC[17]);
+        s1[2] = aesEnc(s2[2], RC[18]);
+        s1[3] = aesEnc(s2[3], RC[19]);
+        s1[0] = aesEnc(s1[0], RC[20]);
+        s1[1] = aesEnc(s1[1], RC[21]);
+        s1[2] = aesEnc(s1[2], RC[22]);
+        s1[3] = aesEnc(s1[3], RC[23]);
+        mix512(s1, s2);
+
+        s1[0] = aesEnc(s2[0], RC[24]);
+        s1[1] = aesEnc(s2[1], RC[25]);
+        s1[2] = aesEnc(s2[2], RC[26]);
+        s1[3] = aesEnc(s2[3], RC[27]);
+        s1[0] = aesEnc(s1[0], RC[28]);
+        s1[1] = aesEnc(s1[1], RC[29]);
+        s1[2] = aesEnc(s1[2], RC[30]);
+        s1[3] = aesEnc(s1[3], RC[31]);
+        mix512(s1, s2);
+
+        s1[0] = aesEnc(s2[0], RC[32]);
+        s1[1] = aesEnc(s2[1], RC[33]);
+        s1[2] = aesEnc(s2[2], RC[34]);
+        s1[3] = aesEnc(s2[3], RC[35]);
+        s1[0] = aesEnc(s1[0], RC[36]);
+        s1[1] = aesEnc(s1[1], RC[37]);
+        s1[2] = aesEnc(s1[2], RC[38]);
+        s1[3] = aesEnc(s1[3], RC[39]);
+        mix512(s1, s2);
+
+        Bytes.xor(16, s2[0], 0, msg,  0, s1[0], 0);
+        Bytes.xor(16, s2[1], 0, msg, 16, s1[1], 0);
+        Bytes.xor(16, s2[2], 0, msg, 32, s1[2], 0);
+        Bytes.xor(16, s2[3], 0, msg, 48, s1[3], 0);
+
+        System.arraycopy(s1[0], 8, out, outOff, 8);
+        System.arraycopy(s1[1], 8, out, outOff + 8, 8);
+        System.arraycopy(s1[2], 0, out, outOff + 16, 8);
+        System.arraycopy(s1[3], 0, out, outOff + 24, 8);
+
+        return DIGEST_SIZE;
+    }
+
+    public String getAlgorithmName()
+    {
+        return "Haraka-512";
+    }
+
+    public void update(byte in)
+    {
+        if (off > 64 - 1)
+        {
+            throw new IllegalArgumentException("total input cannot be more than 64 bytes");
+        }
+
+        buffer[off++] = in;
+    }
+
+    public void update(byte[] in, int inOff, int len)
+    {
+        if (off > 64 - len)
+        {
+            throw new IllegalArgumentException("total input cannot be more than 64 bytes");
+        }
+
+        System.arraycopy(in, inOff, buffer, off, len);
+        off += len;
+    }
+
+    public int doFinal(byte[] out, int outOff)
+    {
+        if (off != 64)
+        {
+            throw new IllegalStateException("input must be exactly 64 bytes");
+        }
+
+        if (out.length - outOff < 32)
+        {
+            throw new IllegalArgumentException("output too short to receive digest");
+        }
+
+        int rv = haraka512256(buffer, out, outOff);
+
+        reset();
+
+        return rv;
+    }
+
+    public void reset()
+    {
+        off = 0;
+        Arrays.clear(buffer);
+    }
+}
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/digests/HarakaBase.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/digests/HarakaBase.java
new file mode 100644
index 0000000..785e60b
--- /dev/null
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/digests/HarakaBase.java
@@ -0,0 +1,149 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.crypto.digests;
+
+import com.android.internal.org.bouncycastle.crypto.Digest;
+import com.android.internal.org.bouncycastle.util.Bytes;
+
+/**
+ * Base class for Haraka v2, https://eprint.iacr.org/2016/098.pdf
+ * @hide This class is not part of the Android public SDK API
+ */
+public abstract class HarakaBase
+    implements Digest
+{
+    protected static final int DIGEST_SIZE = 32;
+
+    // Haraka round constants
+    static final byte[][] RC = new byte[][]
+    {
+        new byte[]{ (byte)0x9D, (byte)0x7B, (byte)0x81, (byte)0x75, (byte)0xF0, (byte)0xFE, (byte)0xC5, (byte)0xB2, (byte)0x0A, (byte)0xC0, (byte)0x20, (byte)0xE6, (byte)0x4C, (byte)0x70, (byte)0x84, (byte)0x06 },
+        new byte[]{ (byte)0x17, (byte)0xF7, (byte)0x08, (byte)0x2F, (byte)0xA4, (byte)0x6B, (byte)0x0F, (byte)0x64, (byte)0x6B, (byte)0xA0, (byte)0xF3, (byte)0x88, (byte)0xE1, (byte)0xB4, (byte)0x66, (byte)0x8B },
+        new byte[]{ (byte)0x14, (byte)0x91, (byte)0x02, (byte)0x9F, (byte)0x60, (byte)0x9D, (byte)0x02, (byte)0xCF, (byte)0x98, (byte)0x84, (byte)0xF2, (byte)0x53, (byte)0x2D, (byte)0xDE, (byte)0x02, (byte)0x34 },
+        new byte[]{ (byte)0x79, (byte)0x4F, (byte)0x5B, (byte)0xFD, (byte)0xAF, (byte)0xBC, (byte)0xF3, (byte)0xBB, (byte)0x08, (byte)0x4F, (byte)0x7B, (byte)0x2E, (byte)0xE6, (byte)0xEA, (byte)0xD6, (byte)0x0E },
+        new byte[]{ (byte)0x44, (byte)0x70, (byte)0x39, (byte)0xBE, (byte)0x1C, (byte)0xCD, (byte)0xEE, (byte)0x79, (byte)0x8B, (byte)0x44, (byte)0x72, (byte)0x48, (byte)0xCB, (byte)0xB0, (byte)0xCF, (byte)0xCB },
+        new byte[]{ (byte)0x7B, (byte)0x05, (byte)0x8A, (byte)0x2B, (byte)0xED, (byte)0x35, (byte)0x53, (byte)0x8D, (byte)0xB7, (byte)0x32, (byte)0x90, (byte)0x6E, (byte)0xEE, (byte)0xCD, (byte)0xEA, (byte)0x7E },
+        new byte[]{ (byte)0x1B, (byte)0xEF, (byte)0x4F, (byte)0xDA, (byte)0x61, (byte)0x27, (byte)0x41, (byte)0xE2, (byte)0xD0, (byte)0x7C, (byte)0x2E, (byte)0x5E, (byte)0x43, (byte)0x8F, (byte)0xC2, (byte)0x67 },
+        new byte[]{ (byte)0x3B, (byte)0x0B, (byte)0xC7, (byte)0x1F, (byte)0xE2, (byte)0xFD, (byte)0x5F, (byte)0x67, (byte)0x07, (byte)0xCC, (byte)0xCA, (byte)0xAF, (byte)0xB0, (byte)0xD9, (byte)0x24, (byte)0x29 },
+        new byte[]{ (byte)0xEE, (byte)0x65, (byte)0xD4, (byte)0xB9, (byte)0xCA, (byte)0x8F, (byte)0xDB, (byte)0xEC, (byte)0xE9, (byte)0x7F, (byte)0x86, (byte)0xE6, (byte)0xF1, (byte)0x63, (byte)0x4D, (byte)0xAB },
+        new byte[]{ (byte)0x33, (byte)0x7E, (byte)0x03, (byte)0xAD, (byte)0x4F, (byte)0x40, (byte)0x2A, (byte)0x5B, (byte)0x64, (byte)0xCD, (byte)0xB7, (byte)0xD4, (byte)0x84, (byte)0xBF, (byte)0x30, (byte)0x1C },
+        new byte[]{ (byte)0x00, (byte)0x98, (byte)0xF6, (byte)0x8D, (byte)0x2E, (byte)0x8B, (byte)0x02, (byte)0x69, (byte)0xBF, (byte)0x23, (byte)0x17, (byte)0x94, (byte)0xB9, (byte)0x0B, (byte)0xCC, (byte)0xB2 },
+        new byte[]{ (byte)0x8A, (byte)0x2D, (byte)0x9D, (byte)0x5C, (byte)0xC8, (byte)0x9E, (byte)0xAA, (byte)0x4A, (byte)0x72, (byte)0x55, (byte)0x6F, (byte)0xDE, (byte)0xA6, (byte)0x78, (byte)0x04, (byte)0xFA },
+        new byte[]{ (byte)0xD4, (byte)0x9F, (byte)0x12, (byte)0x29, (byte)0x2E, (byte)0x4F, (byte)0xFA, (byte)0x0E, (byte)0x12, (byte)0x2A, (byte)0x77, (byte)0x6B, (byte)0x2B, (byte)0x9F, (byte)0xB4, (byte)0xDF },
+        new byte[]{ (byte)0xEE, (byte)0x12, (byte)0x6A, (byte)0xBB, (byte)0xAE, (byte)0x11, (byte)0xD6, (byte)0x32, (byte)0x36, (byte)0xA2, (byte)0x49, (byte)0xF4, (byte)0x44, (byte)0x03, (byte)0xA1, (byte)0x1E },
+        new byte[]{ (byte)0xA6, (byte)0xEC, (byte)0xA8, (byte)0x9C, (byte)0xC9, (byte)0x00, (byte)0x96, (byte)0x5F, (byte)0x84, (byte)0x00, (byte)0x05, (byte)0x4B, (byte)0x88, (byte)0x49, (byte)0x04, (byte)0xAF },
+        new byte[]{ (byte)0xEC, (byte)0x93, (byte)0xE5, (byte)0x27, (byte)0xE3, (byte)0xC7, (byte)0xA2, (byte)0x78, (byte)0x4F, (byte)0x9C, (byte)0x19, (byte)0x9D, (byte)0xD8, (byte)0x5E, (byte)0x02, (byte)0x21 },
+        new byte[]{ (byte)0x73, (byte)0x01, (byte)0xD4, (byte)0x82, (byte)0xCD, (byte)0x2E, (byte)0x28, (byte)0xB9, (byte)0xB7, (byte)0xC9, (byte)0x59, (byte)0xA7, (byte)0xF8, (byte)0xAA, (byte)0x3A, (byte)0xBF },
+        new byte[]{ (byte)0x6B, (byte)0x7D, (byte)0x30, (byte)0x10, (byte)0xD9, (byte)0xEF, (byte)0xF2, (byte)0x37, (byte)0x17, (byte)0xB0, (byte)0x86, (byte)0x61, (byte)0x0D, (byte)0x70, (byte)0x60, (byte)0x62 },
+        new byte[]{ (byte)0xC6, (byte)0x9A, (byte)0xFC, (byte)0xF6, (byte)0x53, (byte)0x91, (byte)0xC2, (byte)0x81, (byte)0x43, (byte)0x04, (byte)0x30, (byte)0x21, (byte)0xC2, (byte)0x45, (byte)0xCA, (byte)0x5A },
+        new byte[]{ (byte)0x3A, (byte)0x94, (byte)0xD1, (byte)0x36, (byte)0xE8, (byte)0x92, (byte)0xAF, (byte)0x2C, (byte)0xBB, (byte)0x68, (byte)0x6B, (byte)0x22, (byte)0x3C, (byte)0x97, (byte)0x23, (byte)0x92 },
+        new byte[]{ (byte)0xB4, (byte)0x71, (byte)0x10, (byte)0xE5, (byte)0x58, (byte)0xB9, (byte)0xBA, (byte)0x6C, (byte)0xEB, (byte)0x86, (byte)0x58, (byte)0x22, (byte)0x38, (byte)0x92, (byte)0xBF, (byte)0xD3 },
+        new byte[]{ (byte)0x8D, (byte)0x12, (byte)0xE1, (byte)0x24, (byte)0xDD, (byte)0xFD, (byte)0x3D, (byte)0x93, (byte)0x77, (byte)0xC6, (byte)0xF0, (byte)0xAE, (byte)0xE5, (byte)0x3C, (byte)0x86, (byte)0xDB },
+        new byte[]{ (byte)0xB1, (byte)0x12, (byte)0x22, (byte)0xCB, (byte)0xE3, (byte)0x8D, (byte)0xE4, (byte)0x83, (byte)0x9C, (byte)0xA0, (byte)0xEB, (byte)0xFF, (byte)0x68, (byte)0x62, (byte)0x60, (byte)0xBB },
+        new byte[]{ (byte)0x7D, (byte)0xF7, (byte)0x2B, (byte)0xC7, (byte)0x4E, (byte)0x1A, (byte)0xB9, (byte)0x2D, (byte)0x9C, (byte)0xD1, (byte)0xE4, (byte)0xE2, (byte)0xDC, (byte)0xD3, (byte)0x4B, (byte)0x73 },
+        new byte[]{ (byte)0x4E, (byte)0x92, (byte)0xB3, (byte)0x2C, (byte)0xC4, (byte)0x15, (byte)0x14, (byte)0x4B, (byte)0x43, (byte)0x1B, (byte)0x30, (byte)0x61, (byte)0xC3, (byte)0x47, (byte)0xBB, (byte)0x43 },
+        new byte[]{ (byte)0x99, (byte)0x68, (byte)0xEB, (byte)0x16, (byte)0xDD, (byte)0x31, (byte)0xB2, (byte)0x03, (byte)0xF6, (byte)0xEF, (byte)0x07, (byte)0xE7, (byte)0xA8, (byte)0x75, (byte)0xA7, (byte)0xDB },
+        new byte[]{ (byte)0x2C, (byte)0x47, (byte)0xCA, (byte)0x7E, (byte)0x02, (byte)0x23, (byte)0x5E, (byte)0x8E, (byte)0x77, (byte)0x59, (byte)0x75, (byte)0x3C, (byte)0x4B, (byte)0x61, (byte)0xF3, (byte)0x6D },
+        new byte[]{ (byte)0xF9, (byte)0x17, (byte)0x86, (byte)0xB8, (byte)0xB9, (byte)0xE5, (byte)0x1B, (byte)0x6D, (byte)0x77, (byte)0x7D, (byte)0xDE, (byte)0xD6, (byte)0x17, (byte)0x5A, (byte)0xA7, (byte)0xCD },
+        new byte[]{ (byte)0x5D, (byte)0xEE, (byte)0x46, (byte)0xA9, (byte)0x9D, (byte)0x06, (byte)0x6C, (byte)0x9D, (byte)0xAA, (byte)0xE9, (byte)0xA8, (byte)0x6B, (byte)0xF0, (byte)0x43, (byte)0x6B, (byte)0xEC },
+        new byte[]{ (byte)0xC1, (byte)0x27, (byte)0xF3, (byte)0x3B, (byte)0x59, (byte)0x11, (byte)0x53, (byte)0xA2, (byte)0x2B, (byte)0x33, (byte)0x57, (byte)0xF9, (byte)0x50, (byte)0x69, (byte)0x1E, (byte)0xCB },
+        new byte[]{ (byte)0xD9, (byte)0xD0, (byte)0x0E, (byte)0x60, (byte)0x53, (byte)0x03, (byte)0xED, (byte)0xE4, (byte)0x9C, (byte)0x61, (byte)0xDA, (byte)0x00, (byte)0x75, (byte)0x0C, (byte)0xEE, (byte)0x2C },
+        new byte[]{ (byte)0x50, (byte)0xA3, (byte)0xA4, (byte)0x63, (byte)0xBC, (byte)0xBA, (byte)0xBB, (byte)0x80, (byte)0xAB, (byte)0x0C, (byte)0xE9, (byte)0x96, (byte)0xA1, (byte)0xA5, (byte)0xB1, (byte)0xF0 },
+        new byte[]{ (byte)0x39, (byte)0xCA, (byte)0x8D, (byte)0x93, (byte)0x30, (byte)0xDE, (byte)0x0D, (byte)0xAB, (byte)0x88, (byte)0x29, (byte)0x96, (byte)0x5E, (byte)0x02, (byte)0xB1, (byte)0x3D, (byte)0xAE },
+        new byte[]{ (byte)0x42, (byte)0xB4, (byte)0x75, (byte)0x2E, (byte)0xA8, (byte)0xF3, (byte)0x14, (byte)0x88, (byte)0x0B, (byte)0xA4, (byte)0x54, (byte)0xD5, (byte)0x38, (byte)0x8F, (byte)0xBB, (byte)0x17 },
+        new byte[]{ (byte)0xF6, (byte)0x16, (byte)0x0A, (byte)0x36, (byte)0x79, (byte)0xB7, (byte)0xB6, (byte)0xAE, (byte)0xD7, (byte)0x7F, (byte)0x42, (byte)0x5F, (byte)0x5B, (byte)0x8A, (byte)0xBB, (byte)0x34 },
+        new byte[]{ (byte)0xDE, (byte)0xAF, (byte)0xBA, (byte)0xFF, (byte)0x18, (byte)0x59, (byte)0xCE, (byte)0x43, (byte)0x38, (byte)0x54, (byte)0xE5, (byte)0xCB, (byte)0x41, (byte)0x52, (byte)0xF6, (byte)0x26 },
+        new byte[]{ (byte)0x78, (byte)0xC9, (byte)0x9E, (byte)0x83, (byte)0xF7, (byte)0x9C, (byte)0xCA, (byte)0xA2, (byte)0x6A, (byte)0x02, (byte)0xF3, (byte)0xB9, (byte)0x54, (byte)0x9A, (byte)0xE9, (byte)0x4C },
+        new byte[]{ (byte)0x35, (byte)0x12, (byte)0x90, (byte)0x22, (byte)0x28, (byte)0x6E, (byte)0xC0, (byte)0x40, (byte)0xBE, (byte)0xF7, (byte)0xDF, (byte)0x1B, (byte)0x1A, (byte)0xA5, (byte)0x51, (byte)0xAE },
+        new byte[]{ (byte)0xCF, (byte)0x59, (byte)0xA6, (byte)0x48, (byte)0x0F, (byte)0xBC, (byte)0x73, (byte)0xC1, (byte)0x2B, (byte)0xD2, (byte)0x7E, (byte)0xBA, (byte)0x3C, (byte)0x61, (byte)0xC1, (byte)0xA0 },
+        new byte[]{ (byte)0xA1, (byte)0x9D, (byte)0xC5, (byte)0xE9, (byte)0xFD, (byte)0xBD, (byte)0xD6, (byte)0x4A, (byte)0x88, (byte)0x82, (byte)0x28, (byte)0x02, (byte)0x03, (byte)0xCC, (byte)0x6A, (byte)0x75 },
+    };
+
+    private static final byte[][] S = new byte[][]{
+        { (byte)0x63, (byte)0x7C, (byte)0x77, (byte)0x7B, (byte)0xF2, (byte)0x6B, (byte)0x6F, (byte)0xC5, (byte)0x30, (byte)0x01, (byte)0x67, (byte)0x2B, (byte)0xFE, (byte)0xD7, (byte)0xAB, (byte)0x76 },
+        { (byte)0xCA, (byte)0x82, (byte)0xC9, (byte)0x7D, (byte)0xFA, (byte)0x59, (byte)0x47, (byte)0xF0, (byte)0xAD, (byte)0xD4, (byte)0xA2, (byte)0xAF, (byte)0x9C, (byte)0xA4, (byte)0x72, (byte)0xC0 },
+        { (byte)0xB7, (byte)0xFD, (byte)0x93, (byte)0x26, (byte)0x36, (byte)0x3F, (byte)0xF7, (byte)0xCC, (byte)0x34, (byte)0xA5, (byte)0xE5, (byte)0xF1, (byte)0x71, (byte)0xD8, (byte)0x31, (byte)0x15 },
+        { (byte)0x04, (byte)0xC7, (byte)0x23, (byte)0xC3, (byte)0x18, (byte)0x96, (byte)0x05, (byte)0x9A, (byte)0x07, (byte)0x12, (byte)0x80, (byte)0xE2, (byte)0xEB, (byte)0x27, (byte)0xB2, (byte)0x75 },
+        { (byte)0x09, (byte)0x83, (byte)0x2C, (byte)0x1A, (byte)0x1B, (byte)0x6E, (byte)0x5A, (byte)0xA0, (byte)0x52, (byte)0x3B, (byte)0xD6, (byte)0xB3, (byte)0x29, (byte)0xE3, (byte)0x2F, (byte)0x84 },
+        { (byte)0x53, (byte)0xD1, (byte)0x00, (byte)0xED, (byte)0x20, (byte)0xFC, (byte)0xB1, (byte)0x5B, (byte)0x6A, (byte)0xCB, (byte)0xBE, (byte)0x39, (byte)0x4A, (byte)0x4C, (byte)0x58, (byte)0xCF },
+        { (byte)0xD0, (byte)0xEF, (byte)0xAA, (byte)0xFB, (byte)0x43, (byte)0x4D, (byte)0x33, (byte)0x85, (byte)0x45, (byte)0xF9, (byte)0x02, (byte)0x7F, (byte)0x50, (byte)0x3C, (byte)0x9F, (byte)0xA8 },
+        { (byte)0x51, (byte)0xA3, (byte)0x40, (byte)0x8F, (byte)0x92, (byte)0x9D, (byte)0x38, (byte)0xF5, (byte)0xBC, (byte)0xB6, (byte)0xDA, (byte)0x21, (byte)0x10, (byte)0xFF, (byte)0xF3, (byte)0xD2 },
+        { (byte)0xCD, (byte)0x0C, (byte)0x13, (byte)0xEC, (byte)0x5F, (byte)0x97, (byte)0x44, (byte)0x17, (byte)0xC4, (byte)0xA7, (byte)0x7E, (byte)0x3D, (byte)0x64, (byte)0x5D, (byte)0x19, (byte)0x73 },
+        { (byte)0x60, (byte)0x81, (byte)0x4F, (byte)0xDC, (byte)0x22, (byte)0x2A, (byte)0x90, (byte)0x88, (byte)0x46, (byte)0xEE, (byte)0xB8, (byte)0x14, (byte)0xDE, (byte)0x5E, (byte)0x0B, (byte)0xDB },
+        { (byte)0xE0, (byte)0x32, (byte)0x3A, (byte)0x0A, (byte)0x49, (byte)0x06, (byte)0x24, (byte)0x5C, (byte)0xC2, (byte)0xD3, (byte)0xAC, (byte)0x62, (byte)0x91, (byte)0x95, (byte)0xE4, (byte)0x79 },
+        { (byte)0xE7, (byte)0xC8, (byte)0x37, (byte)0x6D, (byte)0x8D, (byte)0xD5, (byte)0x4E, (byte)0xA9, (byte)0x6C, (byte)0x56, (byte)0xF4, (byte)0xEA, (byte)0x65, (byte)0x7A, (byte)0xAE, (byte)0x08 },
+        { (byte)0xBA, (byte)0x78, (byte)0x25, (byte)0x2E, (byte)0x1C, (byte)0xA6, (byte)0xB4, (byte)0xC6, (byte)0xE8, (byte)0xDD, (byte)0x74, (byte)0x1F, (byte)0x4B, (byte)0xBD, (byte)0x8B, (byte)0x8A },
+        { (byte)0x70, (byte)0x3E, (byte)0xB5, (byte)0x66, (byte)0x48, (byte)0x03, (byte)0xF6, (byte)0x0E, (byte)0x61, (byte)0x35, (byte)0x57, (byte)0xB9, (byte)0x86, (byte)0xC1, (byte)0x1D, (byte)0x9E },
+        { (byte)0xE1, (byte)0xF8, (byte)0x98, (byte)0x11, (byte)0x69, (byte)0xD9, (byte)0x8E, (byte)0x94, (byte)0x9B, (byte)0x1E, (byte)0x87, (byte)0xE9, (byte)0xCE, (byte)0x55, (byte)0x28, (byte)0xDF },
+        { (byte)0x8C, (byte)0xA1, (byte)0x89, (byte)0x0D, (byte)0xBF, (byte)0xE6, (byte)0x42, (byte)0x68, (byte)0x41, (byte)0x99, (byte)0x2D, (byte)0x0F, (byte)0xB0, (byte)0x54, (byte)0xBB, (byte)0x16 },
+    };
+
+    static byte sBox(byte x)
+    {
+        return S[(((x & 0xFF) >>> 4))][x & 0xF];
+    }
+
+    static byte[] subBytes(byte[] s)
+    {
+        byte[] out = new byte[s.length];
+        out[0] = sBox(s[0]);
+        out[1] = sBox(s[1]);
+        out[2] = sBox(s[2]);
+        out[3] = sBox(s[3]);
+        out[4] = sBox(s[4]);
+        out[5] = sBox(s[5]);
+        out[6] = sBox(s[6]);
+        out[7] = sBox(s[7]);
+        out[8] = sBox(s[8]);
+        out[9] = sBox(s[9]);
+        out[10] = sBox(s[10]);
+        out[11] = sBox(s[11]);
+        out[12] = sBox(s[12]);
+        out[13] = sBox(s[13]);
+        out[14] = sBox(s[14]);
+        out[15] = sBox(s[15]);
+        return out;
+    }
+
+    static byte[] shiftRows(byte[] s)
+    {
+        return new byte[]{
+            s[0], s[5], s[10], s[15],
+            s[4], s[9], s[14], s[3],
+            s[8], s[13], s[2], s[7],
+            s[12], s[1], s[6], s[11]
+        };
+    }
+
+    static byte[] aesEnc(byte[] s, byte[] rk)
+    {
+        s = subBytes(s);
+        s = shiftRows(s);
+        s = mixColumns(s);
+        Bytes.xorTo(16, rk, s);
+        return s;
+    }
+
+    static byte mulX(byte p)
+    {
+        return (byte)(((p & 0x7F) << 1) ^ (((p & 0x80) >> 7) * 0x1B));
+    }
+
+    private static byte[] mixColumns(byte[] s)
+    {
+        byte[] out = new byte[s.length];
+        int j = 0;
+        for (int i = 0; i < 4; i++)
+        {
+            out[j++] = (byte)(mulX(s[4 * i]) ^ mulX(s[4 * i + 1]) ^ s[4 * i + 1] ^ s[4 * i + 2] ^ s[4 * i + 3]);
+            out[j++] = (byte)(s[4 * i] ^ mulX(s[4 * i + 1]) ^ mulX(s[4 * i + 2]) ^ s[4 * i + 2] ^ s[4 * i + 3]);
+            out[j++] = (byte)(s[4 * i] ^ s[4 * i + 1] ^ mulX(s[4 * i + 2]) ^ mulX(s[4 * i + 3]) ^ s[4 * i + 3]);
+            out[j++] = (byte)(mulX(s[4 * i]) ^ s[4 * i] ^ s[4 * i + 1] ^ s[4 * i + 2] ^ mulX(s[4 * i + 3]));
+        }
+        return out;
+    }
+
+    public int getDigestSize()
+    {
+        return DIGEST_SIZE;
+    }
+}
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/digests/ISAPDigest.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/digests/ISAPDigest.java
new file mode 100644
index 0000000..1bcb481
--- /dev/null
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/digests/ISAPDigest.java
@@ -0,0 +1,148 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.crypto.digests;
+
+import java.io.ByteArrayOutputStream;
+
+import com.android.internal.org.bouncycastle.crypto.DataLengthException;
+import com.android.internal.org.bouncycastle.crypto.Digest;
+import com.android.internal.org.bouncycastle.crypto.OutputLengthException;
+import com.android.internal.org.bouncycastle.util.Pack;
+
+/**
+ * ISAP Hash v2, https://isap.iaik.tugraz.at/
+ * https://csrc.nist.gov/CSRC/media/Projects/lightweight-cryptography/documents/finalist-round/updated-spec-doc/isap-spec-final.pdf
+ * <p>
+ * ISAP Hash v2 with reference to C Reference Impl from: https://github.com/isap-lwc/isap-code-package
+ * </p>
+ * @hide This class is not part of the Android public SDK API
+ */
+
+public class ISAPDigest
+    implements Digest
+{
+    private long x0, x1, x2, x3, x4;
+    private long t0, t1, t2, t3, t4;
+    private ByteArrayOutputStream buffer = new ByteArrayOutputStream();
+
+    private void ROUND(long C)
+    {
+        t0 = x0 ^ x1 ^ x2 ^ x3 ^ C ^ (x1 & (x0 ^ x2 ^ x4 ^ C));
+        t1 = x0 ^ x2 ^ x3 ^ x4 ^ C ^ ((x1 ^ x2 ^ C) & (x1 ^ x3));
+        t2 = x1 ^ x2 ^ x4 ^ C ^ (x3 & x4);
+        t3 = x0 ^ x1 ^ x2 ^ C ^ ((~x0) & (x3 ^ x4));
+        t4 = x1 ^ x3 ^ x4 ^ ((x0 ^ x4) & x1);
+        x0 = t0 ^ ROTR(t0, 19) ^ ROTR(t0, 28);
+        x1 = t1 ^ ROTR(t1, 39) ^ ROTR(t1, 61);
+        x2 = ~(t2 ^ ROTR(t2, 1) ^ ROTR(t2, 6));
+        x3 = t3 ^ ROTR(t3, 10) ^ ROTR(t3, 17);
+        x4 = t4 ^ ROTR(t4, 7) ^ ROTR(t4, 41);
+    }
+
+    private void P12()
+    {
+        ROUND(0xf0);
+        ROUND(0xe1);
+        ROUND(0xd2);
+        ROUND(0xc3);
+        ROUND(0xb4);
+        ROUND(0xa5);
+        ROUND(0x96);
+        ROUND(0x87);
+        ROUND(0x78);
+        ROUND(0x69);
+        ROUND(0x5a);
+        ROUND(0x4b);
+    }
+
+    private long ROTR(long x, long n)
+    {
+        return (x >>> n) | (x << (64 - n));
+    }
+
+    protected long U64BIG(long x)
+    {
+        return ((ROTR(x, 8) & (0xFF000000FF000000L)) | (ROTR(x, 24) & (0x00FF000000FF0000L)) |
+            (ROTR(x, 40) & (0x0000FF000000FF00L)) | (ROTR(x, 56) & (0x000000FF000000FFL)));
+    }
+
+    @Override
+    public String getAlgorithmName()
+    {
+        return "ISAP Hash";
+    }
+
+    @Override
+    public int getDigestSize()
+    {
+        return 32;
+    }
+
+    @Override
+    public void update(byte input)
+    {
+        buffer.write(input);
+    }
+
+    @Override
+    public void update(byte[] input, int inOff, int len)
+    {
+        if ((inOff + len) > input.length)
+        {
+            throw new DataLengthException("input buffer too short");
+        }
+        buffer.write(input, inOff, len);
+    }
+
+    @Override
+    public int doFinal(byte[] out, int outOff)
+    {
+        if (32 + outOff > out.length)
+        {
+            throw new OutputLengthException("output buffer is too short");
+        }
+        t0 = t1 = t2 = t3 = t4 = 0;
+        /* init state */
+        x0 = -1255492011513352131L;
+        x1 = -8380609354527731710L;
+        x2 = -5437372128236807582L;
+        x3 = 4834782570098516968L;
+        x4 = 3787428097924915520L;
+        /* absorb */
+        byte[] input = buffer.toByteArray();
+        int len = input.length;
+        long[] in64 = new long[len >> 3];
+        Pack.littleEndianToLong(input, 0, in64, 0, in64.length);
+        int idx = 0;
+        while (len >= 8)
+        {
+            x0 ^= U64BIG(in64[idx++]);
+            P12();
+            len -= 8;
+        }
+        /* absorb final input block */
+        x0 ^= 0x80L << ((7 - len) << 3);
+        while (len > 0)
+        {
+            x0 ^= (input[(idx << 3) + --len] & 0xFFL) << ((7 - len) << 3);
+        }
+        P12();
+        // squeeze
+        long[] out64 = new long[4];
+        for (idx = 0; idx < 3; ++idx)
+        {
+            out64[idx] = U64BIG(x0);
+            P12();
+        }
+        /* squeeze final output block */
+        out64[idx] = U64BIG(x0);
+        Pack.longToLittleEndian(out64, out, outOff);
+        buffer.reset();
+        return 32;
+    }
+
+    @Override
+    public void reset()
+    {
+        buffer.reset();
+    }
+}
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/digests/LongDigest.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/digests/LongDigest.java
index cc7411e..5346d09 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/digests/LongDigest.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/digests/LongDigest.java
@@ -1,6 +1,8 @@
 /* GENERATED SOURCE. DO NOT MODIFY. */
 package com.android.internal.org.bouncycastle.crypto.digests;
 
+import com.android.internal.org.bouncycastle.crypto.CryptoServiceProperties;
+import com.android.internal.org.bouncycastle.crypto.CryptoServicePurpose;
 import com.android.internal.org.bouncycastle.crypto.ExtendedDigest;
 import com.android.internal.org.bouncycastle.util.Memoable;
 import com.android.internal.org.bouncycastle.util.Pack;
@@ -14,6 +16,8 @@
 {
     private static final int BYTE_LENGTH = 128;
 
+    protected final CryptoServicePurpose purpose;
+
     private byte[] xBuf = new byte[8];
     private int     xBufOff;
 
@@ -30,6 +34,16 @@
      */
     protected LongDigest()
     {
+        this(CryptoServicePurpose.ANY);
+    }
+
+    /**
+     * Constructor for variable length word
+     */
+    protected LongDigest(CryptoServicePurpose purpose)
+    {
+        this.purpose = purpose;
+
         xBufOff = 0;
 
         reset();
@@ -42,6 +56,8 @@
      */
     protected LongDigest(LongDigest t)
     {
+        this.purpose = t.purpose;
+
         copyIn(t);
     }
 
@@ -149,7 +165,7 @@
         //
         // process whole words.
         //
-        while (len > xBuf.length)
+        while (len >= xBuf.length)
         {
             processWord(in, inOff);
 
@@ -408,4 +424,5 @@
 0x4cc5d4becb3e42b6L, 0x597f299cfc657e2aL, 0x5fcb6fab3ad6faecL, 0x6c44198c4a475817L
     };
 
+    protected abstract CryptoServiceProperties cryptoServiceProperties();
 }
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/digests/MD4Digest.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/digests/MD4Digest.java
index 9b8176d..7b8508a 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/digests/MD4Digest.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/digests/MD4Digest.java
@@ -1,4 +1,5 @@
 /* GENERATED SOURCE. DO NOT MODIFY. */
+// BEGIN Android-changed: maintain old behaviour
 package com.android.internal.org.bouncycastle.crypto.digests;
 
 
@@ -291,3 +292,4 @@
         copyIn(d);
     }
 }
+// END Android-changed: maintain old behaviour
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/digests/MD5Digest.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/digests/MD5Digest.java
index ba66202..0231c30 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/digests/MD5Digest.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/digests/MD5Digest.java
@@ -1,4 +1,5 @@
 /* GENERATED SOURCE. DO NOT MODIFY. */
+// BEGIN Android-changed: maintain old behaviour
 package com.android.internal.org.bouncycastle.crypto.digests;
 
 
@@ -361,3 +362,4 @@
         return state;
     }
 }
+// END Android-changed: maintain old behaviour
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/digests/PhotonBeetleDigest.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/digests/PhotonBeetleDigest.java
new file mode 100644
index 0000000..3487148
--- /dev/null
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/digests/PhotonBeetleDigest.java
@@ -0,0 +1,221 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.crypto.digests;
+
+import java.io.ByteArrayOutputStream;
+
+import com.android.internal.org.bouncycastle.crypto.DataLengthException;
+import com.android.internal.org.bouncycastle.crypto.Digest;
+import com.android.internal.org.bouncycastle.crypto.OutputLengthException;
+import com.android.internal.org.bouncycastle.util.Arrays;
+import com.android.internal.org.bouncycastle.util.Bytes;
+
+/**
+ * Photon-Beetle, https://www.isical.ac.in/~lightweight/beetle/
+ * https://csrc.nist.gov/CSRC/media/Projects/lightweight-cryptography/documents/finalist-round/updated-spec-doc/photon-beetle-spec-final.pdf
+ * <p>
+ * Photon-Beetle with reference to C Reference Impl from: https://github.com/PHOTON-Beetle/Software
+ * </p>
+ * @hide This class is not part of the Android public SDK API
+ */
+public class PhotonBeetleDigest
+    implements Digest
+{
+    private byte[] state;
+    private byte[][] state_2d;
+    private ByteArrayOutputStream buffer = new ByteArrayOutputStream();
+    private final int INITIAL_RATE_INBYTES = 16;
+    private int RATE_INBYTES = 4;
+    private int SQUEEZE_RATE_INBYTES = 16;
+    private int STATE_INBYTES = 32;
+    private int TAG_INBYTES = 32;
+    private int LAST_THREE_BITS_OFFSET = 5;
+    private int ROUND = 12;
+    private int D = 8;
+    private int Dq = 3;
+    private int Dr = 7;
+    private int DSquare = 64;
+    private int S = 4;
+    private int S_1 = 3;
+    private byte[][] RC = {//[D][12]
+        {1, 3, 7, 14, 13, 11, 6, 12, 9, 2, 5, 10},
+        {0, 2, 6, 15, 12, 10, 7, 13, 8, 3, 4, 11},
+        {2, 0, 4, 13, 14, 8, 5, 15, 10, 1, 6, 9},
+        {6, 4, 0, 9, 10, 12, 1, 11, 14, 5, 2, 13},
+        {14, 12, 8, 1, 2, 4, 9, 3, 6, 13, 10, 5},
+        {15, 13, 9, 0, 3, 5, 8, 2, 7, 12, 11, 4},
+        {13, 15, 11, 2, 1, 7, 10, 0, 5, 14, 9, 6},
+        {9, 11, 15, 6, 5, 3, 14, 4, 1, 10, 13, 2}
+    };
+    private byte[][] MixColMatrix = { //[D][D]
+        {2, 4, 2, 11, 2, 8, 5, 6},
+        {12, 9, 8, 13, 7, 7, 5, 2},
+        {4, 4, 13, 13, 9, 4, 13, 9},
+        {1, 6, 5, 1, 12, 13, 15, 14},
+        {15, 12, 9, 13, 14, 5, 14, 13},
+        {9, 14, 5, 15, 4, 12, 9, 6},
+        {12, 2, 2, 10, 3, 1, 1, 14},
+        {15, 1, 13, 10, 5, 10, 2, 3}
+    };
+
+    private byte[] sbox = {12, 5, 6, 11, 9, 0, 10, 13, 3, 14, 15, 8, 4, 7, 1, 2};
+
+    public PhotonBeetleDigest()
+    {
+        state = new byte[STATE_INBYTES];
+        state_2d = new byte[D][D];
+    }
+
+    @Override
+    public String getAlgorithmName()
+    {
+        return "Photon-Beetle Hash";
+    }
+
+    @Override
+    public int getDigestSize()
+    {
+        return TAG_INBYTES;
+    }
+
+    @Override
+    public void update(byte input)
+    {
+        buffer.write(input);
+    }
+
+    @Override
+    public void update(byte[] input, int inOff, int len)
+    {
+        if ((inOff + len) > input.length)
+        {
+            throw new DataLengthException("input buffer too short");
+        }
+        buffer.write(input, inOff, len);
+    }
+
+    @Override
+    public int doFinal(byte[] output, int outOff)
+    {
+        if (32 + outOff > output.length)
+        {
+            throw new OutputLengthException("output buffer is too short");
+        }
+        byte[] input = buffer.toByteArray();
+        int inlen = input.length;
+        if (inlen == 0)
+        {
+            state[STATE_INBYTES - 1] ^= 1 << LAST_THREE_BITS_OFFSET;
+        }
+        else if (inlen <= INITIAL_RATE_INBYTES)
+        {
+            System.arraycopy(input, 0, state, 0, inlen);
+            if (inlen < INITIAL_RATE_INBYTES)
+            {
+                state[inlen] ^= 0x01; // ozs
+            }
+            state[STATE_INBYTES - 1] ^= (inlen < INITIAL_RATE_INBYTES ? (byte)1 : (byte)2) << LAST_THREE_BITS_OFFSET;
+        }
+        else
+        {
+            System.arraycopy(input, 0, state, 0, INITIAL_RATE_INBYTES);
+            inlen -= INITIAL_RATE_INBYTES;
+            int Dlen_inblocks = (inlen + RATE_INBYTES - 1) / RATE_INBYTES;
+            int i, LastDBlocklen;
+            for (i = 0; i < Dlen_inblocks - 1; i++)
+            {
+                PHOTON_Permutation();
+                Bytes.xorTo(RATE_INBYTES, input, INITIAL_RATE_INBYTES + i * RATE_INBYTES, state, 0);
+            }
+            PHOTON_Permutation();
+            LastDBlocklen = inlen - i * RATE_INBYTES;
+            Bytes.xorTo(LastDBlocklen, input, INITIAL_RATE_INBYTES + i * RATE_INBYTES, state, 0);
+            if (LastDBlocklen < RATE_INBYTES)
+            {
+                state[LastDBlocklen] ^= 0x01; // ozs
+            }
+            state[STATE_INBYTES - 1] ^= (inlen % RATE_INBYTES == 0 ? (byte)1 : (byte)2) << LAST_THREE_BITS_OFFSET;
+        }
+        PHOTON_Permutation();
+        System.arraycopy(state, 0, output, outOff, SQUEEZE_RATE_INBYTES);
+        PHOTON_Permutation();
+        System.arraycopy(state, 0, output, outOff + SQUEEZE_RATE_INBYTES, TAG_INBYTES - SQUEEZE_RATE_INBYTES);
+        return TAG_INBYTES;
+    }
+
+    @Override
+    public void reset()
+    {
+        buffer.reset();
+        Arrays.fill(state, (byte)0);
+    }
+
+    void PHOTON_Permutation()
+    {
+        int i, j, k, l;
+        for (i = 0; i < DSquare; i++)
+        {
+            state_2d[i >>> Dq][i & Dr] = (byte)(((state[i >> 1] & 0xFF) >>> (4 * (i & 1))) & 0xf);
+        }
+        for (int round = 0; round < ROUND; round++)
+        {
+            //AddKey
+            for (i = 0; i < D; i++)
+            {
+                state_2d[i][0] ^= RC[i][round];
+            }
+            //SubCell
+            for (i = 0; i < D; i++)
+            {
+                for (j = 0; j < D; j++)
+                {
+                    state_2d[i][j] = sbox[state_2d[i][j]];
+                }
+            }
+            //ShiftRow
+            for (i = 1; i < D; i++)
+            {
+                System.arraycopy(state_2d[i], 0, state, 0, D);
+                System.arraycopy(state, i, state_2d[i], 0, D - i);
+                System.arraycopy(state, 0, state_2d[i], D - i, i);
+            }
+            //MixColumn
+            for (j = 0; j < D; j++)
+            {
+                for (i = 0; i < D; i++)
+                {
+                    byte sum = 0;
+                    for (k = 0; k < D; k++)
+                    {
+                        int x = MixColMatrix[i][k], ret = 0, b = state_2d[k][j];
+                        for (l = 0; l < S; l++)
+                        {
+                            if (((b >>> l) & 1) != 0)
+                            {
+                                ret ^= x;
+                            }
+                            if (((x >>> S_1) & 1) != 0)
+                            {
+                                x <<= 1;
+                                x ^= 0x3;
+                            }
+                            else
+                            {
+                                x <<= 1;
+                            }
+                        }
+                        sum ^= ret & 15;
+                    }
+                    state[i] = sum;
+                }
+                for (i = 0; i < D; i++)
+                {
+                    state_2d[i][j] = state[i];
+                }
+            }
+        }
+        for (i = 0; i < DSquare; i += 2)
+        {
+            state[i >>> 1] = (byte)(((state_2d[i >>> Dq][i & Dr] & 0xf)) | ((state_2d[i >>> Dq][(i + 1) & Dr] & 0xf) << 4));
+        }
+    }
+}
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/digests/SHA1Digest.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/digests/SHA1Digest.java
index 12f82e5..b035ece 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/digests/SHA1Digest.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/digests/SHA1Digest.java
@@ -1,4 +1,5 @@
 /* GENERATED SOURCE. DO NOT MODIFY. */
+// BEGIN Android-changed: maintain old behaviour
 package com.android.internal.org.bouncycastle.crypto.digests;
 
 import com.android.internal.org.bouncycastle.util.Memoable;
@@ -350,6 +351,4 @@
     }
 }
 
-
-
-
+// END Android-changed: maintain old behaviour
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/digests/SHA224Digest.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/digests/SHA224Digest.java
index 8c3f9a4..82b6570 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/digests/SHA224Digest.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/digests/SHA224Digest.java
@@ -1,4 +1,5 @@
 /* GENERATED SOURCE. DO NOT MODIFY. */
+// BEGIN Android-changed: maintain old behaviour
 package com.android.internal.org.bouncycastle.crypto.digests;
 
 
@@ -360,4 +361,4 @@
         return state;
     }
 }
-
+// END Android-changed: maintain old behaviour
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/digests/SHA256Digest.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/digests/SHA256Digest.java
index 2626336..f7cd35e 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/digests/SHA256Digest.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/digests/SHA256Digest.java
@@ -1,7 +1,13 @@
 /* GENERATED SOURCE. DO NOT MODIFY. */
+// BEGIN Android-changed: adapt to old version of GeneralDigest
 package com.android.internal.org.bouncycastle.crypto.digests;
 
 
+import com.android.internal.org.bouncycastle.crypto.CryptoServiceProperties;
+import com.android.internal.org.bouncycastle.crypto.CryptoServicePurpose;
+import com.android.internal.org.bouncycastle.crypto.CryptoServicesRegistrar;
+import com.android.internal.org.bouncycastle.crypto.Digest;
+import com.android.internal.org.bouncycastle.crypto.SavableDigest;
 import com.android.internal.org.bouncycastle.util.Memoable;
 import com.android.internal.org.bouncycastle.util.Pack;
 
@@ -20,20 +26,60 @@
  */
 public class SHA256Digest
     extends GeneralDigest
-    implements EncodableDigest
+    implements SavableDigest
 {
     private static final int    DIGEST_LENGTH = 32;
+    protected final CryptoServicePurpose purpose;
 
     private int     H1, H2, H3, H4, H5, H6, H7, H8;
 
     private int[]   X = new int[64];
     private int     xOff;
 
+    public static SavableDigest newInstance()
+    {
+        return new SHA256Digest();
+    }
+
+    public static SavableDigest newInstance(CryptoServicePurpose purpose)
+    {
+        return new SHA256Digest(purpose);
+    }
+
+    public static SavableDigest newInstance(Digest digest)
+    {
+        if (digest instanceof SHA256Digest)
+        {
+            return new SHA256Digest((SHA256Digest) digest);
+        }
+
+        throw new IllegalArgumentException("receiver digest not available for input type " + (digest != null ? digest.getClass().getName() : "null"));
+    }
+
+    public static SavableDigest newInstance(byte[] encoded)
+    {
+        return new SHA256Digest(encoded);
+    }
+
     /**
      * Standard constructor
      */
     public SHA256Digest()
     {
+        this(CryptoServicePurpose.ANY);
+    }
+
+    /**
+     * Standard constructor, with purpose
+     */
+    public SHA256Digest(CryptoServicePurpose purpose)
+    {
+        super();
+
+        this.purpose = purpose;
+
+        CryptoServicesRegistrar.checkConstraints(cryptoServiceProperties());
+
         reset();
     }
 
@@ -45,6 +91,8 @@
     {
         super(t);
 
+        this.purpose = CryptoServicePurpose.ANY;
+
         copyIn(t);
     }
 
@@ -74,6 +122,8 @@
     {
         super(encodedState);
 
+        this.purpose = CryptoServicePurpose.ANY;
+
         H1 = Pack.bigEndianToInt(encodedState, 16);
         H2 = Pack.bigEndianToInt(encodedState, 20);
         H3 = Pack.bigEndianToInt(encodedState, 24);
@@ -105,13 +155,7 @@
         byte[]  in,
         int     inOff)
     {
-        // Note: Inlined for performance
-//        X[xOff] = Pack.bigEndianToInt(in, inOff);
-        int n = in[inOff] << 24;
-        n |= (in[++inOff] & 0xff) << 16;
-        n |= (in[++inOff] & 0xff) << 8;
-        n |= (in[++inOff] & 0xff);
-        X[xOff] = n;
+        X[xOff] = Pack.bigEndianToInt(in, inOff);
 
         if (++xOff == 16)
         {
@@ -131,9 +175,7 @@
         X[15] = (int)(bitLength & 0xffffffff);
     }
 
-    public int doFinal(
-        byte[]  out,
-        int     outOff)
+    public int doFinal(byte[] out, int outOff)
     {
         finish();
 
@@ -201,7 +243,7 @@
         int     g = H7;
         int     h = H8;
 
-        int t = 0;     
+        int t = 0;
         for(int i = 0; i < 8; i ++)
         {
             // t = 8 * i
@@ -334,7 +376,7 @@
 
     public byte[] getEncodedState()
     {
-        byte[] state = new byte[52 + xOff * 4];
+        byte[] state = new byte[52 + xOff * 4 + 1];
 
         super.populateState(state);
 
@@ -353,7 +395,14 @@
             Pack.intToBigEndian(X[i], state, 52 + (i * 4));
         }
 
+        state[state.length - 1] = (byte)purpose.ordinal();
+
         return state;
     }
-}
 
+    protected CryptoServiceProperties cryptoServiceProperties()
+    {
+        return Utils.getDefaultProperties(this, 256, purpose);
+    }
+}
+// END Android-changed: adapt to old version of GeneralDigest
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/digests/SHA384Digest.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/digests/SHA384Digest.java
index 77a0db3..9eafed7 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/digests/SHA384Digest.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/digests/SHA384Digest.java
@@ -1,6 +1,9 @@
 /* GENERATED SOURCE. DO NOT MODIFY. */
 package com.android.internal.org.bouncycastle.crypto.digests;
 
+import com.android.internal.org.bouncycastle.crypto.CryptoServiceProperties;
+import com.android.internal.org.bouncycastle.crypto.CryptoServicePurpose;
+import com.android.internal.org.bouncycastle.crypto.CryptoServicesRegistrar;
 import com.android.internal.org.bouncycastle.util.Memoable;
 import com.android.internal.org.bouncycastle.util.Pack;
 
@@ -27,6 +30,19 @@
      */
     public SHA384Digest()
     {
+        this(CryptoServicePurpose.ANY);
+    }
+
+    /**
+     * Standard constructor, with purpose
+     */
+    public SHA384Digest(CryptoServicePurpose purpose)
+    {
+        super(purpose);
+
+        CryptoServicesRegistrar.checkConstraints(cryptoServiceProperties());
+
+        reset();
     }
 
     /**
@@ -36,6 +52,8 @@
     public SHA384Digest(SHA384Digest t)
     {
         super(t);
+
+        CryptoServicesRegistrar.checkConstraints(cryptoServiceProperties());
     }
 
     /**
@@ -45,7 +63,11 @@
      */
     public SHA384Digest(byte[] encodedState)
     {
+        super(CryptoServicePurpose.values()[encodedState[encodedState.length - 1]]);
+
         restoreState(encodedState);
+
+        CryptoServicesRegistrar.checkConstraints(cryptoServiceProperties());
     }
 
     public String getAlgorithmName()
@@ -111,8 +133,16 @@
 
     public byte[] getEncodedState()
     {
-        byte[] encoded = new byte[getEncodedStateSize()];
+        byte[] encoded = new byte[getEncodedStateSize() + 1];
         super.populateState(encoded);
+
+        encoded[encoded.length - 1] = (byte)purpose.ordinal();
+
         return encoded;
     }
+
+    protected CryptoServiceProperties cryptoServiceProperties()
+    {
+        return Utils.getDefaultProperties(this, 256, purpose);
+    }
 }
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/digests/SHA512Digest.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/digests/SHA512Digest.java
index 5a84732..e052a9e 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/digests/SHA512Digest.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/digests/SHA512Digest.java
@@ -1,6 +1,9 @@
 /* GENERATED SOURCE. DO NOT MODIFY. */
 package com.android.internal.org.bouncycastle.crypto.digests;
 
+import com.android.internal.org.bouncycastle.crypto.CryptoServiceProperties;
+import com.android.internal.org.bouncycastle.crypto.CryptoServicePurpose;
+import com.android.internal.org.bouncycastle.crypto.CryptoServicesRegistrar;
 import com.android.internal.org.bouncycastle.util.Memoable;
 import com.android.internal.org.bouncycastle.util.Pack;
 
@@ -27,6 +30,19 @@
      */
     public SHA512Digest()
     {
+        this(CryptoServicePurpose.ANY);
+    }
+
+    /**
+     * Standard constructor, with purpose
+     */
+    public SHA512Digest(CryptoServicePurpose purpose)
+    {
+        super(purpose);
+
+        CryptoServicesRegistrar.checkConstraints(cryptoServiceProperties());
+
+        reset();
     }
 
     /**
@@ -36,6 +52,8 @@
     public SHA512Digest(SHA512Digest t)
     {
         super(t);
+
+        CryptoServicesRegistrar.checkConstraints(cryptoServiceProperties());
     }
 
     /**
@@ -45,7 +63,11 @@
      */
     public SHA512Digest(byte[] encodedState)
     {
+        super(CryptoServicePurpose.values()[encodedState[encodedState.length - 1]]);
+
         restoreState(encodedState);
+
+        CryptoServicesRegistrar.checkConstraints(cryptoServiceProperties());
     }
 
     public String getAlgorithmName()
@@ -113,9 +135,17 @@
 
     public byte[] getEncodedState()
     {
-        byte[] encoded = new byte[getEncodedStateSize()];
+        byte[] encoded = new byte[getEncodedStateSize() + 1];
         super.populateState(encoded);
+
+        encoded[encoded.length - 1] = (byte)purpose.ordinal();
+
         return encoded;
     }
+
+    protected CryptoServiceProperties cryptoServiceProperties()
+    {
+        return Utils.getDefaultProperties(this, 256, purpose);
+    }
 }
 
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/digests/Utils.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/digests/Utils.java
new file mode 100644
index 0000000..a58cd6f
--- /dev/null
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/digests/Utils.java
@@ -0,0 +1,98 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.crypto.digests;
+
+import com.android.internal.org.bouncycastle.crypto.CryptoServiceProperties;
+import com.android.internal.org.bouncycastle.crypto.CryptoServicePurpose;
+import com.android.internal.org.bouncycastle.crypto.Digest;
+
+class Utils
+{
+    static CryptoServiceProperties getDefaultProperties(Digest digest, CryptoServicePurpose purpose)
+    {
+        return new DefaultProperties(digest.getDigestSize() * 4, digest.getAlgorithmName(), purpose);
+    }
+
+    static CryptoServiceProperties getDefaultProperties(Digest digest, int prfBitsOfSecurity, CryptoServicePurpose purpose)
+    {
+        return new DefaultPropertiesWithPRF(digest.getDigestSize() * 4, prfBitsOfSecurity, digest.getAlgorithmName(), purpose);
+    }
+
+    // Service Definitions
+    private static class DefaultPropertiesWithPRF
+        implements CryptoServiceProperties
+    {
+        private final int bitsOfSecurity;
+        private final int prfBitsOfSecurity;
+        private final String algorithmName;
+        private final CryptoServicePurpose purpose;
+
+        public DefaultPropertiesWithPRF(int bitsOfSecurity, int prfBitsOfSecurity, String algorithmName, CryptoServicePurpose purpose)
+        {
+            this.bitsOfSecurity = bitsOfSecurity;
+            this.prfBitsOfSecurity = prfBitsOfSecurity;
+            this.algorithmName = algorithmName;
+            this.purpose = purpose;
+        }
+
+        public int bitsOfSecurity()
+        {
+            if (purpose == CryptoServicePurpose.PRF)
+            {
+                return prfBitsOfSecurity;
+            }
+
+            return bitsOfSecurity;
+        }
+
+        public String getServiceName()
+        {
+            return algorithmName;
+        }
+
+        public CryptoServicePurpose getPurpose()
+        {
+            return purpose;
+        }
+
+        public Object getParams()
+        {
+            return null;
+        }
+    }
+
+    // Service Definitions
+    private static class DefaultProperties
+        implements CryptoServiceProperties
+    {
+        private final int bitsOfSecurity;
+        private final String algorithmName;
+        private final CryptoServicePurpose purpose;
+
+        public DefaultProperties(int bitsOfSecurity, String algorithmName, CryptoServicePurpose purpose)
+        {
+            this.bitsOfSecurity = bitsOfSecurity;
+            this.algorithmName = algorithmName;
+            this.purpose = purpose;
+        }
+
+        public int bitsOfSecurity()
+        {
+            return bitsOfSecurity;
+        }
+
+        public String getServiceName()
+        {
+            return algorithmName;
+        }
+
+        public CryptoServicePurpose getPurpose()
+        {
+            return purpose;
+        }
+
+        public Object getParams()
+        {
+            return null;
+        }
+    }
+}
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/digests/XofUtils.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/digests/XofUtils.java
index 88c2a4a..ae45edd 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/digests/XofUtils.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/digests/XofUtils.java
@@ -1,6 +1,8 @@
 /* GENERATED SOURCE. DO NOT MODIFY. */
 package com.android.internal.org.bouncycastle.crypto.digests;
 
+import com.android.internal.org.bouncycastle.util.Arrays;
+
 /**
  * @hide This class is not part of the Android public SDK API
  */
@@ -8,24 +10,24 @@
 {
     public static byte[] leftEncode(long strLen)
     {
-    	byte n = 1;
+        byte n = 1;
 
         long v = strLen;
-    	while ((v >>= 8) != 0)
+        while ((v >>= 8) != 0)
         {
-    		n++;
-    	}
+            n++;
+        }
 
         byte[] b = new byte[n + 1];
 
-    	b[0] = n;
+        b[0] = n;
 
-    	for (int i = 1; i <= n; i++)
-    	{
-    		b[i] = (byte)(strLen >> (8 * (n - i)));
-    	}
+        for (int i = 1; i <= n; i++)
+        {
+            b[i] = (byte)(strLen >> (8 * (n - i)));
+        }
 
-    	return b;
+        return b;
     }
 
     public static byte[] rightEncode(long strLen)
@@ -49,4 +51,18 @@
 
         return b;
     }
+
+    static byte[] encode(byte X)
+    {
+        return Arrays.concatenate(XofUtils.leftEncode(8), new byte[] { X });
+    }
+
+    static byte[] encode(byte[] in, int inOff, int len)
+    {
+        if (in.length == len)
+        {
+            return Arrays.concatenate(XofUtils.leftEncode(len * 8), in);
+        }
+        return Arrays.concatenate(XofUtils.leftEncode(len * 8), Arrays.copyOfRange(in, inOff, inOff + len));
+    }
 }
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/digests/XoodyakDigest.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/digests/XoodyakDigest.java
new file mode 100644
index 0000000..3ec8de5
--- /dev/null
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/digests/XoodyakDigest.java
@@ -0,0 +1,208 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.crypto.digests;
+
+import java.io.ByteArrayOutputStream;
+
+import com.android.internal.org.bouncycastle.crypto.DataLengthException;
+import com.android.internal.org.bouncycastle.crypto.Digest;
+import com.android.internal.org.bouncycastle.crypto.OutputLengthException;
+import com.android.internal.org.bouncycastle.util.Arrays;
+import com.android.internal.org.bouncycastle.util.Pack;
+
+/**
+ * Xoodyak v1, https://csrc.nist.gov/CSRC/media/Projects/lightweight-cryptography/documents/finalist-round/updated-spec-doc/xoodyak-spec-final.pdf
+ * <p>
+ * Xoodyak with reference to C Reference Impl from: https://github.com/XKCP/XKCP
+ * </p>
+ * @hide This class is not part of the Android public SDK API
+ */
+
+public class XoodyakDigest
+    implements Digest
+{
+    private byte[] state;
+    private int phase;
+    private MODE mode;
+    private int Rabsorb;
+    private final int f_bPrime = 48;
+    private final int Rhash = 16;
+    private final int PhaseDown = 1;
+    private final int PhaseUp = 2;
+    private final int NLANES = 12;
+    private final int NROWS = 3;
+    private final int NCOLUMS = 4;
+    private final int MAXROUNDS = 12;
+    private final int TAGLEN = 16;
+    private final int[] RC = {0x00000058, 0x00000038, 0x000003C0, 0x000000D0, 0x00000120, 0x00000014, 0x00000060,
+        0x0000002C, 0x00000380, 0x000000F0, 0x000001A0, 0x00000012};
+    private final ByteArrayOutputStream buffer = new ByteArrayOutputStream();
+
+    enum MODE
+    {
+        ModeHash,
+        ModeKeyed
+    }
+
+    public XoodyakDigest()
+    {
+        state = new byte[48];
+        reset();
+    }
+
+    @Override
+    public String getAlgorithmName()
+    {
+        return "Xoodyak Hash";
+    }
+
+    @Override
+    public int getDigestSize()
+    {
+        return 32;
+    }
+
+    @Override
+    public void update(byte input)
+    {
+        buffer.write(input);
+    }
+
+    @Override
+    public void update(byte[] input, int inOff, int len)
+    {
+        if ((inOff + len) > input.length)
+        {
+            throw new DataLengthException("input buffer too short");
+        }
+        buffer.write(input, inOff, len);
+
+    }
+
+    @Override
+    public int doFinal(byte[] output, int outOff)
+    {
+        if (32 + outOff > output.length)
+        {
+            throw new OutputLengthException("output buffer is too short");
+        }
+        byte[] input = buffer.toByteArray();
+        int inOff = 0;
+        int len = buffer.size();
+        int Cd = 0x03;
+        int splitLen;
+        do
+        {
+            if (phase != PhaseUp)
+            {
+                Up(null, 0, 0, 0);
+            }
+            splitLen = Math.min(len, Rabsorb);
+            Down(input, inOff, splitLen, Cd);
+            Cd = 0;
+            inOff += splitLen;
+            len -= splitLen;
+        }
+        while (len != 0);
+        Up(output, outOff, TAGLEN, 0x40);
+        Down(null, 0, 0, 0);
+        Up(output, outOff + TAGLEN, TAGLEN, 0);
+        return 32;
+    }
+
+    @Override
+    public void reset()
+    {
+        Arrays.fill(state, (byte)0);
+        phase = PhaseUp;
+        mode = MODE.ModeHash;
+        Rabsorb = Rhash;
+        buffer.reset();
+    }
+
+    private void Up(byte[] Yi, int YiOff, int YiLen, int Cu)
+    {
+        if (mode != MODE.ModeHash)
+        {
+            state[f_bPrime - 1] ^= Cu;
+        }
+        int[] a = new int[NLANES];
+        Pack.littleEndianToInt(state, 0, a, 0, a.length);
+        int x, y;
+        int[] b = new int[NLANES];
+        int[] p = new int[NCOLUMS];
+        int[] e = new int[NCOLUMS];
+        for (int i = 0; i < MAXROUNDS; ++i)
+        {
+            /* Theta: Column Parity Mixer */
+            for (x = 0; x < NCOLUMS; ++x)
+            {
+                p[x] = a[index(x, 0)] ^ a[index(x, 1)] ^ a[index(x, 2)];
+            }
+            for (x = 0; x < NCOLUMS; ++x)
+            {
+                y = p[(x + 3) & 3];
+                e[x] = ROTL32(y, 5) ^ ROTL32(y, 14);
+            }
+            for (x = 0; x < NCOLUMS; ++x)
+            {
+                for (y = 0; y < NROWS; ++y)
+                {
+                    a[index(x, y)] ^= e[x];
+                }
+            }
+            /* Rho-west: plane shift */
+            for (x = 0; x < NCOLUMS; ++x)
+            {
+                b[index(x, 0)] = a[index(x, 0)];
+                b[index(x, 1)] = a[index(x + 3, 1)];
+                b[index(x, 2)] = ROTL32(a[index(x, 2)], 11);
+            }
+            /* Iota: round ant */
+            b[0] ^= RC[i];
+            /* Chi: non linear layer */
+            for (x = 0; x < NCOLUMS; ++x)
+            {
+                for (y = 0; y < NROWS; ++y)
+                {
+                    a[index(x, y)] = b[index(x, y)] ^ (~b[index(x, y + 1)] & b[index(x, y + 2)]);
+                }
+            }
+            /* Rho-east: plane shift */
+            for (x = 0; x < NCOLUMS; ++x)
+            {
+                b[index(x, 0)] = a[index(x, 0)];
+                b[index(x, 1)] = ROTL32(a[index(x, 1)], 1);
+                b[index(x, 2)] = ROTL32(a[index(x + 2, 2)], 8);
+            }
+            System.arraycopy(b, 0, a, 0, NLANES);
+        }
+        Pack.intToLittleEndian(a, 0, a.length, state, 0);
+        phase = PhaseUp;
+        if (Yi != null)
+        {
+            System.arraycopy(state, 0, Yi, YiOff, YiLen);
+        }
+    }
+
+    void Down(byte[] Xi, int XiOff, int XiLen, int Cd)
+    {
+        for (int i = 0; i < XiLen; i++)
+        {
+            state[i] ^= Xi[XiOff++];
+        }
+        state[XiLen] ^= 0x01;
+        state[f_bPrime - 1] ^= (mode == MODE.ModeHash) ? (Cd & 0x01) : Cd;
+        phase = PhaseDown;
+    }
+
+    private int index(int x, int y)
+    {
+        return (((y % NROWS) * NCOLUMS) + ((x) % NCOLUMS));
+    }
+
+    private int ROTL32(int a, int offset)
+    {
+        return (a << (offset & 31)) ^ (a >>> ((32 - (offset)) & 31));
+    }
+
+}
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/ec/CustomNamedCurves.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/ec/CustomNamedCurves.java
index d2f0a3a..1eecf91 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/ec/CustomNamedCurves.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/ec/CustomNamedCurves.java
@@ -87,10 +87,15 @@
      *
     static X9ECParametersHolder curve25519 = new X9ECParametersHolder()
     {
+        protected ECCurve createCurve()
+        {
+            return configureCurve(new Curve25519());
+        }
+
         protected X9ECParameters createParameters()
         {
             byte[] S = null;
-            ECCurve curve = configureCurve(new Curve25519());
+            ECCurve curve = getCurve();
 
             /*
              * NOTE: Curve25519 was specified in Montgomery form. Rewriting in Weierstrass form
@@ -113,10 +118,15 @@
      *
     static X9ECParametersHolder secp128r1 = new X9ECParametersHolder()
     {
+        protected ECCurve createCurve()
+        {
+            return configureCurve(new SecP128R1Curve());
+        }
+
         protected X9ECParameters createParameters()
         {
             byte[] S = Hex.decodeStrict("000E0D4D696E6768756151750CC03A4473D03679");
-            ECCurve curve = configureCurve(new SecP128R1Curve());
+            ECCurve curve = getCurve();
             X9ECPoint G = configureBasepoint(curve,
                 "04161FF7528B899B2D0C28607CA52C5B86CF5AC8395BAFEB13C02DA292DDED7A83");
             return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
@@ -128,9 +138,8 @@
      *
     static X9ECParametersHolder secp160k1 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
-            byte[] S = null;
             GLVTypeBParameters glv = new GLVTypeBParameters(
                 new BigInteger("9ba48cba5ebcb9b6bd33b92830b2a2e0e192f10a", 16),
                 new BigInteger("c39c6c3b3a36d7701b9c71a1f5804ae5d0003f4", 16),
@@ -144,7 +153,13 @@
                     new BigInteger("9162fbe73984472a0a9d0590", 16),
                     new BigInteger("96341f1138933bc2f503fd44", 16),
                     176));
-            ECCurve curve = configureCurveGLV(new SecP160K1Curve(), glv);
+            return configureCurveGLV(new SecP160K1Curve(), glv);
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = null;
+            ECCurve curve = getCurve();
             X9ECPoint G = configureBasepoint(curve,
                 "043B4C382CE37AA192A4019E763036F4F5DD4D7EBB938CF935318FDCED6BC28286531733C3F03C4FEE");
             return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
@@ -156,10 +171,15 @@
      *
     static X9ECParametersHolder secp160r1 = new X9ECParametersHolder()
     {
+        protected ECCurve createCurve()
+        {
+            return configureCurve(new SecP160R1Curve());
+        }
+
         protected X9ECParameters createParameters()
         {
             byte[] S = Hex.decodeStrict("1053CDE42C14D696E67687561517533BF3F83345");
-            ECCurve curve = configureCurve(new SecP160R1Curve());
+            ECCurve curve = getCurve();
             X9ECPoint G = configureBasepoint(curve,
                 "044A96B5688EF573284664698968C38BB913CBFC8223A628553168947D59DCC912042351377AC5FB32");
             return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
@@ -171,10 +191,15 @@
      *
     static X9ECParametersHolder secp160r2 = new X9ECParametersHolder()
     {
+        protected ECCurve createCurve()
+        {
+            return configureCurve(new SecP160R2Curve());
+        }
+
         protected X9ECParameters createParameters()
         {
             byte[] S = Hex.decodeStrict("B99B99B099B323E02709A4D696E6768756151751");
-            ECCurve curve = configureCurve(new SecP160R2Curve());
+            ECCurve curve = getCurve();
             X9ECPoint G = configureBasepoint(curve,
                 "0452DCB034293A117E1F4FF11B30F7199D3144CE6DFEAFFEF2E331F296E071FA0DF9982CFEA7D43F2E");
             return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
@@ -188,9 +213,8 @@
      */
     static X9ECParametersHolder secp192k1 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
-            byte[] S = null;
             GLVTypeBParameters glv = new GLVTypeBParameters(
                 new BigInteger("bb85691939b869c1d087f601554b96b80cb4f55b35f433c2", 16),
                 new BigInteger("3d84f26c12238d7b4f3d516613c1759033b1a5800175d0b1", 16),
@@ -204,7 +228,13 @@
                     new BigInteger("71169be7330b3038edb025f1d0f9", 16),
                     new BigInteger("b3fb3400dec5c4adceb8655d4c94", 16),
                     208));
-            ECCurve curve = configureCurveGLV(new SecP192K1Curve(), glv);
+            return configureCurveGLV(new SecP192K1Curve(), glv);
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = null;
+            ECCurve curve = getCurve();
             X9ECPoint G = configureBasepoint(curve,
                 "04DB4FF10EC057E9AE26B07D0280B7F4341DA5D1B1EAE06C7D9B2F2F6D9C5628A7844163D015BE86344082AA88D95E2F9D");
             return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
@@ -216,10 +246,15 @@
      */
     static X9ECParametersHolder secp192r1 = new X9ECParametersHolder()
     {
+        protected ECCurve createCurve()
+        {
+            return configureCurve(new SecP192R1Curve());
+        }
+
         protected X9ECParameters createParameters()
         {
             byte[] S = Hex.decodeStrict("3045AE6FC8422F64ED579528D38120EAE12196D5");
-            ECCurve curve = configureCurve(new SecP192R1Curve());
+            ECCurve curve = getCurve();
             X9ECPoint G = configureBasepoint(curve,
                 "04188DA80EB03090F67CBF20EB43A18800F4FF0AFD82FF101207192B95FFC8DA78631011ED6B24CDD573F977A11E794811");
             return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
@@ -231,9 +266,8 @@
      */
     static X9ECParametersHolder secp224k1 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
-            byte[] S = null;
             GLVTypeBParameters glv = new GLVTypeBParameters(
                 new BigInteger("fe0e87005b4e83761908c5131d552a850b3f58b749c37cf5b84d6768", 16),
                 new BigInteger("60dcd2104c4cbc0be6eeefc2bdd610739ec34e317f9b33046c9e4788", 16),
@@ -247,7 +281,13 @@
                     new BigInteger("6b8cf07d4ca75c88957d9d67059037a4", 16),
                     new BigInteger("b8adf1378a6eb73409fa6c9c637ba7f5", 16),
                     240));
-            ECCurve curve = configureCurveGLV(new SecP224K1Curve(), glv);
+            return configureCurveGLV(new SecP224K1Curve(), glv);
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = null;
+            ECCurve curve = getCurve();
             X9ECPoint G = configureBasepoint(curve,
                 "04A1455B334DF099DF30FC28A169A467E9E47075A90F7E650EB6B7A45C7E089FED7FBA344282CAFBD6F7E319F7C0B0BD59E2CA4BDB556D61A5");
             return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
@@ -259,10 +299,15 @@
      */
     static X9ECParametersHolder secp224r1 = new X9ECParametersHolder()
     {
+        protected ECCurve createCurve()
+        {
+            return configureCurve(new SecP224R1Curve());
+        }
+
         protected X9ECParameters createParameters()
         {
             byte[] S = Hex.decodeStrict("BD71344799D5C7FCDC45B59FA3B9AB8F6A948BC5");
-            ECCurve curve = configureCurve(new SecP224R1Curve());
+            ECCurve curve = getCurve();
             X9ECPoint G = configureBasepoint(curve,
                 "04B70E0CBD6BB4BF7F321390B94A03C1D356C21122343280D6115C1D21BD376388B5F723FB4C22DFE6CD4375A05A07476444D5819985007E34");
             return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
@@ -274,9 +319,8 @@
      */
     static X9ECParametersHolder secp256k1 = new X9ECParametersHolder()
     {
-        protected X9ECParameters createParameters()
+        protected ECCurve createCurve()
         {
-            byte[] S = null;
             GLVTypeBParameters glv = new GLVTypeBParameters(
                 new BigInteger("7ae96a2b657c07106e64479eac3434e99cf0497512f58995c1396c28719501ee", 16),
                 new BigInteger("5363ad4cc05c30e0a5261c028812645a122e22ea20816678df02967c1b23bd72", 16),
@@ -290,7 +334,13 @@
                     new BigInteger("3086d221a7d46bcde86c90e49284eb153dab", 16),
                     new BigInteger("e4437ed6010e88286f547fa90abfe4c42212", 16),
                     272));
-            ECCurve curve = configureCurveGLV(new SecP256K1Curve(), glv);
+            return configureCurveGLV(new SecP256K1Curve(), glv);
+        }
+
+        protected X9ECParameters createParameters()
+        {
+            byte[] S = null;
+            ECCurve curve = getCurve();
             X9ECPoint G = configureBasepoint(curve,
                 "0479BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8");
             return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
@@ -302,10 +352,15 @@
      */
     static X9ECParametersHolder secp256r1 = new X9ECParametersHolder()
     {
+        protected ECCurve createCurve()
+        {
+            return configureCurve(new SecP256R1Curve());
+        }
+
         protected X9ECParameters createParameters()
         {
             byte[] S = Hex.decodeStrict("C49D360886E704936A6678E1139D26B7819F7E90");
-            ECCurve curve = configureCurve(new SecP256R1Curve());
+            ECCurve curve = getCurve();
             X9ECPoint G = configureBasepoint(curve,
                 "046B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C2964FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5");
             return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
@@ -317,10 +372,15 @@
      */
     static X9ECParametersHolder secp384r1 = new X9ECParametersHolder()
     {
+        protected ECCurve createCurve()
+        {
+            return configureCurve(new SecP384R1Curve());
+        }
+
         protected X9ECParameters createParameters()
         {
             byte[] S = Hex.decodeStrict("A335926AA319A27A1D00896A6773A4827ACDAC73");
-            ECCurve curve = configureCurve(new SecP384R1Curve());
+            ECCurve curve = getCurve();
             X9ECPoint G = configureBasepoint(curve, "04"
                 + "AA87CA22BE8B05378EB1C71EF320AD746E1D3B628BA79B9859F741E082542A385502F25DBF55296C3A545E3872760AB7"
                 + "3617DE4A96262C6F5D9E98BF9292DC29F8F41DBD289A147CE9DA3113B5F0B8C00A60B1CE1D7E819D7A431D7C90EA0E5F");
@@ -333,10 +393,15 @@
      */
     static X9ECParametersHolder secp521r1 = new X9ECParametersHolder()
     {
+        protected ECCurve createCurve()
+        {
+            return configureCurve(new SecP521R1Curve());
+        }
+
         protected X9ECParameters createParameters()
         {
             byte[] S = Hex.decodeStrict("D09E8800291CB85396CC6717393284AAA0DA64BA");
-            ECCurve curve = configureCurve(new SecP521R1Curve());
+            ECCurve curve = getCurve();
             X9ECPoint G = configureBasepoint(curve, "04"
                 + "00C6858E06B70404E9CD9E3ECB662395B4429C648139053FB521F828AF606B4D3DBAA14B5E77EFE75928FE1DC127A2FFA8DE3348B3C1856A429BF97E7E31C2E5BD66"
                 + "011839296A789A3BC0045C8A5FB42C7D1BD998F54449579B446817AFBD17273E662C97EE72995EF42640C550B9013FAD0761353C7086A272C24088BE94769FD16650");
@@ -351,10 +416,15 @@
      *
     static X9ECParametersHolder sect113r1 = new X9ECParametersHolder()
     {
+        protected ECCurve createCurve()
+        {
+            return configureCurve(new SecT113R1Curve());
+        }
+
         protected X9ECParameters createParameters()
         {
             byte[] S = Hex.decodeStrict("10E723AB14D696E6768756151756FEBF8FCB49A9");
-            ECCurve curve = configureCurve(new SecT113R1Curve());
+            ECCurve curve = getCurve();
             X9ECPoint G = configureBasepoint(curve,
                 "04009D73616F35F4AB1407D73562C10F00A52830277958EE84D1315ED31886");
             return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
@@ -366,10 +436,15 @@
      *
     static X9ECParametersHolder sect113r2 = new X9ECParametersHolder()
     {
+        protected ECCurve createCurve()
+        {
+            return configureCurve(new SecT113R2Curve());
+        }
+
         protected X9ECParameters createParameters()
         {
             byte[] S = Hex.decodeStrict("10C0FB15760860DEF1EEF4D696E676875615175D");
-            ECCurve curve = configureCurve(new SecT113R2Curve());
+            ECCurve curve = getCurve();
             X9ECPoint G = configureBasepoint(curve,
                 "0401A57A6A7B26CA5EF52FCDB816479700B3ADC94ED1FE674C06E695BABA1D");
             return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
@@ -381,10 +456,15 @@
      *
     static X9ECParametersHolder sect131r1 = new X9ECParametersHolder()
     {
+        protected ECCurve createCurve()
+        {
+            return configureCurve(new SecT131R1Curve());
+        }
+
         protected X9ECParameters createParameters()
         {
             byte[] S = Hex.decodeStrict("4D696E676875615175985BD3ADBADA21B43A97E2");
-            ECCurve curve = configureCurve(new SecT131R1Curve());
+            ECCurve curve = getCurve();
             X9ECPoint G = configureBasepoint(curve,
                 "040081BAF91FDF9833C40F9C181343638399078C6E7EA38C001F73C8134B1B4EF9E150");
             return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
@@ -396,10 +476,15 @@
      *
     static X9ECParametersHolder sect131r2 = new X9ECParametersHolder()
     {
+        protected ECCurve createCurve()
+        {
+            return configureCurve(new SecT131R2Curve());
+        }
+
         protected X9ECParameters createParameters()
         {
             byte[] S = Hex.decodeStrict("985BD3ADBAD4D696E676875615175A21B43A97E3");
-            ECCurve curve = configureCurve(new SecT131R2Curve());
+            ECCurve curve = getCurve();
             X9ECPoint G = configureBasepoint(curve,
                 "040356DCD8F2F95031AD652D23951BB366A80648F06D867940A5366D9E265DE9EB240F");
             return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
@@ -411,10 +496,15 @@
      *
     static X9ECParametersHolder sect163k1 = new X9ECParametersHolder()
     {
+        protected ECCurve createCurve()
+        {
+            return configureCurve(new SecT163K1Curve());
+        }
+
         protected X9ECParameters createParameters()
         {
             byte[] S = null;
-            ECCurve curve = configureCurve(new SecT163K1Curve());
+            ECCurve curve = getCurve();
             X9ECPoint G = configureBasepoint(curve,
                 "0402FE13C0537BBC11ACAA07D793DE4E6D5E5C94EEE80289070FB05D38FF58321F2E800536D538CCDAA3D9");
             return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
@@ -426,10 +516,15 @@
      *
     static X9ECParametersHolder sect163r1 = new X9ECParametersHolder()
     {
+        protected ECCurve createCurve()
+        {
+            return configureCurve(new SecT163R1Curve());
+        }
+
         protected X9ECParameters createParameters()
         {
             byte[] S = Hex.decodeStrict("24B7B137C8A14D696E6768756151756FD0DA2E5C");
-            ECCurve curve = configureCurve(new SecT163R1Curve());
+            ECCurve curve = getCurve();
             X9ECPoint G = configureBasepoint(curve,
                 "040369979697AB43897789566789567F787A7876A65400435EDB42EFAFB2989D51FEFCE3C80988F41FF883");
             return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
@@ -441,10 +536,15 @@
      *
     static X9ECParametersHolder sect163r2 = new X9ECParametersHolder()
     {
+        protected ECCurve createCurve()
+        {
+            return configureCurve(new SecT163R2Curve());
+        }
+
         protected X9ECParameters createParameters()
         {
             byte[] S = Hex.decodeStrict("85E25BFE5C86226CDB12016F7553F9D0E693A268");
-            ECCurve curve = configureCurve(new SecT163R2Curve());
+            ECCurve curve = getCurve();
             X9ECPoint G = configureBasepoint(curve,
                 "0403F0EBA16286A2D57EA0991168D4994637E8343E3600D51FBC6C71A0094FA2CDD545B11C5C0C797324F1");
             return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
@@ -456,10 +556,15 @@
      *
     static X9ECParametersHolder sect193r1 = new X9ECParametersHolder()
     {
+        protected ECCurve createCurve()
+        {
+            return configureCurve(new SecT193R1Curve());
+        }
+
         protected X9ECParameters createParameters()
         {
             byte[] S = Hex.decodeStrict("103FAEC74D696E676875615175777FC5B191EF30");
-            ECCurve curve = configureCurve(new SecT193R1Curve());
+            ECCurve curve = getCurve();
             X9ECPoint G = configureBasepoint(curve,
                 "0401F481BC5F0FF84A74AD6CDF6FDEF4BF6179625372D8C0C5E10025E399F2903712CCF3EA9E3A1AD17FB0B3201B6AF7CE1B05");
             return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
@@ -471,10 +576,15 @@
      *
     static X9ECParametersHolder sect193r2 = new X9ECParametersHolder()
     {
+        protected ECCurve createCurve()
+        {
+            return configureCurve(new SecT193R2Curve());
+        }
+
         protected X9ECParameters createParameters()
         {
             byte[] S = Hex.decodeStrict("10B7B4D696E676875615175137C8A16FD0DA2211");
-            ECCurve curve = configureCurve(new SecT193R2Curve());
+            ECCurve curve = getCurve();
             X9ECPoint G = configureBasepoint(curve,
                 "0400D9B67D192E0367C803F39E1A7E82CA14A651350AAE617E8F01CE94335607C304AC29E7DEFBD9CA01F596F927224CDECF6C");
             return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
@@ -486,10 +596,15 @@
      *
     static X9ECParametersHolder sect233k1 = new X9ECParametersHolder()
     {
+        protected ECCurve createCurve()
+        {
+            return configureCurve(new SecT233K1Curve());
+        }
+
         protected X9ECParameters createParameters()
         {
             byte[] S = null;
-            ECCurve curve = configureCurve(new SecT233K1Curve());
+            ECCurve curve = getCurve();
             X9ECPoint G = configureBasepoint(curve,
                 "04017232BA853A7E731AF129F22FF4149563A419C26BF50A4C9D6EEFAD612601DB537DECE819B7F70F555A67C427A8CD9BF18AEB9B56E0C11056FAE6A3");
             return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
@@ -501,10 +616,15 @@
      *
     static X9ECParametersHolder sect233r1 = new X9ECParametersHolder()
     {
+        protected ECCurve createCurve()
+        {
+            return configureCurve(new SecT233R1Curve());
+        }
+
         protected X9ECParameters createParameters()
         {
             byte[] S = Hex.decodeStrict("74D59FF07F6B413D0EA14B344B20A2DB049B50C3");
-            ECCurve curve = configureCurve(new SecT233R1Curve());
+            ECCurve curve = getCurve();
             X9ECPoint G = configureBasepoint(curve,
                 "0400FAC9DFCBAC8313BB2139F1BB755FEF65BC391F8B36F8F8EB7371FD558B01006A08A41903350678E58528BEBF8A0BEFF867A7CA36716F7E01F81052");
             return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
@@ -516,10 +636,15 @@
      *
     static X9ECParametersHolder sect239k1 = new X9ECParametersHolder()
     {
+        protected ECCurve createCurve()
+        {
+            return configureCurve(new SecT239K1Curve());
+        }
+
         protected X9ECParameters createParameters()
         {
             byte[] S = null;
-            ECCurve curve = configureCurve(new SecT239K1Curve());
+            ECCurve curve = getCurve();
             X9ECPoint G = configureBasepoint(curve,
                 "0429A0B6A887A983E9730988A68727A8B2D126C44CC2CC7B2A6555193035DC76310804F12E549BDB011C103089E73510ACB275FC312A5DC6B76553F0CA");
             return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
@@ -531,10 +656,15 @@
      *
     static X9ECParametersHolder sect283k1 = new X9ECParametersHolder()
     {
+        protected ECCurve createCurve()
+        {
+            return configureCurve(new SecT283K1Curve());
+        }
+
         protected X9ECParameters createParameters()
         {
             byte[] S = null;
-            ECCurve curve = configureCurve(new SecT283K1Curve());
+            ECCurve curve = getCurve();
             X9ECPoint G = configureBasepoint(curve, "04"
                 + "0503213F78CA44883F1A3B8162F188E553CD265F23C1567A16876913B0C2AC2458492836"
                 + "01CCDA380F1C9E318D90F95D07E5426FE87E45C0E8184698E45962364E34116177DD2259");
@@ -547,10 +677,15 @@
      *
     static X9ECParametersHolder sect283r1 = new X9ECParametersHolder()
     {
+        protected ECCurve createCurve()
+        {
+            return configureCurve(new SecT283R1Curve());
+        }
+
         protected X9ECParameters createParameters()
         {
             byte[] S = Hex.decodeStrict("77E2B07370EB0F832A6DD5B62DFC88CD06BB84BE");
-            ECCurve curve = configureCurve(new SecT283R1Curve());
+            ECCurve curve = getCurve();
             X9ECPoint G = configureBasepoint(curve, "04"
                 + "05F939258DB7DD90E1934F8C70B0DFEC2EED25B8557EAC9C80E2E198F8CDBECD86B12053"
                 + "03676854FE24141CB98FE6D4B20D02B4516FF702350EDDB0826779C813F0DF45BE8112F4");
@@ -563,10 +698,15 @@
      *
     static X9ECParametersHolder sect409k1 = new X9ECParametersHolder()
     {
+        protected ECCurve createCurve()
+        {
+            return configureCurve(new SecT409K1Curve());
+        }
+
         protected X9ECParameters createParameters()
         {
             byte[] S = null;
-            ECCurve curve = configureCurve(new SecT409K1Curve());
+            ECCurve curve = getCurve();
             X9ECPoint G = configureBasepoint(curve, "04"
                 + "0060F05F658F49C1AD3AB1890F7184210EFD0987E307C84C27ACCFB8F9F67CC2C460189EB5AAAA62EE222EB1B35540CFE9023746"
                 + "01E369050B7C4E42ACBA1DACBF04299C3460782F918EA427E6325165E9EA10E3DA5F6C42E9C55215AA9CA27A5863EC48D8E0286B");
@@ -579,10 +719,15 @@
      *
     static X9ECParametersHolder sect409r1 = new X9ECParametersHolder()
     {
+        protected ECCurve createCurve()
+        {
+            return configureCurve(new SecT409R1Curve());
+        }
+
         protected X9ECParameters createParameters()
         {
             byte[] S = Hex.decodeStrict("4099B5A457F9D69F79213D094C4BCD4D4262210B");
-            ECCurve curve = configureCurve(new SecT409R1Curve());
+            ECCurve curve = getCurve();
             X9ECPoint G = configureBasepoint(curve, "04"
                 + "015D4860D088DDB3496B0C6064756260441CDE4AF1771D4DB01FFE5B34E59703DC255A868A1180515603AEAB60794E54BB7996A7"
                 + "0061B1CFAB6BE5F32BBFA78324ED106A7636B9C5A7BD198D0158AA4F5488D08F38514F1FDF4B4F40D2181B3681C364BA0273C706");
@@ -595,10 +740,15 @@
      *
     static X9ECParametersHolder sect571k1 = new X9ECParametersHolder()
     {
+        protected ECCurve createCurve()
+        {
+            return configureCurve(new SecT571K1Curve());
+        }
+
         protected X9ECParameters createParameters()
         {
             byte[] S = null;
-            ECCurve curve = configureCurve(new SecT571K1Curve());
+            ECCurve curve = getCurve();
             X9ECPoint G = configureBasepoint(curve, "04"
                 + "026EB7A859923FBC82189631F8103FE4AC9CA2970012D5D46024804801841CA44370958493B205E647DA304DB4CEB08CBBD1BA39494776FB988B47174DCA88C7E2945283A01C8972"
                 + "0349DC807F4FBF374F4AEADE3BCA95314DD58CEC9F307A54FFC61EFC006D8A2C9D4979C0AC44AEA74FBEBBB9F772AEDCB620B01A7BA7AF1B320430C8591984F601CD4C143EF1C7A3");
@@ -611,10 +761,15 @@
      *
     static X9ECParametersHolder sect571r1 = new X9ECParametersHolder()
     {
+        protected ECCurve createCurve()
+        {
+            return configureCurve(new SecT571R1Curve());
+        }
+
         protected X9ECParameters createParameters()
         {
             byte[] S = Hex.decodeStrict("2AA058F73A0E33AB486B0F610410C53A7F132310");
-            ECCurve curve = configureCurve(new SecT571R1Curve());
+            ECCurve curve = getCurve();
             X9ECPoint G = configureBasepoint(curve, "04"
                 + "0303001D34B856296C16C0D40D3CD7750A93D1D2955FA80AA5F40FC8DB7B2ABDBDE53950F4C0D293CDD711A35B67FB1499AE60038614F1394ABFA3B4C850D927E1E7769C8EEC2D19"
                 + "037BF27342DA639B6DCCFFFEB73D69D78C6C27A6009CBBCA1980F8533921E8A684423E43BAB08A576291AF8F461BB2A8B3531D2F0485C19B16E2F1516E23DD3C1A4827AF1B8AC15B");
@@ -627,10 +782,15 @@
      *
     static X9ECParametersHolder sm2p256v1 = new X9ECParametersHolder()
     {
+        protected ECCurve createCurve()
+        {
+            return configureCurve(new SM2P256V1Curve());
+        }
+
         protected X9ECParameters createParameters()
         {
             byte[] S = null;
-            ECCurve curve = configureCurve(new SM2P256V1Curve());
+            ECCurve curve = getCurve();
             X9ECPoint G = configureBasepoint(curve,
                 "0432C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7BC3736A2F4F6779C59BDCEE36B692153D0A9877CC62A474002DF32E52139F0A0");
             return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
@@ -640,6 +800,7 @@
     // END Android-removed: Unsupported curves
 
 
+
     static final Hashtable nameToCurve = new Hashtable();
     static final Hashtable nameToOID = new Hashtable();
     static final Hashtable oidToCurve = new Hashtable();
@@ -746,10 +907,15 @@
 
     public static X9ECParameters getByName(String name)
     {
-        X9ECParametersHolder holder = (X9ECParametersHolder)nameToCurve.get(Strings.toLowerCase(name));
+        X9ECParametersHolder holder = getByNameLazy(name);
         return holder == null ? null : holder.getParameters();
     }
 
+    public static X9ECParametersHolder getByNameLazy(String name)
+    {
+        return (X9ECParametersHolder)nameToCurve.get(Strings.toLowerCase(name));
+    }
+
     /**
      * return the X9ECParameters object for the named curve represented by the passed in object
      * identifier. Null if the curve isn't present.
@@ -759,10 +925,15 @@
      */
     public static X9ECParameters getByOID(ASN1ObjectIdentifier oid)
     {
-        X9ECParametersHolder holder = (X9ECParametersHolder)oidToCurve.get(oid);
+        X9ECParametersHolder holder = getByOIDLazy(oid);
         return holder == null ? null : holder.getParameters();
     }
 
+    public static X9ECParametersHolder getByOIDLazy(ASN1ObjectIdentifier oid)
+    {
+        return (X9ECParametersHolder)oidToCurve.get(oid);
+    }
+
     /**
      * return the object identifier signified by the passed in name. Null if there is no object
      * identifier associated with name.
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/encodings/OAEPEncoding.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/encodings/OAEPEncoding.java
index 23fcdd6..bb835c7 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/encodings/OAEPEncoding.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/encodings/OAEPEncoding.java
@@ -14,6 +14,7 @@
 // import org.bouncycastle.crypto.util.DigestFactory;
 import com.android.internal.org.bouncycastle.crypto.digests.AndroidDigestFactory;
 import com.android.internal.org.bouncycastle.util.Arrays;
+import com.android.internal.org.bouncycastle.util.Pack;
 
 /**
  * Optimal Asymmetric Encryption Padding (OAEP) - see PKCS 1 V 2.
@@ -227,7 +228,9 @@
         // on encryption, we need to make sure our decrypted block comes back
         // the same size.
         //
-        boolean wrongData = (block.length < (2 * defHash.length) + 1);
+
+        // i.e. wrong when block.length < (2 * defHash.length) + 1
+        int wrongMask = (block.length - ((2 * defHash.length) + 1)) >> 31;
 
         if (data.length <= block.length)
         {
@@ -236,7 +239,7 @@
         else
         {
             System.arraycopy(data, 0, block, 0, block.length);
-            wrongData = true;
+            wrongMask |= 1;
         }
 
         //
@@ -264,39 +267,38 @@
         // check the hash of the encoding params.
         // long check to try to avoid this been a source of a timing attack.
         //
-        boolean defHashWrong = false;
-
         for (int i = 0; i != defHash.length; i++)
         {
-            if (defHash[i] != block[defHash.length + i])
-            {
-                defHashWrong = true;
-            }
+            wrongMask |= defHash[i] ^ block[defHash.length + i];
         }
 
         //
         // find the data block
         //
-        int start = block.length;
+        int start = -1;
 
         for (int index = 2 * defHash.length; index != block.length; index++)
         {
-            if (block[index] != 0 & start == block.length)
-            {
-                start = index;
-            }
+            int octet = block[index] & 0xFF;
+
+            // i.e. mask will be 0xFFFFFFFF if octet is non-zero and start is (still) negative, else 0.
+            int shouldSetMask = (-octet & start) >> 31;
+
+            start += index & shouldSetMask;
         }
 
-        boolean dataStartWrong = (start > (block.length - 1) | block[start] != 1);
+        wrongMask |= start >> 31;
+        ++start;
+        wrongMask |= block[start] ^ 1;
 
-        start++;
-
-        if (defHashWrong | wrongData | dataStartWrong)
+        if (wrongMask != 0)
         {
             Arrays.fill(block, (byte)0);
             throw new InvalidCipherTextException("data wrong");
         }
 
+        ++start;
+
         //
         // extract the data block
         //
@@ -309,19 +311,6 @@
     }
 
     /**
-     * int to octet string.
-     */
-    private void ItoOSP(
-        int     i,
-        byte[]  sp)
-    {
-        sp[0] = (byte)(i >>> 24);
-        sp[1] = (byte)(i >>> 16);
-        sp[2] = (byte)(i >>> 8);
-        sp[3] = (byte)(i >>> 0);
-    }
-
-    /**
      * mask generator function, as described in PKCS1v2.
      */
     private byte[] maskGeneratorFunction1(
@@ -339,7 +328,7 @@
 
         while (counter < (length / hashBuf.length))
         {
-            ItoOSP(counter, C);
+            Pack.intToBigEndian(counter, C, 0);
 
             mgf1Hash.update(Z, zOff, zLen);
             mgf1Hash.update(C, 0, C.length);
@@ -352,7 +341,7 @@
 
         if ((counter * hashBuf.length) < length)
         {
-            ItoOSP(counter, C);
+            Pack.intToBigEndian(counter, C, 0);
 
             mgf1Hash.update(Z, zOff, zLen);
             mgf1Hash.update(C, 0, C.length);
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/encodings/PKCS1Encoding.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/encodings/PKCS1Encoding.java
index aafbf6e..d3b8c97 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/encodings/PKCS1Encoding.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/encodings/PKCS1Encoding.java
@@ -235,52 +235,86 @@
     }
 
     /**
-     * Checks if the argument is a correctly PKCS#1.5 encoded Plaintext
-     * for encryption.
-     *
-     * @param encoded The Plaintext.
-     * @param pLen    Expected length of the plaintext.
-     * @return Either 0, if the encoding is correct, or -1, if it is incorrect.
+     * Check the argument is a valid encoding with type 1. Returns the plaintext length if valid, or -1 if invalid.
      */
-    private static int checkPkcs1Encoding(byte[] encoded, int pLen)
+    private static int checkPkcs1Encoding1(byte[] buf)
     {
-        int correct = 0;
-        /*
-		 * Check if the first two bytes are 0 2
-		 */
-        correct |= (encoded[0] ^ 2);
+        int foundZeroMask = 0;
+        int lastPadPos = 0;
 
-		/*
-		 * Now the padding check, check for no 0 byte in the padding
-		 */
-        int plen = encoded.length - (
-            pLen /* Length of the PMS */
-                + 1 /* Final 0-byte before PMS */
-        );
+        // The first byte should be 0x01
+        int badPadSign = -((buf[0] & 0xFF) ^ 0x01);
 
-        for (int i = 1; i < plen; i++)
+        // There must be a zero terminator for the padding somewhere
+        for (int i = 1; i < buf.length; ++i)
         {
-            int tmp = encoded[i];
-            tmp |= tmp >> 1;
-            tmp |= tmp >> 2;
-            tmp |= tmp >> 4;
-            correct |= (tmp & 1) - 1;
+            int padByte = buf[i] & 0xFF;
+            int is0x00Mask = ((padByte ^ 0x00) - 1) >> 31;
+            int is0xFFMask = ((padByte ^ 0xFF) - 1) >> 31;
+            lastPadPos ^= i & ~foundZeroMask & is0x00Mask;
+            foundZeroMask |= is0x00Mask;
+            badPadSign |= ~(foundZeroMask | is0xFFMask);
         }
 
-		/*
-		 * Make sure the padding ends with a 0 byte.
-		 */
-        correct |= encoded[encoded.length - (pLen + 1)];
+        // The header should be at least 10 bytes
+        badPadSign |= lastPadPos - 9;
 
-		/*
-		 * Return 0 or 1, depending on the result.
-		 */
-        correct |= correct >> 1;
-        correct |= correct >> 2;
-        correct |= correct >> 4;
-        return ~((correct & 1) - 1);
+        int plaintextLength = buf.length - 1 - lastPadPos;
+        return plaintextLength | badPadSign >> 31;
     }
 
+    /**
+     * Check the argument is a valid encoding with type 2. Returns the plaintext length if valid, or -1 if invalid.
+     */
+    private static int checkPkcs1Encoding2(byte[] buf)
+    {
+        int foundZeroMask = 0;
+        int lastPadPos = 0;
+
+        // The first byte should be 0x02
+        int badPadSign = -((buf[0] & 0xFF) ^ 0x02);
+
+        // There must be a zero terminator for the padding somewhere
+        for (int i = 1; i < buf.length; ++i)
+        {
+            int padByte = buf[i] & 0xFF;
+            int is0x00Mask = ((padByte ^ 0x00) - 1) >> 31;
+            lastPadPos ^= i & ~foundZeroMask & is0x00Mask;
+            foundZeroMask |= is0x00Mask;
+        }
+
+        // The header should be at least 10 bytes
+        badPadSign |= lastPadPos - 9;
+
+        int plaintextLength = buf.length - 1 - lastPadPos;
+        return plaintextLength | badPadSign >> 31;
+    }
+
+    /**
+     * Check the argument is a valid encoding with type 2 of a plaintext with the given length. Returns 0 if
+     * valid, or -1 if invalid.
+     */
+    private static int checkPkcs1Encoding2(byte[] buf, int plaintextLength)
+    {
+        // The first byte should be 0x02
+        int badPadSign = -((buf[0] & 0xFF) ^ 0x02);
+
+        int lastPadPos = buf.length - 1 - plaintextLength;
+
+        // The header should be at least 10 bytes
+        badPadSign |= lastPadPos - 9;
+
+        // All pad bytes before the last one should be non-zero
+        for (int i = 1; i < lastPadPos; ++i)
+        {
+            badPadSign |= (buf[i] & 0xFF) - 1;
+        }
+
+        // Last pad byte should be zero
+        badPadSign |= -(buf[lastPadPos] & 0xFF);
+
+        return badPadSign >> 31;
+    }
 
     /**
      * Decode PKCS#1.5 encoding, and return a random value if the padding is not correct.
@@ -300,36 +334,43 @@
             throw new InvalidCipherTextException("sorry, this method is only for decryption, not for signing");
         }
 
-        byte[] block = engine.processBlock(in, inOff, inLen);
-        byte[] random;
-        if (this.fallback == null)
+        int plaintextLength = this.pLen;
+
+        byte[] random = fallback;
+        if (fallback == null)
         {
-            random = new byte[this.pLen];
+            random = new byte[plaintextLength];
             this.random.nextBytes(random);
         }
-        else
+
+        int badPadMask = 0;
+        int strictBlockSize = engine.getOutputBlockSize();
+        byte[] block = engine.processBlock(in, inOff, inLen);
+
+        byte[] data = block;
+        if (block.length != strictBlockSize)
         {
-            random = fallback;
+            if (useStrictLength || block.length < strictBlockSize)
+            {
+                data = blockBuffer;
+            }
         }
 
-        byte[] data = (useStrictLength & (block.length != engine.getOutputBlockSize())) ? blockBuffer : block;
+        badPadMask |= checkPkcs1Encoding2(data, plaintextLength);
 
-		/*
-		 * Check the padding.
-		 */
-        int correct = PKCS1Encoding.checkPkcs1Encoding(data, this.pLen);
-		
-		/*
-		 * Now, to a constant time constant memory copy of the decrypted value
-		 * or the random value, depending on the validity of the padding.
-		 */
-        byte[] result = new byte[this.pLen];
-        for (int i = 0; i < this.pLen; i++)
+        /*
+         * Now, to a constant time constant memory copy of the decrypted value
+         * or the random value, depending on the validity of the padding.
+         */
+        int dataOff = data.length - plaintextLength; 
+        byte[] result = new byte[plaintextLength];
+        for (int i = 0; i < plaintextLength; ++i)
         {
-            result[i] = (byte)((data[i + (data.length - pLen)] & (~correct)) | (random[i] & correct));
+            result[i] = (byte)((data[dataOff + i] & ~badPadMask) | (random[i] & badPadMask));
         }
 
-        Arrays.fill(data, (byte)0);
+        Arrays.fill(block, (byte)0);
+        Arrays.fill(blockBuffer, 0, Math.max(0, blockBuffer.length - block.length), (byte)0);
 
         return result;
     }
@@ -337,95 +378,50 @@
     /**
      * @throws InvalidCipherTextException if the decrypted block is not in PKCS1 format.
      */
-    private byte[] decodeBlock(
-        byte[] in,
-        int inOff,
-        int inLen)
+    private byte[] decodeBlock(byte[] in, int inOff, int inLen)
         throws InvalidCipherTextException
     {
         /*
          * If the length of the expected plaintext is known, we use a constant-time decryption.
          * If the decryption fails, we return a random value.
          */
-        if (this.pLen != -1)
+        if (forPrivateKey && this.pLen != -1)
         {
             return this.decodeBlockOrRandom(in, inOff, inLen);
         }
 
+        int strictBlockSize = engine.getOutputBlockSize();
         byte[] block = engine.processBlock(in, inOff, inLen);
-        boolean incorrectLength = (useStrictLength & (block.length != engine.getOutputBlockSize()));
 
-        byte[] data;
-        if (block.length < getOutputBlockSize())
+        boolean incorrectLength = useStrictLength & (block.length != strictBlockSize);
+
+        byte[] data = block;
+        if (block.length < strictBlockSize)
         {
             data = blockBuffer;
         }
-        else
+
+        int plaintextLength = forPrivateKey ? checkPkcs1Encoding2(data) : checkPkcs1Encoding1(data);
+
+        try
         {
-            data = block;
-        }
-
-        byte type = data[0];
-
-        boolean badType;
-        if (forPrivateKey)
-        {
-            badType = (type != 2);
-        }
-        else
-        {
-            badType = (type != 1);
-        }
-
-        //
-        // find and extract the message block.
-        //
-        int start = findStart(type, data);
-
-        start++;           // data should start at the next byte
-
-        if (badType | start < HEADER_LENGTH)
-        {
-            Arrays.fill(data, (byte)0);
-            throw new InvalidCipherTextException("block incorrect");
-        }
-
-        // if we get this far, it's likely to be a genuine encoding error
-        if (incorrectLength)
-        {
-            Arrays.fill(data, (byte)0);
-            throw new InvalidCipherTextException("block incorrect size");
-        }
-
-        byte[] result = new byte[data.length - start];
-
-        System.arraycopy(data, start, result, 0, result.length);
-
-        return result;
-    }
-
-    private int findStart(byte type, byte[] block)
-        throws InvalidCipherTextException
-    {
-        int start = -1;
-        boolean padErr = false;
-
-        for (int i = 1; i != block.length; i++)
-        {
-            byte pad = block[i];
-
-            if (pad == 0 & start < 0)
+            if (plaintextLength < 0)
             {
-                start = i;
+                throw new InvalidCipherTextException("block incorrect");
             }
-            padErr |= (type == 1 & start < 0 & pad != (byte)0xff);
-        }
+            if (incorrectLength)
+            {
+                throw new InvalidCipherTextException("block incorrect size");
+            }
 
-        if (padErr)
+            byte[] result = new byte[plaintextLength];
+            System.arraycopy(data, data.length - plaintextLength, result, 0, plaintextLength);
+            return result;
+        }
+        finally
         {
-            return -1;
+            Arrays.fill(block, (byte)0);
+            Arrays.fill(blockBuffer, 0, Math.max(0, blockBuffer.length - block.length), (byte)0);
         }
-
-        return start;
     }
 }
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/engines/AESEngine.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/engines/AESEngine.java
index 1ad46f6..513faf7 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/engines/AESEngine.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/engines/AESEngine.java
@@ -1,10 +1,13 @@
 /* GENERATED SOURCE. DO NOT MODIFY. */
 package com.android.internal.org.bouncycastle.crypto.engines;
 
-import com.android.internal.org.bouncycastle.crypto.BlockCipher;
 import com.android.internal.org.bouncycastle.crypto.CipherParameters;
+import com.android.internal.org.bouncycastle.crypto.CryptoServicesRegistrar;
 import com.android.internal.org.bouncycastle.crypto.DataLengthException;
+import com.android.internal.org.bouncycastle.crypto.DefaultMultiBlockCipher;
+import com.android.internal.org.bouncycastle.crypto.MultiBlockCipher;
 import com.android.internal.org.bouncycastle.crypto.OutputLengthException;
+import com.android.internal.org.bouncycastle.crypto.constraints.DefaultServiceProperties;
 import com.android.internal.org.bouncycastle.crypto.params.KeyParameter;
 import com.android.internal.org.bouncycastle.util.Arrays;
 import com.android.internal.org.bouncycastle.util.Pack;
@@ -15,7 +18,7 @@
  * For further details see: <a href="https://csrc.nist.gov/encryption/aes/">https://csrc.nist.gov/encryption/aes/</a>.
  *
  * This implementation is based on optimizations from Dr. Brian Gladman's paper and C code at
- * <a href="http://fp.gladman.plus.com/cryptography_technology/rijndael/">http://fp.gladman.plus.com/cryptography_technology/rijndael/</a>
+ * <a href="https://fp.gladman.plus.com/cryptography_technology/rijndael/">https://fp.gladman.plus.com/cryptography_technology/rijndael/</a>
  *
  * There are three levels of tradeoff of speed vs memory
  * Because java has no preprocessor, they are written as three separate classes from which to choose
@@ -34,7 +37,7 @@
  *
  */
 public class AESEngine
-    implements BlockCipher
+    extends DefaultMultiBlockCipher
 {
     // The S box
     private static final byte[] S = {
@@ -414,7 +417,6 @@
 
     private int         ROUNDS;
     private int[][]     WorkingKey = null;
-    private int         C0, C1, C2, C3;
     private boolean     forEncryption;
 
     private byte[]      s;
@@ -422,10 +424,22 @@
     private static final int BLOCK_SIZE = 16;
 
     /**
+      * Return an AESEngine.
+      *
+      * @return an AES ECB mode cipher.
+      */
+     public static MultiBlockCipher newInstance()
+     {
+         return new AESEngine();
+     }
+
+    /**
      * default constructor - 128 bit block size.
+     * @deprecated use AESEngine.newInstance()
      */
     public AESEngine()
     {
+        CryptoServicesRegistrar.checkConstraints(new DefaultServiceProperties(getAlgorithmName(), 256));
     }
 
     /**
@@ -452,6 +466,9 @@
             {
                 s = Arrays.clone(Si);
             }
+
+            CryptoServicesRegistrar.checkConstraints(new DefaultServiceProperties(getAlgorithmName(), bitsOfSecurity(), params, Utils.getPurpose(forEncryption)));
+
             return;
         }
 
@@ -468,38 +485,30 @@
         return BLOCK_SIZE;
     }
 
-    public int processBlock(
-        byte[] in,
-        int inOff,
-        byte[] out,
-        int outOff)
+    public int processBlock(byte[] in, int inOff, byte[] out, int outOff)
     {
         if (WorkingKey == null)
         {
             throw new IllegalStateException("AES engine not initialised");
         }
 
-        if ((inOff + (32 / 2)) > in.length)
+        if (inOff > (in.length - BLOCK_SIZE))
         {
             throw new DataLengthException("input buffer too short");
         }
 
-        if ((outOff + (32 / 2)) > out.length)
+        if (outOff > (out.length - BLOCK_SIZE))
         {
             throw new OutputLengthException("output buffer too short");
         }
 
         if (forEncryption)
         {
-            unpackBlock(in, inOff);
-            encryptBlock(WorkingKey);
-            packBlock(out, outOff);
+            encryptBlock(in, inOff, out, outOff, WorkingKey);
         }
         else
         {
-            unpackBlock(in, inOff);
-            decryptBlock(WorkingKey);
-            packBlock(out, outOff);
+            decryptBlock(in, inOff, out, outOff, WorkingKey);
         }
 
         return BLOCK_SIZE;
@@ -509,68 +518,18 @@
     {
     }
 
-    private void unpackBlock(
-        byte[]      bytes,
-        int         off)
+    private void encryptBlock(byte[] in, int inOff, byte[] out, int outOff, int[][] KW)
     {
-        int     index = off;
+        int C0 = Pack.littleEndianToInt(in, inOff +  0);
+        int C1 = Pack.littleEndianToInt(in, inOff +  4);
+        int C2 = Pack.littleEndianToInt(in, inOff +  8);
+        int C3 = Pack.littleEndianToInt(in, inOff + 12);
 
-        C0 = (bytes[index++] & 0xff);
-        C0 |= (bytes[index++] & 0xff) << 8;
-        C0 |= (bytes[index++] & 0xff) << 16;
-        C0 |= bytes[index++] << 24;
+        int t0 = C0 ^ KW[0][0];
+        int t1 = C1 ^ KW[0][1];
+        int t2 = C2 ^ KW[0][2];
 
-        C1 = (bytes[index++] & 0xff);
-        C1 |= (bytes[index++] & 0xff) << 8;
-        C1 |= (bytes[index++] & 0xff) << 16;
-        C1 |= bytes[index++] << 24;
-
-        C2 = (bytes[index++] & 0xff);
-        C2 |= (bytes[index++] & 0xff) << 8;
-        C2 |= (bytes[index++] & 0xff) << 16;
-        C2 |= bytes[index++] << 24;
-
-        C3 = (bytes[index++] & 0xff);
-        C3 |= (bytes[index++] & 0xff) << 8;
-        C3 |= (bytes[index++] & 0xff) << 16;
-        C3 |= bytes[index++] << 24;
-    }
-
-    private void packBlock(
-        byte[]      bytes,
-        int         off)
-    {
-        int     index = off;
-
-        bytes[index++] = (byte)C0;
-        bytes[index++] = (byte)(C0 >> 8);
-        bytes[index++] = (byte)(C0 >> 16);
-        bytes[index++] = (byte)(C0 >> 24);
-
-        bytes[index++] = (byte)C1;
-        bytes[index++] = (byte)(C1 >> 8);
-        bytes[index++] = (byte)(C1 >> 16);
-        bytes[index++] = (byte)(C1 >> 24);
-
-        bytes[index++] = (byte)C2;
-        bytes[index++] = (byte)(C2 >> 8);
-        bytes[index++] = (byte)(C2 >> 16);
-        bytes[index++] = (byte)(C2 >> 24);
-
-        bytes[index++] = (byte)C3;
-        bytes[index++] = (byte)(C3 >> 8);
-        bytes[index++] = (byte)(C3 >> 16);
-        bytes[index++] = (byte)(C3 >> 24);
-    }
-
-
-    private void encryptBlock(int[][] KW)
-    {
-        int t0 = this.C0 ^ KW[0][0];
-        int t1 = this.C1 ^ KW[0][1];
-        int t2 = this.C2 ^ KW[0][2];
-
-        int r = 1, r0, r1, r2, r3 = this.C3 ^ KW[0][3];
+        int r = 1, r0, r1, r2, r3 = C3 ^ KW[0][3];
         while (r < ROUNDS - 1)
         {
             r0 = T0[t0&255] ^ shift(T0[(t1>>8)&255], 24) ^ shift(T0[(t2>>16)&255], 16) ^ shift(T0[(r3>>24)&255], 8) ^ KW[r][0];
@@ -590,19 +549,29 @@
 
         // the final round's table is a simple function of S so we don't use a whole other four tables for it
 
-        this.C0 = (S[r0&255]&255) ^ ((S[(r1>>8)&255]&255)<<8) ^ ((s[(r2>>16)&255]&255)<<16) ^ (s[(r3>>24)&255]<<24) ^ KW[r][0];
-        this.C1 = (s[r1&255]&255) ^ ((S[(r2>>8)&255]&255)<<8) ^ ((S[(r3>>16)&255]&255)<<16) ^ (s[(r0>>24)&255]<<24) ^ KW[r][1];
-        this.C2 = (s[r2&255]&255) ^ ((S[(r3>>8)&255]&255)<<8) ^ ((S[(r0>>16)&255]&255)<<16) ^ (S[(r1>>24)&255]<<24) ^ KW[r][2];
-        this.C3 = (s[r3&255]&255) ^ ((s[(r0>>8)&255]&255)<<8) ^ ((s[(r1>>16)&255]&255)<<16) ^ (S[(r2>>24)&255]<<24) ^ KW[r][3];
+        C0 = (S[r0&255]&255) ^ ((S[(r1>>8)&255]&255)<<8) ^ ((s[(r2>>16)&255]&255)<<16) ^ (s[(r3>>24)&255]<<24) ^ KW[r][0];
+        C1 = (s[r1&255]&255) ^ ((S[(r2>>8)&255]&255)<<8) ^ ((S[(r3>>16)&255]&255)<<16) ^ (s[(r0>>24)&255]<<24) ^ KW[r][1];
+        C2 = (s[r2&255]&255) ^ ((S[(r3>>8)&255]&255)<<8) ^ ((S[(r0>>16)&255]&255)<<16) ^ (S[(r1>>24)&255]<<24) ^ KW[r][2];
+        C3 = (s[r3&255]&255) ^ ((s[(r0>>8)&255]&255)<<8) ^ ((s[(r1>>16)&255]&255)<<16) ^ (S[(r2>>24)&255]<<24) ^ KW[r][3];
+
+        Pack.intToLittleEndian(C0, out, outOff +  0);
+        Pack.intToLittleEndian(C1, out, outOff +  4);
+        Pack.intToLittleEndian(C2, out, outOff +  8);
+        Pack.intToLittleEndian(C3, out, outOff + 12);
     }
 
-    private void decryptBlock(int[][] KW)
+    private void decryptBlock(byte[] in, int inOff, byte[] out, int outOff, int[][] KW)
     {
-        int t0 = this.C0 ^ KW[ROUNDS][0];
-        int t1 = this.C1 ^ KW[ROUNDS][1];
-        int t2 = this.C2 ^ KW[ROUNDS][2];
+        int C0 = Pack.littleEndianToInt(in, inOff +  0);
+        int C1 = Pack.littleEndianToInt(in, inOff +  4);
+        int C2 = Pack.littleEndianToInt(in, inOff +  8);
+        int C3 = Pack.littleEndianToInt(in, inOff + 12);
 
-        int r = ROUNDS - 1, r0, r1, r2, r3 = this.C3 ^ KW[ROUNDS][3];
+        int t0 = C0 ^ KW[ROUNDS][0];
+        int t1 = C1 ^ KW[ROUNDS][1];
+        int t2 = C2 ^ KW[ROUNDS][2];
+
+        int r = ROUNDS - 1, r0, r1, r2, r3 = C3 ^ KW[ROUNDS][3];
         while (r > 1)
         {
             r0 = Tinv0[t0&255] ^ shift(Tinv0[(r3>>8)&255], 24) ^ shift(Tinv0[(t2>>16)&255], 16) ^ shift(Tinv0[(t1>>24)&255], 8) ^ KW[r][0];
@@ -622,9 +591,23 @@
         
         // the final round's table is a simple function of Si so we don't use a whole other four tables for it
 
-        this.C0 = (Si[r0&255]&255) ^ ((s[(r3>>8)&255]&255)<<8) ^ ((s[(r2>>16)&255]&255)<<16) ^ (Si[(r1>>24)&255]<<24) ^ KW[0][0];
-        this.C1 = (s[r1&255]&255) ^ ((s[(r0>>8)&255]&255)<<8) ^ ((Si[(r3>>16)&255]&255)<<16) ^ (s[(r2>>24)&255]<<24) ^ KW[0][1];
-        this.C2 = (s[r2&255]&255) ^ ((Si[(r1>>8)&255]&255)<<8) ^ ((Si[(r0>>16)&255]&255)<<16) ^ (s[(r3>>24)&255]<<24) ^ KW[0][2];
-        this.C3 = (Si[r3&255]&255) ^ ((s[(r2>>8)&255]&255)<<8) ^ ((s[(r1>>16)&255]&255)<<16) ^ (s[(r0>>24)&255]<<24) ^ KW[0][3];
+        C0 = (Si[r0&255]&255) ^ ((s[(r3>>8)&255]&255)<<8) ^ ((s[(r2>>16)&255]&255)<<16) ^ (Si[(r1>>24)&255]<<24) ^ KW[0][0];
+        C1 = (s[r1&255]&255) ^ ((s[(r0>>8)&255]&255)<<8) ^ ((Si[(r3>>16)&255]&255)<<16) ^ (s[(r2>>24)&255]<<24) ^ KW[0][1];
+        C2 = (s[r2&255]&255) ^ ((Si[(r1>>8)&255]&255)<<8) ^ ((Si[(r0>>16)&255]&255)<<16) ^ (s[(r3>>24)&255]<<24) ^ KW[0][2];
+        C3 = (Si[r3&255]&255) ^ ((s[(r2>>8)&255]&255)<<8) ^ ((s[(r1>>16)&255]&255)<<16) ^ (s[(r0>>24)&255]<<24) ^ KW[0][3];
+
+        Pack.intToLittleEndian(C0, out, outOff +  0);
+        Pack.intToLittleEndian(C1, out, outOff +  4);
+        Pack.intToLittleEndian(C2, out, outOff +  8);
+        Pack.intToLittleEndian(C3, out, outOff + 12);
+    }
+
+    private int bitsOfSecurity()
+    {
+        if (WorkingKey == null)
+        {
+            return 256;
+        }
+        return (WorkingKey.length - 7) << 5;
     }
 }
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/engines/AESFastEngine.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/engines/AESFastEngine.java
index 71345c1..82cf0a6 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/engines/AESFastEngine.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/engines/AESFastEngine.java
@@ -3,8 +3,10 @@
 
 import com.android.internal.org.bouncycastle.crypto.BlockCipher;
 import com.android.internal.org.bouncycastle.crypto.CipherParameters;
+import com.android.internal.org.bouncycastle.crypto.CryptoServicesRegistrar;
 import com.android.internal.org.bouncycastle.crypto.DataLengthException;
 import com.android.internal.org.bouncycastle.crypto.OutputLengthException;
+import com.android.internal.org.bouncycastle.crypto.constraints.DefaultServiceProperties;
 import com.android.internal.org.bouncycastle.crypto.params.KeyParameter;
 import com.android.internal.org.bouncycastle.util.Pack;
 
@@ -14,7 +16,7 @@
  * For further details see: <a href="https://csrc.nist.gov/encryption/aes/">https://csrc.nist.gov/encryption/aes/</a>.
  *
  * This implementation is based on optimizations from Dr. Brian Gladman's paper and C code at
- * <a href="http://fp.gladman.plus.com/cryptography_technology/rijndael/">http://fp.gladman.plus.com/cryptography_technology/rijndael/</a>
+ * <a href="https://fp.gladman.plus.com/cryptography_technology/rijndael/">https://fp.gladman.plus.com/cryptography_technology/rijndael/</a>
  *
  * There are three levels of tradeoff of speed vs memory
  * Because java has no preprocessor, they are written as three separate classes from which to choose
@@ -743,7 +745,6 @@
 
     private int         ROUNDS;
     private int[][]     WorkingKey = null;
-    private int         C0, C1, C2, C3;
     private boolean     forEncryption;
 
     private static final int BLOCK_SIZE = 16;
@@ -753,6 +754,7 @@
      */
     public AESFastEngine()
     {
+        CryptoServicesRegistrar.checkConstraints(new DefaultServiceProperties(getAlgorithmName(), 256));
     }
 
     /**
@@ -771,6 +773,7 @@
         {
             WorkingKey = generateWorkingKey(((KeyParameter)params).getKey(), forEncryption);
             this.forEncryption = forEncryption;
+            CryptoServicesRegistrar.checkConstraints(new DefaultServiceProperties(getAlgorithmName(), bitsOfSecurity(), params, Utils.getPurpose(forEncryption)));
             return;
         }
 
@@ -787,40 +790,32 @@
         return BLOCK_SIZE;
     }
 
-    public int processBlock(
-        byte[] in,
-        int inOff,
-        byte[] out,
-        int outOff)
+    public int processBlock(byte[] in, int inOff, byte[] out, int outOff)
     {
         if (WorkingKey == null)
         {
             throw new IllegalStateException("AES engine not initialised");
         }
 
-        if ((inOff + (32 / 2)) > in.length)
+        if (inOff > (in.length - BLOCK_SIZE))
         {
             throw new DataLengthException("input buffer too short");
         }
 
-        if ((outOff + (32 / 2)) > out.length)
+        if (outOff > (out.length - BLOCK_SIZE))
         {
             throw new OutputLengthException("output buffer too short");
         }
 
-        unpackBlock(in, inOff);
-
         if (forEncryption)
         {
-            encryptBlock(WorkingKey);
+            encryptBlock(in, inOff, out, outOff, WorkingKey);
         }
         else
         {
-            decryptBlock(WorkingKey);
+            decryptBlock(in, inOff, out, outOff, WorkingKey);
         }
 
-        packBlock(out, outOff);
-
         return BLOCK_SIZE;
     }
 
@@ -828,27 +823,16 @@
     {
     }
 
-    private void unpackBlock(byte[] bytes, int off)
+    private void encryptBlock(byte[] in, int inOff, byte[] out, int outOff, int[][] KW)
     {
-        this.C0 = Pack.littleEndianToInt(bytes, off);
-        this.C1 = Pack.littleEndianToInt(bytes, off + 4);
-        this.C2 = Pack.littleEndianToInt(bytes, off + 8);
-        this.C3 = Pack.littleEndianToInt(bytes, off + 12);
-    }
+        int C0 = Pack.littleEndianToInt(in, inOff +  0);
+        int C1 = Pack.littleEndianToInt(in, inOff +  4);
+        int C2 = Pack.littleEndianToInt(in, inOff +  8);
+        int C3 = Pack.littleEndianToInt(in, inOff + 12);
 
-    private void packBlock(byte[] bytes, int off)
-    {
-        Pack.intToLittleEndian(this.C0, bytes, off);
-        Pack.intToLittleEndian(this.C1, bytes, off + 4);
-        Pack.intToLittleEndian(this.C2, bytes, off + 8);
-        Pack.intToLittleEndian(this.C3, bytes, off + 12);
-    }
-
-    private void encryptBlock(int[][] KW)
-    {
-        int t0 = this.C0 ^ KW[0][0];
-        int t1 = this.C1 ^ KW[0][1];
-        int t2 = this.C2 ^ KW[0][2];
+        int t0 = C0 ^ KW[0][0];
+        int t1 = C1 ^ KW[0][1];
+        int t2 = C2 ^ KW[0][2];
 
         /*
          * Fast engine has precomputed rotr(T0, 8/16/24) tables T1/T2/T3.
@@ -857,7 +841,7 @@
          * avoids additional array range checks on 3 more arrays (which on HotSpot are more
          * expensive than the offset additions).
          */
-        int r = 1, r0, r1, r2, r3 = this.C3 ^ KW[0][3];
+        int r = 1, r0, r1, r2, r3 = C3 ^ KW[0][3];
         int i0, i1, i2, i3;
 
         while (r < ROUNDS - 1)
@@ -915,28 +899,38 @@
 
         i0 = r0; i1 = r1 >>> 8; i2 = r2 >>> 16; i3 = r3 >>> 24;
         i0 = S[i0 & 255] & 255; i1 = S[i1 & 255] & 255; i2 = S[i2 & 255] & 255; i3 = S[i3 & 255] & 255;
-        this.C0 = i0 ^ i1 << 8 ^ i2 << 16 ^ i3 << 24 ^ KW[r][0];
+        C0 = i0 ^ i1 << 8 ^ i2 << 16 ^ i3 << 24 ^ KW[r][0];
 
         i0 = r1; i1 = r2 >>> 8; i2 = r3 >>> 16; i3 = r0 >>> 24;
         i0 = S[i0 & 255] & 255; i1 = S[i1 & 255] & 255; i2 = S[i2 & 255] & 255; i3 = S[i3 & 255] & 255;
-        this.C1 = i0 ^ i1 << 8 ^ i2 << 16 ^ i3 << 24 ^ KW[r][1];
+        C1 = i0 ^ i1 << 8 ^ i2 << 16 ^ i3 << 24 ^ KW[r][1];
 
         i0 = r2; i1 = r3 >>> 8; i2 = r0 >>> 16; i3 = r1 >>> 24;
         i0 = S[i0 & 255] & 255; i1 = S[i1 & 255] & 255; i2 = S[i2 & 255] & 255; i3 = S[i3 & 255] & 255;
-        this.C2 = i0 ^ i1 << 8 ^ i2 << 16 ^ i3 << 24 ^ KW[r][2];
+        C2 = i0 ^ i1 << 8 ^ i2 << 16 ^ i3 << 24 ^ KW[r][2];
 
         i0 = r3; i1 = r0 >>> 8; i2 = r1 >>> 16; i3 = r2 >>> 24;
         i0 = S[i0 & 255] & 255; i1 = S[i1 & 255] & 255; i2 = S[i2 & 255] & 255; i3 = S[i3 & 255] & 255;
-        this.C3 = i0 ^ i1 << 8 ^ i2 << 16 ^ i3 << 24 ^ KW[r][3];
+        C3 = i0 ^ i1 << 8 ^ i2 << 16 ^ i3 << 24 ^ KW[r][3];
+
+        Pack.intToLittleEndian(C0, out, outOff +  0);
+        Pack.intToLittleEndian(C1, out, outOff +  4);
+        Pack.intToLittleEndian(C2, out, outOff +  8);
+        Pack.intToLittleEndian(C3, out, outOff + 12);
     }
 
-    private void decryptBlock(int[][] KW)
+    private void decryptBlock(byte[] in, int inOff, byte[] out, int outOff, int[][] KW)
     {
-        int t0 = this.C0 ^ KW[ROUNDS][0];
-        int t1 = this.C1 ^ KW[ROUNDS][1];
-        int t2 = this.C2 ^ KW[ROUNDS][2];
+        int C0 = Pack.littleEndianToInt(in, inOff +  0);
+        int C1 = Pack.littleEndianToInt(in, inOff +  4);
+        int C2 = Pack.littleEndianToInt(in, inOff +  8);
+        int C3 = Pack.littleEndianToInt(in, inOff + 12);
 
-        int r = ROUNDS - 1, r0, r1, r2, r3 = this.C3 ^ KW[ROUNDS][3];
+        int t0 = C0 ^ KW[ROUNDS][0];
+        int t1 = C1 ^ KW[ROUNDS][1];
+        int t2 = C2 ^ KW[ROUNDS][2];
+
+        int r = ROUNDS - 1, r0, r1, r2, r3 = C3 ^ KW[ROUNDS][3];
         int i0, i1, i2, i3;
 
         while (r > 1)
@@ -994,18 +988,33 @@
 
         i0 = r0; i1 = r3 >>> 8; i2 = r2 >>> 16; i3 = r1 >>> 24;
         i0 = Si[i0 & 255] & 255; i1 = Si[i1 & 255] & 255; i2 = Si[i2 & 255] & 255; i3 = Si[i3 & 255] & 255;
-        this.C0 = i0 ^ i1 << 8 ^ i2 << 16 ^ i3 << 24 ^ KW[0][0];
+        C0 = i0 ^ i1 << 8 ^ i2 << 16 ^ i3 << 24 ^ KW[0][0];
 
         i0 = r1; i1 = r0 >>> 8; i2 = r3 >>> 16; i3 = r2 >>> 24;
         i0 = Si[i0 & 255] & 255; i1 = Si[i1 & 255] & 255; i2 = Si[i2 & 255] & 255; i3 = Si[i3 & 255] & 255;
-        this.C1 = i0 ^ i1 << 8 ^ i2 << 16 ^ i3 << 24 ^ KW[0][1];
+        C1 = i0 ^ i1 << 8 ^ i2 << 16 ^ i3 << 24 ^ KW[0][1];
 
         i0 = r2; i1 = r1 >>> 8; i2 = r0 >>> 16; i3 = r3 >>> 24;
         i0 = Si[i0 & 255] & 255; i1 = Si[i1 & 255] & 255; i2 = Si[i2 & 255] & 255; i3 = Si[i3 & 255] & 255;
-        this.C2 = i0 ^ i1 << 8 ^ i2 << 16 ^ i3 << 24 ^ KW[0][2];
+        C2 = i0 ^ i1 << 8 ^ i2 << 16 ^ i3 << 24 ^ KW[0][2];
 
         i0 = r3; i1 = r2 >>> 8; i2 = r1 >>> 16; i3 = r0 >>> 24;
         i0 = Si[i0 & 255] & 255; i1 = Si[i1 & 255] & 255; i2 = Si[i2 & 255] & 255; i3 = Si[i3 & 255] & 255;
-        this.C3 = i0 ^ i1 << 8 ^ i2 << 16 ^ i3 << 24 ^ KW[0][3];
+        C3 = i0 ^ i1 << 8 ^ i2 << 16 ^ i3 << 24 ^ KW[0][3];
+
+        Pack.intToLittleEndian(C0, out, outOff +  0);
+        Pack.intToLittleEndian(C1, out, outOff +  4);
+        Pack.intToLittleEndian(C2, out, outOff +  8);
+        Pack.intToLittleEndian(C3, out, outOff + 12);
+    }
+
+    // Service Definitions
+    private int bitsOfSecurity()
+    {
+        if (WorkingKey == null)
+        {
+            return 256;
+        }
+        return (WorkingKey.length - 7) << 5;
     }
 }
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/engines/AESWrapEngine.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/engines/AESWrapEngine.java
index 2b45af8..1b921ee 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/engines/AESWrapEngine.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/engines/AESWrapEngine.java
@@ -16,16 +16,16 @@
      */
     public AESWrapEngine()
     {
-        super(new AESEngine());
+        super(AESEngine.newInstance());
     }
 
     /**
-     * Create an AESWrapEngine where the underlying cipher is set to decrypt for wrapping, encrypt for unwrapping.
+     * Create an AESWrapEngine where the underlying cipher is (optionally) set to decrypt for wrapping, encrypt for unwrapping.
      *
      * @param useReverseDirection true if underlying cipher should be used in decryption mode, false otherwise.
      */
     public AESWrapEngine(boolean useReverseDirection)
     {
-        super(new AESEngine(), useReverseDirection);
+        super(AESEngine.newInstance(), useReverseDirection);
     }
 }
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/engines/AsconEngine.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/engines/AsconEngine.java
new file mode 100644
index 0000000..1350758
--- /dev/null
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/engines/AsconEngine.java
@@ -0,0 +1,743 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.crypto.engines;
+
+import com.android.internal.org.bouncycastle.crypto.CipherParameters;
+import com.android.internal.org.bouncycastle.crypto.CryptoServicesRegistrar;
+import com.android.internal.org.bouncycastle.crypto.DataLengthException;
+import com.android.internal.org.bouncycastle.crypto.InvalidCipherTextException;
+import com.android.internal.org.bouncycastle.crypto.OutputLengthException;
+import com.android.internal.org.bouncycastle.crypto.constraints.DefaultServiceProperties;
+import com.android.internal.org.bouncycastle.crypto.modes.AEADCipher;
+import com.android.internal.org.bouncycastle.crypto.params.AEADParameters;
+import com.android.internal.org.bouncycastle.crypto.params.KeyParameter;
+import com.android.internal.org.bouncycastle.crypto.params.ParametersWithIV;
+import com.android.internal.org.bouncycastle.util.Arrays;
+import com.android.internal.org.bouncycastle.util.Longs;
+import com.android.internal.org.bouncycastle.util.Pack;
+
+/**
+ * ASCON AEAD v1.2, https://ascon.iaik.tugraz.at/
+ * https://csrc.nist.gov/CSRC/media/Projects/lightweight-cryptography/documents/finalist-round/updated-spec-doc/ascon-spec-final.pdf
+ * <p>
+ * ASCON AEAD v1.2 with reference to C Reference Impl from: https://github.com/ascon/ascon-c
+ * </p>
+ * @hide This class is not part of the Android public SDK API
+ */
+public class AsconEngine
+    implements AEADCipher
+{
+    /**
+     * @hide This class is not part of the Android public SDK API
+     */
+    public enum AsconParameters
+    {
+        ascon80pq,
+        ascon128a,
+        ascon128
+    }
+
+    private enum State
+    {
+        Uninitialized,
+        EncInit,
+        EncAad,
+        EncData,
+        EncFinal,
+        DecInit,
+        DecAad,
+        DecData,
+        DecFinal,
+    }
+
+    private final AsconParameters asconParameters;
+    private State m_state = State.Uninitialized;
+    private byte[] mac;
+    private byte[] initialAssociatedText;
+    private final String algorithmName;
+    private final int CRYPTO_KEYBYTES;
+    private final int CRYPTO_ABYTES;
+    private final int ASCON_AEAD_RATE;
+    private final int nr;
+    private long K0;
+    private long K1;
+    private long K2;
+    private long N0;
+    private long N1;
+    private final long ASCON_IV;
+    private long x0;
+    private long x1;
+    private long x2;
+    private long x3;
+    private long x4;
+    private final int m_bufferSizeDecrypt;
+    private final byte[] m_buf;
+    private int m_bufPos = 0;
+
+    public AsconEngine(AsconParameters asconParameters)
+    {
+        this.asconParameters = asconParameters;
+        switch (asconParameters)
+        {
+        case ascon80pq:
+            CRYPTO_KEYBYTES = 20;
+            CRYPTO_ABYTES = 16;
+            ASCON_AEAD_RATE = 8;
+            ASCON_IV = 0xa0400c0600000000L;
+            algorithmName = "Ascon-80pq AEAD";
+            break;
+        case ascon128a:
+            CRYPTO_KEYBYTES = 16;
+            CRYPTO_ABYTES = 16;
+            ASCON_AEAD_RATE = 16;
+            ASCON_IV = 0x80800c0800000000L;
+            algorithmName = "Ascon-128a AEAD";
+            break;
+        case ascon128:
+            CRYPTO_KEYBYTES = 16;
+            CRYPTO_ABYTES = 16;
+            ASCON_AEAD_RATE = 8;
+            ASCON_IV = 0x80400c0600000000L;
+            algorithmName = "Ascon-128 AEAD";
+            break;
+        default:
+            throw new IllegalArgumentException("invalid parameter setting for ASCON AEAD");
+        }
+        nr = (ASCON_AEAD_RATE == 8) ? 6 : 8;
+        m_bufferSizeDecrypt = ASCON_AEAD_RATE + CRYPTO_ABYTES;
+        m_buf = new byte[m_bufferSizeDecrypt];
+    }
+
+    private long PAD(int i)
+    {
+        return 0x80L << (56 - (i << 3));
+    }
+
+    private void ROUND(long C)
+    {
+        long t0 = x0 ^ x1 ^ x2 ^ x3 ^ C ^ (x1 & (x0 ^ x2 ^ x4 ^ C));
+        long t1 = x0 ^ x2 ^ x3 ^ x4 ^ C ^ ((x1 ^ x2 ^ C) & (x1 ^ x3));
+        long t2 = x1 ^ x2 ^ x4 ^ C ^ (x3 & x4);
+        long t3 = x0 ^ x1 ^ x2 ^ C ^ ((~x0) & (x3 ^ x4));
+        long t4 = x1 ^ x3 ^ x4 ^ ((x0 ^ x4) & x1);
+        x0 = t0 ^ Longs.rotateRight(t0, 19) ^ Longs.rotateRight(t0, 28);
+        x1 = t1 ^ Longs.rotateRight(t1, 39) ^ Longs.rotateRight(t1, 61);
+        x2 = ~(t2 ^ Longs.rotateRight(t2, 1) ^ Longs.rotateRight(t2, 6));
+        x3 = t3 ^ Longs.rotateRight(t3, 10) ^ Longs.rotateRight(t3, 17);
+        x4 = t4 ^ Longs.rotateRight(t4, 7) ^ Longs.rotateRight(t4, 41);
+    }
+
+    private void P(int nr)
+    {
+        if (nr >= 8)
+        {
+            if (nr == 12)
+            {
+                ROUND(0xf0L);
+                ROUND(0xe1L);
+                ROUND(0xd2L);
+                ROUND(0xc3L);
+            }
+            ROUND(0xb4L);
+            ROUND(0xa5L);
+        }
+        ROUND(0x96L);
+        ROUND(0x87L);
+        ROUND(0x78L);
+        ROUND(0x69L);
+        ROUND(0x5aL);
+        ROUND(0x4bL);
+    }
+
+    private void ascon_aeadinit()
+    {
+        /* initialize */
+        x0 = ASCON_IV;
+        if (CRYPTO_KEYBYTES == 20)
+        {
+            x0 ^= K0;
+        }
+        x1 = K1;
+        x2 = K2;
+        x3 = N0;
+        x4 = N1;
+        P(12);
+        if (CRYPTO_KEYBYTES == 20)
+        {
+            x2 ^= K0;
+        }
+        x3 ^= K1;
+        x4 ^= K2;
+    }
+
+    private void checkAAD()
+    {
+        switch (m_state)
+        {
+        case DecInit:
+            m_state = State.DecAad;
+            break;
+        case EncInit:
+            m_state = State.EncAad;
+            break;
+        case DecAad:
+        case EncAad:
+            break;
+        case EncFinal:
+            throw new IllegalStateException(getAlgorithmName() + " cannot be reused for encryption");
+        default:
+            throw new IllegalStateException(getAlgorithmName() + " needs to be initialized");
+        }
+    }
+
+    private boolean checkData()
+    {
+        switch (m_state)
+        {
+        case DecInit:
+        case DecAad:
+            finishAAD(State.DecData);
+            return false;
+        case EncInit:
+        case EncAad:
+            finishAAD(State.EncData);
+            return true;
+        case DecData:
+            return false;
+        case EncData:
+            return true;
+        case EncFinal:
+            throw new IllegalStateException(getAlgorithmName() + " cannot be reused for encryption");
+        default:
+            throw new IllegalStateException(getAlgorithmName() + " needs to be initialized");
+        }
+    }
+
+    private void processBufferAAD(byte[] buffer, int inOff)
+    {
+        x0 ^= Pack.bigEndianToLong(buffer, inOff);
+        if (ASCON_AEAD_RATE == 16)
+        {
+            x1 ^= Pack.bigEndianToLong(buffer, 8 + inOff);
+        }
+        P(nr);
+    }
+
+    private void finishAAD(State nextState)
+    {
+        // State indicates whether we ever received AAD
+        switch (m_state)
+        {
+        case DecAad:
+        case EncAad:
+            m_buf[m_bufPos] = (byte)0x80;
+            if (m_bufPos >= 8) // ASCON_AEAD_RATE == 16 is implied
+            {
+                x0 ^= Pack.bigEndianToLong(m_buf, 0);
+                x1 ^= Pack.bigEndianToLong(m_buf, 8) & (-1L << (56 - ((m_bufPos - 8) << 3)));
+            }
+            else
+            {
+                x0 ^= Pack.bigEndianToLong(m_buf, 0) & (-1L << (56 - (m_bufPos << 3)));
+            }
+            P(nr);
+            break;
+        default:
+            break;
+        }
+        // domain separation
+        x4 ^= 1L;
+        m_bufPos = 0;
+        m_state = nextState;
+    }
+
+    private void processBufferDecrypt(byte[] buffer, int bufOff, byte[] output, int outOff)
+    {
+        if (outOff + ASCON_AEAD_RATE > output.length)
+        {
+            throw new OutputLengthException("output buffer too short");
+        }
+        long t0 = Pack.bigEndianToLong(buffer, bufOff);
+        Pack.longToBigEndian(x0 ^ t0, output, outOff);
+        x0 = t0;
+
+        if (ASCON_AEAD_RATE == 16)
+        {
+            long t1 = Pack.bigEndianToLong(buffer, bufOff + 8);
+            Pack.longToBigEndian(x1 ^ t1, output, outOff + 8);
+            x1 = t1;
+        }
+        P(nr);
+    }
+
+    private void processBufferEncrypt(byte[] buffer, int bufOff, byte[] output, int outOff)
+    {
+        if (outOff + ASCON_AEAD_RATE > output.length)
+        {
+            throw new OutputLengthException("output buffer too short");
+        }
+        x0 ^= Pack.bigEndianToLong(buffer, bufOff);
+        Pack.longToBigEndian(x0, output, outOff);
+
+        if (ASCON_AEAD_RATE == 16)
+        {
+            x1 ^= Pack.bigEndianToLong(buffer, bufOff + 8);
+            Pack.longToBigEndian(x1, output, outOff + 8);
+        }
+
+        P(nr);
+    }
+
+    private void processFinalDecrypt(byte[] input, int inOff, int inLen, byte[] output, int outOff)
+    {
+        if (inLen >= 8) // ASCON_AEAD_RATE == 16 is implied
+        {
+            long c0 = Pack.bigEndianToLong(input, inOff);
+            x0 ^= c0;
+            Pack.longToBigEndian(x0, output, outOff);
+            x0 = c0;
+            inOff += 8;
+            outOff += 8;
+            inLen -= 8;
+            x1 ^= PAD(inLen);
+            if (inLen != 0)
+            {
+                long c1 = Pack.littleEndianToLong_High(input, inOff, inLen);
+                x1 ^= c1;
+                Pack.longToLittleEndian_High(x1, output, outOff, inLen);
+                x1 &= -1L >>> (inLen << 3);
+                x1 ^= c1;
+            }
+        }
+        else
+        {
+            x0 ^= PAD(inLen);
+            if (inLen != 0)
+            {
+                long c0 = Pack.littleEndianToLong_High(input, inOff, inLen);
+                x0 ^= c0;
+                Pack.longToLittleEndian_High(x0, output, outOff, inLen);
+                x0 &= -1L >>> (inLen << 3);
+                x0 ^= c0;
+            }
+        }
+
+        finishData(State.DecFinal);
+    }
+
+    private void processFinalEncrypt(byte[] input, int inOff, int inLen, byte[] output, int outOff)
+    {
+        if (inLen >= 8) // ASCON_AEAD_RATE == 16 is implied
+        {
+            x0 ^= Pack.bigEndianToLong(input, inOff);
+            Pack.longToBigEndian(x0, output, outOff);
+            inOff += 8;
+            outOff += 8;
+            inLen -= 8;
+            x1 ^= PAD(inLen);
+            if (inLen != 0)
+            {
+                x1 ^= Pack.littleEndianToLong_High(input, inOff, inLen);
+                Pack.longToLittleEndian_High(x1, output, outOff, inLen);
+            }
+        }
+        else
+        {
+            x0 ^= PAD(inLen);
+            if (inLen != 0)
+            {
+                x0 ^= Pack.littleEndianToLong_High(input, inOff, inLen);
+                Pack.longToLittleEndian_High(x0, output, outOff, inLen);
+            }
+        }
+        finishData(State.EncFinal);
+    }
+
+    private void finishData(State nextState)
+    {
+        switch (asconParameters)
+        {
+        case ascon128:
+            x1 ^= K1;
+            x2 ^= K2;
+            break;
+        case ascon128a:
+            x2 ^= K1;
+            x3 ^= K2;
+            break;
+        case ascon80pq:
+            x1 ^= (K0 << 32 | K1 >> 32);
+            x2 ^= (K1 << 32 | K2 >> 32);
+            x3 ^= K2 << 32;
+            break;
+        default:
+            throw new IllegalStateException();
+        }
+        P(12);
+        x3 ^= K1;
+        x4 ^= K2;
+
+        m_state = nextState;
+    }
+
+    public void init(boolean forEncryption, CipherParameters params)
+        throws IllegalArgumentException
+    {
+        KeyParameter key;
+        byte[] npub;
+        if (params instanceof AEADParameters)
+        {
+            AEADParameters aeadParameters = (AEADParameters)params;
+            key = aeadParameters.getKey();
+            npub = aeadParameters.getNonce();
+            initialAssociatedText = aeadParameters.getAssociatedText();
+
+            int macSizeBits = aeadParameters.getMacSize();
+            if (macSizeBits != CRYPTO_ABYTES * 8)
+            {
+                throw new IllegalArgumentException("Invalid value for MAC size: " + macSizeBits);
+            }
+        }
+        else if (params instanceof ParametersWithIV)
+        {
+            ParametersWithIV withIV = (ParametersWithIV)params;
+            key = (KeyParameter)withIV.getParameters();
+            npub = withIV.getIV();
+            initialAssociatedText = null;
+        }
+        else
+        {
+            throw new IllegalArgumentException("invalid parameters passed to Ascon");
+        }
+
+        if (key == null)
+        {
+            throw new IllegalArgumentException("Ascon Init parameters must include a key");
+        }
+        if (npub == null || npub.length != CRYPTO_ABYTES)
+        {
+            throw new IllegalArgumentException(asconParameters + " requires exactly " + CRYPTO_ABYTES + " bytes of IV");
+        }
+
+        byte[] k = key.getKey();
+        if (k.length != CRYPTO_KEYBYTES)
+        {
+            throw new IllegalArgumentException(asconParameters + " key must be " + CRYPTO_KEYBYTES + " bytes long");
+        }
+
+        CryptoServicesRegistrar.checkConstraints(new DefaultServiceProperties(
+            this.getAlgorithmName(), 128, params, Utils.getPurpose(forEncryption)));
+        N0 = Pack.bigEndianToLong(npub, 0);
+        N1 = Pack.bigEndianToLong(npub, 8);
+        if (CRYPTO_KEYBYTES == 16)
+        {
+            K1 = Pack.bigEndianToLong(k, 0);
+            K2 = Pack.bigEndianToLong(k, 8);
+        }
+        else if (CRYPTO_KEYBYTES == 20)
+        {
+            K0 = Pack.bigEndianToInt(k, 0);
+            K1 = Pack.bigEndianToLong(k, 4);
+            K2 = Pack.bigEndianToLong(k, 12);
+        }
+        else
+        {
+            throw new IllegalStateException();
+        }
+
+        m_state = forEncryption ? State.EncInit : State.DecInit;
+
+        reset(true);
+    }
+
+    public String getAlgorithmName()
+    {
+        return algorithmName;
+    }
+
+    public String getAlgorithmVersion()
+    {
+        return "v1.2";
+    }
+
+    public void processAADByte(byte in)
+    {
+        checkAAD();
+        m_buf[m_bufPos] = in;
+        if (++m_bufPos == ASCON_AEAD_RATE)
+        {
+            processBufferAAD(m_buf, 0);
+        }
+    }
+
+    public void processAADBytes(byte[] inBytes, int inOff, int len)
+    {
+        if ((inOff + len) > inBytes.length)
+        {
+            throw new DataLengthException("input buffer too short");
+        }
+        // Don't enter AAD state until we actually get input
+        if (len <= 0)
+        {
+            return;
+        }
+        checkAAD();
+        if (m_bufPos > 0)
+        {
+            int available = ASCON_AEAD_RATE - m_bufPos;
+            if (len < available)
+            {
+                System.arraycopy(inBytes, inOff, m_buf, m_bufPos, len);
+                m_bufPos += len;
+                return;
+            }
+            System.arraycopy(inBytes, inOff, m_buf, m_bufPos, available);
+            inOff += available;
+            len -= available;
+            processBufferAAD(m_buf, 0);
+            //m_bufPos = 0;
+        }
+        while (len >= ASCON_AEAD_RATE)
+        {
+            processBufferAAD(inBytes, inOff);
+            inOff += ASCON_AEAD_RATE;
+            len -= ASCON_AEAD_RATE;
+        }
+        System.arraycopy(inBytes, inOff, m_buf, 0, len);
+        m_bufPos = len;
+    }
+
+    public int processByte(byte in, byte[] out, int outOff)
+        throws DataLengthException
+    {
+        return processBytes(new byte[]{in}, 0, 1, out, outOff);
+    }
+
+    public int processBytes(byte[] inBytes, int inOff, int len, byte[] outBytes, int outOff)
+        throws DataLengthException
+    {
+        if ((inOff + len) > inBytes.length)
+        {
+            throw new DataLengthException("input buffer too short");
+        }
+        boolean forEncryption = checkData();
+        int resultLength = 0;
+
+        if (forEncryption)
+        {
+            if (m_bufPos > 0)
+            {
+                int available = ASCON_AEAD_RATE - m_bufPos;
+                if (len < available)
+                {
+                    System.arraycopy(inBytes, inOff, m_buf, m_bufPos, len);
+                    m_bufPos += len;
+                    return 0;
+                }
+
+                System.arraycopy(inBytes, inOff, m_buf, m_bufPos, available);
+                inOff += available;
+                len -= available;
+
+                processBufferEncrypt(m_buf, 0, outBytes, outOff);
+                resultLength = ASCON_AEAD_RATE;
+                //m_bufPos = 0;
+            }
+
+            while (len >= ASCON_AEAD_RATE)
+            {
+                processBufferEncrypt(inBytes, inOff, outBytes, outOff + resultLength);
+                inOff += ASCON_AEAD_RATE;
+                len -= ASCON_AEAD_RATE;
+                resultLength += ASCON_AEAD_RATE;
+            }
+        }
+        else
+        {
+            int available = m_bufferSizeDecrypt - m_bufPos;
+            if (len < available)
+            {
+                System.arraycopy(inBytes, inOff, m_buf, m_bufPos, len);
+                m_bufPos += len;
+                return 0;
+            }
+
+            // NOTE: Need 'while' here because ASCON_AEAD_RATE < CRYPTO_ABYTES in some parameter sets
+            while (m_bufPos >= ASCON_AEAD_RATE)
+            {
+                processBufferDecrypt(m_buf, 0, outBytes, outOff + resultLength);
+                m_bufPos -= ASCON_AEAD_RATE;
+                System.arraycopy(m_buf, ASCON_AEAD_RATE, m_buf, 0, m_bufPos);
+                resultLength += ASCON_AEAD_RATE;
+
+                available += ASCON_AEAD_RATE;
+                if (len < available)
+                {
+                    System.arraycopy(inBytes, inOff, m_buf, m_bufPos, len);
+                    m_bufPos += len;
+                    return resultLength;
+                }
+            }
+
+            available = ASCON_AEAD_RATE - m_bufPos;
+            System.arraycopy(inBytes, inOff, m_buf, m_bufPos, available);
+            inOff += available;
+            len -= available;
+            processBufferDecrypt(m_buf, 0, outBytes, outOff + resultLength);
+            resultLength += ASCON_AEAD_RATE;
+            //m_bufPos = 0;
+
+            while (len >= m_bufferSizeDecrypt)
+            {
+                processBufferDecrypt(inBytes, inOff, outBytes, outOff + resultLength);
+                inOff += ASCON_AEAD_RATE;
+                len -= ASCON_AEAD_RATE;
+                resultLength += ASCON_AEAD_RATE;
+            }
+        }
+
+        System.arraycopy(inBytes, inOff, m_buf, 0, len);
+        m_bufPos = len;
+
+        return resultLength;
+    }
+
+    public int doFinal(byte[] outBytes, int outOff)
+        throws IllegalStateException, InvalidCipherTextException, DataLengthException
+    {
+        boolean forEncryption = checkData();
+        int resultLength;
+        if (forEncryption)
+        {
+            resultLength = m_bufPos + CRYPTO_ABYTES;
+            if (outOff + resultLength > outBytes.length)
+            {
+                throw new OutputLengthException("output buffer too short");
+            }
+            processFinalEncrypt(m_buf, 0, m_bufPos, outBytes, outOff);
+            mac = new byte[CRYPTO_ABYTES];
+            Pack.longToBigEndian(x3, mac, 0);
+            Pack.longToBigEndian(x4, mac, 8);
+            System.arraycopy(mac, 0, outBytes, outOff + m_bufPos, CRYPTO_ABYTES);
+            reset(false);
+        }
+        else
+        {
+            if (m_bufPos < CRYPTO_ABYTES)
+            {
+                throw new InvalidCipherTextException("data too short");
+            }
+            m_bufPos -= CRYPTO_ABYTES;
+            resultLength = m_bufPos;
+            if (outOff + resultLength > outBytes.length)
+            {
+                throw new OutputLengthException("output buffer too short");
+            }
+            processFinalDecrypt(m_buf, 0, m_bufPos, outBytes, outOff);
+            x3 ^= Pack.bigEndianToLong(m_buf, m_bufPos);
+            x4 ^= Pack.bigEndianToLong(m_buf, m_bufPos + 8);
+            if ((x3 | x4) != 0L)
+            {
+                throw new InvalidCipherTextException("mac check in " + getAlgorithmName() + " failed");
+            }
+            reset(true);
+        }
+        return resultLength;
+    }
+
+    public byte[] getMac()
+    {
+        return mac;
+    }
+
+    public int getUpdateOutputSize(int len)
+    {
+        int total = Math.max(0, len);
+        switch (m_state)
+        {
+        case DecInit:
+        case DecAad:
+            total = Math.max(0, total - CRYPTO_ABYTES);
+            break;
+        case DecData:
+        case DecFinal:
+            total = Math.max(0, total + m_bufPos - CRYPTO_ABYTES);
+            break;
+        case EncData:
+        case EncFinal:
+            total += m_bufPos;
+            break;
+        default:
+            break;
+        }
+        return total - total % ASCON_AEAD_RATE;
+    }
+
+    public int getOutputSize(int len)
+    {
+        int total = Math.max(0, len);
+
+        switch (m_state)
+        {
+        case DecInit:
+        case DecAad:
+            return Math.max(0, total - CRYPTO_ABYTES);
+        case DecData:
+        case DecFinal:
+            return Math.max(0, total + m_bufPos - CRYPTO_ABYTES);
+        case EncData:
+        case EncFinal:
+            return total + m_bufPos + CRYPTO_ABYTES;
+        default:
+            return total + CRYPTO_ABYTES;
+        }
+    }
+
+    public void reset()
+    {
+        reset(true);
+    }
+
+    private void reset(boolean clearMac)
+    {
+        if (clearMac)
+        {
+            mac = null;
+        }
+        Arrays.clear(m_buf);
+        m_bufPos = 0;
+
+        switch (m_state)
+        {
+        case DecInit:
+        case EncInit:
+            break;
+        case DecAad:
+        case DecData:
+        case DecFinal:
+            m_state = State.DecInit;
+            break;
+        case EncAad:
+        case EncData:
+        case EncFinal:
+            m_state = State.EncFinal;
+            return;
+        default:
+            throw new IllegalStateException(getAlgorithmName() + " needs to be initialized");
+        }
+        ascon_aeadinit();
+        if (initialAssociatedText != null)
+        {
+            processAADBytes(initialAssociatedText, 0, initialAssociatedText.length);
+        }
+    }
+
+    public int getKeyBytesSize()
+    {
+        return CRYPTO_KEYBYTES;
+    }
+
+    public int getIVBytesSize()
+    {
+        return CRYPTO_ABYTES;
+    }
+}
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/engines/BlowfishEngine.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/engines/BlowfishEngine.java
index adbb0d3..b4dec74 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/engines/BlowfishEngine.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/engines/BlowfishEngine.java
@@ -3,8 +3,11 @@
 
 import com.android.internal.org.bouncycastle.crypto.BlockCipher;
 import com.android.internal.org.bouncycastle.crypto.CipherParameters;
+import com.android.internal.org.bouncycastle.crypto.CryptoServicePurpose;
+import com.android.internal.org.bouncycastle.crypto.CryptoServicesRegistrar;
 import com.android.internal.org.bouncycastle.crypto.DataLengthException;
 import com.android.internal.org.bouncycastle.crypto.OutputLengthException;
+import com.android.internal.org.bouncycastle.crypto.constraints.DefaultServiceProperties;
 import com.android.internal.org.bouncycastle.crypto.params.KeyParameter;
 
 /**
@@ -317,6 +320,7 @@
         S2 = new int[SBOX_SK];
         S3 = new int[SBOX_SK];
         P = new int[P_SZ];
+        CryptoServicesRegistrar.checkConstraints(new DefaultServiceProperties(getAlgorithmName(), bitsOfSecurity()));
     }
 
     /**
@@ -337,6 +341,7 @@
             this.workingKey = ((KeyParameter)params).getKey();
             setKey(this.workingKey);
 
+            CryptoServicesRegistrar.checkConstraints(new DefaultServiceProperties(getAlgorithmName(), bitsOfSecurity(), params, getPurpose()));
             return;
         }
 
@@ -433,6 +438,11 @@
 
     private void setKey(byte[] key)
     {
+        if (key.length < 4 || key.length > 56)
+        {
+            throw new IllegalArgumentException("key length must be in range 32 to 448 bits");
+        }
+
         /*
          * - comments are from _Applied Crypto_, Schneier, p338
          * please be careful comparing the two, AC numbers the
@@ -577,4 +587,22 @@
         b[offset + 1] = (byte)(in >> 16);
         b[offset]     = (byte)(in >> 24);
     }
+
+    private int bitsOfSecurity()
+    {
+        if (workingKey == null)
+        {
+            return 256;
+        }
+        return (workingKey.length > 32) ? 256 : workingKey.length * 8;
+    }
+
+    private CryptoServicePurpose getPurpose()
+    {
+        if (workingKey == null)
+        {
+            return CryptoServicePurpose.ANY;
+        }
+        return encrypting ? CryptoServicePurpose.ENCRYPTION : CryptoServicePurpose.DECRYPTION;
+    }
 }
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/engines/DESBase.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/engines/DESBase.java
new file mode 100644
index 0000000..9d50163
--- /dev/null
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/engines/DESBase.java
@@ -0,0 +1,408 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.crypto.engines;
+
+import com.android.internal.org.bouncycastle.util.Pack;
+
+/**
+ * a class that provides a basic DES engine.
+ */
+class DESBase
+{
+    protected static final int  BLOCK_SIZE = 8;
+
+    /**
+     * standard constructor.
+     */
+    public DESBase()
+    {
+    }
+
+    /**
+     * what follows is mainly taken from "Applied Cryptography", by
+     * Bruce Schneier, however it also bears great resemblance to Richard
+     * Outerbridge's D3DES...
+     */
+
+//    private static final short[]    Df_Key =
+//        {
+//            0x01,0x23,0x45,0x67,0x89,0xab,0xcd,0xef,
+//            0xfe,0xdc,0xba,0x98,0x76,0x54,0x32,0x10,
+//            0x89,0xab,0xcd,0xef,0x01,0x23,0x45,0x67
+//        };
+
+    private static final short[]    bytebit =
+        {
+            0200, 0100, 040, 020, 010, 04, 02, 01
+        };
+
+    private static final int[]    bigbyte =
+        {
+            0x800000, 0x400000, 0x200000, 0x100000,
+            0x80000,  0x40000,  0x20000,  0x10000,
+            0x8000,      0x4000,   0x2000,   0x1000,
+            0x800,    0x400,    0x200,    0x100,
+            0x80,      0x40,        0x20,     0x10,
+            0x8,      0x4,      0x2,      0x1
+        };
+
+    /*
+     * Use the key schedule specified in the Standard (ANSI X3.92-1981).
+     */
+
+    private static final byte[]    pc1 =
+        {
+            56, 48, 40, 32, 24, 16,  8,   0, 57, 49, 41, 33, 25, 17,
+             9,  1, 58, 50, 42, 34, 26,  18, 10,  2, 59, 51, 43, 35,
+            62, 54, 46, 38, 30, 22, 14,   6, 61, 53, 45, 37, 29, 21,
+            13,  5, 60, 52, 44, 36, 28,  20, 12,  4, 27, 19, 11,  3
+        };
+
+    private static final byte[] totrot =
+        {
+            1, 2, 4, 6, 8, 10, 12, 14,
+            15, 17, 19, 21, 23, 25, 27, 28
+        };
+
+    private static final byte[] pc2 =
+        {
+            13, 16, 10, 23,  0,  4,  2, 27, 14,  5, 20,  9,
+            22, 18, 11,  3, 25,  7, 15,  6, 26, 19, 12,  1,
+            40, 51, 30, 36, 46, 54, 29, 39, 50, 44, 32, 47,
+            43, 48, 38, 55, 33, 52, 45, 41, 49, 35, 28, 31
+        };
+
+    private static final int[] SP1 = {
+        0x01010400, 0x00000000, 0x00010000, 0x01010404,
+        0x01010004, 0x00010404, 0x00000004, 0x00010000,
+        0x00000400, 0x01010400, 0x01010404, 0x00000400,
+        0x01000404, 0x01010004, 0x01000000, 0x00000004,
+        0x00000404, 0x01000400, 0x01000400, 0x00010400,
+        0x00010400, 0x01010000, 0x01010000, 0x01000404,
+        0x00010004, 0x01000004, 0x01000004, 0x00010004,
+        0x00000000, 0x00000404, 0x00010404, 0x01000000,
+        0x00010000, 0x01010404, 0x00000004, 0x01010000,
+        0x01010400, 0x01000000, 0x01000000, 0x00000400,
+        0x01010004, 0x00010000, 0x00010400, 0x01000004,
+        0x00000400, 0x00000004, 0x01000404, 0x00010404,
+        0x01010404, 0x00010004, 0x01010000, 0x01000404,
+        0x01000004, 0x00000404, 0x00010404, 0x01010400,
+        0x00000404, 0x01000400, 0x01000400, 0x00000000,
+        0x00010004, 0x00010400, 0x00000000, 0x01010004
+    };
+
+    private static final int[] SP2 = {
+        0x80108020, 0x80008000, 0x00008000, 0x00108020,
+        0x00100000, 0x00000020, 0x80100020, 0x80008020,
+        0x80000020, 0x80108020, 0x80108000, 0x80000000,
+        0x80008000, 0x00100000, 0x00000020, 0x80100020,
+        0x00108000, 0x00100020, 0x80008020, 0x00000000,
+        0x80000000, 0x00008000, 0x00108020, 0x80100000,
+        0x00100020, 0x80000020, 0x00000000, 0x00108000,
+        0x00008020, 0x80108000, 0x80100000, 0x00008020,
+        0x00000000, 0x00108020, 0x80100020, 0x00100000,
+        0x80008020, 0x80100000, 0x80108000, 0x00008000,
+        0x80100000, 0x80008000, 0x00000020, 0x80108020,
+        0x00108020, 0x00000020, 0x00008000, 0x80000000,
+        0x00008020, 0x80108000, 0x00100000, 0x80000020,
+        0x00100020, 0x80008020, 0x80000020, 0x00100020,
+        0x00108000, 0x00000000, 0x80008000, 0x00008020,
+        0x80000000, 0x80100020, 0x80108020, 0x00108000
+    };
+
+    private static final int[] SP3 = {
+        0x00000208, 0x08020200, 0x00000000, 0x08020008,
+        0x08000200, 0x00000000, 0x00020208, 0x08000200,
+        0x00020008, 0x08000008, 0x08000008, 0x00020000,
+        0x08020208, 0x00020008, 0x08020000, 0x00000208,
+        0x08000000, 0x00000008, 0x08020200, 0x00000200,
+        0x00020200, 0x08020000, 0x08020008, 0x00020208,
+        0x08000208, 0x00020200, 0x00020000, 0x08000208,
+        0x00000008, 0x08020208, 0x00000200, 0x08000000,
+        0x08020200, 0x08000000, 0x00020008, 0x00000208,
+        0x00020000, 0x08020200, 0x08000200, 0x00000000,
+        0x00000200, 0x00020008, 0x08020208, 0x08000200,
+        0x08000008, 0x00000200, 0x00000000, 0x08020008,
+        0x08000208, 0x00020000, 0x08000000, 0x08020208,
+        0x00000008, 0x00020208, 0x00020200, 0x08000008,
+        0x08020000, 0x08000208, 0x00000208, 0x08020000,
+        0x00020208, 0x00000008, 0x08020008, 0x00020200
+    };
+
+    private static final int[] SP4 = {
+        0x00802001, 0x00002081, 0x00002081, 0x00000080,
+        0x00802080, 0x00800081, 0x00800001, 0x00002001,
+        0x00000000, 0x00802000, 0x00802000, 0x00802081,
+        0x00000081, 0x00000000, 0x00800080, 0x00800001,
+        0x00000001, 0x00002000, 0x00800000, 0x00802001,
+        0x00000080, 0x00800000, 0x00002001, 0x00002080,
+        0x00800081, 0x00000001, 0x00002080, 0x00800080,
+        0x00002000, 0x00802080, 0x00802081, 0x00000081,
+        0x00800080, 0x00800001, 0x00802000, 0x00802081,
+        0x00000081, 0x00000000, 0x00000000, 0x00802000,
+        0x00002080, 0x00800080, 0x00800081, 0x00000001,
+        0x00802001, 0x00002081, 0x00002081, 0x00000080,
+        0x00802081, 0x00000081, 0x00000001, 0x00002000,
+        0x00800001, 0x00002001, 0x00802080, 0x00800081,
+        0x00002001, 0x00002080, 0x00800000, 0x00802001,
+        0x00000080, 0x00800000, 0x00002000, 0x00802080
+    };
+
+    private static final int[] SP5 = {
+        0x00000100, 0x02080100, 0x02080000, 0x42000100,
+        0x00080000, 0x00000100, 0x40000000, 0x02080000,
+        0x40080100, 0x00080000, 0x02000100, 0x40080100,
+        0x42000100, 0x42080000, 0x00080100, 0x40000000,
+        0x02000000, 0x40080000, 0x40080000, 0x00000000,
+        0x40000100, 0x42080100, 0x42080100, 0x02000100,
+        0x42080000, 0x40000100, 0x00000000, 0x42000000,
+        0x02080100, 0x02000000, 0x42000000, 0x00080100,
+        0x00080000, 0x42000100, 0x00000100, 0x02000000,
+        0x40000000, 0x02080000, 0x42000100, 0x40080100,
+        0x02000100, 0x40000000, 0x42080000, 0x02080100,
+        0x40080100, 0x00000100, 0x02000000, 0x42080000,
+        0x42080100, 0x00080100, 0x42000000, 0x42080100,
+        0x02080000, 0x00000000, 0x40080000, 0x42000000,
+        0x00080100, 0x02000100, 0x40000100, 0x00080000,
+        0x00000000, 0x40080000, 0x02080100, 0x40000100
+    };
+
+    private static final int[] SP6 = {
+        0x20000010, 0x20400000, 0x00004000, 0x20404010,
+        0x20400000, 0x00000010, 0x20404010, 0x00400000,
+        0x20004000, 0x00404010, 0x00400000, 0x20000010,
+        0x00400010, 0x20004000, 0x20000000, 0x00004010,
+        0x00000000, 0x00400010, 0x20004010, 0x00004000,
+        0x00404000, 0x20004010, 0x00000010, 0x20400010,
+        0x20400010, 0x00000000, 0x00404010, 0x20404000,
+        0x00004010, 0x00404000, 0x20404000, 0x20000000,
+        0x20004000, 0x00000010, 0x20400010, 0x00404000,
+        0x20404010, 0x00400000, 0x00004010, 0x20000010,
+        0x00400000, 0x20004000, 0x20000000, 0x00004010,
+        0x20000010, 0x20404010, 0x00404000, 0x20400000,
+        0x00404010, 0x20404000, 0x00000000, 0x20400010,
+        0x00000010, 0x00004000, 0x20400000, 0x00404010,
+        0x00004000, 0x00400010, 0x20004010, 0x00000000,
+        0x20404000, 0x20000000, 0x00400010, 0x20004010
+    };
+
+    private static final int[] SP7 = {
+        0x00200000, 0x04200002, 0x04000802, 0x00000000,
+        0x00000800, 0x04000802, 0x00200802, 0x04200800,
+        0x04200802, 0x00200000, 0x00000000, 0x04000002,
+        0x00000002, 0x04000000, 0x04200002, 0x00000802,
+        0x04000800, 0x00200802, 0x00200002, 0x04000800,
+        0x04000002, 0x04200000, 0x04200800, 0x00200002,
+        0x04200000, 0x00000800, 0x00000802, 0x04200802,
+        0x00200800, 0x00000002, 0x04000000, 0x00200800,
+        0x04000000, 0x00200800, 0x00200000, 0x04000802,
+        0x04000802, 0x04200002, 0x04200002, 0x00000002,
+        0x00200002, 0x04000000, 0x04000800, 0x00200000,
+        0x04200800, 0x00000802, 0x00200802, 0x04200800,
+        0x00000802, 0x04000002, 0x04200802, 0x04200000,
+        0x00200800, 0x00000000, 0x00000002, 0x04200802,
+        0x00000000, 0x00200802, 0x04200000, 0x00000800,
+        0x04000002, 0x04000800, 0x00000800, 0x00200002
+    };
+
+    private static final int[] SP8 = {
+        0x10001040, 0x00001000, 0x00040000, 0x10041040,
+        0x10000000, 0x10001040, 0x00000040, 0x10000000,
+        0x00040040, 0x10040000, 0x10041040, 0x00041000,
+        0x10041000, 0x00041040, 0x00001000, 0x00000040,
+        0x10040000, 0x10000040, 0x10001000, 0x00001040,
+        0x00041000, 0x00040040, 0x10040040, 0x10041000,
+        0x00001040, 0x00000000, 0x00000000, 0x10040040,
+        0x10000040, 0x10001000, 0x00041040, 0x00040000,
+        0x00041040, 0x00040000, 0x10041000, 0x00001000,
+        0x00000040, 0x10040040, 0x00001000, 0x00041040,
+        0x10001000, 0x00000040, 0x10000040, 0x10040000,
+        0x10040040, 0x10000000, 0x00040000, 0x10001040,
+        0x00000000, 0x10041040, 0x00040040, 0x10000040,
+        0x10040000, 0x10001000, 0x10001040, 0x00000000,
+        0x10041040, 0x00041000, 0x00041000, 0x00001040,
+        0x00001040, 0x00040040, 0x10000000, 0x10041000
+    };
+
+    /**
+     * generate an integer based working key based on our secret key
+     * and what we processing we are planning to do.
+     *
+     * Acknowledgements for this routine go to James Gillogly &amp; Phil Karn.
+     *         (whoever, and wherever they are!).
+     */
+    protected int[] generateWorkingKey(
+        boolean encrypting,
+        byte[]  key)
+    {
+        int[]       newKey = new int[32];
+        boolean[]   pc1m = new boolean[56],
+                    pcr = new boolean[56];
+
+        for (int j = 0; j < 56; j++)
+        {
+            int    l = pc1[j];
+
+            pc1m[j] = ((key[l >>> 3] & bytebit[l & 07]) != 0);
+        }
+
+        for (int i = 0; i < 16; i++)
+        {
+            int    l, m, n;
+
+            if (encrypting)
+            {
+                m = i << 1;
+            }
+            else
+            {
+                m = (15 - i) << 1;
+            }
+
+            n = m + 1;
+            newKey[m] = newKey[n] = 0;
+
+            for (int j = 0; j < 28; j++)
+            {
+                l = j + totrot[i];
+                if (l < 28)
+                {
+                    pcr[j] = pc1m[l];
+                }
+                else
+                {
+                    pcr[j] = pc1m[l - 28];
+                }
+            }
+
+            for (int j = 28; j < 56; j++)
+            {
+                l = j + totrot[i];
+                if (l < 56)
+                {
+                    pcr[j] = pc1m[l];
+                }
+                else
+                {
+                    pcr[j] = pc1m[l - 28];
+                }
+            }
+
+            for (int j = 0; j < 24; j++)
+            {
+                if (pcr[pc2[j]])
+                {
+                    newKey[m] |= bigbyte[j];
+                }
+
+                if (pcr[pc2[j + 24]])
+                {
+                    newKey[n] |= bigbyte[j];
+                }
+            }
+        }
+
+        //
+        // store the processed key
+        //
+        for (int i = 0; i != 32; i += 2)
+        {
+            int    i1, i2;
+
+            i1 = newKey[i];
+            i2 = newKey[i + 1];
+
+            newKey[i] = ((i1 & 0x00fc0000) << 6) | ((i1 & 0x00000fc0) << 10)
+                                   | ((i2 & 0x00fc0000) >>> 10) | ((i2 & 0x00000fc0) >>> 6);
+
+            newKey[i + 1] = ((i1 & 0x0003f000) << 12) | ((i1 & 0x0000003f) << 16)
+                                   | ((i2 & 0x0003f000) >>> 4) | (i2 & 0x0000003f);
+        }
+
+        return newKey;
+    }
+
+    /**
+     * the DES engine.
+     */
+    protected void desFunc(
+        int[]   wKey,
+        byte[]  in,
+        int     inOff,
+        byte[]  out,
+        int     outOff)
+    {
+        int     work, right, left;
+
+        left = Pack.bigEndianToInt(in, inOff);
+        right = Pack.bigEndianToInt(in, inOff + 4);
+
+        work = ((left >>> 4) ^ right) & 0x0f0f0f0f;
+        right ^= work;
+        left ^= (work << 4);
+        work = ((left >>> 16) ^ right) & 0x0000ffff;
+        right ^= work;
+        left ^= (work << 16);
+        work = ((right >>> 2) ^ left) & 0x33333333;
+        left ^= work;
+        right ^= (work << 2);
+        work = ((right >>> 8) ^ left) & 0x00ff00ff;
+        left ^= work;
+        right ^= (work << 8);
+        right = (right << 1) | (right >>> 31);
+        work = (left ^ right) & 0xaaaaaaaa;
+        left ^= work;
+        right ^= work;
+        left = (left << 1) | (left >>> 31);
+
+        for (int round = 0; round < 8; round++)
+        {
+            int     fval;
+
+            work  = (right << 28) | (right >>> 4);
+            work ^= wKey[round * 4 + 0];
+            fval  = SP7[ work      & 0x3f];
+            fval |= SP5[(work >>>  8) & 0x3f];
+            fval |= SP3[(work >>> 16) & 0x3f];
+            fval |= SP1[(work >>> 24) & 0x3f];
+            work  = right ^ wKey[round * 4 + 1];
+            fval |= SP8[ work      & 0x3f];
+            fval |= SP6[(work >>>  8) & 0x3f];
+            fval |= SP4[(work >>> 16) & 0x3f];
+            fval |= SP2[(work >>> 24) & 0x3f];
+            left ^= fval;
+            work  = (left << 28) | (left >>> 4);
+            work ^= wKey[round * 4 + 2];
+            fval  = SP7[ work      & 0x3f];
+            fval |= SP5[(work >>>  8) & 0x3f];
+            fval |= SP3[(work >>> 16) & 0x3f];
+            fval |= SP1[(work >>> 24) & 0x3f];
+            work  = left ^ wKey[round * 4 + 3];
+            fval |= SP8[ work      & 0x3f];
+            fval |= SP6[(work >>>  8) & 0x3f];
+            fval |= SP4[(work >>> 16) & 0x3f];
+            fval |= SP2[(work >>> 24) & 0x3f];
+            right ^= fval;
+        }
+
+        right = (right << 31) | (right >>> 1);
+        work = (left ^ right) & 0xaaaaaaaa;
+        left ^= work;
+        right ^= work;
+        left = (left << 31) | (left >>> 1);
+        work = ((left >>> 8) ^ right) & 0x00ff00ff;
+        right ^= work;
+        left ^= (work << 8);
+        work = ((left >>> 2) ^ right) & 0x33333333;
+        right ^= work;
+        left ^= (work << 2);
+        work = ((right >>> 16) ^ left) & 0x0000ffff;
+        left ^= work;
+        right ^= (work << 16);
+        work = ((right >>> 4) ^ left) & 0x0f0f0f0f;
+        left ^= work;
+        right ^= (work << 4);
+
+        Pack.intToBigEndian(right, out, outOff);
+        Pack.intToBigEndian(left, out, outOff + 4);
+    }
+}
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/engines/DESEngine.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/engines/DESEngine.java
index dbef671..48996c0 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/engines/DESEngine.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/engines/DESEngine.java
@@ -3,8 +3,10 @@
 
 import com.android.internal.org.bouncycastle.crypto.BlockCipher;
 import com.android.internal.org.bouncycastle.crypto.CipherParameters;
+import com.android.internal.org.bouncycastle.crypto.CryptoServicesRegistrar;
 import com.android.internal.org.bouncycastle.crypto.DataLengthException;
 import com.android.internal.org.bouncycastle.crypto.OutputLengthException;
+import com.android.internal.org.bouncycastle.crypto.constraints.DefaultServiceProperties;
 import com.android.internal.org.bouncycastle.crypto.params.KeyParameter;
 import com.android.internal.org.bouncycastle.util.Pack;
 
@@ -13,10 +15,12 @@
  * @hide This class is not part of the Android public SDK API
  */
 public class DESEngine
+    extends DESBase
     implements BlockCipher
 {
     protected static final int  BLOCK_SIZE = 8;
 
+    private boolean             forEncryption;
     private int[]               workingKey = null;
 
     /**
@@ -24,6 +28,7 @@
      */
     public DESEngine()
     {
+        CryptoServicesRegistrar.checkConstraints(new DefaultServiceProperties(getAlgorithmName(), 56));
     }
 
     /**
@@ -40,14 +45,17 @@
     {
         if (params instanceof KeyParameter)
         {
-            if (((KeyParameter)params).getKey().length > 8)
+            if (((KeyParameter)params).getKeyLength() > 8)
             {
                 throw new IllegalArgumentException("DES key too long - should be 8 bytes");
             }
-            
+
+            forEncryption = encrypting;
             workingKey = generateWorkingKey(encrypting,
                                   ((KeyParameter)params).getKey());
 
+            CryptoServicesRegistrar.checkConstraints(new DefaultServiceProperties(getAlgorithmName(), 56, params, Utils.getPurpose(forEncryption)));
+
             return;
         }
 
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/engines/DESedeEngine.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/engines/DESedeEngine.java
index 7fa0b9f..115102a 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/engines/DESedeEngine.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/engines/DESedeEngine.java
@@ -1,9 +1,12 @@
 /* GENERATED SOURCE. DO NOT MODIFY. */
 package com.android.internal.org.bouncycastle.crypto.engines;
 
+import com.android.internal.org.bouncycastle.crypto.BlockCipher;
 import com.android.internal.org.bouncycastle.crypto.CipherParameters;
+import com.android.internal.org.bouncycastle.crypto.CryptoServicesRegistrar;
 import com.android.internal.org.bouncycastle.crypto.DataLengthException;
 import com.android.internal.org.bouncycastle.crypto.OutputLengthException;
+import com.android.internal.org.bouncycastle.crypto.constraints.DefaultServiceProperties;
 import com.android.internal.org.bouncycastle.crypto.params.KeyParameter;
 
 /**
@@ -11,34 +14,36 @@
  * @hide This class is not part of the Android public SDK API
  */
 public class DESedeEngine
-    extends DESEngine
+    extends DESBase
+    implements BlockCipher
 {
-    protected static final int  BLOCK_SIZE = 8;
+    protected static final int BLOCK_SIZE = 8;
 
-    private int[]               workingKey1 = null;
-    private int[]               workingKey2 = null;
-    private int[]               workingKey3 = null;
+    private int[] workingKey1 = null;
+    private int[] workingKey2 = null;
+    private int[] workingKey3 = null;
 
-    private boolean             forEncryption;
+    private boolean forEncryption;
 
     /**
      * standard constructor.
      */
     public DESedeEngine()
     {
+        CryptoServicesRegistrar.checkConstraints(new DefaultServiceProperties(getAlgorithmName(), bitsOfSecurity()));
     }
 
     /**
      * initialise a DESede cipher.
      *
      * @param encrypting whether or not we are for encryption.
-     * @param params the parameters required to set up the cipher.
-     * @exception IllegalArgumentException if the params argument is
-     * inappropriate.
+     * @param params     the parameters required to set up the cipher.
+     * @throws IllegalArgumentException if the params argument is
+     *                                  inappropriate.
      */
     public void init(
-        boolean           encrypting,
-        CipherParameters  params)
+        boolean encrypting,
+        CipherParameters params)
     {
         if (!(params instanceof KeyParameter))
         {
@@ -72,6 +77,8 @@
         {
             workingKey3 = workingKey1;
         }
+
+        CryptoServicesRegistrar.checkConstraints(new DefaultServiceProperties(getAlgorithmName(), bitsOfSecurity(), params, Utils.getPurpose(forEncryption)));
     }
 
     public String getAlgorithmName()
@@ -126,4 +133,14 @@
     public void reset()
     {
     }
+
+    // Service Definitions
+    private int bitsOfSecurity()
+    {
+        if (workingKey1 != null && workingKey1 == workingKey3)
+        {
+            return 80;
+        }
+        return 112;
+    }
 }
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/engines/DESedeWrapEngine.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/engines/DESedeWrapEngine.java
index d3ee6ca..8f1a4d4 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/engines/DESedeWrapEngine.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/engines/DESedeWrapEngine.java
@@ -184,8 +184,8 @@
       System.arraycopy(this.iv, 0, TEMP2, 0, this.iv.length);
       System.arraycopy(TEMP1, 0, TEMP2, this.iv.length, TEMP1.length);
 
-      // Reverse the order of the octets in TEMP2 and call the result TEMP3.
-      byte[] TEMP3 = reverse(TEMP2);
+      // Reverse the order of the octets in TEMP2.
+      Arrays.reverseInPlace(TEMP2);
 
       // Encrypt TEMP3 in CBC mode using the KEK and an initialization vector
       // of 0x 4a dd a2 2c 79 e8 21 05. The resulting cipher text is the desired
@@ -194,12 +194,12 @@
 
       this.engine.init(true, param2);
 
-      for (int currentBytePos = 0; currentBytePos != TEMP3.length; currentBytePos += blockSize) 
+      for (int currentBytePos = 0; currentBytePos != TEMP2.length; currentBytePos += blockSize) 
       {
-         engine.processBlock(TEMP3, currentBytePos, TEMP3, currentBytePos);
+         engine.processBlock(TEMP2, currentBytePos, TEMP2, currentBytePos);
       }
 
-      return TEMP3;
+      return TEMP2;
    }
 
    /**
@@ -252,15 +252,15 @@
 
       this.engine.init(false, param2);
 
-      byte TEMP3[] = new byte[inLen];
+      byte TEMP2[] = new byte[inLen];
 
       for (int currentBytePos = 0; currentBytePos != inLen; currentBytePos += blockSize) 
       {
-         engine.processBlock(in, inOff + currentBytePos, TEMP3, currentBytePos);
+         engine.processBlock(in, inOff + currentBytePos, TEMP2, currentBytePos);
       }
 
-      // Reverse the order of the octets in TEMP3 and call the result TEMP2.
-      byte[] TEMP2 = reverse(TEMP3);
+      // Reverse the order of the octets in TEMP2.
+      Arrays.reverseInPlace(TEMP2);
 
       // Decompose TEMP2 into IV, the first 8 octets, and TEMP1, the remaining octets.
       this.iv = new byte[8];
@@ -343,14 +343,4 @@
     {
         return Arrays.constantTimeAreEqual(calculateCMSKeyChecksum(key), checksum);
     }
-
-    private static byte[] reverse(byte[] bs)
-    {
-        byte[] result = new byte[bs.length];
-        for (int i = 0; i < bs.length; i++) 
-        {
-           result[i] = bs[bs.length - (i + 1)];
-        }
-        return result;
-    }
 }
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/engines/RC2Engine.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/engines/RC2Engine.java
index 997f854..4caeaf0 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/engines/RC2Engine.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/engines/RC2Engine.java
@@ -1,10 +1,8 @@
 /* GENERATED SOURCE. DO NOT MODIFY. */
 package com.android.internal.org.bouncycastle.crypto.engines;
 
-import com.android.internal.org.bouncycastle.crypto.BlockCipher;
-import com.android.internal.org.bouncycastle.crypto.CipherParameters;
-import com.android.internal.org.bouncycastle.crypto.DataLengthException;
-import com.android.internal.org.bouncycastle.crypto.OutputLengthException;
+import com.android.internal.org.bouncycastle.crypto.*;
+import com.android.internal.org.bouncycastle.crypto.constraints.DefaultServiceProperties;
 import com.android.internal.org.bouncycastle.crypto.params.KeyParameter;
 import com.android.internal.org.bouncycastle.crypto.params.RC2Parameters;
 
@@ -124,17 +122,18 @@
         CipherParameters  params)
     {
         this.encrypting = encrypting;
-
+        byte[] key;
         if (params instanceof RC2Parameters)
         {
             RC2Parameters   param = (RC2Parameters)params;
 
             workingKey = generateWorkingKey(param.getKey(),
                                             param.getEffectiveKeyBits());
+            key = param.getKey();
         }
         else if (params instanceof KeyParameter)
         {
-            byte[]    key = ((KeyParameter)params).getKey();
+            key = ((KeyParameter)params).getKey();
 
             workingKey = generateWorkingKey(key, key.length * 8);
         }
@@ -143,6 +142,7 @@
             throw new IllegalArgumentException("invalid parameter passed to RC2 init - " + params.getClass().getName());
         }
 
+        CryptoServicesRegistrar.checkConstraints(new DefaultServiceProperties(getAlgorithmName(), key.length * 8, params, Utils.getPurpose(encrypting)));
     }
 
     public void reset()
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/engines/RC4Engine.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/engines/RC4Engine.java
index 02c138c..d49a920 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/engines/RC4Engine.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/engines/RC4Engine.java
@@ -2,15 +2,18 @@
 package com.android.internal.org.bouncycastle.crypto.engines;
 
 import com.android.internal.org.bouncycastle.crypto.CipherParameters;
+import com.android.internal.org.bouncycastle.crypto.CryptoServicesRegistrar;
 import com.android.internal.org.bouncycastle.crypto.DataLengthException;
 import com.android.internal.org.bouncycastle.crypto.OutputLengthException;
 import com.android.internal.org.bouncycastle.crypto.StreamCipher;
+import com.android.internal.org.bouncycastle.crypto.constraints.DefaultServiceProperties;
 import com.android.internal.org.bouncycastle.crypto.params.KeyParameter;
 
 /**
  * @hide This class is not part of the Android public SDK API
  */
-public class RC4Engine implements StreamCipher
+public class RC4Engine
+    implements StreamCipher
 {
     private final static int STATE_LENGTH = 256;
 
@@ -23,6 +26,12 @@
     private int         x = 0;
     private int         y = 0;
     private byte[]      workingKey = null;
+    private boolean     forEncryption;
+
+    public RC4Engine()
+    {
+        CryptoServicesRegistrar.checkConstraints(new DefaultServiceProperties(getAlgorithmName(), 20));
+    }
 
     /**
      * initialise a RC4 cipher.
@@ -34,8 +43,7 @@
      */
     public void init(
         boolean             forEncryption, 
-        CipherParameters     params
-   )
+        CipherParameters     params)
     {
         if (params instanceof KeyParameter)
         {
@@ -44,9 +52,12 @@
              * symmetrical, so the 'forEncryption' is 
              * irrelevant.
              */
-            workingKey = ((KeyParameter)params).getKey();
+            this.workingKey = ((KeyParameter)params).getKey();
+            this.forEncryption = forEncryption;
             setKey(workingKey);
 
+            CryptoServicesRegistrar.checkConstraints(new DefaultServiceProperties(getAlgorithmName(), 20, params, Utils.getPurpose(forEncryption)));
+
             return;
         }
 
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/engines/RFC3394WrapEngine.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/engines/RFC3394WrapEngine.java
index ed18b61..40b01b6 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/engines/RFC3394WrapEngine.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/engines/RFC3394WrapEngine.java
@@ -22,14 +22,15 @@
 public class RFC3394WrapEngine
     implements Wrapper
 {
-    private BlockCipher     engine;
-    private boolean         wrapCipherMode;
-    private KeyParameter    param;
-    private boolean         forWrapping;
+    private static final byte[] DEFAULT_IV = { (byte)0xa6, (byte)0xa6, (byte)0xa6, (byte)0xa6, (byte)0xa6, (byte)0xa6,
+        (byte)0xa6, (byte)0xa6 };
 
-    private byte[]          iv = {
-                              (byte)0xa6, (byte)0xa6, (byte)0xa6, (byte)0xa6,
-                              (byte)0xa6, (byte)0xa6, (byte)0xa6, (byte)0xa6 };
+    private final BlockCipher engine;
+    private final boolean wrapCipherMode;
+    private final byte[] iv = new byte[8];
+
+    private KeyParameter param = null;
+    private boolean forWrapping = true;
 
     /**
      * Create a RFC 3394 WrapEngine specifying the encrypt for wrapping, decrypt for unwrapping.
@@ -50,7 +51,7 @@
     public RFC3394WrapEngine(BlockCipher engine, boolean useReverseDirection)
     {
         this.engine = engine;
-        this.wrapCipherMode = (useReverseDirection) ? false : true;
+        this.wrapCipherMode = !useReverseDirection;
     }
 
     public void init(
@@ -67,15 +68,24 @@
         if (param instanceof KeyParameter)
         {
             this.param = (KeyParameter)param;
+            System.arraycopy(DEFAULT_IV, 0, iv, 0, 8);
         }
         else if (param instanceof ParametersWithIV)
         {
-            this.iv = ((ParametersWithIV)param).getIV();
-            this.param = (KeyParameter)((ParametersWithIV) param).getParameters();
-            if (this.iv.length != 8)
+            ParametersWithIV withIV = (ParametersWithIV)param;
+
+            byte[] iv = withIV.getIV();
+            if (iv.length != 8)
             {
                throw new IllegalArgumentException("IV not equal to 8");
             }
+
+            this.param = (KeyParameter)withIV.getParameters();
+            System.arraycopy(iv, 0, this.iv, 0, 8);
+        }
+        else
+        {
+            // TODO Throw an exception for bad parameters?
         }
     }
 
@@ -93,6 +103,10 @@
         {
             throw new IllegalStateException("not set for wrapping");
         }
+        if (inLen < 8)
+        {
+            throw new DataLengthException("wrap data must be at least 8 bytes");
+        }
 
         int     n = inLen / 8;
 
@@ -101,34 +115,41 @@
             throw new DataLengthException("wrap data must be a multiple of 8 bytes");
         }
 
-        byte[]  block = new byte[inLen + iv.length];
-        byte[]  buf = new byte[8 + iv.length];
+        engine.init(wrapCipherMode, param);
 
+        byte[] block = new byte[inLen + iv.length];
         System.arraycopy(iv, 0, block, 0, iv.length);
         System.arraycopy(in, inOff, block, iv.length, inLen);
 
-        engine.init(wrapCipherMode, param);
-
-        for (int j = 0; j != 6; j++)
+        if (n == 1)
         {
-            for (int i = 1; i <= n; i++)
+            engine.processBlock(block, 0, block, 0);
+        }
+        else
+        {
+            byte[] buf = new byte[8 + iv.length];
+
+            for (int j = 0; j != 6; j++)
             {
-                System.arraycopy(block, 0, buf, 0, iv.length);
-                System.arraycopy(block, 8 * i, buf, iv.length, 8);
-                engine.processBlock(buf, 0, buf, 0);
-
-                int t = n * j + i;
-                for (int k = 1; t != 0; k++)
+                for (int i = 1; i <= n; i++)
                 {
-                    byte    v = (byte)t;
+                    System.arraycopy(block, 0, buf, 0, iv.length);
+                    System.arraycopy(block, 8 * i, buf, iv.length, 8);
+                    engine.processBlock(buf, 0, buf, 0);
 
-                    buf[iv.length - k] ^= v;
+                    int t = n * j + i;
+                    for (int k = 1; t != 0; k++)
+                    {
+                        byte    v = (byte)t;
 
-                    t >>>= 8;
+                        buf[iv.length - k] ^= v;
+
+                        t >>>= 8;
+                    }
+
+                    System.arraycopy(buf, 0, block, 0, 8);
+                    System.arraycopy(buf, 8, block, 8 * i, 8);
                 }
-
-                System.arraycopy(buf, 0, block, 0, 8);
-                System.arraycopy(buf, 8, block, 8 * i, 8);
             }
         }
 
@@ -145,6 +166,10 @@
         {
             throw new IllegalStateException("not set for unwrapping");
         }
+        if (inLen < 16)
+        {
+            throw new InvalidCipherTextException("unwrap data too short");
+        }
 
         int     n = inLen / 8;
 
@@ -153,43 +178,89 @@
             throw new InvalidCipherTextException("unwrap data must be a multiple of 8 bytes");
         }
 
-        byte[]  block = new byte[inLen - iv.length];
-        byte[]  a = new byte[iv.length];
-        byte[]  buf = new byte[8 + iv.length];
-
-        System.arraycopy(in, inOff, a, 0, iv.length);
-        System.arraycopy(in, inOff + iv.length, block, 0, inLen - iv.length);
-
         engine.init(!wrapCipherMode, param);
 
+        byte[] block = new byte[inLen - iv.length];
+        byte[] a = new byte[iv.length];
+        byte[] buf = new byte[8 + iv.length];
+
         n = n - 1;
 
-        for (int j = 5; j >= 0; j--)
+        if (n == 1)
         {
-            for (int i = n; i >= 1; i--)
+            engine.processBlock(in, inOff, buf, 0);
+            System.arraycopy(buf, 0, a, 0, iv.length);
+            System.arraycopy(buf, iv.length, block, 0, 8);
+        }
+        else
+        {
+            System.arraycopy(in, inOff, a, 0, iv.length);
+            System.arraycopy(in, inOff + iv.length, block, 0, inLen - iv.length);
+
+            for (int j = 5; j >= 0; j--)
             {
-                System.arraycopy(a, 0, buf, 0, iv.length);
-                System.arraycopy(block, 8 * (i - 1), buf, iv.length, 8);
-
-                int t = n * j + i;
-                for (int k = 1; t != 0; k++)
+                for (int i = n; i >= 1; i--)
                 {
-                    byte    v = (byte)t;
-
-                    buf[iv.length - k] ^= v;
-
-                    t >>>= 8;
+                    System.arraycopy(a, 0, buf, 0, iv.length);
+                    System.arraycopy(block, 8 * (i - 1), buf, iv.length, 8);
+    
+                    int t = n * j + i;
+                    for (int k = 1; t != 0; k++)
+                    {
+                        byte    v = (byte)t;
+    
+                        buf[iv.length - k] ^= v;
+    
+                        t >>>= 8;
+                    }
+    
+                    engine.processBlock(buf, 0, buf, 0);
+                    System.arraycopy(buf, 0, a, 0, 8);
+                    System.arraycopy(buf, 8, block, 8 * (i - 1), 8);
                 }
-
-                engine.processBlock(buf, 0, buf, 0);
-                System.arraycopy(buf, 0, a, 0, 8);
-                System.arraycopy(buf, 8, block, 8 * (i - 1), 8);
             }
         }
 
-        if (!Arrays.constantTimeAreEqual(a, iv))
+        if (n != 1)
         {
-            throw new InvalidCipherTextException("checksum failed");
+            if (!Arrays.constantTimeAreEqual(a, iv))
+            {
+                throw new InvalidCipherTextException("checksum failed");
+            }
+        }
+        else
+        {
+            // TODO: old (incorrect) backwards compatible unwrap - will be removed.
+            if (!Arrays.constantTimeAreEqual(a, iv))
+            {
+                System.arraycopy(in, inOff, a, 0, iv.length);
+                System.arraycopy(in, inOff + iv.length, block, 0, inLen - iv.length);
+
+                for (int j = 5; j >= 0; j--)
+                {
+                    System.arraycopy(a, 0, buf, 0, iv.length);
+                    System.arraycopy(block, 0, buf, iv.length, 8);
+
+                    int t = n * j + 1;
+                    for (int k = 1; t != 0; k++)
+                    {
+                        byte v = (byte)t;
+
+                        buf[iv.length - k] ^= v;
+
+                        t >>>= 8;
+                    }
+
+                    engine.processBlock(buf, 0, buf, 0);
+                    System.arraycopy(buf, 0, a, 0, 8);
+                    System.arraycopy(buf, 8, block, 0, 8);
+                }
+                
+                if (!Arrays.constantTimeAreEqual(a, iv))
+                {
+                    throw new InvalidCipherTextException("checksum failed");
+                }
+            }
         }
 
         return block;
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/engines/RSABlindedEngine.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/engines/RSABlindedEngine.java
index 14f6c2f..c595f19 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/engines/RSABlindedEngine.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/engines/RSABlindedEngine.java
@@ -112,39 +112,31 @@
         }
 
         BigInteger input = core.convertInput(in, inOff, inLen);
+        BigInteger result = processInput(input);
+        return core.convertOutput(result);
+    }
 
-        BigInteger result;
+    private BigInteger processInput(BigInteger input)
+    {
         if (key instanceof RSAPrivateCrtKeyParameters)
         {
-            RSAPrivateCrtKeyParameters k = (RSAPrivateCrtKeyParameters)key;
+            RSAPrivateCrtKeyParameters crtKey = (RSAPrivateCrtKeyParameters)key;
 
-            BigInteger e = k.getPublicExponent();
+            BigInteger e = crtKey.getPublicExponent();
             if (e != null)   // can't do blinding without a public exponent
             {
-                BigInteger m = k.getModulus();
+                BigInteger m = crtKey.getModulus();
+
                 BigInteger r = BigIntegers.createRandomInRange(ONE, m.subtract(ONE), random);
+                BigInteger blind = r.modPow(e, m);
+                BigInteger unblind = BigIntegers.modOddInverse(m, r);
 
-                BigInteger blindedInput = r.modPow(e, m).multiply(input).mod(m);
+                BigInteger blindedInput = blind.multiply(input).mod(m);
                 BigInteger blindedResult = core.processBlock(blindedInput);
-
-                BigInteger rInv = BigIntegers.modOddInverse(m, r);
-                result = blindedResult.multiply(rInv).mod(m);
-                // defence against Arjen Lenstra’s CRT attack
-                if (!input.equals(result.modPow(e, m)))
-                {
-                    throw new IllegalStateException("RSA engine faulty decryption/signing detected");
-                }
-            }
-            else
-            {
-                result = core.processBlock(input);
+                return unblind.multiply(blindedResult).mod(m);
             }
         }
-        else
-        {
-            result = core.processBlock(input);
-        }
 
-        return core.convertOutput(result);
+        return core.processBlock(input);
     }
 }
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/engines/RSACoreEngine.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/engines/RSACoreEngine.java
index 7ab640d..5cf3657 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/engines/RSACoreEngine.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/engines/RSACoreEngine.java
@@ -4,7 +4,11 @@
 import java.math.BigInteger;
 
 import com.android.internal.org.bouncycastle.crypto.CipherParameters;
+import com.android.internal.org.bouncycastle.crypto.CryptoServicePurpose;
+import com.android.internal.org.bouncycastle.crypto.CryptoServicesRegistrar;
 import com.android.internal.org.bouncycastle.crypto.DataLengthException;
+import com.android.internal.org.bouncycastle.crypto.constraints.ConstraintUtils;
+import com.android.internal.org.bouncycastle.crypto.constraints.DefaultServiceProperties;
 import com.android.internal.org.bouncycastle.crypto.params.ParametersWithRandom;
 import com.android.internal.org.bouncycastle.crypto.params.RSAKeyParameters;
 import com.android.internal.org.bouncycastle.crypto.params.RSAPrivateCrtKeyParameters;
@@ -16,21 +20,21 @@
 class RSACoreEngine
 {
     private RSAKeyParameters key;
-    private boolean          forEncryption;
+    private boolean forEncryption;
 
     /**
      * initialise the RSA engine.
      *
      * @param forEncryption true if we are encrypting, false otherwise.
-     * @param param the necessary RSA key parameters.
+     * @param param         the necessary RSA key parameters.
      */
     public void init(
-        boolean          forEncryption,
+        boolean forEncryption,
         CipherParameters param)
     {
         if (param instanceof ParametersWithRandom)
         {
-            ParametersWithRandom    rParam = (ParametersWithRandom)param;
+            ParametersWithRandom rParam = (ParametersWithRandom)param;
 
             key = (RSAKeyParameters)rParam.getParameters();
         }
@@ -40,6 +44,8 @@
         }
 
         this.forEncryption = forEncryption;
+
+        CryptoServicesRegistrar.checkConstraints(new DefaultServiceProperties("RSA", ConstraintUtils.bitsOfSecurityFor(key.getModulus()), key, getPurpose(key.isPrivate(), forEncryption)));
     }
 
     /**
@@ -51,7 +57,7 @@
      */
     public int getInputBlockSize()
     {
-        int     bitSize = key.getModulus().bitLength();
+        int bitSize = key.getModulus().bitLength();
 
         if (forEncryption)
         {
@@ -72,7 +78,7 @@
      */
     public int getOutputBlockSize()
     {
-        int     bitSize = key.getModulus().bitLength();
+        int bitSize = key.getModulus().bitLength();
 
         if (forEncryption)
         {
@@ -85,9 +91,9 @@
     }
 
     public BigInteger convertInput(
-        byte[]  in,
-        int     inOff,
-        int     inLen)
+        byte[] in,
+        int inOff,
+        int inLen)
     {
         if (inLen > (getInputBlockSize() + 1))
         {
@@ -98,7 +104,7 @@
             throw new DataLengthException("input too large for RSA cipher.");
         }
 
-        byte[]  block;
+        byte[] block;
 
         if (inOff != 0 || inLen != in.length)
         {
@@ -123,13 +129,13 @@
     public byte[] convertOutput(
         BigInteger result)
     {
-        byte[]      output = result.toByteArray();
+        byte[] output = result.toByteArray();
 
         if (forEncryption)
         {
             if (output[0] == 0 && output.length > getOutputBlockSize())        // have ended up with an extra zero byte, copy down.
             {
-                byte[]  tmp = new byte[output.length - 1];
+                byte[] tmp = new byte[output.length - 1];
 
                 System.arraycopy(output, 1, tmp, 0, tmp.length);
 
@@ -138,7 +144,7 @@
 
             if (output.length < getOutputBlockSize())     // have ended up with less bytes than normal, lengthen
             {
-                byte[]  tmp = new byte[getOutputBlockSize()];
+                byte[] tmp = new byte[getOutputBlockSize()];
 
                 System.arraycopy(output, 0, tmp, tmp.length - output.length, output.length);
 
@@ -149,7 +155,7 @@
         }
         else
         {
-            byte[]  rv;
+            byte[] rv;
             if (output[0] == 0)        // have ended up with an extra zero byte, copy down.
             {
                 rv = new byte[output.length - 1];
@@ -180,35 +186,64 @@
             //
             RSAPrivateCrtKeyParameters crtKey = (RSAPrivateCrtKeyParameters)key;
 
-            BigInteger p = crtKey.getP();
-            BigInteger q = crtKey.getQ();
-            BigInteger dP = crtKey.getDP();
-            BigInteger dQ = crtKey.getDQ();
-            BigInteger qInv = crtKey.getQInv();
+            BigInteger e = crtKey.getPublicExponent();
+            if (e != null)   // can't apply fault-attack countermeasure without public exponent
+            {
+                BigInteger p = crtKey.getP();
+                BigInteger q = crtKey.getQ();
+                BigInteger dP = crtKey.getDP();
+                BigInteger dQ = crtKey.getDQ();
+                BigInteger qInv = crtKey.getQInv();
 
-            BigInteger mP, mQ, h, m;
+                BigInteger mP, mQ, h, m;
 
-            // mP = ((input mod p) ^ dP)) mod p
-            mP = (input.remainder(p)).modPow(dP, p);
+                // mP = ((input mod p) ^ dP)) mod p
+                mP = (input.remainder(p)).modPow(dP, p);
 
-            // mQ = ((input mod q) ^ dQ)) mod q
-            mQ = (input.remainder(q)).modPow(dQ, q);
+                // mQ = ((input mod q) ^ dQ)) mod q
+                mQ = (input.remainder(q)).modPow(dQ, q);
 
-            // h = qInv * (mP - mQ) mod p
-            h = mP.subtract(mQ);
-            h = h.multiply(qInv);
-            h = h.mod(p);               // mod (in Java) returns the positive residual
+                // h = qInv * (mP - mQ) mod p
+                h = mP.subtract(mQ);
+                h = h.multiply(qInv);
+                h = h.mod(p);               // mod (in Java) returns the positive residual
 
-            // m = h * q + mQ
-            m = h.multiply(q);
-            m = m.add(mQ);
+                // m = h * q + mQ
+                m = h.multiply(q).add(mQ);
 
-            return m;
+                // defence against Arjen Lenstra’s CRT attack
+                BigInteger check = m.modPow(e, crtKey.getModulus()); 
+                if (!check.equals(input))
+                {
+                    throw new IllegalStateException("RSA engine faulty decryption/signing detected");
+                }
+
+                return m;
+            }
         }
-        else
+
+        return input.modPow(key.getExponent(), key.getModulus());
+    }
+
+    private CryptoServicePurpose getPurpose(boolean isPrivate, boolean forEncryption)
+    {
+        boolean isSigning = isPrivate && forEncryption;
+        boolean isEncryption = !isPrivate && forEncryption;
+        boolean isVerifying = !isPrivate && !forEncryption;
+
+        if (isSigning)
         {
-            return input.modPow(
-                        key.getExponent(), key.getModulus());
+            return CryptoServicePurpose.SIGNING;
         }
+        if (isEncryption)
+        {
+            return CryptoServicePurpose.ENCRYPTION;
+        }
+        if (isVerifying)
+        {
+            return CryptoServicePurpose.VERIFYING;
+        }
+
+        return CryptoServicePurpose.DECRYPTION;
     }
 }
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/engines/TwofishEngine.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/engines/TwofishEngine.java
index e7ba6de..e0d9669 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/engines/TwofishEngine.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/engines/TwofishEngine.java
@@ -3,9 +3,13 @@
 
 import com.android.internal.org.bouncycastle.crypto.BlockCipher;
 import com.android.internal.org.bouncycastle.crypto.CipherParameters;
+import com.android.internal.org.bouncycastle.crypto.CryptoServicesRegistrar;
 import com.android.internal.org.bouncycastle.crypto.DataLengthException;
 import com.android.internal.org.bouncycastle.crypto.OutputLengthException;
+import com.android.internal.org.bouncycastle.crypto.constraints.DefaultServiceProperties;
 import com.android.internal.org.bouncycastle.crypto.params.KeyParameter;
+import com.android.internal.org.bouncycastle.util.Integers;
+import com.android.internal.org.bouncycastle.util.Pack;
 
 /**
  * A class that provides Twofish encryption operations.
@@ -226,6 +230,8 @@
 
     public TwofishEngine()
     {
+        CryptoServicesRegistrar.checkConstraints(new DefaultServiceProperties(getAlgorithmName(), 256));
+
         // calculate the MDS matrix
         int[] m1 = new int[2];
         int[] mX = new int[2];
@@ -274,7 +280,21 @@
         {
             this.encrypting = encrypting;
             this.workingKey = ((KeyParameter)params).getKey();
-            this.k64Cnt = (this.workingKey.length / 8); // pre-padded ?
+
+            int keyBits = this.workingKey.length * 8;
+            switch (keyBits)
+            {
+            case 128:
+            case 192:
+            case 256:
+                break;
+            default:
+                throw new IllegalArgumentException("Key length not 128/192/256 bits.");
+            }
+
+            CryptoServicesRegistrar.checkConstraints(new DefaultServiceProperties(getAlgorithmName(), keyBits, params, Utils.getPurpose(encrypting)));
+
+            this.k64Cnt = this.workingKey.length / 8;
             setKey(this.workingKey);
 
             return;
@@ -346,28 +366,16 @@
         int[] sBoxKeys = new int[MAX_KEY_BITS/64]; // 4 
         gSubKeys = new int[TOTAL_SUBKEYS];
 
-        if (k64Cnt < 1) 
-        {
-            throw new IllegalArgumentException("Key size less than 64 bits");
-        }
-        
-        if (k64Cnt > 4)
-        {
-            throw new IllegalArgumentException("Key size larger than 256 bits");
-        }
-
         /*
-         * k64Cnt is the number of 8 byte blocks (64 chunks)
-         * that are in the input key.  The input key is a
-         * maximum of 32 bytes (256 bits), so the range
-         * for k64Cnt is 1..4
+         * k64Cnt is the number of 8 byte blocks (64 chunks) that are in the input key.
+         * The input key is 16, 24 or 32 bytes, so the range for k64Cnt is 2..4
          */
         for (int i=0; i<k64Cnt ; i++)
         {
             int p = i* 8;
 
-            k32e[i] = BytesTo32Bits(key, p);
-            k32o[i] = BytesTo32Bits(key, p+4);
+            k32e[i] = Pack.littleEndianToInt(key, p);
+            k32o[i] = Pack.littleEndianToInt(key, p + 4);
 
             sBoxKeys[k64Cnt-1-i] = RS_MDS_Encode(k32e[i], k32o[i]);
         }
@@ -378,7 +386,7 @@
             q = i*SK_STEP;
             A = F32(q,         k32e);
             B = F32(q+SK_BUMP, k32o);
-            B = B << 8 | B >>> 24;
+            B = Integers.rotateLeft(B, 8);
             A += B;
             gSubKeys[i*2] = A;
             A += B;
@@ -450,10 +458,10 @@
         byte[] dst,
         int dstIndex)
     {
-        int x0 = BytesTo32Bits(src, srcIndex) ^ gSubKeys[INPUT_WHITEN];
-        int x1 = BytesTo32Bits(src, srcIndex + 4) ^ gSubKeys[INPUT_WHITEN + 1];
-        int x2 = BytesTo32Bits(src, srcIndex + 8) ^ gSubKeys[INPUT_WHITEN + 2];
-        int x3 = BytesTo32Bits(src, srcIndex + 12) ^ gSubKeys[INPUT_WHITEN + 3];
+        int x0 = Pack.littleEndianToInt(src, srcIndex) ^ gSubKeys[INPUT_WHITEN];
+        int x1 = Pack.littleEndianToInt(src, srcIndex + 4) ^ gSubKeys[INPUT_WHITEN + 1];
+        int x2 = Pack.littleEndianToInt(src, srcIndex + 8) ^ gSubKeys[INPUT_WHITEN + 2];
+        int x3 = Pack.littleEndianToInt(src, srcIndex + 12) ^ gSubKeys[INPUT_WHITEN + 3];
 
         int k = ROUND_SUBKEYS;
         int t0, t1;
@@ -462,20 +470,20 @@
             t0 = Fe32_0(x0);
             t1 = Fe32_3(x1);
             x2 ^= t0 + t1 + gSubKeys[k++];
-            x2 = x2 >>>1 | x2 << 31;
-            x3 = (x3 << 1 | x3 >>> 31) ^ (t0 + 2*t1 + gSubKeys[k++]);
+            x2 = Integers.rotateRight(x2, 1);
+            x3 = Integers.rotateLeft(x3, 1) ^ (t0 + 2*t1 + gSubKeys[k++]);
 
             t0 = Fe32_0(x2);
             t1 = Fe32_3(x3);
             x0 ^= t0 + t1 + gSubKeys[k++];
-            x0 = x0 >>>1 | x0 << 31;
-            x1 = (x1 << 1 | x1 >>> 31) ^ (t0 + 2*t1 + gSubKeys[k++]);
+            x0 = Integers.rotateRight(x0, 1);
+            x1 = Integers.rotateLeft(x1, 1) ^ (t0 + 2*t1 + gSubKeys[k++]);
         }
 
-        Bits32ToBytes(x2 ^ gSubKeys[OUTPUT_WHITEN], dst, dstIndex);
-        Bits32ToBytes(x3 ^ gSubKeys[OUTPUT_WHITEN + 1], dst, dstIndex + 4);
-        Bits32ToBytes(x0 ^ gSubKeys[OUTPUT_WHITEN + 2], dst, dstIndex + 8);
-        Bits32ToBytes(x1 ^ gSubKeys[OUTPUT_WHITEN + 3], dst, dstIndex + 12);
+        Pack.intToLittleEndian(x2 ^ gSubKeys[OUTPUT_WHITEN], dst, dstIndex);
+        Pack.intToLittleEndian(x3 ^ gSubKeys[OUTPUT_WHITEN + 1], dst, dstIndex + 4);
+        Pack.intToLittleEndian(x0 ^ gSubKeys[OUTPUT_WHITEN + 2], dst, dstIndex + 8);
+        Pack.intToLittleEndian(x1 ^ gSubKeys[OUTPUT_WHITEN + 3], dst, dstIndex + 12);
     }
 
     /**
@@ -489,10 +497,10 @@
         byte[] dst,
         int dstIndex)
     {
-        int x2 = BytesTo32Bits(src, srcIndex) ^ gSubKeys[OUTPUT_WHITEN];
-        int x3 = BytesTo32Bits(src, srcIndex+4) ^ gSubKeys[OUTPUT_WHITEN + 1];
-        int x0 = BytesTo32Bits(src, srcIndex+8) ^ gSubKeys[OUTPUT_WHITEN + 2];
-        int x1 = BytesTo32Bits(src, srcIndex+12) ^ gSubKeys[OUTPUT_WHITEN + 3];
+        int x2 = Pack.littleEndianToInt(src, srcIndex) ^ gSubKeys[OUTPUT_WHITEN];
+        int x3 = Pack.littleEndianToInt(src, srcIndex + 4) ^ gSubKeys[OUTPUT_WHITEN + 1];
+        int x0 = Pack.littleEndianToInt(src, srcIndex + 8) ^ gSubKeys[OUTPUT_WHITEN + 2];
+        int x1 = Pack.littleEndianToInt(src, srcIndex + 12) ^ gSubKeys[OUTPUT_WHITEN + 3];
 
         int k = ROUND_SUBKEYS + 2 * ROUNDS -1 ;
         int t0, t1;
@@ -501,20 +509,20 @@
             t0 = Fe32_0(x2);
             t1 = Fe32_3(x3);
             x1 ^= t0 + 2*t1 + gSubKeys[k--];
-            x0 = (x0 << 1 | x0 >>> 31) ^ (t0 + t1 + gSubKeys[k--]);
-            x1 = x1 >>>1 | x1 << 31;
+            x0 = Integers.rotateLeft(x0, 1) ^ (t0 + t1 + gSubKeys[k--]);
+            x1 = Integers.rotateRight(x1, 1);
 
             t0 = Fe32_0(x0);
             t1 = Fe32_3(x1);
             x3 ^= t0 + 2*t1 + gSubKeys[k--];
-            x2 = (x2 << 1 | x2 >>> 31) ^ (t0 + t1 + gSubKeys[k--]);
-            x3 = x3 >>>1 | x3 << 31;
+            x2 = Integers.rotateLeft(x2, 1) ^ (t0 + t1 + gSubKeys[k--]);
+            x3 = Integers.rotateRight(x3, 1);
         }
 
-        Bits32ToBytes(x0 ^ gSubKeys[INPUT_WHITEN], dst, dstIndex);
-        Bits32ToBytes(x1 ^ gSubKeys[INPUT_WHITEN + 1], dst, dstIndex + 4);
-        Bits32ToBytes(x2 ^ gSubKeys[INPUT_WHITEN + 2], dst, dstIndex + 8);
-        Bits32ToBytes(x3 ^ gSubKeys[INPUT_WHITEN + 3], dst, dstIndex + 12);
+        Pack.intToLittleEndian(x0 ^ gSubKeys[INPUT_WHITEN], dst, dstIndex);
+        Pack.intToLittleEndian(x1 ^ gSubKeys[INPUT_WHITEN + 1], dst, dstIndex + 4);
+        Pack.intToLittleEndian(x2 ^ gSubKeys[INPUT_WHITEN + 2], dst, dstIndex + 8);
+        Pack.intToLittleEndian(x3 ^ gSubKeys[INPUT_WHITEN + 3], dst, dstIndex + 12);
     }
 
     /* 
@@ -663,20 +671,4 @@
                gSBox[ 0x200 + 2*((x >>> 8) & 0xff) ] ^
                gSBox[ 0x201 + 2*((x >>> 16) & 0xff) ];
     }
-    
-    private int BytesTo32Bits(byte[] b, int p)
-    {
-        return ((b[p] & 0xff)) | 
-             ((b[p+1] & 0xff) << 8) |
-             ((b[p+2] & 0xff) << 16) |
-             ((b[p+3] & 0xff) << 24);
-    }
-
-    private void Bits32ToBytes(int in,  byte[] b, int offset)
-    {
-        b[offset] = (byte)in;
-        b[offset + 1] = (byte)(in >> 8);
-        b[offset + 2] = (byte)(in >> 16);
-        b[offset + 3] = (byte)(in >> 24);
-    }
 }
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/engines/Utils.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/engines/Utils.java
new file mode 100644
index 0000000..65d6c47
--- /dev/null
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/engines/Utils.java
@@ -0,0 +1,12 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.crypto.engines;
+
+import com.android.internal.org.bouncycastle.crypto.CryptoServicePurpose;
+
+class Utils
+{
+    static CryptoServicePurpose getPurpose(boolean forEncryption)
+    {
+        return forEncryption ? CryptoServicePurpose.ENCRYPTION : CryptoServicePurpose.DECRYPTION;
+    }
+}
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/engines/Zuc128CoreEngine.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/engines/Zuc128CoreEngine.java
new file mode 100644
index 0000000..c591818
--- /dev/null
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/engines/Zuc128CoreEngine.java
@@ -0,0 +1,577 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.crypto.engines;
+
+import com.android.internal.org.bouncycastle.crypto.CipherParameters;
+import com.android.internal.org.bouncycastle.crypto.CryptoServicePurpose;
+import com.android.internal.org.bouncycastle.crypto.CryptoServicesRegistrar;
+import com.android.internal.org.bouncycastle.crypto.DataLengthException;
+import com.android.internal.org.bouncycastle.crypto.OutputLengthException;
+import com.android.internal.org.bouncycastle.crypto.StreamCipher;
+import com.android.internal.org.bouncycastle.crypto.constraints.DefaultServiceProperties;
+import com.android.internal.org.bouncycastle.crypto.params.KeyParameter;
+import com.android.internal.org.bouncycastle.crypto.params.ParametersWithIV;
+import com.android.internal.org.bouncycastle.util.Memoable;
+
+/**
+ * Zuc128Engine implementation.
+ * Based on https://www.gsma.com/aboutus/wp-content/uploads/2014/12/eea3eia3zucv16.pdf
+ * @hide This class is not part of the Android public SDK API
+ */
+public class Zuc128CoreEngine
+    implements StreamCipher, Memoable
+{
+    /* the s-boxes */
+    private static final byte[] S0 = new byte[]{
+        (byte)0x3e, (byte)0x72, (byte)0x5b, (byte)0x47, (byte)0xca, (byte)0xe0, (byte)0x00, (byte)0x33, (byte)0x04, (byte)0xd1, (byte)0x54, (byte)0x98, (byte)0x09, (byte)0xb9, (byte)0x6d, (byte)0xcb,
+        (byte)0x7b, (byte)0x1b, (byte)0xf9, (byte)0x32, (byte)0xaf, (byte)0x9d, (byte)0x6a, (byte)0xa5, (byte)0xb8, (byte)0x2d, (byte)0xfc, (byte)0x1d, (byte)0x08, (byte)0x53, (byte)0x03, (byte)0x90,
+        (byte)0x4d, (byte)0x4e, (byte)0x84, (byte)0x99, (byte)0xe4, (byte)0xce, (byte)0xd9, (byte)0x91, (byte)0xdd, (byte)0xb6, (byte)0x85, (byte)0x48, (byte)0x8b, (byte)0x29, (byte)0x6e, (byte)0xac,
+        (byte)0xcd, (byte)0xc1, (byte)0xf8, (byte)0x1e, (byte)0x73, (byte)0x43, (byte)0x69, (byte)0xc6, (byte)0xb5, (byte)0xbd, (byte)0xfd, (byte)0x39, (byte)0x63, (byte)0x20, (byte)0xd4, (byte)0x38,
+        (byte)0x76, (byte)0x7d, (byte)0xb2, (byte)0xa7, (byte)0xcf, (byte)0xed, (byte)0x57, (byte)0xc5, (byte)0xf3, (byte)0x2c, (byte)0xbb, (byte)0x14, (byte)0x21, (byte)0x06, (byte)0x55, (byte)0x9b,
+        (byte)0xe3, (byte)0xef, (byte)0x5e, (byte)0x31, (byte)0x4f, (byte)0x7f, (byte)0x5a, (byte)0xa4, (byte)0x0d, (byte)0x82, (byte)0x51, (byte)0x49, (byte)0x5f, (byte)0xba, (byte)0x58, (byte)0x1c,
+        (byte)0x4a, (byte)0x16, (byte)0xd5, (byte)0x17, (byte)0xa8, (byte)0x92, (byte)0x24, (byte)0x1f, (byte)0x8c, (byte)0xff, (byte)0xd8, (byte)0xae, (byte)0x2e, (byte)0x01, (byte)0xd3, (byte)0xad,
+        (byte)0x3b, (byte)0x4b, (byte)0xda, (byte)0x46, (byte)0xeb, (byte)0xc9, (byte)0xde, (byte)0x9a, (byte)0x8f, (byte)0x87, (byte)0xd7, (byte)0x3a, (byte)0x80, (byte)0x6f, (byte)0x2f, (byte)0xc8,
+        (byte)0xb1, (byte)0xb4, (byte)0x37, (byte)0xf7, (byte)0x0a, (byte)0x22, (byte)0x13, (byte)0x28, (byte)0x7c, (byte)0xcc, (byte)0x3c, (byte)0x89, (byte)0xc7, (byte)0xc3, (byte)0x96, (byte)0x56,
+        (byte)0x07, (byte)0xbf, (byte)0x7e, (byte)0xf0, (byte)0x0b, (byte)0x2b, (byte)0x97, (byte)0x52, (byte)0x35, (byte)0x41, (byte)0x79, (byte)0x61, (byte)0xa6, (byte)0x4c, (byte)0x10, (byte)0xfe,
+        (byte)0xbc, (byte)0x26, (byte)0x95, (byte)0x88, (byte)0x8a, (byte)0xb0, (byte)0xa3, (byte)0xfb, (byte)0xc0, (byte)0x18, (byte)0x94, (byte)0xf2, (byte)0xe1, (byte)0xe5, (byte)0xe9, (byte)0x5d,
+        (byte)0xd0, (byte)0xdc, (byte)0x11, (byte)0x66, (byte)0x64, (byte)0x5c, (byte)0xec, (byte)0x59, (byte)0x42, (byte)0x75, (byte)0x12, (byte)0xf5, (byte)0x74, (byte)0x9c, (byte)0xaa, (byte)0x23,
+        (byte)0x0e, (byte)0x86, (byte)0xab, (byte)0xbe, (byte)0x2a, (byte)0x02, (byte)0xe7, (byte)0x67, (byte)0xe6, (byte)0x44, (byte)0xa2, (byte)0x6c, (byte)0xc2, (byte)0x93, (byte)0x9f, (byte)0xf1,
+        (byte)0xf6, (byte)0xfa, (byte)0x36, (byte)0xd2, (byte)0x50, (byte)0x68, (byte)0x9e, (byte)0x62, (byte)0x71, (byte)0x15, (byte)0x3d, (byte)0xd6, (byte)0x40, (byte)0xc4, (byte)0xe2, (byte)0x0f,
+        (byte)0x8e, (byte)0x83, (byte)0x77, (byte)0x6b, (byte)0x25, (byte)0x05, (byte)0x3f, (byte)0x0c, (byte)0x30, (byte)0xea, (byte)0x70, (byte)0xb7, (byte)0xa1, (byte)0xe8, (byte)0xa9, (byte)0x65,
+        (byte)0x8d, (byte)0x27, (byte)0x1a, (byte)0xdb, (byte)0x81, (byte)0xb3, (byte)0xa0, (byte)0xf4, (byte)0x45, (byte)0x7a, (byte)0x19, (byte)0xdf, (byte)0xee, (byte)0x78, (byte)0x34, (byte)0x60
+    };
+
+    private static final byte[] S1 = new byte[]{
+        (byte)0x55, (byte)0xc2, (byte)0x63, (byte)0x71, (byte)0x3b, (byte)0xc8, (byte)0x47, (byte)0x86, (byte)0x9f, (byte)0x3c, (byte)0xda, (byte)0x5b, (byte)0x29, (byte)0xaa, (byte)0xfd, (byte)0x77,
+        (byte)0x8c, (byte)0xc5, (byte)0x94, (byte)0x0c, (byte)0xa6, (byte)0x1a, (byte)0x13, (byte)0x00, (byte)0xe3, (byte)0xa8, (byte)0x16, (byte)0x72, (byte)0x40, (byte)0xf9, (byte)0xf8, (byte)0x42,
+        (byte)0x44, (byte)0x26, (byte)0x68, (byte)0x96, (byte)0x81, (byte)0xd9, (byte)0x45, (byte)0x3e, (byte)0x10, (byte)0x76, (byte)0xc6, (byte)0xa7, (byte)0x8b, (byte)0x39, (byte)0x43, (byte)0xe1,
+        (byte)0x3a, (byte)0xb5, (byte)0x56, (byte)0x2a, (byte)0xc0, (byte)0x6d, (byte)0xb3, (byte)0x05, (byte)0x22, (byte)0x66, (byte)0xbf, (byte)0xdc, (byte)0x0b, (byte)0xfa, (byte)0x62, (byte)0x48,
+        (byte)0xdd, (byte)0x20, (byte)0x11, (byte)0x06, (byte)0x36, (byte)0xc9, (byte)0xc1, (byte)0xcf, (byte)0xf6, (byte)0x27, (byte)0x52, (byte)0xbb, (byte)0x69, (byte)0xf5, (byte)0xd4, (byte)0x87,
+        (byte)0x7f, (byte)0x84, (byte)0x4c, (byte)0xd2, (byte)0x9c, (byte)0x57, (byte)0xa4, (byte)0xbc, (byte)0x4f, (byte)0x9a, (byte)0xdf, (byte)0xfe, (byte)0xd6, (byte)0x8d, (byte)0x7a, (byte)0xeb,
+        (byte)0x2b, (byte)0x53, (byte)0xd8, (byte)0x5c, (byte)0xa1, (byte)0x14, (byte)0x17, (byte)0xfb, (byte)0x23, (byte)0xd5, (byte)0x7d, (byte)0x30, (byte)0x67, (byte)0x73, (byte)0x08, (byte)0x09,
+        (byte)0xee, (byte)0xb7, (byte)0x70, (byte)0x3f, (byte)0x61, (byte)0xb2, (byte)0x19, (byte)0x8e, (byte)0x4e, (byte)0xe5, (byte)0x4b, (byte)0x93, (byte)0x8f, (byte)0x5d, (byte)0xdb, (byte)0xa9,
+        (byte)0xad, (byte)0xf1, (byte)0xae, (byte)0x2e, (byte)0xcb, (byte)0x0d, (byte)0xfc, (byte)0xf4, (byte)0x2d, (byte)0x46, (byte)0x6e, (byte)0x1d, (byte)0x97, (byte)0xe8, (byte)0xd1, (byte)0xe9,
+        (byte)0x4d, (byte)0x37, (byte)0xa5, (byte)0x75, (byte)0x5e, (byte)0x83, (byte)0x9e, (byte)0xab, (byte)0x82, (byte)0x9d, (byte)0xb9, (byte)0x1c, (byte)0xe0, (byte)0xcd, (byte)0x49, (byte)0x89,
+        (byte)0x01, (byte)0xb6, (byte)0xbd, (byte)0x58, (byte)0x24, (byte)0xa2, (byte)0x5f, (byte)0x38, (byte)0x78, (byte)0x99, (byte)0x15, (byte)0x90, (byte)0x50, (byte)0xb8, (byte)0x95, (byte)0xe4,
+        (byte)0xd0, (byte)0x91, (byte)0xc7, (byte)0xce, (byte)0xed, (byte)0x0f, (byte)0xb4, (byte)0x6f, (byte)0xa0, (byte)0xcc, (byte)0xf0, (byte)0x02, (byte)0x4a, (byte)0x79, (byte)0xc3, (byte)0xde,
+        (byte)0xa3, (byte)0xef, (byte)0xea, (byte)0x51, (byte)0xe6, (byte)0x6b, (byte)0x18, (byte)0xec, (byte)0x1b, (byte)0x2c, (byte)0x80, (byte)0xf7, (byte)0x74, (byte)0xe7, (byte)0xff, (byte)0x21,
+        (byte)0x5a, (byte)0x6a, (byte)0x54, (byte)0x1e, (byte)0x41, (byte)0x31, (byte)0x92, (byte)0x35, (byte)0xc4, (byte)0x33, (byte)0x07, (byte)0x0a, (byte)0xba, (byte)0x7e, (byte)0x0e, (byte)0x34,
+        (byte)0x88, (byte)0xb1, (byte)0x98, (byte)0x7c, (byte)0xf3, (byte)0x3d, (byte)0x60, (byte)0x6c, (byte)0x7b, (byte)0xca, (byte)0xd3, (byte)0x1f, (byte)0x32, (byte)0x65, (byte)0x04, (byte)0x28,
+        (byte)0x64, (byte)0xbe, (byte)0x85, (byte)0x9b, (byte)0x2f, (byte)0x59, (byte)0x8a, (byte)0xd7, (byte)0xb0, (byte)0x25, (byte)0xac, (byte)0xaf, (byte)0x12, (byte)0x03, (byte)0xe2, (byte)0xf2
+    };
+
+    /* the constants D */
+    private static final short[] EK_d = new short[]{
+        0x44D7, 0x26BC, 0x626B, 0x135E, 0x5789, 0x35E2, 0x7135, 0x09AF,
+        0x4D78, 0x2F13, 0x6BC4, 0x1AF1, 0x5E26, 0x3C4D, 0x789A, 0x47AC
+    };
+
+    /**
+     * State.
+     */
+    private final int[] LFSR = new int[16];
+    private final int[] F = new int[2];
+    private final int[] BRC = new int[4];
+
+    /**
+     * index of next byte in keyStream.
+     */
+    private int theIndex;
+
+    /**
+     * Advanced stream.
+     */
+    private final byte[] keyStream = new byte[4];   // Integer.BYTES
+
+    /**
+     * The iterations.
+     */
+    private int theIterations;
+
+    /**
+     * Reset state.
+     */
+    private Zuc128CoreEngine theResetState;
+
+    /**
+     * Constructor.
+     */
+    protected Zuc128CoreEngine()
+    {
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param pSource the source engine
+     */
+    protected Zuc128CoreEngine(final Zuc128CoreEngine pSource)
+    {
+        reset(pSource);
+    }
+
+    /**
+     * initialise a Snow3G cipher.
+     *
+     * @param forEncryption whether or not we are for encryption.
+     * @param params        the parameters required to set up the cipher.
+     * @throws IllegalArgumentException if the params argument is inappropriate.
+     */
+    public void init(final boolean forEncryption,
+                     final CipherParameters params)
+    {
+        /*
+         * encryption and decryption is completely symmetrical.
+         */
+
+        /* Determine parameters */
+        CipherParameters myParams = params;
+        byte[] newKey = null;
+        byte[] newIV = null;
+        if ((myParams instanceof ParametersWithIV))
+        {
+            final ParametersWithIV ivParams = (ParametersWithIV)myParams;
+            newIV = ivParams.getIV();
+            myParams = ivParams.getParameters();
+        }
+        if (myParams instanceof KeyParameter)
+        {
+            final KeyParameter keyParam = (KeyParameter)myParams;
+            newKey = keyParam.getKey();
+        }
+
+        /* Initialise engine and mark as initialised */
+        theIndex = 0;
+        theIterations = 0;
+        setKeyAndIV(newKey, newIV);
+
+        CryptoServicesRegistrar.checkConstraints(new DefaultServiceProperties(getAlgorithmName(), newKey.length * 8,
+            params, forEncryption ? CryptoServicePurpose.ENCRYPTION : CryptoServicePurpose.DECRYPTION));
+
+        /* Save reset state */
+        theResetState = (Zuc128CoreEngine)copy();
+    }
+
+    /**
+     * Obtain Max iterations.
+     *
+     * @return the maximum iterations
+     */
+    protected int getMaxIterations()
+    {
+        return 2047;
+    }
+
+    /**
+     * Obtain Algorithm Name.
+     *
+     * @return the name
+     */
+    public String getAlgorithmName()
+    {
+        return "Zuc-128";
+    }
+
+    /**
+     * Process bytes.
+     *
+     * @param in     the input buffer
+     * @param inOff  the starting offset in the input buffer
+     * @param len    the length of data in the input buffer
+     * @param out    the output buffer
+     * @param outOff the starting offset in the output buffer
+     * @return the number of bytes returned in the output buffer
+     */
+    public int processBytes(final byte[] in,
+                            final int inOff,
+                            final int len,
+                            final byte[] out,
+                            final int outOff)
+    {
+        /* Check for errors */
+        if (theResetState == null)
+        {
+            throw new IllegalStateException(getAlgorithmName() + " not initialised");
+        }
+        if ((inOff + len) > in.length)
+        {
+            throw new DataLengthException("input buffer too short");
+        }
+        if ((outOff + len) > out.length)
+        {
+            throw new OutputLengthException("output buffer too short");
+        }
+
+        /* Loop through the input bytes */
+        for (int i = 0; i < len; i++)
+        {
+            out[i + outOff] = returnByte(in[i + inOff]);
+        }
+        return len;
+    }
+
+    /**
+     * Reset the engine.
+     */
+    public void reset()
+    {
+        if (theResetState != null)
+        {
+            reset(theResetState);
+        }
+    }
+
+    /**
+     * Process single byte.
+     *
+     * @param in the input byte
+     * @return the output byte
+     */
+    public byte returnByte(final byte in)
+    {
+        /* Make the keyStream if required */
+        if (theIndex == 0)
+        {
+            makeKeyStream();
+        }
+
+        /* Map the next byte and adjust index */
+        final byte out = (byte)(keyStream[theIndex] ^ in);
+        theIndex = (theIndex + 1) % 4; // Integer.BYTES
+
+        /* Return the mapped character */
+        return out;
+    }
+
+    /**
+     * Encode a 32-bit value into a buffer (little-endian).
+     *
+     * @param val the value to encode
+     * @param buf the output buffer
+     * @param off the output offset
+     */
+    public static void encode32be(int val, byte[] buf, int off)
+    {
+        buf[off] = (byte)(val >> 24);
+        buf[off + 1] = (byte)(val >> 16);
+        buf[off + 2] = (byte)(val >> 8);
+        buf[off + 3] = (byte)val;
+    }
+
+    /* ����������������������- */
+
+    /**
+     * Modular add c = a + b mod (2^31 � 1).
+     *
+     * @param a value A
+     * @param b value B
+     * @return the result
+     */
+    private int AddM(final int a, final int b)
+    {
+        final int c = a + b;
+        return (c & 0x7FFFFFFF) + (c >>> 31);
+    }
+
+    /**
+     * Multiply by power of two.
+     *
+     * @param x input value
+     * @param k the power of two
+     * @return the result
+     */
+    private static int MulByPow2(final int x, final int k)
+    {
+        return ((((x) << k) | ((x) >>> (31 - k))) & 0x7FFFFFFF);
+    }
+
+    /**
+     * LFSR with initialisation mode.
+     *
+     * @param u
+     */
+    private void LFSRWithInitialisationMode(final int u)
+    {
+        int f = LFSR[0];
+        int v = MulByPow2(LFSR[0], 8);
+        f = AddM(f, v);
+        v = MulByPow2(LFSR[4], 20);
+        f = AddM(f, v);
+        v = MulByPow2(LFSR[10], 21);
+        f = AddM(f, v);
+        v = MulByPow2(LFSR[13], 17);
+        f = AddM(f, v);
+        v = MulByPow2(LFSR[15], 15);
+        f = AddM(f, v);
+        f = AddM(f, u);
+
+        /* update the state */
+        LFSR[0] = LFSR[1];
+        LFSR[1] = LFSR[2];
+        LFSR[2] = LFSR[3];
+        LFSR[3] = LFSR[4];
+        LFSR[4] = LFSR[5];
+        LFSR[5] = LFSR[6];
+        LFSR[6] = LFSR[7];
+        LFSR[7] = LFSR[8];
+        LFSR[8] = LFSR[9];
+        LFSR[9] = LFSR[10];
+        LFSR[10] = LFSR[11];
+        LFSR[11] = LFSR[12];
+        LFSR[12] = LFSR[13];
+        LFSR[13] = LFSR[14];
+        LFSR[14] = LFSR[15];
+        LFSR[15] = f;
+    }
+
+    /**
+     * LFSR with work mode.
+     */
+    private void LFSRWithWorkMode()
+    {
+        int f, v;
+        f = LFSR[0];
+        v = MulByPow2(LFSR[0], 8);
+        f = AddM(f, v);
+        v = MulByPow2(LFSR[4], 20);
+        f = AddM(f, v);
+        v = MulByPow2(LFSR[10], 21);
+        f = AddM(f, v);
+        v = MulByPow2(LFSR[13], 17);
+        f = AddM(f, v);
+        v = MulByPow2(LFSR[15], 15);
+        f = AddM(f, v);
+
+        /* update the state */
+        LFSR[0] = LFSR[1];
+        LFSR[1] = LFSR[2];
+        LFSR[2] = LFSR[3];
+        LFSR[3] = LFSR[4];
+        LFSR[4] = LFSR[5];
+        LFSR[5] = LFSR[6];
+        LFSR[6] = LFSR[7];
+        LFSR[7] = LFSR[8];
+        LFSR[8] = LFSR[9];
+        LFSR[9] = LFSR[10];
+        LFSR[10] = LFSR[11];
+        LFSR[11] = LFSR[12];
+        LFSR[12] = LFSR[13];
+        LFSR[13] = LFSR[14];
+        LFSR[14] = LFSR[15];
+        LFSR[15] = f;
+    }
+
+    /**
+     * BitReorganization.
+     */
+    private void BitReorganization()
+    {
+        BRC[0] = ((LFSR[15] & 0x7FFF8000) << 1) | (LFSR[14] & 0xFFFF);
+        BRC[1] = ((LFSR[11] & 0xFFFF) << 16) | (LFSR[9] >>> 15);
+        BRC[2] = ((LFSR[7] & 0xFFFF) << 16) | (LFSR[5] >>> 15);
+        BRC[3] = ((LFSR[2] & 0xFFFF) << 16) | (LFSR[0] >>> 15);
+    }
+
+    /**
+     * Rotate integer.
+     *
+     * @param a the integer
+     * @param k the shift
+     * @return the result
+     */
+    static int ROT(int a, int k)
+    {
+        return (((a) << k) | ((a) >>> (32 - k)));
+    }
+
+    /**
+     * L1.
+     *
+     * @param X the input integer.
+     * @return the result
+     */
+    private static int L1(final int X)
+    {
+        return (X ^ ROT(X, 2) ^ ROT(X, 10) ^ ROT(X, 18) ^ ROT(X, 24));
+    }
+
+    /**
+     * L2.
+     *
+     * @param X the input integer.
+     * @return the result
+     */
+    private static int L2(final int X)
+    {
+        return (X ^ ROT(X, 8) ^ ROT(X, 14) ^ ROT(X, 22) ^ ROT(X, 30));
+    }
+
+    /**
+     * Build a 32-bit integer from constituent parts.
+     *
+     * @param a part A
+     * @param b part B
+     * @param c part C
+     * @param d part D
+     * @return the built integer
+     */
+    private static int MAKEU32(final byte a,
+                               final byte b,
+                               final byte c,
+                               final byte d)
+    {
+        return (((a & 0xFF) << 24) | ((b & 0xFF) << 16) | ((c & 0xFF) << 8) | ((d & 0xFF)));
+    }
+
+    /**
+     * F.
+     */
+    int F()
+    {
+        int W, W1, W2, u, v;
+        W = (BRC[0] ^ F[0]) + F[1];
+        W1 = F[0] + BRC[1];
+        W2 = F[1] ^ BRC[2];
+        u = L1((W1 << 16) | (W2 >>> 16));
+        v = L2((W2 << 16) | (W1 >>> 16));
+        F[0] = MAKEU32(S0[u >>> 24], S1[(u >>> 16) & 0xFF],
+            S0[(u >>> 8) & 0xFF], S1[u & 0xFF]);
+        F[1] = MAKEU32(S0[v >>> 24], S1[(v >>> 16) & 0xFF],
+            S0[(v >>> 8) & 0xFF], S1[v & 0xFF]);
+        return W;
+    }
+
+    /**
+     * Build a 31-bit integer from constituent parts.
+     *
+     * @param a part A
+     * @param b part B
+     * @param c part C
+     * @return the built integer
+     */
+    private static int MAKEU31(final byte a,
+                               final short b,
+                               final byte c)
+    {
+        return (((a & 0xFF) << 23) | ((b & 0xFFFF) << 8) | (c & 0xFF));
+    }
+
+    /**
+     * Process key and IV into LFSR.
+     *
+     * @param pLFSR the LFSR
+     * @param k     the key
+     * @param iv    the iv
+     */
+    protected void setKeyAndIV(final int[] pLFSR,
+                               final byte[] k,
+                               final byte[] iv)
+    {
+        /* Check lengths */
+        if (k == null || k.length != 16)
+        {
+            throw new IllegalArgumentException("A key of 16 bytes is needed");
+        }
+        if (iv == null || iv.length != 16)
+        {
+            throw new IllegalArgumentException("An IV of 16 bytes is needed");
+        }
+
+        /* expand key */
+        LFSR[0] = MAKEU31(k[0], EK_d[0], iv[0]);
+        LFSR[1] = MAKEU31(k[1], EK_d[1], iv[1]);
+        LFSR[2] = MAKEU31(k[2], EK_d[2], iv[2]);
+        LFSR[3] = MAKEU31(k[3], EK_d[3], iv[3]);
+        LFSR[4] = MAKEU31(k[4], EK_d[4], iv[4]);
+        LFSR[5] = MAKEU31(k[5], EK_d[5], iv[5]);
+        LFSR[6] = MAKEU31(k[6], EK_d[6], iv[6]);
+        LFSR[7] = MAKEU31(k[7], EK_d[7], iv[7]);
+        LFSR[8] = MAKEU31(k[8], EK_d[8], iv[8]);
+        LFSR[9] = MAKEU31(k[9], EK_d[9], iv[9]);
+        LFSR[10] = MAKEU31(k[10], EK_d[10], iv[10]);
+        LFSR[11] = MAKEU31(k[11], EK_d[11], iv[11]);
+        LFSR[12] = MAKEU31(k[12], EK_d[12], iv[12]);
+        LFSR[13] = MAKEU31(k[13], EK_d[13], iv[13]);
+        LFSR[14] = MAKEU31(k[14], EK_d[14], iv[14]);
+        LFSR[15] = MAKEU31(k[15], EK_d[15], iv[15]);
+    }
+
+    /**
+     * Process key and IV.
+     *
+     * @param k  the key
+     * @param iv the IV
+     */
+    private void setKeyAndIV(final byte[] k,
+                             final byte[] iv)
+    {
+        /* Initialise LFSR */
+        setKeyAndIV(LFSR, k, iv);
+
+        /* set F_R1 and F_R2 to zero */
+        F[0] = 0;
+        F[1] = 0;
+        int nCount = 32;
+        while (nCount > 0)
+        {
+            BitReorganization();
+            final int w = F();
+            LFSRWithInitialisationMode(w >>> 1);
+            nCount--;
+        }
+        BitReorganization();
+        F(); /* discard the output of F */
+        LFSRWithWorkMode();
+    }
+
+    /**
+     * Create the next byte keyStream.
+     */
+    private void makeKeyStream()
+    {
+        encode32be(makeKeyStreamWord(), keyStream, 0);
+    }
+
+    /**
+     * Create the next keyStream word.
+     *
+     * @return the next word
+     */
+    protected int makeKeyStreamWord()
+    {
+        if (theIterations++ >= getMaxIterations())
+        {
+            throw new IllegalStateException("Too much data processed by singleKey/IV");
+        }
+        BitReorganization();
+        final int result = F() ^ BRC[3];
+        LFSRWithWorkMode();
+        return result;
+    }
+
+    /**
+     * Create a copy of the engine.
+     *
+     * @return the copy
+     */
+    public Memoable copy()
+    {
+        return new Zuc128CoreEngine(this);
+    }
+
+    /**
+     * Reset from saved engine state.
+     *
+     * @param pState the state to restore
+     */
+    public void reset(final Memoable pState)
+    {
+        final Zuc128CoreEngine e = (Zuc128CoreEngine)pState;
+        System.arraycopy(e.LFSR, 0, LFSR, 0, LFSR.length);
+        System.arraycopy(e.F, 0, F, 0, F.length);
+        System.arraycopy(e.BRC, 0, BRC, 0, BRC.length);
+        System.arraycopy(e.keyStream, 0, keyStream, 0, keyStream.length);
+        theIndex = e.theIndex;
+        theIterations = e.theIterations;
+        theResetState = e;
+    }
+}
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/engines/Zuc256CoreEngine.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/engines/Zuc256CoreEngine.java
new file mode 100644
index 0000000..a9fdeb1
--- /dev/null
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/engines/Zuc256CoreEngine.java
@@ -0,0 +1,183 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.crypto.engines;
+
+import com.android.internal.org.bouncycastle.util.Memoable;
+
+/**
+ * Zuc256 implementation.
+ * Based on https://www.is.cas.cn/ztzl2016/zouchongzhi/201801/W020180126529970733243.pdf
+ * @hide This class is not part of the Android public SDK API
+ */
+public class Zuc256CoreEngine
+    extends Zuc128CoreEngine
+{
+    /* the constants D */
+    private static final byte[] EK_d = new byte[]{
+        0x22, 0x2f, 0x24, 0x2a, 0x6d, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x52, 0x10, 0x30
+//        0b0100010, 0b0101111, 0b0100100, 0b0101010, 0b1101101, 0b1000000, 0b1000000, 0b1000000,
+//        0b1000000, 0b1000000, 0b1000000, 0b1000000, 0b1000000, 0b1010010, 0b0010000, 0b0110000
+    };
+
+    /* the constants D for 32 bit Mac*/
+    private static final byte[] EK_d32 = new byte[]{
+         0x22, 0x2f, 0x25, 0x2a, 0x6d, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x52, 0x10, 0x30
+//        0b0100010, 0b0101111, 0b0100101, 0b0101010, 0b1101101, 0b1000000, 0b1000000, 0b1000000,
+//        0b1000000, 0b1000000, 0b1000000, 0b1000000, 0b1000000, 0b1010010, 0b0010000, 0b0110000
+    };
+
+    /* the constants D for 64 bit Mac */
+    private static final byte[] EK_d64 = new byte[]{
+        0x23, 0x2f, 0x24, 0x2a, 0x6d, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x52, 0x10, 0x30
+//        0b0100011, 0b0101111, 0b0100100, 0b0101010, 0b1101101, 0b1000000, 0b1000000, 0b1000000,
+//        0b1000000, 0b1000000, 0b1000000, 0b1000000, 0b1000000, 0b1010010, 0b0010000, 0b0110000
+    };
+
+    /* the constants D for 128 bit Mac */
+    private static final byte[] EK_d128 = new byte[]{
+        0x23, 0x2f, 0x25, 0x2a, 0x6d, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x52, 0x10, 0x30
+//        0b0100011, 0b0101111, 0b0100101, 0b0101010, 0b1101101, 0b1000000, 0b1000000, 0b1000000,
+//        0b1000000, 0b1000000, 0b1000000, 0b1000000, 0b1000000, 0b1010010, 0b0010000, 0b0110000
+    };
+
+    /**
+     * The selected D constants.
+     */
+    private byte[] theD;
+
+    /**
+     * Constructor for streamCipher.
+     */
+    protected Zuc256CoreEngine()
+    {
+        theD = EK_d;
+    }
+
+    /**
+     * Constructor for Mac.
+     *
+     * @param pLength the Mac length
+     */
+    protected Zuc256CoreEngine(final int pLength)
+    {
+        switch (pLength)
+        {
+        case 32:
+            theD = EK_d32;
+            break;
+        case 64:
+            theD = EK_d64;
+            break;
+        case 128:
+            theD = EK_d128;
+            break;
+        default:
+            throw new IllegalArgumentException("Unsupported length: " + pLength);
+        }
+    }
+
+    /**
+     * Constructor for Memoable.
+     *
+     * @param pSource the source engine
+     */
+    protected Zuc256CoreEngine(final Zuc256CoreEngine pSource)
+    {
+        super(pSource);
+    }
+
+    /**
+     * Obtain Max iterations.
+     *
+     * @return the maximum iterations
+     */
+    protected int getMaxIterations()
+    {
+        return 625;
+    }
+
+    /**
+     * Obtain Algorithm Name.
+     *
+     * @return the name
+     */
+    public String getAlgorithmName()
+    {
+        return "Zuc-256";
+    }
+
+    /**
+     * Build a 31-bit integer from constituent parts.
+     *
+     * @param a part A
+     * @param b part B
+     * @param c part C
+     * @param d part D
+     * @return the built integer
+     */
+    private static int MAKEU31(byte a, byte b, byte c, byte d)
+    {
+        return (((a & 0xFF) << 23) | ((b & 0xFF) << 16) | ((c & 0xFF) << 8) | (d & 0xFF));
+    }
+
+    /**
+     * Process key and IV into LFSR.
+     *
+     * @param pLFSR the LFSR
+     * @param k     the key
+     * @param iv    the iv
+     */
+    protected void setKeyAndIV(final int[] pLFSR,
+                               final byte[] k,
+                               final byte[] iv)
+    {
+        /* Check lengths */
+        if (k == null || k.length != 32)
+        {
+            throw new IllegalArgumentException("A key of 32 bytes is needed");
+        }
+        if (iv == null || iv.length != 25)
+        {
+            throw new IllegalArgumentException("An IV of 25 bytes is needed");
+        }
+
+        /* expand key and IV */
+        pLFSR[0] = MAKEU31(k[0], theD[0], k[21], k[16]);
+        pLFSR[1] = MAKEU31(k[1], theD[1], k[22], k[17]);
+        pLFSR[2] = MAKEU31(k[2], theD[2], k[23], k[18]);
+        pLFSR[3] = MAKEU31(k[3], theD[3], k[24], k[19]);
+        pLFSR[4] = MAKEU31(k[4], theD[4], k[25], k[20]);
+        pLFSR[5] = MAKEU31(iv[0], (byte)(theD[5] | (iv[17] & 0x3F)), k[5], k[26]);
+        pLFSR[6] = MAKEU31(iv[1], (byte)(theD[6] | (iv[18] & 0x3F)), k[6], k[27]);
+        pLFSR[7] = MAKEU31(iv[10], (byte)(theD[7] | (iv[19] & 0x3F)), k[7], iv[2]);
+        pLFSR[8] = MAKEU31(k[8], (byte)(theD[8] | (iv[20] & 0x3F)), iv[3], iv[11]);
+        pLFSR[9] = MAKEU31(k[9], (byte)(theD[9] | (iv[21] & 0x3F)), iv[12], iv[4]);
+        pLFSR[10] = MAKEU31(iv[5], (byte)(theD[10] | (iv[22] & 0x3F)), k[10], k[28]);
+        pLFSR[11] = MAKEU31(k[11], (byte)(theD[11] | (iv[23] & 0x3F)), iv[6], iv[13]);
+        pLFSR[12] = MAKEU31(k[12], (byte)(theD[12] | (iv[24] & 0x3F)), iv[7], iv[14]);
+        pLFSR[13] = MAKEU31(k[13], theD[13], iv[15], iv[8]);
+        pLFSR[14] = MAKEU31(k[14], (byte)(theD[14] | ((k[31] >>> 4) & 0xF)), iv[16], iv[9]);
+        pLFSR[15] = MAKEU31(k[15], (byte)(theD[15] | (k[31] & 0xF)), k[30], k[29]);
+    }
+
+    /**
+     * Create a copy of the engine.
+     *
+     * @return the copy
+     */
+    public Memoable copy()
+    {
+        return new Zuc256CoreEngine(this);
+    }
+
+    /**
+     * Reset from saved engine state.
+     *
+     * @param pState the state to restore
+     */
+    public void reset(final Memoable pState)
+    {
+        final Zuc256CoreEngine e = (Zuc256CoreEngine)pState;
+        super.reset(pState);
+        theD = e.theD;
+    }
+}
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/engines/Zuc256Engine.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/engines/Zuc256Engine.java
new file mode 100644
index 0000000..5b58ec5
--- /dev/null
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/engines/Zuc256Engine.java
@@ -0,0 +1,51 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.crypto.engines;
+
+import com.android.internal.org.bouncycastle.util.Memoable;
+
+/**
+ * Zuc256 implementation.
+ * Based on https://www.is.cas.cn/ztzl2016/zouchongzhi/201801/W020180126529970733243.pdf
+ * @hide This class is not part of the Android public SDK API
+ */
+public final class Zuc256Engine
+    extends Zuc256CoreEngine
+{
+    /**
+     * Constructor for streamCipher.
+     */
+    public Zuc256Engine()
+    {
+        super();
+    }
+
+    /**
+     * Constructor for Mac.
+     *
+     * @param pLength the Mac length
+     */
+    public Zuc256Engine(final int pLength)
+    {
+        super(pLength);
+    }
+
+    /**
+     * Constructor for Memoable.
+     *
+     * @param pSource the source engine
+     */
+    private Zuc256Engine(final Zuc256Engine pSource)
+    {
+        super(pSource);
+    }
+
+    /**
+     * Create a copy of the engine.
+     *
+     * @return the copy
+     */
+    public Memoable copy()
+    {
+        return new Zuc256Engine(this);
+    }
+}
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/generators/DESKeyGenerator.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/generators/DESKeyGenerator.java
index 3a5090a..2b2c1a3 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/generators/DESKeyGenerator.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/generators/DESKeyGenerator.java
@@ -2,7 +2,10 @@
 package com.android.internal.org.bouncycastle.crypto.generators;
 
 import com.android.internal.org.bouncycastle.crypto.CipherKeyGenerator;
+import com.android.internal.org.bouncycastle.crypto.CryptoServicePurpose;
+import com.android.internal.org.bouncycastle.crypto.CryptoServicesRegistrar;
 import com.android.internal.org.bouncycastle.crypto.KeyGenerationParameters;
+import com.android.internal.org.bouncycastle.crypto.constraints.DefaultServiceProperties;
 import com.android.internal.org.bouncycastle.crypto.params.DESParameters;
 
 /**
@@ -33,6 +36,8 @@
                     + (DESParameters.DES_KEY_LENGTH * 8)
                     + " bits long.");
         }
+
+        CryptoServicesRegistrar.checkConstraints(new DefaultServiceProperties("DESKeyGen", 56, null, CryptoServicePurpose.KEYGEN));
     }
 
     public byte[] generateKey()
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/generators/DESedeKeyGenerator.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/generators/DESedeKeyGenerator.java
index c718a7d..836e2aa 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/generators/DESedeKeyGenerator.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/generators/DESedeKeyGenerator.java
@@ -1,7 +1,10 @@
 /* GENERATED SOURCE. DO NOT MODIFY. */
 package com.android.internal.org.bouncycastle.crypto.generators;
 
+import com.android.internal.org.bouncycastle.crypto.CryptoServicePurpose;
+import com.android.internal.org.bouncycastle.crypto.CryptoServicesRegistrar;
 import com.android.internal.org.bouncycastle.crypto.KeyGenerationParameters;
+import com.android.internal.org.bouncycastle.crypto.constraints.DefaultServiceProperties;
 import com.android.internal.org.bouncycastle.crypto.params.DESedeParameters;
 
 /**
@@ -43,6 +46,8 @@
                 + (2 * 8 * DESedeParameters.DES_KEY_LENGTH)
                 + " bits long.");
         }
+
+        CryptoServicesRegistrar.checkConstraints(new DefaultServiceProperties("DESedeKeyGen", 112, null, CryptoServicePurpose.KEYGEN));
     }
 
     public byte[] generateKey()
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/generators/DHBasicKeyPairGenerator.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/generators/DHBasicKeyPairGenerator.java
index 75d1e3c..ee4c402 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/generators/DHBasicKeyPairGenerator.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/generators/DHBasicKeyPairGenerator.java
@@ -1,16 +1,20 @@
 /* GENERATED SOURCE. DO NOT MODIFY. */
 package com.android.internal.org.bouncycastle.crypto.generators;
 
+import java.math.BigInteger;
+
 import com.android.internal.org.bouncycastle.crypto.AsymmetricCipherKeyPair;
 import com.android.internal.org.bouncycastle.crypto.AsymmetricCipherKeyPairGenerator;
+import com.android.internal.org.bouncycastle.crypto.CryptoServicePurpose;
+import com.android.internal.org.bouncycastle.crypto.CryptoServicesRegistrar;
 import com.android.internal.org.bouncycastle.crypto.KeyGenerationParameters;
+import com.android.internal.org.bouncycastle.crypto.constraints.ConstraintUtils;
+import com.android.internal.org.bouncycastle.crypto.constraints.DefaultServiceProperties;
 import com.android.internal.org.bouncycastle.crypto.params.DHKeyGenerationParameters;
 import com.android.internal.org.bouncycastle.crypto.params.DHParameters;
 import com.android.internal.org.bouncycastle.crypto.params.DHPrivateKeyParameters;
 import com.android.internal.org.bouncycastle.crypto.params.DHPublicKeyParameters;
 
-import java.math.BigInteger;
-
 /**
  * a basic Diffie-Hellman key pair generator.
  *
@@ -27,6 +31,8 @@
         KeyGenerationParameters param)
     {
         this.param = (DHKeyGenerationParameters)param;
+
+        CryptoServicesRegistrar.checkConstraints(new DefaultServiceProperties("DHBasicKeyGen", ConstraintUtils.bitsOfSecurityFor(this.param.getParameters().getP()), this.param.getParameters(), CryptoServicePurpose.KEYGEN));
     }
 
     public AsymmetricCipherKeyPair generateKeyPair()
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/generators/DSAKeyPairGenerator.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/generators/DSAKeyPairGenerator.java
index 570d45a..5f10d67 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/generators/DSAKeyPairGenerator.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/generators/DSAKeyPairGenerator.java
@@ -6,7 +6,11 @@
 
 import com.android.internal.org.bouncycastle.crypto.AsymmetricCipherKeyPair;
 import com.android.internal.org.bouncycastle.crypto.AsymmetricCipherKeyPairGenerator;
+import com.android.internal.org.bouncycastle.crypto.CryptoServicePurpose;
+import com.android.internal.org.bouncycastle.crypto.CryptoServicesRegistrar;
 import com.android.internal.org.bouncycastle.crypto.KeyGenerationParameters;
+import com.android.internal.org.bouncycastle.crypto.constraints.ConstraintUtils;
+import com.android.internal.org.bouncycastle.crypto.constraints.DefaultServiceProperties;
 import com.android.internal.org.bouncycastle.crypto.params.DSAKeyGenerationParameters;
 import com.android.internal.org.bouncycastle.crypto.params.DSAParameters;
 import com.android.internal.org.bouncycastle.crypto.params.DSAPrivateKeyParameters;
@@ -32,6 +36,8 @@
         KeyGenerationParameters param)
     {
         this.param = (DSAKeyGenerationParameters)param;
+
+        CryptoServicesRegistrar.checkConstraints(new DefaultServiceProperties("DSAKeyGen", ConstraintUtils.bitsOfSecurityFor(this.param.getParameters().getP()), this.param.getParameters(), CryptoServicePurpose.KEYGEN));
     }
 
     public AsymmetricCipherKeyPair generateKeyPair()
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/generators/ECKeyPairGenerator.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/generators/ECKeyPairGenerator.java
index 1b649eb..79d16ef 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/generators/ECKeyPairGenerator.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/generators/ECKeyPairGenerator.java
@@ -6,7 +6,11 @@
 
 import com.android.internal.org.bouncycastle.crypto.AsymmetricCipherKeyPair;
 import com.android.internal.org.bouncycastle.crypto.AsymmetricCipherKeyPairGenerator;
+import com.android.internal.org.bouncycastle.crypto.CryptoServicePurpose;
+import com.android.internal.org.bouncycastle.crypto.CryptoServicesRegistrar;
 import com.android.internal.org.bouncycastle.crypto.KeyGenerationParameters;
+import com.android.internal.org.bouncycastle.crypto.constraints.ConstraintUtils;
+import com.android.internal.org.bouncycastle.crypto.constraints.DefaultServiceProperties;
 import com.android.internal.org.bouncycastle.crypto.params.ECDomainParameters;
 import com.android.internal.org.bouncycastle.crypto.params.ECKeyGenerationParameters;
 import com.android.internal.org.bouncycastle.crypto.params.ECPrivateKeyParameters;
@@ -24,9 +28,20 @@
 public class ECKeyPairGenerator
     implements AsymmetricCipherKeyPairGenerator, ECConstants
 {
+    private final String name;
     ECDomainParameters  params;
     SecureRandom        random;
 
+    public ECKeyPairGenerator()
+    {
+        this("ECKeyGen");
+    }
+
+    protected ECKeyPairGenerator(String name)
+    {
+        this.name = name;
+    }
+
     public void init(
         KeyGenerationParameters param)
     {
@@ -34,6 +49,8 @@
 
         this.random = ecP.getRandom();
         this.params = ecP.getDomainParameters();
+
+        CryptoServicesRegistrar.checkConstraints(new DefaultServiceProperties(name, ConstraintUtils.bitsOfSecurityFor(this.params.getCurve()), ecP.getDomainParameters(), CryptoServicePurpose.KEYGEN));
     }
 
     /**
@@ -51,7 +68,7 @@
         {
             d = BigIntegers.createRandomBigInteger(nBitLength, random);
 
-            if (d.compareTo(ONE) < 0  || (d.compareTo(n) >= 0))
+            if (isOutOfRangeD(d, n))
             {
                 continue;
             }
@@ -71,6 +88,11 @@
             new ECPrivateKeyParameters(d, params));
     }
 
+    protected boolean isOutOfRangeD(BigInteger d, BigInteger n)
+    {
+        return d.compareTo(ONE) < 0 || (d.compareTo(n) >= 0);
+    }
+
     protected ECMultiplier createBasePointMultiplier()
     {
         return new FixedPointCombMultiplier();
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/generators/OpenSSLPBEParametersGenerator.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/generators/OpenSSLPBEParametersGenerator.java
index 43b5b5c..c336379 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/generators/OpenSSLPBEParametersGenerator.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/generators/OpenSSLPBEParametersGenerator.java
@@ -13,8 +13,9 @@
 /**
  * Generator for PBE derived keys and ivs as usd by OpenSSL.
  * <p>
- * The scheme is a simple extension of PKCS 5 V2.0 Scheme 1 using MD5 with an
- * iteration count of 1.
+ * Originally this scheme was a simple extension of PKCS 5 V2.0 Scheme 1 using MD5 with an
+ * iteration count of 1. The default digest was changed to SHA-256 with OpenSSL 1.1.0. This
+ * implementation still defaults to MD5, but the digest can now be set.
  * <p>
  * @hide This class is not part of the Android public SDK API
  */
@@ -26,7 +27,7 @@
     private Digest  digest = AndroidDigestFactory.getMD5();
 
     /**
-     * Construct a OpenSSL Parameters generator. 
+     * Construct a OpenSSL Parameters generator.
      */
     public OpenSSLPBEParametersGenerator()
     {
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/generators/RSAKeyPairGenerator.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/generators/RSAKeyPairGenerator.java
index 86a62fe..9552ffe 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/generators/RSAKeyPairGenerator.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/generators/RSAKeyPairGenerator.java
@@ -5,7 +5,11 @@
 
 import com.android.internal.org.bouncycastle.crypto.AsymmetricCipherKeyPair;
 import com.android.internal.org.bouncycastle.crypto.AsymmetricCipherKeyPairGenerator;
+import com.android.internal.org.bouncycastle.crypto.CryptoServicePurpose;
+import com.android.internal.org.bouncycastle.crypto.CryptoServicesRegistrar;
 import com.android.internal.org.bouncycastle.crypto.KeyGenerationParameters;
+import com.android.internal.org.bouncycastle.crypto.constraints.ConstraintUtils;
+import com.android.internal.org.bouncycastle.crypto.constraints.DefaultServiceProperties;
 import com.android.internal.org.bouncycastle.crypto.params.RSAKeyGenerationParameters;
 import com.android.internal.org.bouncycastle.crypto.params.RSAKeyParameters;
 import com.android.internal.org.bouncycastle.crypto.params.RSAPrivateCrtKeyParameters;
@@ -27,6 +31,8 @@
     public void init(KeyGenerationParameters param)
     {
         this.param = (RSAKeyGenerationParameters)param;
+
+        CryptoServicesRegistrar.checkConstraints(new DefaultServiceProperties("RSAKeyGen", ConstraintUtils.bitsOfSecurityForFF(param.getStrength()), null, CryptoServicePurpose.KEYGEN));
     }
 
     public AsymmetricCipherKeyPair generateKeyPair()
@@ -93,12 +99,12 @@
                     continue;
                 }
 
-	            /*
+                /*
                  * Require a minimum weight of the NAF representation, since low-weight composites may
-	             * be weak against a version of the number-field-sieve for factoring.
-	             *
-	             * See "The number field sieve for integers of low weight", Oliver Schirokauer.
-	             */
+                 * be weak against a version of the number-field-sieve for factoring.
+                 *
+                 * See "The number field sieve for integers of low weight", Oliver Schirokauer.
+                 */
                 if (WNafUtil.getNafWeight(n) < minWeight)
                 {
                     p = chooseRandomPrime(pbitlength, e, squaredBound);
@@ -144,8 +150,8 @@
             qInv = BigIntegers.modOddInverse(p, q);
 
             result = new AsymmetricCipherKeyPair(
-                new RSAKeyParameters(false, n, e),
-                new RSAPrivateCrtKeyParameters(n, e, d, p, q, dP, dQ, qInv));
+                new RSAKeyParameters(false, n, e, true),
+                new RSAPrivateCrtKeyParameters(n, e, d, p, q, dP, dQ, qInv, true));
         }
 
         return result;
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/generators/SM2KeyPairGenerator.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/generators/SM2KeyPairGenerator.java
new file mode 100644
index 0000000..35815e2
--- /dev/null
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/generators/SM2KeyPairGenerator.java
@@ -0,0 +1,40 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.crypto.generators;
+
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
+import com.android.internal.org.bouncycastle.crypto.AsymmetricCipherKeyPair;
+import com.android.internal.org.bouncycastle.crypto.AsymmetricCipherKeyPairGenerator;
+import com.android.internal.org.bouncycastle.crypto.CryptoServicePurpose;
+import com.android.internal.org.bouncycastle.crypto.CryptoServicesRegistrar;
+import com.android.internal.org.bouncycastle.crypto.KeyGenerationParameters;
+import com.android.internal.org.bouncycastle.crypto.constraints.ConstraintUtils;
+import com.android.internal.org.bouncycastle.crypto.constraints.DefaultServiceProperties;
+import com.android.internal.org.bouncycastle.crypto.params.ECDomainParameters;
+import com.android.internal.org.bouncycastle.crypto.params.ECKeyGenerationParameters;
+import com.android.internal.org.bouncycastle.crypto.params.ECPrivateKeyParameters;
+import com.android.internal.org.bouncycastle.crypto.params.ECPublicKeyParameters;
+import com.android.internal.org.bouncycastle.math.ec.ECConstants;
+import com.android.internal.org.bouncycastle.math.ec.ECMultiplier;
+import com.android.internal.org.bouncycastle.math.ec.ECPoint;
+import com.android.internal.org.bouncycastle.math.ec.FixedPointCombMultiplier;
+import com.android.internal.org.bouncycastle.math.ec.WNafUtil;
+import com.android.internal.org.bouncycastle.util.BigIntegers;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+public class SM2KeyPairGenerator
+    extends ECKeyPairGenerator
+{
+    public SM2KeyPairGenerator()
+    {
+        super("SM2KeyGen");
+    }
+
+    protected boolean isOutOfRangeD(BigInteger d, BigInteger n)
+    {
+        return d.compareTo(ONE) < 0 || (d.compareTo(n.subtract(BigIntegers.ONE)) >= 0);
+    }
+}
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/macs/CBCBlockCipherMac.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/macs/CBCBlockCipherMac.java
index 38efe5c..5b83b75 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/macs/CBCBlockCipherMac.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/macs/CBCBlockCipherMac.java
@@ -94,7 +94,7 @@
             throw new IllegalArgumentException("MAC size must be multiple of 8");
         }
 
-        this.cipher = new CBCBlockCipher(cipher);
+        this.cipher = CBCBlockCipher.newInstance(cipher);
         this.padding = padding;
         this.macSize = macSizeInBits / 8;
 
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/macs/HMac.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/macs/HMac.java
index c88919b..0f5ff11 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/macs/HMac.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/macs/HMac.java
@@ -218,15 +218,15 @@
      */
     public void reset()
     {
-        /*
-         * reset the underlying digest.
-         */
-        digest.reset();
-
-        /*
-         * reinitialize the digest.
-         */
-        digest.update(inputPad, 0, inputPad.length);
+        if (ipadState != null)
+        {
+            ((Memoable)digest).reset(ipadState);
+        }
+        else
+        {
+            digest.reset();
+            digest.update(inputPad, 0, inputPad.length);
+        }
     }
 
     private static void xorPad(byte[] pad, int len, byte n)
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/macs/Zuc128Mac.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/macs/Zuc128Mac.java
new file mode 100644
index 0000000..64fee91
--- /dev/null
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/macs/Zuc128Mac.java
@@ -0,0 +1,247 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.crypto.macs;
+
+import com.android.internal.org.bouncycastle.crypto.CipherParameters;
+import com.android.internal.org.bouncycastle.crypto.Mac;
+import com.android.internal.org.bouncycastle.crypto.engines.Zuc128CoreEngine;
+
+/**
+ * Zuc128 Mac implementation.
+ * Based on https://www.qtc.jp/3GPP/Specs/eea3eia3specificationv16.pdf
+ * @hide This class is not part of the Android public SDK API
+ */
+public final class Zuc128Mac
+    implements Mac
+{
+    /**
+     * The Maximum Bit Mask.
+     */
+    private static final int TOPBIT = 0x80;
+
+    /**
+     * The Zuc128 Engine.
+     */
+    private final InternalZuc128Engine theEngine;
+
+    /**
+     * The calculated Mac in words.
+     */
+    private int theMac;
+
+    /**
+     * The active keyStream.
+     */
+    private final int[] theKeyStream;
+
+    /**
+     * The initialised state.
+     */
+    private Zuc128CoreEngine theState;
+
+    /**
+     * The current word index.
+     */
+    private int theWordIndex;
+
+    /**
+     * The current byte index.
+     */
+    private int theByteIndex;
+
+    /**
+     * Constructor.
+     */
+    public Zuc128Mac()
+    {
+        theEngine = new InternalZuc128Engine();
+        theKeyStream = new int[2];
+    }
+
+    /**
+     * Obtain Algorithm Name.
+     *
+     * @return the name
+     */
+    public String getAlgorithmName()
+    {
+        return "Zuc128Mac";
+    }
+
+    /**
+     * Obtain Mac Size.
+     *
+     * @return the size in Bytes
+     */
+    public int getMacSize()
+    {
+        return 4; // Integer.Bytes
+    }
+
+    /**
+     * Initialise the Mac.
+     *
+     * @param pParams the parameters
+     */
+    public void init(final CipherParameters pParams)
+    {
+        /* Initialise the engine */
+        theEngine.init(true, pParams);
+        theState = (Zuc128CoreEngine)theEngine.copy();
+        initKeyStream();
+    }
+
+    /**
+     * Initialise the keyStream.
+     */
+    private void initKeyStream()
+    {
+        /* Initialise the Mac */
+        theMac = 0;
+
+        /* Initialise the KeyStream */
+        for (int i = 0; i < theKeyStream.length - 1; i++)
+        {
+            theKeyStream[i] = theEngine.createKeyStreamWord();
+        }
+        theWordIndex = theKeyStream.length - 1;
+        theByteIndex = 3; //Integer.BYTES - 1;
+    }
+
+    /**
+     * Update the mac with a single byte.
+     *
+     * @param in the byte to update with
+     */
+    public void update(final byte in)
+    {
+        /* shift for next byte */
+        shift4NextByte();
+
+        /* Loop through the bits */
+        final int bitBase = theByteIndex * 8; //Byte.SIZE;
+        for (int bitMask = TOPBIT, bitNo = 0; bitMask > 0; bitMask >>= 1, bitNo++)
+        {
+            /* If the bit is set */
+            if ((in & bitMask) != 0)
+            {
+                /* update theMac */
+                updateMac(bitBase + bitNo);
+            }
+        }
+    }
+
+    /**
+     * Shift for next byte.
+     */
+    private void shift4NextByte()
+    {
+        /* Adjust the byte index */
+        theByteIndex = (theByteIndex + 1) % 4; //Integer.BYTES;
+
+        /* Adjust keyStream if required */
+        if (theByteIndex == 0)
+        {
+            theKeyStream[theWordIndex] = theEngine.createKeyStreamWord();
+            theWordIndex = (theWordIndex + 1) % theKeyStream.length;
+        }
+    }
+
+    /**
+     * Update the Mac.
+     *
+     * @param bitNo the bit number
+     */
+    private void updateMac(final int bitNo)
+    {
+        /* Update the Mac */
+        theMac ^= getKeyStreamWord(bitNo);
+    }
+
+    /**
+     * Obtain the keyStreamWord.
+     *
+     * @param bitNo the bitNumber
+     * @return the word
+     */
+    private int getKeyStreamWord(final int bitNo)
+    {
+        /* Access the first word and return it if this is bit 0 */
+        final int myFirst = theKeyStream[theWordIndex];
+        if (bitNo == 0)
+        {
+            return myFirst;
+        }
+
+        /* Access the second word */
+        final int mySecond = theKeyStream[(theWordIndex + 1) % theKeyStream.length];
+        return (myFirst << bitNo) | (mySecond >>> (32 - bitNo)); // Integer.SIZE - bitNo
+    }
+
+    /**
+     * Update the mac.
+     *
+     * @param in    the input buffer
+     * @param inOff the starting offset in the input buffer
+     * @param len   the length of data to process
+     */
+    public void update(final byte[] in, final int inOff, final int len)
+    {
+        for (int byteNo = 0; byteNo < len; byteNo++)
+        {
+            update(in[inOff + byteNo]);
+        }
+    }
+
+    /**
+     * Obtain the final word.
+     *
+     * @return the final word
+     */
+    private int getFinalWord()
+    {
+        if (theByteIndex != 0)
+        {
+            return theEngine.createKeyStreamWord();
+        }
+        theWordIndex = (theWordIndex + 1) % theKeyStream.length;
+        return theKeyStream[theWordIndex];
+    }
+
+    /**
+     * Finalize the mac.
+     *
+     * @param out    the output buffer
+     * @param outOff the starting offset in the input buffer
+     * @return the size of the mac
+     */
+    public int doFinal(final byte[] out, final int outOff)
+    {
+        /* Finish the Mac and output it */
+        shift4NextByte();
+        theMac ^= getKeyStreamWord(theByteIndex * 8); //Byte.SIZE
+        theMac ^= getFinalWord();
+        Zuc128CoreEngine.encode32be(theMac, out, outOff);
+
+        /* Reset the Mac */
+        reset();
+        return getMacSize();
+    }
+
+    public void reset()
+    {
+        if (theState != null)
+        {
+            theEngine.reset(theState);
+        }
+        initKeyStream();
+    }
+
+    private static class InternalZuc128Engine
+        extends Zuc128CoreEngine
+    {
+        int createKeyStreamWord()
+        {
+            return super.makeKeyStreamWord();
+        }
+    }
+}
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/macs/Zuc256Mac.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/macs/Zuc256Mac.java
new file mode 100644
index 0000000..afd00f5
--- /dev/null
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/macs/Zuc256Mac.java
@@ -0,0 +1,276 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.crypto.macs;
+
+import com.android.internal.org.bouncycastle.crypto.CipherParameters;
+import com.android.internal.org.bouncycastle.crypto.Mac;
+import com.android.internal.org.bouncycastle.crypto.engines.Zuc256CoreEngine;
+
+/**
+ * Zuc256 Mac implementation.
+ * Based on https://www.is.cas.cn/ztzl2016/zouchongzhi/201801/W020180126529970733243.pdf
+ * @hide This class is not part of the Android public SDK API
+ */
+public final class Zuc256Mac
+    implements Mac
+{
+    /**
+     * The Maximum Bit Mask.
+     */
+    private static final int TOPBIT = 0x80;
+
+    /**
+     * The Zuc256 Engine.
+     */
+    private final InternalZuc256Engine theEngine;
+
+    /**
+     * The mac length.
+     */
+    private final int theMacLength;
+
+    /**
+     * The calculated Mac in words.
+     */
+    private final int[] theMac;
+
+    /**
+     * The active keyStream.
+     */
+    private final int[] theKeyStream;
+
+    /**
+     * The initialised state.
+     */
+    private Zuc256CoreEngine theState;
+
+    /**
+     * The current word index.
+     */
+    private int theWordIndex;
+
+    /**
+     * The current byte index.
+     */
+    private int theByteIndex;
+
+    /**
+     * Constructor.
+     *
+     * @param pLength the bit length of the Mac
+     */
+    public Zuc256Mac(final int pLength)
+    {
+        theEngine = new InternalZuc256Engine(pLength);
+        theMacLength = pLength;
+        final int numWords = pLength / 32; // Integer.SIZE
+        theMac = new int[numWords];
+        theKeyStream = new int[numWords + 1];
+    }
+
+    /**
+     * Obtain Algorithm Name.
+     *
+     * @return the name
+     */
+    public String getAlgorithmName()
+    {
+        return "Zuc256Mac-" + theMacLength;
+    }
+
+    /**
+     * Obtain Mac Size.
+     *
+     * @return the size in Bytes
+     */
+    public int getMacSize()
+    {
+        return theMacLength / 8; //Byte.SIZE
+    }
+
+    /**
+     * Initialise the Mac.
+     *
+     * @param pParams the parameters
+     */
+    public void init(final CipherParameters pParams)
+    {
+        /* Initialise the engine */
+        theEngine.init(true, pParams);
+        theState = (Zuc256CoreEngine)theEngine.copy();
+        initKeyStream();
+    }
+
+    /**
+     * Initialise the keyStream.
+     */
+    private void initKeyStream()
+    {
+        /* Initialise the Mac */
+        for (int i = 0; i < theMac.length; i++)
+        {
+            theMac[i] = theEngine.createKeyStreamWord();
+        }
+
+        /* Initialise the KeyStream */
+        for (int i = 0; i < theKeyStream.length - 1; i++)
+        {
+            theKeyStream[i] = theEngine.createKeyStreamWord();
+        }
+        theWordIndex = theKeyStream.length - 1;
+        theByteIndex = 4 - 1;    // Integer.SIZE
+    }
+
+    /**
+     * Update the mac with a single byte.
+     *
+     * @param in the byte to update with
+     */
+    public void update(final byte in)
+    {
+        /* shift for next byte */
+        shift4NextByte();
+
+        /* Loop through the bits */
+        final int bitBase = theByteIndex * 8; //Byte.SIZE;
+        for (int bitMask = TOPBIT, bitNo = 0; bitMask > 0; bitMask >>= 1, bitNo++)
+        {
+            /* If the bit is set */
+            if ((in & bitMask) != 0)
+            {
+                /* update theMac */
+                updateMac(bitBase + bitNo);
+            }
+        }
+    }
+
+    /**
+     * Shift for next byte.
+     */
+    private void shift4NextByte()
+    {
+        /* Adjust the byte index */
+        theByteIndex = (theByteIndex + 1) % 4; //Integer.BYTES
+
+        /* Adjust keyStream if required */
+        if (theByteIndex == 0)
+        {
+            theKeyStream[theWordIndex] = theEngine.createKeyStreamWord();
+            theWordIndex = (theWordIndex + 1) % theKeyStream.length;
+        }
+    }
+
+    /**
+     * Shift for final update.
+     */
+    private void shift4Final()
+    {
+        /* Adjust the byte index */
+        theByteIndex = (theByteIndex + 1) % 4; //Integer.BYTES
+
+        /* No need to read another word to the keyStream */
+        if (theByteIndex == 0)
+        {
+            theWordIndex = (theWordIndex + 1) % theKeyStream.length;
+        }
+    }
+
+    /**
+     * Update the Mac.
+     *
+     * @param bitNo the bit number
+     */
+    private void updateMac(final int bitNo)
+    {
+        /* Loop through the Mac */
+        for (int wordNo = 0; wordNo < theMac.length; wordNo++)
+        {
+            theMac[wordNo] ^= getKeyStreamWord(wordNo, bitNo);
+        }
+    }
+
+    /**
+     * Obtain the keyStreamWord.
+     *
+     * @param wordNo the wordNumber
+     * @param bitNo  the bitNumber
+     * @return the word
+     */
+    private int getKeyStreamWord(final int wordNo, final int bitNo)
+    {
+        /* Access the first word and return it if this is bit 0 */
+        final int myFirst = theKeyStream[(theWordIndex + wordNo) % theKeyStream.length];
+        if (bitNo == 0)
+        {
+            return myFirst;
+        }
+
+        /* Access the second word */
+        final int mySecond = theKeyStream[(theWordIndex + wordNo + 1) % theKeyStream.length];
+        return (myFirst << bitNo) | (mySecond >>> (32 - bitNo)); //Integer.SIZE - bitNo
+    }
+
+    /**
+     * Update the mac.
+     *
+     * @param in    the input buffer
+     * @param inOff the starting offset in the input buffer
+     * @param len   the length of data to process
+     */
+    public void update(final byte[] in, final int inOff, final int len)
+    {
+        for (int byteNo = 0; byteNo < len; byteNo++)
+        {
+            update(in[inOff + byteNo]);
+        }
+    }
+
+    /**
+     * Finalize the mac.
+     *
+     * @param out    the output buffer
+     * @param outOff the starting offset in the output buffer
+     * @return the size of the mac
+     */
+    public int doFinal(final byte[] out, final int outOff)
+    {
+        /* shift for final update */
+        shift4Final();
+
+        /* Finish the Mac and output it */
+        updateMac(theByteIndex * 8); //Byte.SIZE)
+        for (int i = 0; i < theMac.length; i++)
+        {
+            Zuc256CoreEngine.encode32be(theMac[i], out, outOff + i * 4); //Integer.BYTES)
+        }
+
+        /* Reset the Mac */
+        reset();
+        return getMacSize();
+    }
+
+    /**
+     * Reset the Mac.
+     */
+    public void reset()
+    {
+        if (theState != null)
+        {
+            theEngine.reset(theState);
+        }
+        initKeyStream();
+    }
+
+    private static class InternalZuc256Engine
+        extends Zuc256CoreEngine
+    {
+        public InternalZuc256Engine(int pLength)
+        {
+            super(pLength);
+        }
+
+        int createKeyStreamWord()
+        {
+            return super.makeKeyStreamWord();
+        }
+    }
+}
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/modes/CBCBlockCipher.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/modes/CBCBlockCipher.java
index d767a6a..4c6aa12 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/modes/CBCBlockCipher.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/modes/CBCBlockCipher.java
@@ -4,6 +4,7 @@
 import com.android.internal.org.bouncycastle.crypto.BlockCipher;
 import com.android.internal.org.bouncycastle.crypto.CipherParameters;
 import com.android.internal.org.bouncycastle.crypto.DataLengthException;
+import com.android.internal.org.bouncycastle.crypto.DefaultMultiBlockCipher;
 import com.android.internal.org.bouncycastle.crypto.params.ParametersWithIV;
 import com.android.internal.org.bouncycastle.util.Arrays;
 
@@ -12,7 +13,8 @@
  * @hide This class is not part of the Android public SDK API
  */
 public class CBCBlockCipher
-    implements BlockCipher
+    extends DefaultMultiBlockCipher
+    implements CBCModeCipher
 {
     private byte[]          IV;
     private byte[]          cbcV;
@@ -23,9 +25,20 @@
     private boolean         encrypting;
 
     /**
+     * Return a new CBC mode cipher based on the passed in base cipher
+     *
+     * @param cipher the base cipher for the CBC mode.
+     */
+    public static CBCModeCipher newInstance(BlockCipher cipher)
+    {
+        return new CBCBlockCipher(cipher);
+    }
+
+    /**
      * Basic constructor.
      *
      * @param cipher the block cipher to be used as the basis of chaining.
+     * @deprecated use the CBCBlockCipher.newInstance() static method.
      */
     public CBCBlockCipher(
         BlockCipher cipher)
@@ -79,31 +92,23 @@
 
             System.arraycopy(iv, 0, IV, 0, iv.length);
 
-            reset();
-
-            // if null it's an IV changed only.
-            if (ivParam.getParameters() != null)
-            {
-                cipher.init(encrypting, ivParam.getParameters());
-            }
-            else if (oldEncrypting != encrypting)
-            {
-                throw new IllegalArgumentException("cannot change encrypting state without providing key.");
-            }
+            params = ivParam.getParameters();
         }
         else
         {
-            reset();
+            Arrays.fill(IV, (byte)0);
+        }
 
-            // if it's null, key is to be reused.
-            if (params != null)
-            {
-                cipher.init(encrypting, params);
-            }
-            else if (oldEncrypting != encrypting)
-            {
-                throw new IllegalArgumentException("cannot change encrypting state without providing key.");
-            }
+        reset();
+
+        // if null it's an IV changed only (key is to be reused).
+        if (params != null)
+        {
+            cipher.init(encrypting, params);
+        }
+        else if (oldEncrypting != encrypting)
+        {
+            throw new IllegalArgumentException("cannot change encrypting state without providing key.");
         }
     }
 
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/modes/CBCModeCipher.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/modes/CBCModeCipher.java
new file mode 100644
index 0000000..d88d300
--- /dev/null
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/modes/CBCModeCipher.java
@@ -0,0 +1,19 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.crypto.modes;
+
+import com.android.internal.org.bouncycastle.crypto.BlockCipher;
+import com.android.internal.org.bouncycastle.crypto.MultiBlockCipher;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+public interface CBCModeCipher
+    extends MultiBlockCipher
+{
+    /**
+     * return the underlying block cipher that we are wrapping.
+     *
+     * @return the underlying block cipher that we are wrapping.
+     */
+    BlockCipher getUnderlyingCipher();
+}
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/modes/CCMBlockCipher.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/modes/CCMBlockCipher.java
index f93c727..affcb17 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/modes/CCMBlockCipher.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/modes/CCMBlockCipher.java
@@ -22,7 +22,7 @@
  * @hide This class is not part of the Android public SDK API
  */
 public class CCMBlockCipher
-    implements AEADBlockCipher
+    implements CCMModeCipher
 {
     private BlockCipher           cipher;
     private int                   blockSize;
@@ -36,9 +36,20 @@
     private ExposedByteArrayOutputStream data = new ExposedByteArrayOutputStream();
 
     /**
+     * Return a new CCM mode cipher based on the passed in base cipher
+     *
+     * @param cipher the base cipher for the CCM mode.
+     */
+    public static CCMModeCipher newInstance(BlockCipher cipher)
+    {
+        return new CCMBlockCipher(cipher);
+    }
+
+    /**
      * Basic constructor.
      *
      * @param c the block cipher to be used.
+     * @deprecated use the CCMBlockCipher.newInstance() static method.
      */
     public CCMBlockCipher(BlockCipher c)
     {
@@ -262,7 +273,7 @@
         iv[0] = (byte)((q - 1) & 0x7);
         System.arraycopy(nonce, 0, iv, 1, nonce.length);
 
-        BlockCipher ctrCipher = new SICBlockCipher(cipher);
+        BlockCipher ctrCipher = SICBlockCipher.newInstance(cipher);
         ctrCipher.init(forEncryption, new ParametersWithIV(keyParam, iv));
 
         int outputLen;
@@ -456,7 +467,7 @@
         return getAssociatedTextLength() > 0;
     }
 
-    private class ExposedByteArrayOutputStream
+    private static class ExposedByteArrayOutputStream
         extends ByteArrayOutputStream
     {
         public ExposedByteArrayOutputStream()
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/modes/CCMModeCipher.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/modes/CCMModeCipher.java
new file mode 100644
index 0000000..ebdcb7e
--- /dev/null
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/modes/CCMModeCipher.java
@@ -0,0 +1,10 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.crypto.modes;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+public interface CCMModeCipher
+    extends AEADBlockCipher
+{
+}
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/modes/CFBBlockCipher.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/modes/CFBBlockCipher.java
index d8f51e3..61f3652 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/modes/CFBBlockCipher.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/modes/CFBBlockCipher.java
@@ -14,6 +14,7 @@
  */
 public class CFBBlockCipher
     extends StreamBlockCipher
+    implements CFBModeCipher
 {
     private byte[]          IV;
     private byte[]          cfbV;
@@ -26,11 +27,23 @@
     private int             byteCount;
 
     /**
+     * Return a new CFB mode cipher based on the passed in base cipher
+     *
+     * @param cipher the base cipher for the CFB mode.
+     * @param blockSize the block size (in bits) used for the CFB mode.
+     */
+    public static CFBModeCipher newInstance(BlockCipher cipher, int blockSize)
+    {
+        return new CFBBlockCipher(cipher, blockSize);
+    }
+
+    /**
      * Basic constructor.
      *
      * @param cipher the block cipher to be used as the basis of the
      * feedback mode.
      * @param bitBlockSize the block size in bits (note: a multiple of 8)
+     * @deprecated use the equivalent CFBBlockCipher.newInstance() static method.
      */
     public CFBBlockCipher(
         BlockCipher cipher,
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/modes/CFBModeCipher.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/modes/CFBModeCipher.java
new file mode 100644
index 0000000..23a16c8
--- /dev/null
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/modes/CFBModeCipher.java
@@ -0,0 +1,13 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.crypto.modes;
+
+import com.android.internal.org.bouncycastle.crypto.MultiBlockCipher;
+import com.android.internal.org.bouncycastle.crypto.StreamCipher;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+public interface CFBModeCipher
+    extends MultiBlockCipher, StreamCipher
+{
+}
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/modes/CTRModeCipher.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/modes/CTRModeCipher.java
new file mode 100644
index 0000000..21b766f
--- /dev/null
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/modes/CTRModeCipher.java
@@ -0,0 +1,20 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.crypto.modes;
+
+import com.android.internal.org.bouncycastle.crypto.BlockCipher;
+import com.android.internal.org.bouncycastle.crypto.MultiBlockCipher;
+import com.android.internal.org.bouncycastle.crypto.SkippingStreamCipher;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+public interface CTRModeCipher
+    extends MultiBlockCipher, SkippingStreamCipher
+{
+    /**
+     * return the underlying block cipher that we are wrapping.
+     *
+     * @return the underlying block cipher that we are wrapping.
+     */
+    BlockCipher getUnderlyingCipher();
+}
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/modes/CTSBlockCipher.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/modes/CTSBlockCipher.java
index a277ae2..3fc15c0 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/modes/CTSBlockCipher.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/modes/CTSBlockCipher.java
@@ -2,8 +2,8 @@
 package com.android.internal.org.bouncycastle.crypto.modes;
 
 import com.android.internal.org.bouncycastle.crypto.BlockCipher;
-import com.android.internal.org.bouncycastle.crypto.BufferedBlockCipher;
 import com.android.internal.org.bouncycastle.crypto.DataLengthException;
+import com.android.internal.org.bouncycastle.crypto.DefaultBufferedBlockCipher;
 import com.android.internal.org.bouncycastle.crypto.InvalidCipherTextException;
 import com.android.internal.org.bouncycastle.crypto.OutputLengthException;
 import com.android.internal.org.bouncycastle.crypto.StreamBlockCipher;
@@ -14,7 +14,7 @@
  * @hide This class is not part of the Android public SDK API
  */
 public class CTSBlockCipher
-    extends BufferedBlockCipher
+    extends DefaultBufferedBlockCipher
 {
     private int     blockSize;
 
@@ -223,9 +223,9 @@
                     buf[i] ^= block[i - blockSize];
                 }
 
-                if (cipher instanceof CBCBlockCipher)
+                if (cipher instanceof CBCModeCipher)
                 {
-                    BlockCipher c = ((CBCBlockCipher)cipher).getUnderlyingCipher();
+                    BlockCipher c = ((CBCModeCipher)cipher).getUnderlyingCipher();
 
                     c.processBlock(buf, blockSize, out, outOff);
                 }
@@ -252,9 +252,9 @@
 
             if (bufOff > blockSize)
             {
-                if (cipher instanceof CBCBlockCipher)
+                if (cipher instanceof CBCModeCipher)
                 {
-                    BlockCipher c = ((CBCBlockCipher)cipher).getUnderlyingCipher();
+                    BlockCipher c = ((CBCModeCipher)cipher).getUnderlyingCipher();
 
                     c.processBlock(buf, 0, block, 0);
                 }
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/modes/GCMBlockCipher.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/modes/GCMBlockCipher.java
index e74dd6b..fdf8cf9 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/modes/GCMBlockCipher.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/modes/GCMBlockCipher.java
@@ -23,7 +23,7 @@
  * @hide This class is not part of the Android public SDK API
  */
 public class GCMBlockCipher
-    implements AEADBlockCipher
+    implements GCMModeCipher
 {
     private static final int BLOCK_SIZE = 16;
     // BEGIN Android-added: Max input size limitation from NIST.
@@ -60,11 +60,45 @@
     private long        atLength;
     private long        atLengthPre;
 
+    /**
+     * Return a new GCM mode cipher based on the passed in base cipher
+     *
+     * @param cipher the base cipher for the GCM mode.
+     */
+    public static GCMModeCipher newInstance(BlockCipher cipher)
+    {
+        return new GCMBlockCipher(cipher);
+    }
+
+    /**
+     * Return a new GCM mode cipher based on the passed in base cipher and multiplier.
+     *
+     * @param cipher the base cipher for the GCM mode.
+     * @param m the GCM multiplier to use.
+     */
+    public static GCMModeCipher newInstance(BlockCipher cipher, GCMMultiplier m)
+    {
+        return new GCMBlockCipher(cipher, m);
+    }
+
+    /**
+     * Base constructor - GCM mode over base cipher c.
+     *
+     * @param c the base cipher.
+     * @deprecated use the GCMBlockCipher.newInstance() static method.
+     */
     public GCMBlockCipher(BlockCipher c)
     {
         this(c, null);
     }
 
+    /**
+     * Base constructor - GCM mode over base cipher c over base multiplier m.
+     *
+     * @param c the base cipher.
+     * @param m the GCM multiplier to use.
+     * @deprecated use the CBCBlockCipher.newInstance() static method.
+     */
     public GCMBlockCipher(BlockCipher c, GCMMultiplier m)
     {
         if (c.getBlockSize() != BLOCK_SIZE)
@@ -289,17 +323,35 @@
         }
         // END Android-added: Max input size limitation from NIST.
 
-        for (int i = 0; i < len; ++i)
+        if (atBlockPos > 0)
         {
-            atBlock[atBlockPos] = in[inOff + i];
-            if (++atBlockPos == BLOCK_SIZE)
+            int available = BLOCK_SIZE - atBlockPos;
+            if (len < available)
             {
-                // Hash each block as it fills
-                gHASHBlock(S_at, atBlock);
-                atBlockPos = 0;
-                atLength += BLOCK_SIZE;
+                System.arraycopy(in, inOff, atBlock, atBlockPos, len);
+                atBlockPos += len;
+                return;
             }
+
+            System.arraycopy(in, inOff, atBlock, atBlockPos, available);
+            gHASHBlock(S_at, atBlock);
+            atLength += BLOCK_SIZE;
+            inOff += available;
+            len -= available;
+            //atBlockPos = 0;
         }
+
+        int inLimit = inOff + len - BLOCK_SIZE;
+
+        while (inOff <= inLimit)
+        {
+            gHASHBlock(S_at, in, inOff);
+            atLength += BLOCK_SIZE;
+            inOff += BLOCK_SIZE;
+        }
+
+        atBlockPos = BLOCK_SIZE + inLimit - inOff;
+        System.arraycopy(in, inOff, atBlock, 0, atBlockPos);
     }
 
     private void initCipher()
@@ -336,13 +388,14 @@
         bufBlock[bufOff] = in;
         if (++bufOff == bufBlock.length)
         {
-            processBlock(bufBlock, 0, out, outOff);
             if (forEncryption)
             {
+                encryptBlock(bufBlock, 0, out, outOff);
                 bufOff = 0;
             }
             else
             {
+                decryptBlock(bufBlock, 0, out, outOff);
                 System.arraycopy(bufBlock, BLOCK_SIZE, bufBlock, 0, macSize);
                 bufOff = macSize;
             }
@@ -370,49 +423,79 @@
 
         if (forEncryption)
         {
-            if (bufOff != 0)
+            if (bufOff > 0)
             {
-                while (len > 0)
+                int available = BLOCK_SIZE - bufOff;
+                if (len < available)
                 {
-                    --len;
-                    bufBlock[bufOff] = in[inOff++];
-                    if (++bufOff == BLOCK_SIZE)
-                    {
-                        processBlock(bufBlock, 0, out, outOff);
-                        bufOff = 0;
-                        resultLen += BLOCK_SIZE;
-                        break;
-                    }
+                    System.arraycopy(in, inOff, bufBlock, bufOff, len);
+                    bufOff += len;
+                    return 0;
                 }
+
+                System.arraycopy(in, inOff, bufBlock, bufOff, available);
+                encryptBlock(bufBlock, 0, out, outOff);
+                inOff += available;
+                len -= available;
+                resultLen = BLOCK_SIZE;
+                //bufOff = 0;
             }
 
-            while (len >= BLOCK_SIZE)
+            int inLimit = inOff + len - BLOCK_SIZE;
+
+            while (inOff <= inLimit)
             {
-                processBlock(in, inOff, out, outOff + resultLen);
+                encryptBlock(in, inOff, out, outOff + resultLen);
                 inOff += BLOCK_SIZE;
-                len -= BLOCK_SIZE;
                 resultLen += BLOCK_SIZE;
             }
 
-            if (len > 0)
-            {
-                System.arraycopy(in, inOff, bufBlock, 0, len);
-                bufOff = len;
-            }
+            bufOff = BLOCK_SIZE + inLimit - inOff;
+            System.arraycopy(in, inOff, bufBlock, 0, bufOff);
         }
         else
         {
-            for (int i = 0; i < len; ++i)
+            int available = bufBlock.length - bufOff;
+            if (len < available)
             {
-                bufBlock[bufOff] = in[inOff + i];
-                if (++bufOff == bufBlock.length)
+                System.arraycopy(in, inOff, bufBlock, bufOff, len);
+                bufOff += len;
+                return 0;
+            }
+
+            if (bufOff >= BLOCK_SIZE)
+            {
+                decryptBlock(bufBlock, 0, out, outOff);
+                System.arraycopy(bufBlock, BLOCK_SIZE, bufBlock, 0, bufOff -= BLOCK_SIZE);
+                resultLen = BLOCK_SIZE;
+
+                available += BLOCK_SIZE;
+                if (len < available)
                 {
-                    processBlock(bufBlock, 0, out, outOff + resultLen);
-                    System.arraycopy(bufBlock, BLOCK_SIZE, bufBlock, 0, macSize);
-                    bufOff = macSize;
-                    resultLen += BLOCK_SIZE;
+                    System.arraycopy(in, inOff, bufBlock, bufOff, len);
+                    bufOff += len;
+                    return resultLen;
                 }
             }
+
+            int inLimit = inOff + len - bufBlock.length;
+
+            available = BLOCK_SIZE - bufOff;
+            System.arraycopy(in, inOff, bufBlock, bufOff, available);
+            decryptBlock(bufBlock, 0, out, outOff + resultLen);
+            inOff += available;
+            resultLen += BLOCK_SIZE;
+            //bufOff = 0;
+
+            while (inOff <= inLimit)
+            {
+                decryptBlock(in, inOff, out, outOff + resultLen);
+                inOff += BLOCK_SIZE;
+                resultLen += BLOCK_SIZE;
+            }
+
+            bufOff = bufBlock.length + inLimit - inOff;
+            System.arraycopy(in, inOff, bufBlock, 0, bufOff);
         }
 
         return resultLen;
@@ -585,7 +668,7 @@
         }
     }
 
-    private void processBlock(byte[] buf, int bufOff, byte[] out, int outOff)
+    private void decryptBlock(byte[] buf, int bufOff, byte[] out, int outOff)
     {
         if ((out.length - outOff) < BLOCK_SIZE)
         {
@@ -599,18 +682,30 @@
         byte[] ctrBlock = new byte[BLOCK_SIZE];
         getNextCTRBlock(ctrBlock);
 
-        if (forEncryption)
+        gHASHBlock(S, buf, bufOff);
+        GCMUtil.xor(ctrBlock, 0, buf, bufOff, out, outOff);
+
+        totalLength += BLOCK_SIZE;
+    }
+
+    private void encryptBlock(byte[] buf, int bufOff, byte[] out, int outOff)
+    {
+        if ((out.length - outOff) < BLOCK_SIZE)
         {
-            GCMUtil.xor(ctrBlock, buf, bufOff);
-            gHASHBlock(S, ctrBlock);
-            System.arraycopy(ctrBlock, 0, out, outOff, BLOCK_SIZE);
+            throw new OutputLengthException("Output buffer too short");
         }
-        else
+        if (totalLength == 0)
         {
-            gHASHBlock(S, buf, bufOff);
-            GCMUtil.xor(ctrBlock, 0, buf, bufOff, out, outOff);
+            initCipher();
         }
 
+        byte[] ctrBlock = new byte[BLOCK_SIZE];
+
+        getNextCTRBlock(ctrBlock);
+        GCMUtil.xor(ctrBlock, buf, bufOff);
+        gHASHBlock(S, ctrBlock);
+        System.arraycopy(ctrBlock, 0, out, outOff, BLOCK_SIZE);
+
         totalLength += BLOCK_SIZE;
     }
 
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/modes/GCMModeCipher.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/modes/GCMModeCipher.java
new file mode 100644
index 0000000..13b5f18
--- /dev/null
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/modes/GCMModeCipher.java
@@ -0,0 +1,10 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.crypto.modes;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+public interface GCMModeCipher
+    extends AEADBlockCipher
+{
+}
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/modes/GCMSIVBlockCipher.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/modes/GCMSIVBlockCipher.java
new file mode 100644
index 0000000..6faf7a8
--- /dev/null
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/modes/GCMSIVBlockCipher.java
@@ -0,0 +1,973 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.crypto.modes;
+
+import java.io.ByteArrayOutputStream;
+
+import com.android.internal.org.bouncycastle.crypto.BlockCipher;
+import com.android.internal.org.bouncycastle.crypto.CipherParameters;
+import com.android.internal.org.bouncycastle.crypto.DataLengthException;
+import com.android.internal.org.bouncycastle.crypto.InvalidCipherTextException;
+import com.android.internal.org.bouncycastle.crypto.OutputLengthException;
+import com.android.internal.org.bouncycastle.crypto.engines.AESEngine;
+import com.android.internal.org.bouncycastle.crypto.modes.gcm.GCMMultiplier;
+import com.android.internal.org.bouncycastle.crypto.modes.gcm.Tables4kGCMMultiplier;
+import com.android.internal.org.bouncycastle.crypto.params.AEADParameters;
+import com.android.internal.org.bouncycastle.crypto.params.KeyParameter;
+import com.android.internal.org.bouncycastle.crypto.params.ParametersWithIV;
+import com.android.internal.org.bouncycastle.util.Arrays;
+import com.android.internal.org.bouncycastle.util.Bytes;
+import com.android.internal.org.bouncycastle.util.Integers;
+import com.android.internal.org.bouncycastle.util.Longs;
+import com.android.internal.org.bouncycastle.util.Pack;
+
+/**
+ * GCM-SIV Mode.
+ * <p>It should be noted that the specified limit of 2<sup>36</sup> bytes is not supported. This is because all bytes are
+ * cached in a <b>ByteArrayOutputStream</b> object (which has a limit of a little less than 2<sup>31</sup> bytes),
+ * and are output on the <b>doFinal</b>() call (which can only process a maximum of 2<sup>31</sup> bytes).</p>
+ * <p>The practical limit of 2<sup>31</sup> - 24 bytes is policed, and attempts to breach the limit will be rejected</p>
+ * <p>In order to properly support the higher limit, an extended form of <b>ByteArrayOutputStream</b> would be needed
+ * which would use multiple arrays to store the data. In addition, a new <b>doOutput</b> method would be required (similar
+ * to that in <b>XOF</b> digests), which would allow the data to be output over multiple calls. Alternatively an extended
+ * form of <b>ByteArrayInputStream</b> could be used to deliver the data.</p>
+ * @hide This class is not part of the Android public SDK API
+ */
+public class GCMSIVBlockCipher
+         implements AEADBlockCipher
+{
+     /**
+      * The buffer length.
+      */
+     private static final int BUFLEN = 16;
+
+     /**
+      * The halfBuffer length.
+      */
+     private static final int HALFBUFLEN = BUFLEN >> 1;
+
+     /**
+      * The nonce length.
+      */
+     private static final int NONCELEN = 12;
+
+     /**
+      * The maximum data length (AEAD/PlainText). Due to implementation constraints this is restricted to the maximum
+      * array length (https://programming.guide/java/array-maximum-length.html) minus the BUFLEN to allow for the MAC
+      */
+     private static final int MAX_DATALEN = Integer.MAX_VALUE - 8 - BUFLEN;
+
+     /**
+      * The top bit mask.
+      */
+     private static final byte MASK = (byte) 0x80;
+
+     /**
+      * The addition constant.
+      */
+     private static final byte ADD = (byte) 0xE1;
+
+     /**
+      * The initialisation flag.
+      */
+     private static final int INIT = 1;
+
+     /**
+      * The aeadComplete flag.
+      */
+     private static final int AEAD_COMPLETE = 2;
+
+     /**
+      * The cipher.
+      */
+     private final BlockCipher theCipher;
+
+     /**
+      * The multiplier.
+      */
+     private final GCMMultiplier theMultiplier;
+
+     /**
+      * The gHash buffer.
+      */
+     private final byte[] theGHash = new byte[BUFLEN];
+
+     /**
+      * The reverse buffer.
+      */
+     private final byte[] theReverse = new byte[BUFLEN];
+
+     /**
+      * The aeadHasher.
+      */
+     private final GCMSIVHasher theAEADHasher;
+
+     /**
+      * The dataHasher.
+      */
+     private final GCMSIVHasher theDataHasher;
+
+     /**
+      * The plainDataStream.
+      */
+     private GCMSIVCache thePlain;
+
+     /**
+      * The encryptedDataStream (decryption only).
+      */
+     private GCMSIVCache theEncData;
+
+     /**
+      * Are we encrypting?
+      */
+     private boolean forEncryption;
+
+     /**
+      * The initialAEAD.
+      */
+     private byte[] theInitialAEAD;
+
+     /**
+      * The nonce.
+      */
+     private byte[] theNonce;
+
+     /**
+      * The flags.
+      */
+     private int theFlags;
+
+     // defined fixed
+     private byte[]      macBlock = new byte[16];
+
+     /**
+      * Constructor.
+      */
+     public GCMSIVBlockCipher()
+     {
+         this(AESEngine.newInstance());
+     }
+
+     /**
+      * Constructor.
+      * @param pCipher the underlying cipher
+      */
+     public GCMSIVBlockCipher(final BlockCipher pCipher)
+     {
+         this(pCipher, new Tables4kGCMMultiplier());
+     }
+
+     /**
+      * Constructor.
+      * @param pCipher the underlying cipher
+      * @param pMultiplier the multiplier
+      */
+     public GCMSIVBlockCipher(final BlockCipher pCipher,
+                              final GCMMultiplier pMultiplier)
+     {
+         /* Ensure that the cipher is the correct size */
+         if (pCipher.getBlockSize() != BUFLEN)
+         {
+             throw new IllegalArgumentException("Cipher required with a block size of " + BUFLEN + ".");
+         }
+
+         /* Store parameters */
+         theCipher = pCipher;
+         theMultiplier = pMultiplier;
+
+         /* Create the hashers */
+         theAEADHasher = new GCMSIVHasher();
+         theDataHasher = new GCMSIVHasher();
+     }
+
+     public BlockCipher getUnderlyingCipher()
+     {
+         return theCipher;
+     }
+
+     public void init(final boolean pEncrypt,
+                      final CipherParameters cipherParameters) throws IllegalArgumentException
+     {
+         /* Set defaults */
+         byte[] myInitialAEAD = null;
+         byte[] myNonce = null;
+         KeyParameter myKey = null;
+
+         /* Access parameters */
+         if (cipherParameters instanceof AEADParameters)
+         {
+             final AEADParameters myAEAD = (AEADParameters) cipherParameters;
+             myInitialAEAD = myAEAD.getAssociatedText();
+             myNonce = myAEAD.getNonce();
+             myKey = myAEAD.getKey();
+         }
+         else if (cipherParameters instanceof ParametersWithIV)
+         {
+             final ParametersWithIV myParms = (ParametersWithIV) cipherParameters;
+             myNonce = myParms.getIV();
+             myKey = (KeyParameter) myParms.getParameters();
+         }
+         else
+         {
+             throw new IllegalArgumentException("invalid parameters passed to GCM-SIV");
+         }
+
+         /* Check nonceSize */
+         if (myNonce == null || myNonce.length != NONCELEN)
+         {
+             throw new IllegalArgumentException("Invalid nonce");
+         }
+
+         /* Check keysize */
+         if (myKey == null
+             || (myKey.getKeyLength() != BUFLEN
+                 && myKey.getKeyLength() != (BUFLEN << 1)))
+         {
+             throw new IllegalArgumentException("Invalid key");
+         }
+
+         /* Reset details */
+         forEncryption = pEncrypt;
+         theInitialAEAD = myInitialAEAD;
+         theNonce = myNonce;
+
+         /* Initialise the keys */
+         deriveKeys(myKey);
+         resetStreams();
+     }
+
+     public String getAlgorithmName()
+     {
+         return theCipher.getAlgorithmName() + "-GCM-SIV";
+     }
+
+     /**
+      * check AEAD status.
+      * @param pLen the aeadLength
+      */
+     private void checkAEADStatus(final int pLen)
+     {
+         /* Check we are initialised */
+         if ((theFlags & INIT) == 0)
+         {
+             throw new IllegalStateException("Cipher is not initialised");
+         }
+
+         /* Check AAD is allowed */
+         if ((theFlags & AEAD_COMPLETE) != 0)
+         {
+             throw new IllegalStateException("AEAD data cannot be processed after ordinary data");
+         }
+
+         /* Make sure that we haven't breached AEAD data limit */
+         if (theAEADHasher.getBytesProcessed() + Long.MIN_VALUE
+              > (MAX_DATALEN - pLen) + Long.MIN_VALUE)
+         {
+             throw new IllegalStateException("AEAD byte count exceeded");
+         }
+     }
+
+     /**
+      * check status.
+      * @param pLen the dataLength
+      */
+     private void checkStatus(final int pLen)
+     {
+         /* Check we are initialised */
+         if ((theFlags & INIT) == 0)
+         {
+             throw new IllegalStateException("Cipher is not initialised");
+         }
+
+         /* Complete the AEAD section if this is the first data */
+         if ((theFlags & AEAD_COMPLETE) == 0)
+         {
+             theAEADHasher.completeHash();
+             theFlags |= AEAD_COMPLETE;
+         }
+
+         /* Make sure that we haven't breached data limit */
+         long dataLimit = MAX_DATALEN;
+         long currBytes = thePlain.size();
+         if (!forEncryption)
+         {
+             dataLimit += BUFLEN;
+             currBytes = theEncData.size();
+         }
+         if (currBytes + Long.MIN_VALUE
+               > (dataLimit - pLen) + Long.MIN_VALUE)
+         {
+             throw new IllegalStateException("byte count exceeded");
+         }
+     }
+
+     public void processAADByte(final byte pByte)
+     {
+         /* Check that we can supply AEAD */
+         checkAEADStatus(1);
+
+         /* Process the aead */
+         theAEADHasher.updateHash(pByte);
+     }
+
+     public void processAADBytes(final byte[] pData,
+                                 final int pOffset,
+                                 final int pLen)
+     {
+         /* Check that we can supply AEAD */
+         checkAEADStatus(pLen);
+
+         /* Check input buffer */
+         checkBuffer(pData, pOffset, pLen, false);
+
+         /* Process the aead */
+         theAEADHasher.updateHash(pData, pOffset, pLen);
+     }
+
+     public int processByte(final byte pByte,
+                            final byte[] pOutput,
+                            final int pOutOffset) throws DataLengthException
+     {
+         /* Check that we have initialised */
+         checkStatus(1);
+
+         /* Store the data */
+         if (forEncryption)
+         {
+             thePlain.write(pByte);
+             theDataHasher.updateHash(pByte);
+         }
+         else
+         {
+             theEncData.write(pByte);
+         }
+
+         /* No data returned */
+         return 0;
+     }
+
+     public int processBytes(final byte[] pData,
+                             final int pOffset,
+                             final int pLen,
+                             final byte[] pOutput,
+                             final int pOutOffset) throws DataLengthException
+     {
+         /* Check that we have initialised */
+         checkStatus(pLen);
+
+         /* Check input buffer */
+         checkBuffer(pData, pOffset, pLen, false);
+
+         /* Store the data */
+         if (forEncryption)
+         {
+             thePlain.write(pData, pOffset, pLen);
+             theDataHasher.updateHash(pData, pOffset, pLen);
+         }
+         else
+         {
+             theEncData.write(pData, pOffset, pLen);
+         }
+
+         /* No data returned */
+         return 0;
+     }
+
+     public int doFinal(final byte[] pOutput,
+                        final int pOffset) throws IllegalStateException, InvalidCipherTextException
+     {
+         /* Check that we have initialised */
+         checkStatus(0);
+
+         /* Check output buffer */
+         checkBuffer(pOutput, pOffset, getOutputSize(0), true);
+
+         /* If we are encrypting */
+         if (forEncryption)
+         {
+             /* Derive the tag */
+             final byte[] myTag = calculateTag();
+
+             /* encrypt the plain text */
+             final int myDataLen = BUFLEN + encryptPlain(myTag, pOutput, pOffset);
+
+             /* Add the tag to the output */
+             System.arraycopy(myTag, 0, pOutput, pOffset + thePlain.size(), BUFLEN);
+
+             System.arraycopy(myTag, 0, macBlock, 0, macBlock.length);
+
+             /* Reset the streams */
+             resetStreams();
+             return myDataLen;
+
+             /* else we are decrypting */
+         }
+         else
+         {
+             /* decrypt to plain text */
+             decryptPlain();
+
+             /* Release plain text */
+             final int myDataLen = thePlain.size();
+             final byte[] mySrc = thePlain.getBuffer();
+             System.arraycopy(mySrc, 0, pOutput, pOffset, myDataLen);
+
+             /* Reset the streams */
+             resetStreams();
+             return myDataLen;
+         }
+     }
+
+     public byte[] getMac()
+     {
+         return Arrays.clone(macBlock);
+     }
+
+     public int getUpdateOutputSize(final int pLen)
+     {
+         return 0;
+     }
+
+     public int getOutputSize(final int pLen)
+     {
+         if (forEncryption)
+         {
+             return pLen + thePlain.size() + BUFLEN;
+         }
+         final int myCurr = pLen + theEncData.size();
+         return myCurr > BUFLEN ? myCurr - BUFLEN : 0;
+     }
+
+     public void reset()
+     {
+         resetStreams();
+     }
+
+     /**
+      * Reset Streams.
+      */
+     private void resetStreams()
+     {
+         /* Clear the plainText buffer */
+         if (thePlain != null)
+         {
+             thePlain.clearBuffer();
+         }
+
+         /* Reset hashers */
+         theAEADHasher.reset();
+         theDataHasher.reset();
+
+         /* Recreate streams (to release memory) */
+         thePlain = new GCMSIVCache();
+         theEncData = forEncryption ? null : new GCMSIVCache();
+
+         /* Initialise AEAD if required */
+         theFlags &= ~AEAD_COMPLETE;
+         Arrays.fill(theGHash, (byte) 0);
+         if (theInitialAEAD != null)
+         {
+             theAEADHasher.updateHash(theInitialAEAD, 0, theInitialAEAD.length);
+         }
+      }
+
+     /**
+      * Obtain buffer length (allowing for null).
+      * @param pBuffer the buffere
+      * @return the length
+      */
+     private static int bufLength(final byte[] pBuffer)
+     {
+         return pBuffer == null ? 0 : pBuffer.length;
+     }
+
+     /**
+      * Check buffer.
+      * @param pBuffer the buffer
+      * @param pOffset the offset
+      * @param pLen the length
+      * @param pOutput is this an output buffer?
+      */
+     private static void checkBuffer(final byte[] pBuffer,
+                                     final int pOffset,
+                                     final int pLen,
+                                     final boolean pOutput)
+     {
+         /* Access lengths */
+         final int myBufLen = bufLength(pBuffer);
+         final int myLast = pOffset + pLen;
+
+         /* Check for negative values and buffer overflow */
+         final boolean badLen = pLen < 0 || pOffset < 0 || myLast < 0;
+         if (badLen || myLast > myBufLen)
+         {
+             throw pOutput
+                     ? new OutputLengthException("Output buffer too short.")
+                     : new DataLengthException("Input buffer too short.");
+         }
+     }
+
+     /**
+      * encrypt data stream.
+      * @param pCounter the counter
+      * @param pTarget the target buffer
+      * @param pOffset the target offset
+      * @return the length of data encrypted
+      */
+     private int encryptPlain(final byte[] pCounter,
+                              final byte[] pTarget,
+                              final int pOffset)
+     {
+         /* Access buffer and length */
+         final byte[] mySrc = thePlain.getBuffer();
+         final byte[] myCounter = Arrays.clone(pCounter);
+         myCounter[BUFLEN - 1] |= MASK;
+         final byte[] myMask = new byte[BUFLEN];
+         int myRemaining = thePlain.size();
+         int myOff = 0;
+
+         /* While we have data to process */
+         while (myRemaining > 0)
+         {
+             /* Generate the next mask */
+             theCipher.processBlock(myCounter, 0, myMask, 0);
+
+             /* Xor data into mask */
+             final int myLen = Math.min(BUFLEN, myRemaining);
+             xorBlock(myMask, mySrc, myOff, myLen);
+
+             /* Copy encrypted data to output */
+             System.arraycopy(myMask, 0, pTarget, pOffset + myOff, myLen);
+
+             /* Adjust counters */
+             myRemaining -= myLen;
+             myOff += myLen;
+             incrementCounter(myCounter);
+         }
+
+         /* Return the amount of data processed */
+         return thePlain.size();
+     }
+
+     /**
+      * decrypt data stream.
+      * @throws InvalidCipherTextException on data too short or mac check failed
+      */
+     private void decryptPlain() throws InvalidCipherTextException
+     {
+         /* Access buffer and length */
+         final byte[] mySrc = theEncData.getBuffer();
+         int myRemaining = theEncData.size() - BUFLEN;
+
+         /* Check for insufficient data */
+         if (myRemaining < 0)
+         {
+             throw new InvalidCipherTextException("Data too short");
+         }
+
+         /* Access counter */
+         final byte[] myExpected = Arrays.copyOfRange(mySrc, myRemaining, myRemaining + BUFLEN);
+         final byte[] myCounter = Arrays.clone(myExpected);
+         myCounter[BUFLEN - 1] |= MASK;
+         final byte[] myMask = new byte[BUFLEN];
+         int myOff = 0;
+
+         /* While we have data to process */
+         while (myRemaining > 0)
+         {
+             /* Generate the next mask */
+             theCipher.processBlock(myCounter, 0, myMask, 0);
+
+             /* Xor data into mask */
+             final int myLen = Math.min(BUFLEN, myRemaining);
+             xorBlock(myMask, mySrc, myOff, myLen);
+
+             /* Write data to plain dataStream */
+             thePlain.write(myMask, 0, myLen);
+             theDataHasher.updateHash(myMask, 0, myLen);
+
+             /* Adjust counters */
+             myRemaining -= myLen;
+             myOff += myLen;
+             incrementCounter(myCounter);
+         }
+
+         /* Derive and check the tag */
+         final byte[] myTag = calculateTag();
+         if (!Arrays.constantTimeAreEqual(myTag, myExpected))
+         {
+             reset();
+             throw new InvalidCipherTextException("mac check failed");
+         }
+
+         System.arraycopy(myTag, 0, macBlock, 0, macBlock.length);
+     }
+
+     /**
+      * calculate tag.
+      * @return the calculated tag
+      */
+     private byte[] calculateTag()
+     {
+         /* Complete the hash */
+         theDataHasher.completeHash();
+         final byte[] myPolyVal = completePolyVal();
+
+         /* calculate polyVal */
+         final byte[] myResult = new byte[BUFLEN];
+
+         /* Fold in the nonce */
+         for (int i = 0; i < NONCELEN; i++)
+         {
+             myPolyVal[i] ^= theNonce[i];
+         }
+
+         /* Clear top bit */
+         myPolyVal[BUFLEN - 1] &= (MASK - 1);
+
+         /* Calculate tag and return it */
+         theCipher.processBlock(myPolyVal, 0, myResult, 0);
+         return myResult;
+     }
+
+     /**
+      * complete polyVAL.
+      * @return the calculated value
+      */
+     private byte[] completePolyVal()
+     {
+         /* Build the polyVal result */
+         final byte[] myResult = new byte[BUFLEN];
+         gHashLengths();
+         fillReverse(theGHash, 0, BUFLEN, myResult);
+         return myResult;
+     }
+
+     /**
+      * process lengths.
+      */
+     private void gHashLengths()
+     {
+         /* Create reversed bigEndian buffer to keep it simple */
+         final byte[] myIn = new byte[BUFLEN];
+         Pack.longToBigEndian(Bytes.SIZE * theDataHasher.getBytesProcessed(), myIn, 0);
+         Pack.longToBigEndian(Bytes.SIZE * theAEADHasher.getBytesProcessed(), myIn, Longs.BYTES);
+
+         /* hash value */
+         gHASH(myIn);
+     }
+
+     /**
+      * perform the next GHASH step.
+      * @param pNext the next value
+      */
+     private void gHASH(final byte[] pNext)
+     {
+         xorBlock(theGHash, pNext);
+         theMultiplier.multiplyH(theGHash);
+     }
+
+     /**
+      * Byte reverse a buffer.
+      * @param pInput the input buffer
+      * @param pOffset the offset
+      * @param pLength the length of data (<= BUFLEN)
+      * @param pOutput the output buffer
+      */
+     private static void fillReverse(final byte[] pInput,
+                                     final int pOffset,
+                                     final int pLength,
+                                     final byte[] pOutput)
+     {
+         /* Loop through the buffer */
+         for (int i = 0, j = BUFLEN - 1; i < pLength; i++, j--)
+         {
+             /* Copy byte */
+             pOutput[j] = pInput[pOffset + i];
+         }
+     }
+
+     /**
+      * xor a full block buffer.
+      * @param pLeft the left operand and result
+      * @param pRight the right operand
+      */
+     private static void xorBlock(final byte[] pLeft,
+                                  final byte[] pRight)
+     {
+         /* Loop through the bytes */
+         for (int i = 0; i < BUFLEN; i++)
+         {
+             pLeft[i] ^= pRight[i];
+         }
+     }
+
+     /**
+      * xor a partial block buffer.
+      * @param pLeft the left operand and result
+      * @param pRight the right operand
+      * @param pOffset the offset in the right operand
+      * @param pLength the length of data in the right operand
+      */
+     private static void xorBlock(final byte[] pLeft,
+                                  final byte[] pRight,
+                                  final int pOffset,
+                                  final int pLength)
+                                  {
+         /* Loop through the bytes */
+         for (int i = 0; i < pLength; i++)
+         {
+             pLeft[i] ^= pRight[i + pOffset];
+         }
+     }
+
+     /**
+      * increment the counter.
+      * @param pCounter the counter to increment
+      */
+     private static void incrementCounter(final byte[] pCounter)
+     {
+         /* Loop through the bytes incrementing counter */
+         for (int i = 0; i < Integers.BYTES; i++)
+         {
+             if (++pCounter[i] != 0)
+             {
+                 break;
+             }
+         }
+     }
+
+     /**
+      * multiply by X.
+      * @param pValue the value to adjust
+      */
+     private static void mulX(final byte[] pValue)
+     {
+         /* Loop through the bytes */
+         byte myMask = (byte) 0;
+         for (int i = 0; i < BUFLEN; i++)
+         {
+             final byte myValue = pValue[i];
+             pValue[i] = (byte) (((myValue >> 1) & ~MASK) | myMask);
+             myMask = (myValue & 1) == 0 ? 0 : MASK;
+         }
+
+         /* Xor in addition if last bit was set */
+         if (myMask != 0)
+         {
+             pValue[0] ^= ADD;
+         }
+     }
+
+     /**
+      * Derive Keys.
+      * @param pKey the keyGeneration key
+      */
+     private void deriveKeys(final KeyParameter pKey)
+     {
+         /* Create the buffers */
+         final byte[] myIn = new byte[BUFLEN];
+         final byte[] myOut = new byte[BUFLEN];
+         final byte[] myResult = new byte[BUFLEN];
+         final byte[] myEncKey = new byte[pKey.getKeyLength()];
+
+         /* Prepare for encryption */
+         System.arraycopy(theNonce, 0, myIn, BUFLEN - NONCELEN, NONCELEN);
+         theCipher.init(true, pKey);
+
+         /* Derive authentication key */
+         int myOff = 0;
+         theCipher.processBlock(myIn, 0, myOut, 0);
+         System.arraycopy(myOut, 0, myResult, myOff, HALFBUFLEN);
+         myIn[0]++;
+         myOff += HALFBUFLEN;
+         theCipher.processBlock(myIn, 0, myOut, 0);
+         System.arraycopy(myOut, 0, myResult, myOff, HALFBUFLEN);
+
+         /* Derive encryption key */
+         myIn[0]++;
+         myOff = 0;
+         theCipher.processBlock(myIn, 0, myOut, 0);
+         System.arraycopy(myOut, 0, myEncKey, myOff, HALFBUFLEN);
+         myIn[0]++;
+         myOff += HALFBUFLEN;
+         theCipher.processBlock(myIn, 0, myOut, 0);
+         System.arraycopy(myOut, 0, myEncKey, myOff, HALFBUFLEN);
+
+         /* If we have a 32byte key */
+         if (myEncKey.length == BUFLEN << 1)
+         {
+             /* Derive remainder of encryption key */
+             myIn[0]++;
+             myOff += HALFBUFLEN;
+             theCipher.processBlock(myIn, 0, myOut, 0);
+             System.arraycopy(myOut, 0, myEncKey, myOff, HALFBUFLEN);
+             myIn[0]++;
+             myOff += HALFBUFLEN;
+             theCipher.processBlock(myIn, 0, myOut, 0);
+             System.arraycopy(myOut, 0, myEncKey, myOff, HALFBUFLEN);
+         }
+
+         /* Initialise the Cipher */
+         theCipher.init(true, new KeyParameter(myEncKey));
+
+         /* Initialise the multiplier */
+         fillReverse(myResult, 0, BUFLEN, myOut);
+         mulX(myOut);
+         theMultiplier.init(myOut);
+         theFlags |= INIT;
+     }
+
+     /**
+      * GCMSIVCache.
+      */
+     private static class GCMSIVCache
+             extends ByteArrayOutputStream
+     {
+         /**
+          * Constructor.
+          */
+         GCMSIVCache()
+         {
+         }
+
+         /**
+          * Obtain the buffer.
+          * @return the buffer
+          */
+         byte[] getBuffer()
+         {
+             return this.buf;
+         }
+
+         /**
+          * Clear the buffer.
+          */
+         void clearBuffer()
+         {
+             Arrays.fill(getBuffer(), (byte) 0);
+         }
+     }
+
+     /**
+      * Hash Control.
+      */
+     private class GCMSIVHasher
+     {
+         /**
+          * Cache.
+          */
+         private final byte[] theBuffer = new byte[BUFLEN];
+
+         /**
+          * Single byte cache.
+          */
+         private final byte[] theByte = new byte[1];
+
+         /**
+          * Count of active bytes in cache.
+          */
+         private int numActive;
+
+         /**
+          * Count of hashed bytes.
+          */
+         private long numHashed;
+
+         /**
+          * Obtain the count of bytes hashed.
+          * @return the count
+          */
+         long getBytesProcessed()
+         {
+             return numHashed;
+         }
+
+         /**
+          * Reset the hasher.
+          */
+         void reset()
+         {
+             numActive = 0;
+             numHashed = 0;
+         }
+
+         /**
+          * update hash.
+          * @param pByte the byte
+          */
+         void updateHash(final byte pByte)
+         {
+             theByte[0] = pByte;
+             updateHash(theByte, 0, 1);
+         }
+
+         /**
+          * update hash.
+          * @param pBuffer the buffer
+          * @param pOffset the offset within the buffer
+          * @param pLen the length of data
+          */
+         void updateHash(final byte[] pBuffer,
+                         final int pOffset,
+                         final int pLen)
+         {
+             /* If we should process the cache */
+             final int mySpace = BUFLEN - numActive;
+             int numProcessed = 0;
+             int myRemaining = pLen;
+             if (numActive > 0
+                     && pLen >= mySpace)
+             {
+                 /* Copy data into the cache and hash it */
+                 System.arraycopy(pBuffer, pOffset, theBuffer, numActive, mySpace);
+                 fillReverse(theBuffer, 0, BUFLEN, theReverse);
+                 gHASH(theReverse);
+
+                 /* Adjust counters */
+                 numProcessed += mySpace;
+                 myRemaining -= mySpace;
+                 numActive = 0;
+             }
+
+             /* While we have full blocks */
+             while (myRemaining >= BUFLEN)
+             {
+                 /* Access the next data */
+                 fillReverse(pBuffer, pOffset + numProcessed, BUFLEN, theReverse);
+                 gHASH(theReverse);
+
+                 /* Adjust counters */
+                 numProcessed += BUFLEN;
+                 myRemaining -= BUFLEN;
+             }
+
+             /* If we have remaining data */
+             if (myRemaining > 0)
+             {
+                 /* Copy data into the cache */
+                 System.arraycopy(pBuffer, pOffset + numProcessed, theBuffer, numActive, myRemaining);
+                 numActive += myRemaining;
+             }
+
+             /* Adjust the number of bytes processed */
+             numHashed += pLen;
+         }
+
+         /**
+          * complete hash.
+          */
+         void completeHash()
+         {
+             /* If we have remaining data */
+             if (numActive > 0)
+             {
+                 /* Access the next data */
+                 Arrays.fill(theReverse, (byte) 0);
+                 fillReverse(theBuffer, 0, numActive, theReverse);
+
+                 /* hash value */
+                 gHASH(theReverse);
+             }
+         }
+     }
+ }
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/modes/SICBlockCipher.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/modes/SICBlockCipher.java
index b4121be..cd7031c 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/modes/SICBlockCipher.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/modes/SICBlockCipher.java
@@ -4,7 +4,7 @@
 import com.android.internal.org.bouncycastle.crypto.BlockCipher;
 import com.android.internal.org.bouncycastle.crypto.CipherParameters;
 import com.android.internal.org.bouncycastle.crypto.DataLengthException;
-import com.android.internal.org.bouncycastle.crypto.SkippingStreamCipher;
+import com.android.internal.org.bouncycastle.crypto.OutputLengthException;
 import com.android.internal.org.bouncycastle.crypto.StreamBlockCipher;
 import com.android.internal.org.bouncycastle.crypto.params.ParametersWithIV;
 import com.android.internal.org.bouncycastle.util.Arrays;
@@ -17,7 +17,7 @@
  */
 public class SICBlockCipher
     extends StreamBlockCipher
-    implements SkippingStreamCipher
+    implements CTRModeCipher
 {
     private final BlockCipher     cipher;
     private final int             blockSize;
@@ -28,9 +28,20 @@
     private int             byteCount;
 
     /**
+     * Return a new SIC/CTR mode cipher based on the passed in base cipher
+     *
+     * @param cipher the base cipher for the SIC/CTR mode.
+     */
+    public static CTRModeCipher newInstance(BlockCipher cipher)
+    {
+        return new SICBlockCipher(cipher);
+    }
+
+    /**
      * Basic constructor.
      *
      * @param c the block cipher to be used.
+     * @deprecated use newInstance() method.
      */
     public SICBlockCipher(BlockCipher c)
     {
@@ -93,16 +104,75 @@
     public int processBlock(byte[] in, int inOff, byte[] out, int outOff)
           throws DataLengthException, IllegalStateException
     {
-        processBytes(in, inOff, blockSize, out, outOff);
+        if (byteCount != 0)
+        {
+            processBytes(in, inOff, blockSize, out, outOff);
+            return blockSize;
+        }
 
+        if (inOff + blockSize > in.length)
+        {
+            throw new DataLengthException("input buffer too small");
+        }
+        if (outOff + blockSize > out.length)
+        {
+            throw new OutputLengthException("output buffer too short");
+        }
+
+        cipher.processBlock(counter, 0, counterOut, 0);
+        for (int i = 0; i < blockSize; ++i)
+        {
+            out[outOff + i] = (byte)(in[inOff + i] ^ counterOut[i]);
+        }
+        incrementCounter();
         return blockSize;
     }
 
+    public int processBytes(byte[] in, int inOff, int len, byte[] out, int outOff)
+        throws DataLengthException
+    {
+        if (inOff + len > in.length)
+        {
+            throw new DataLengthException("input buffer too small");
+        }
+        if (outOff + len > out.length)
+        {
+            throw new OutputLengthException("output buffer too short");
+        }
+
+        for (int i = 0; i < len; ++i)
+        {
+            byte next;
+
+            if (byteCount == 0)
+            {
+                checkLastIncrement();
+
+                cipher.processBlock(counter, 0, counterOut, 0);
+                next = (byte)(in[inOff + i] ^ counterOut[byteCount++]);
+            }
+            else
+            {
+                next = (byte)(in[inOff + i] ^ counterOut[byteCount++]);
+                if (byteCount == counter.length)
+                {
+                    byteCount = 0;
+                    incrementCounter();
+                }
+            }
+            out[outOff + i] = next;
+        }
+
+        return len;
+    }
+
     protected byte calculateByte(byte in)
           throws DataLengthException, IllegalStateException
     {
         if (byteCount == 0)
         {
+            checkLastIncrement();
+
             cipher.processBlock(counter, 0, counterOut, 0);
 
             return (byte)(counterOut[byteCount++] ^ in);
@@ -113,10 +183,7 @@
         if (byteCount == counter.length)
         {
             byteCount = 0;
-
-            incrementCounterAt(0);
-
-            checkCounter();
+            incrementCounter();
         }
 
         return rv;
@@ -127,7 +194,7 @@
         // if the IV is the same as the blocksize we assume the user knows what they are doing
         if (IV.length < blockSize)
         {
-            for (int i = 0; i != IV.length; i++)
+            for (int i = IV.length - 1; i >= 0; i--)
             {
                 if (counter[i] != IV[i])
                 {
@@ -137,6 +204,30 @@
         }
     }
 
+    private void checkLastIncrement()
+    {
+        // if the IV is the same as the blocksize we assume the user knows what they are doing
+        if (IV.length < blockSize)
+        {
+            if (counter[IV.length - 1] != IV[IV.length - 1])
+            {
+                throw new IllegalStateException("Counter in CTR/SIC mode out of range.");
+            }
+        }
+    }
+
+    private void incrementCounter()
+    {
+        int i = counter.length;
+        while (--i >= 0)
+        {
+            if (++counter[i] != 0)
+            {
+                break;
+            }
+        }
+    }
+
     private void incrementCounterAt(int pos)
     {
         int i = counter.length - pos;
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/modes/gcm/BasicGCMExponentiator.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/modes/gcm/BasicGCMExponentiator.java
index 2d317ff..fe86002 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/modes/gcm/BasicGCMExponentiator.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/modes/gcm/BasicGCMExponentiator.java
@@ -1,8 +1,6 @@
 /* GENERATED SOURCE. DO NOT MODIFY. */
 package com.android.internal.org.bouncycastle.crypto.modes.gcm;
 
-import com.android.internal.org.bouncycastle.util.Arrays;
-
 /**
  * @hide This class is not part of the Android public SDK API
  */
@@ -23,7 +21,9 @@
 
         if (pow > 0)
         {
-            long[] powX = Arrays.clone(x);
+            long[] powX = new long[GCMUtil.SIZE_LONGS];
+            GCMUtil.copy(x, powX);
+
             do
             {
                 if ((pow & 1L) != 0)
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/modes/gcm/GCMUtil.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/modes/gcm/GCMUtil.java
index 0797bca..1dc1d48 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/modes/gcm/GCMUtil.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/modes/gcm/GCMUtil.java
@@ -10,76 +10,119 @@
  */
 public abstract class GCMUtil
 {
+    public static final int SIZE_BYTES = 16;
+    public static final int SIZE_INTS = 4;
+    public static final int SIZE_LONGS = 2;
+
     private static final int E1 = 0xe1000000;
     private static final long E1L = (E1 & 0xFFFFFFFFL) << 32;
 
     public static byte[] oneAsBytes()
     {
-        byte[] tmp = new byte[16];
+        byte[] tmp = new byte[SIZE_BYTES];
         tmp[0] = (byte)0x80;
         return tmp;
     }
 
     public static int[] oneAsInts()
     {
-        int[] tmp = new int[4];
+        int[] tmp = new int[SIZE_INTS];
         tmp[0] = 1 << 31;
         return tmp;
     }
 
     public static long[] oneAsLongs()
     {
-        long[] tmp = new long[2];
+        long[] tmp = new long[SIZE_LONGS];
         tmp[0] = 1L << 63;
         return tmp;
     }
 
+    public static byte areEqual(byte[] x, byte[] y)
+    {
+        int d = 0;
+        for (int i = 0; i < SIZE_BYTES; ++i)
+        {
+            d |= x[i] ^ y[i];
+        }
+        d = (d >>> 1) | (d & 1);
+        return (byte)((d - 1) >> 31);
+    }
+
+    public static int areEqual(int[] x, int[] y)
+    {
+        int d = 0;
+        d |= x[0] ^ y[0];
+        d |= x[1] ^ y[1];
+        d |= x[2] ^ y[2];
+        d |= x[3] ^ y[3];
+        d = (d >>> 1) | (d & 1);
+        return (d - 1) >> 31;
+    }
+
+    public static long areEqual(long[] x, long[] y)
+    {
+        long d = 0L;
+        d |= x[0] ^ y[0];
+        d |= x[1] ^ y[1];
+        d = (d >>> 1) | (d & 1L);
+        return (d - 1L) >> 63;
+    }
+
     public static byte[] asBytes(int[] x)
     {
-        byte[] z = new byte[16];
-        Pack.intToBigEndian(x, z, 0);
+        byte[] z = new byte[SIZE_BYTES];
+        Pack.intToBigEndian(x, 0, SIZE_INTS, z, 0);
         return z;
     }
 
     public static void asBytes(int[] x, byte[] z)
     {
-        Pack.intToBigEndian(x, z, 0);
+        Pack.intToBigEndian(x, 0, SIZE_INTS, z, 0);
     }
 
     public static byte[] asBytes(long[] x)
     {
-        byte[] z = new byte[16];
-        Pack.longToBigEndian(x, z, 0);
+        byte[] z = new byte[SIZE_BYTES];
+        Pack.longToBigEndian(x, 0, SIZE_LONGS, z, 0);
         return z;
     }
 
     public static void asBytes(long[] x, byte[] z)
     {
-        Pack.longToBigEndian(x, z, 0);
+        Pack.longToBigEndian(x, 0, SIZE_LONGS, z, 0);
     }
 
     public static int[] asInts(byte[] x)
     {
-        int[] z = new int[4];
-        Pack.bigEndianToInt(x, 0, z);
+        int[] z = new int[SIZE_INTS];
+        Pack.bigEndianToInt(x, 0, z, 0, SIZE_INTS);
         return z;
     }
 
     public static void asInts(byte[] x, int[] z)
     {
-        Pack.bigEndianToInt(x, 0, z);
+        Pack.bigEndianToInt(x, 0, z, 0, SIZE_INTS);
     }
 
     public static long[] asLongs(byte[] x)
     {
-        long[] z = new long[2];
-        Pack.bigEndianToLong(x, 0, z);
+        long[] z = new long[SIZE_LONGS];
+        Pack.bigEndianToLong(x, 0, z, 0, SIZE_LONGS);
         return z;
     }
 
     public static void asLongs(byte[] x, long[] z)
     {
-        Pack.bigEndianToLong(x, 0, z);
+        Pack.bigEndianToLong(x, 0, z, 0, SIZE_LONGS);
+    }
+
+    public static void copy(byte[] x, byte[] z)
+    {
+        for (int i = 0; i < SIZE_BYTES; ++i)
+        {
+            z[i] = x[i];
+        }
     }
 
     public static void copy(int[] x, int[] z)
@@ -107,10 +150,48 @@
 
     public static void multiply(byte[] x, byte[] y)
     {
-        long[] t1 = GCMUtil.asLongs(x);
-        long[] t2 = GCMUtil.asLongs(y);
-        GCMUtil.multiply(t1, t2);
-        GCMUtil.asBytes(t1, x);
+        long[] t1 = asLongs(x);
+        long[] t2 = asLongs(y);
+        multiply(t1, t2);
+        asBytes(t1, x);
+    }
+
+    static void multiply(byte[] x, long[] y)
+    {
+        /*
+         * "Three-way recursion" as described in "Batch binary Edwards", Daniel J. Bernstein.
+         *
+         * Without access to the high part of a 64x64 product x * y, we use a bit reversal to calculate it:
+         *     rev(x) * rev(y) == rev((x * y) << 1) 
+         */
+
+        long x0 = Pack.bigEndianToLong(x, 0);
+        long x1 = Pack.bigEndianToLong(x, 8);
+        long y0 = y[0], y1 = y[1];
+        long x0r = Longs.reverse(x0), x1r = Longs.reverse(x1);
+        long y0r = Longs.reverse(y0), y1r = Longs.reverse(y1);
+
+        long h0  = Longs.reverse(implMul64(x0r, y0r));
+        long h1  = implMul64(x0, y0) << 1;
+        long h2  = Longs.reverse(implMul64(x1r, y1r));
+        long h3  = implMul64(x1, y1) << 1;
+        long h4  = Longs.reverse(implMul64(x0r ^ x1r, y0r ^ y1r));
+        long h5  = implMul64(x0 ^ x1, y0 ^ y1) << 1;
+
+        long z0  = h0;
+        long z1  = h1 ^ h0 ^ h2 ^ h4;
+        long z2  = h2 ^ h1 ^ h3 ^ h5;
+        long z3  = h3;
+
+        z1 ^= z3 ^ (z3 >>>  1) ^ (z3 >>>  2) ^ (z3 >>>  7);
+//      z2 ^=      (z3 <<  63) ^ (z3 <<  62) ^ (z3 <<  57);
+        z2 ^=                    (z3 <<  62) ^ (z3 <<  57);
+
+        z0 ^= z2 ^ (z2 >>>  1) ^ (z2 >>>  2) ^ (z2 >>>  7);
+        z1 ^=      (z2 <<  63) ^ (z2 <<  62) ^ (z2 <<  57);
+
+        Pack.longToBigEndian(z0, x, 0);
+        Pack.longToBigEndian(z1, x, 8);
     }
 
     public static void multiply(int[] x, int[] y)
@@ -118,7 +199,7 @@
         int y0 = y[0], y1 = y[1], y2 = y[2], y3 = y[3];
         int z0 = 0, z1 = 0, z2 = 0, z3 = 0;
 
-        for (int i = 0; i < 4; ++i)
+        for (int i = 0; i < SIZE_INTS; ++i)
         {
             int bits = x[i];
             for (int j = 0; j < 32; ++j)
@@ -301,16 +382,24 @@
         y[1] = (x1 >>> 8) | (x0 << 56);
     }
 
+    public static void multiplyP16(long[] x)
+    {
+        long x0 = x[0], x1 = x[1];
+        long c = x1 << 48;
+        x[0] = (x0 >>> 16) ^ c ^ (c >>> 1) ^ (c >>> 2) ^ (c >>> 7);
+        x[1] = (x1 >>> 16) | (x0 << 48);
+    }
+
     public static long[] pAsLongs()
     {
-        long[] tmp = new long[2];
+        long[] tmp = new long[SIZE_LONGS];
         tmp[0] = 1L << 62;
         return tmp;
     }
 
     public static void square(long[] x, long[] z)
     {
-        long[] t  = new long[4];
+        long[] t  = new long[SIZE_LONGS * 2];
         Interleave.expand64To128Rev(x[0], t, 0);
         Interleave.expand64To128Rev(x[1], t, 2);
 
@@ -336,7 +425,7 @@
             x[i] ^= y[i]; ++i;
             x[i] ^= y[i]; ++i;
         }
-        while (i < 16);
+        while (i < SIZE_BYTES);
     }
 
     public static void xor(byte[] x, byte[] y, int yOff)
@@ -349,7 +438,7 @@
             x[i] ^= y[yOff + i]; ++i;
             x[i] ^= y[yOff + i]; ++i;
         }
-        while (i < 16);
+        while (i < SIZE_BYTES);
     }
 
     public static void xor(byte[] x, int xOff, byte[] y, int yOff, byte[] z, int zOff)
@@ -362,7 +451,7 @@
             z[zOff + i] = (byte)(x[xOff + i] ^ y[yOff + i]); ++i;
             z[zOff + i] = (byte)(x[xOff + i] ^ y[yOff + i]); ++i;
         }
-        while (i < 16);
+        while (i < SIZE_BYTES);
     }
 
     public static void xor(byte[] x, byte[] y, int yOff, int yLen)
@@ -391,7 +480,7 @@
             z[i] = (byte)(x[i] ^ y[i]); ++i;
             z[i] = (byte)(x[i] ^ y[i]); ++i;
         }
-        while (i < 16);
+        while (i < SIZE_BYTES);
     }
 
     public static void xor(int[] x, int[] y)
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/modes/gcm/Tables1kGCMExponentiator.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/modes/gcm/Tables1kGCMExponentiator.java
index 7689396..ae466a0 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/modes/gcm/Tables1kGCMExponentiator.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/modes/gcm/Tables1kGCMExponentiator.java
@@ -1,9 +1,8 @@
 /* GENERATED SOURCE. DO NOT MODIFY. */
 package com.android.internal.org.bouncycastle.crypto.modes.gcm;
 
-import java.util.Vector;
-
-import com.android.internal.org.bouncycastle.util.Arrays;
+import java.util.ArrayList;
+import java.util.List;
 
 /**
  * @hide This class is not part of the Android public SDK API
@@ -13,18 +12,18 @@
 {
     // A lookup table of the power-of-two powers of 'x'
     // - lookupPowX2[i] = x^(2^i)
-    private Vector lookupPowX2;
+    private List lookupPowX2;
 
     public void init(byte[] x)
     {
         long[] y = GCMUtil.asLongs(x);
-        if (lookupPowX2 != null && Arrays.areEqual(y, (long[])lookupPowX2.elementAt(0)))
+        if (lookupPowX2 != null && 0L != GCMUtil.areEqual(y, (long[])lookupPowX2.get(0)))
         {
             return;
         }
 
-        lookupPowX2 = new Vector(8);
-        lookupPowX2.addElement(y);
+        lookupPowX2 = new ArrayList(8);
+        lookupPowX2.add(y);
     }
 
     public void exponentiateX(long pow, byte[] output)
@@ -35,8 +34,7 @@
         {
             if ((pow & 1L) != 0)
             {
-                ensureAvailable(bit);
-                GCMUtil.multiply(y, (long[])lookupPowX2.elementAt(bit));
+                GCMUtil.multiply(y, getPowX2(bit));
             }
             ++bit;
             pow >>>= 1;
@@ -45,19 +43,22 @@
         GCMUtil.asBytes(y, output);
     }
 
-    private void ensureAvailable(int bit)
+    private long[] getPowX2(int bit)
     {
-        int count = lookupPowX2.size();
-        if (count <= bit)
+        int last = lookupPowX2.size() - 1;
+        if (last < bit)
         {
-            long[] tmp = (long[])lookupPowX2.elementAt(count - 1);
+            long[] prev = (long[])lookupPowX2.get(last);
             do
             {
-                tmp = Arrays.clone(tmp);
-                GCMUtil.square(tmp, tmp);
-                lookupPowX2.addElement(tmp);
+                long[] next = new long[GCMUtil.SIZE_LONGS];
+                GCMUtil.square(prev, next);
+                lookupPowX2.add(next);
+                prev = next;
             }
-            while (++count <= bit);
+            while (++last < bit);
         }
+
+        return (long[])lookupPowX2.get(bit);
     }
 }
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/modes/gcm/Tables4kGCMMultiplier.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/modes/gcm/Tables4kGCMMultiplier.java
index 30f2af6..8909017 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/modes/gcm/Tables4kGCMMultiplier.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/modes/gcm/Tables4kGCMMultiplier.java
@@ -1,7 +1,6 @@
 /* GENERATED SOURCE. DO NOT MODIFY. */
 package com.android.internal.org.bouncycastle.crypto.modes.gcm;
 
-import com.android.internal.org.bouncycastle.util.Arrays;
 import com.android.internal.org.bouncycastle.util.Pack;
 
 /**
@@ -19,12 +18,13 @@
         {
             T = new long[256][2];
         }
-        else if (Arrays.areEqual(this.H, H))
+        else if (0 != GCMUtil.areEqual(this.H, H))
         {
             return;
         }
 
-        this.H = Arrays.clone(H);
+        this.H = new byte[GCMUtil.SIZE_BYTES];
+        GCMUtil.copy(H, this.H);
 
         // T[0] = 0
 
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/modes/gcm/Tables8kGCMMultiplier.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/modes/gcm/Tables8kGCMMultiplier.java
index 3b92541..793b271 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/modes/gcm/Tables8kGCMMultiplier.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/modes/gcm/Tables8kGCMMultiplier.java
@@ -1,7 +1,6 @@
 /* GENERATED SOURCE. DO NOT MODIFY. */
 package com.android.internal.org.bouncycastle.crypto.modes.gcm;
 
-import com.android.internal.org.bouncycastle.util.Arrays;
 import com.android.internal.org.bouncycastle.util.Pack;
 
 /**
@@ -17,16 +16,17 @@
     {
         if (T == null)
         {
-            T = new long[32][16][2];
+            T = new long[2][256][2];
         }
-        else if (Arrays.areEqual(this.H, H))
+        else if (0 != GCMUtil.areEqual(this.H, H))
         {
             return;
         }
 
-        this.H = Arrays.clone(H);
+        this.H = new byte[GCMUtil.SIZE_BYTES];
+        GCMUtil.copy(H, this.H);
 
-        for (int i = 0; i < 32; ++i)
+        for (int i = 0; i < 2; ++i)
         {
             long[][] t = T[i];
 
@@ -34,17 +34,17 @@
 
             if (i == 0)
             {
-                // t[1] = H.p^3
+                // t[1] = H.p^7
                 GCMUtil.asLongs(this.H, t[1]);
-                GCMUtil.multiplyP3(t[1], t[1]);
+                GCMUtil.multiplyP7(t[1], t[1]);
             }
             else
             {
-                // t[1] = T[i-1][1].p^4
-                GCMUtil.multiplyP4(T[i - 1][1], t[1]);
+                // t[1] = T[i-1][1].p^8
+                GCMUtil.multiplyP8(T[i - 1][1], t[1]);
             }
 
-            for (int n = 2; n < 16; n += 2)
+            for (int n = 2; n < 256; n += 2)
             {
                 // t[2.n] = t[n].p^-1
                 GCMUtil.divideP(t[n >> 1], t[n]);
@@ -53,28 +53,33 @@
                 GCMUtil.xor(t[n], t[1], t[n + 1]);
             }
         }
-
     }
 
     public void multiplyH(byte[] x)
     {
+        long[][] T0 = T[0], T1 = T[1];
+
 //        long[] z = new long[2];
-//        for (int i = 15; i >= 0; --i)
+//        for (int i = 14; i >= 0; i -= 2)
 //        {
-//            GCMUtil.xor(z, T[i + i + 1][(x[i] & 0x0F)]);
-//            GCMUtil.xor(z, T[i + i    ][(x[i] & 0xF0) >>> 4]);
+//            GCMUtil.multiplyP16(z);
+//            GCMUtil.xor(z, T0[x[i] & 0xFF]);
+//            GCMUtil.xor(z, T1[x[i + 1] & 0xFF]);
 //        }
 //        Pack.longToBigEndian(z, x, 0);
 
-        long z0 = 0, z1 = 0;
+        long[] u = T0[x[14] & 0xFF];
+        long[] v = T1[x[15] & 0xFF];
+        long z0 = u[0] ^ v[0], z1 = u[1] ^ v[1];
 
-        for (int i = 15; i >= 0; --i)
+        for (int i = 12; i >= 0; i -= 2)
         {
-            long[] u = T[i + i + 1][(x[i] & 0x0F)];
-            long[] v = T[i + i    ][(x[i] & 0xF0) >>> 4];
+            u = T0[x[i] & 0xFF];
+            v = T1[x[i + 1] & 0xFF];
 
-            z0 ^= u[0] ^ v[0];
-            z1 ^= u[1] ^ v[1];
+            long c = z1 << 48;
+            z1 = u[1] ^ v[1] ^ ((z1 >>> 16) | (z0 << 48));
+            z0 = u[0] ^ v[0] ^ (z0 >>> 16) ^ c ^ (c >>> 1) ^ (c >>> 2) ^ (c >>> 7);
         }
 
         Pack.longToBigEndian(z0, x, 0);
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/paddings/ISO10126d2Padding.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/paddings/ISO10126d2Padding.java
index 14c634a..7392808 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/paddings/ISO10126d2Padding.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/paddings/ISO10126d2Padding.java
@@ -63,9 +63,11 @@
     public int padCount(byte[] in)
         throws InvalidCipherTextException
     {
-        int count = in[in.length - 1] & 0xff;
+        int count = in[in.length - 1] & 0xFF;
+        int position = in.length - count;
 
-        if (count > in.length)
+        int failed = (position | (count - 1)) >> 31;
+        if (failed != 0)
         {
             throw new InvalidCipherTextException("pad block corrupted");
         }
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/paddings/ISO7816d4Padding.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/paddings/ISO7816d4Padding.java
index 9b20044..84b9d2d 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/paddings/ISO7816d4Padding.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/paddings/ISO7816d4Padding.java
@@ -62,18 +62,20 @@
     public int padCount(byte[] in)
         throws InvalidCipherTextException
     {
-        int count = in.length - 1;
-
-        while (count > 0 && in[count] == 0)
+        int position = -1, still00Mask = -1;
+        int i = in.length;
+        while (--i >= 0)
         {
-            count--;
+            int next = in[i] & 0xFF;
+            int match00Mask = ((next ^ 0x00) - 1) >> 31;
+            int match80Mask = ((next ^ 0x80) - 1) >> 31;
+            position ^= (i ^ position) & (still00Mask & match80Mask);
+            still00Mask &= match00Mask;
         }
-
-        if (in[count] != (byte)0x80)
+        if (position < 0)
         {
             throw new InvalidCipherTextException("pad block corrupted");
         }
-        
-        return in.length - count;
+        return in.length - position;
     }
 }
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/paddings/PKCS7Padding.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/paddings/PKCS7Padding.java
index f96d4b8..4058b56 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/paddings/PKCS7Padding.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/paddings/PKCS7Padding.java
@@ -58,18 +58,16 @@
     public int padCount(byte[] in)
         throws InvalidCipherTextException
     {
-        int count = in[in.length - 1] & 0xff;
-        byte countAsbyte = (byte)count;
+        byte countAsByte = in[in.length - 1];
+        int count = countAsByte & 0xFF;
+        int position = in.length - count;
 
-        // constant time version
-        boolean failed = (count > in.length | count == 0);
-
-        for (int i = 0; i < in.length; i++)
+        int failed = (position | (count - 1)) >> 31;
+        for (int i = 0; i < in.length; ++i)
         {
-            failed |= (in.length - i <= count) & (in[i] != countAsbyte);
+            failed |= (in[i] ^ countAsByte) & ~((i - position) >> 31);
         }
-
-        if (failed)
+        if (failed != 0)
         {
             throw new InvalidCipherTextException("pad block corrupted");
         }
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/paddings/PaddedBufferedBlockCipher.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/paddings/PaddedBufferedBlockCipher.java
index f75ef53..732c2e8 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/paddings/PaddedBufferedBlockCipher.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/paddings/PaddedBufferedBlockCipher.java
@@ -2,9 +2,9 @@
 package com.android.internal.org.bouncycastle.crypto.paddings;
 
 import com.android.internal.org.bouncycastle.crypto.BlockCipher;
-import com.android.internal.org.bouncycastle.crypto.BufferedBlockCipher;
 import com.android.internal.org.bouncycastle.crypto.CipherParameters;
 import com.android.internal.org.bouncycastle.crypto.DataLengthException;
+import com.android.internal.org.bouncycastle.crypto.DefaultBufferedBlockCipher;
 import com.android.internal.org.bouncycastle.crypto.InvalidCipherTextException;
 import com.android.internal.org.bouncycastle.crypto.OutputLengthException;
 import com.android.internal.org.bouncycastle.crypto.params.ParametersWithRandom;
@@ -18,7 +18,7 @@
  * @hide This class is not part of the Android public SDK API
  */
 public class PaddedBufferedBlockCipher
-    extends BufferedBlockCipher
+    extends DefaultBufferedBlockCipher
 {
     BlockCipherPadding  padding;
 
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/paddings/TBCPadding.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/paddings/TBCPadding.java
index fbaa0f3..1040137 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/paddings/TBCPadding.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/paddings/TBCPadding.java
@@ -78,14 +78,15 @@
     public int padCount(byte[] in)
         throws InvalidCipherTextException
     {
-        byte code = in[in.length - 1];
-
-        int index = in.length - 1;
-        while (index > 0 && in[index - 1] == code)
+        int i = in.length;
+        int code = in[--i] & 0xFF, count = 1, countingMask = -1;
+        while (--i >= 0)
         {
-            index--;
+            int next = in[i] & 0xFF;
+            int matchMask = ((next ^ code) - 1) >> 31;
+            countingMask &= matchMask;
+            count -= countingMask;
         }
-
-        return in.length - index;
+        return count;
     }
 }
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/paddings/X923Padding.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/paddings/X923Padding.java
index ca24e81..23b6cdb 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/paddings/X923Padding.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/paddings/X923Padding.java
@@ -70,9 +70,11 @@
     public int padCount(byte[] in)
         throws InvalidCipherTextException
     {
-        int count = in[in.length - 1] & 0xff;
+        int count = in[in.length - 1] & 0xFF;
+        int position = in.length - count;
 
-        if (count > in.length)
+        int failed = (position | (count - 1)) >> 31;
+        if (failed != 0)
         {
             throw new InvalidCipherTextException("pad block corrupted");
         }
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/paddings/ZeroBytePadding.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/paddings/ZeroBytePadding.java
index 9553137..c796e03 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/paddings/ZeroBytePadding.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/paddings/ZeroBytePadding.java
@@ -58,18 +58,15 @@
     public int padCount(byte[] in)
         throws InvalidCipherTextException
     {
-        int count = in.length;
-
-        while (count > 0)
+        int count = 0, still00Mask = -1;
+        int i = in.length;
+        while (--i >= 0)
         {
-            if (in[count - 1] != 0)
-            {
-                break;
-            }
-
-            count--;
+            int next = in[i] & 0xFF;
+            int match00Mask = ((next ^ 0x00) - 1) >> 31;
+            still00Mask &= match00Mask;
+            count -= still00Mask;
         }
-
-        return in.length - count;
+        return count;
     }
 }
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/params/Blake3Parameters.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/params/Blake3Parameters.java
new file mode 100644
index 0000000..576967c
--- /dev/null
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/params/Blake3Parameters.java
@@ -0,0 +1,86 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.crypto.params;
+
+import com.android.internal.org.bouncycastle.crypto.CipherParameters;
+import com.android.internal.org.bouncycastle.util.Arrays;
+
+/**
+ * Blake3 Parameters.
+ * @hide This class is not part of the Android public SDK API
+ */
+public class Blake3Parameters
+        implements CipherParameters
+{
+    /**
+     * The key length.
+     */
+    private static final int KEYLEN = 32;
+
+    /**
+     * The key.
+     */
+    private byte[] theKey;
+
+    /**
+     * The context.
+     */
+    private byte[] theContext;
+
+    /**
+     * Create a key parameter.
+     * @param pContext the context
+     * @return the parameter
+     */
+    public static Blake3Parameters context(final byte[] pContext)
+    {
+        if (pContext == null)
+        {
+            throw new IllegalArgumentException("Invalid context");
+        }
+        final Blake3Parameters myParams = new Blake3Parameters();
+        myParams.theContext = Arrays.clone(pContext);
+        return myParams;
+    }
+
+    /**
+     * Create a key parameter.
+     * @param pKey the key
+     * @return the parameter
+     */
+    public static Blake3Parameters key(final byte[] pKey)
+    {
+        if (pKey == null || pKey.length != KEYLEN)
+        {
+            throw new IllegalArgumentException("Invalid keyLength");
+        }
+        final Blake3Parameters myParams = new Blake3Parameters();
+        myParams.theKey = Arrays.clone(pKey);
+        return myParams;
+    }
+
+    /**
+     * Obtain the key.
+     * @return the key
+     */
+    public byte[] getKey()
+    {
+        return Arrays.clone(theKey);
+    }
+
+    /**
+     * Clear the key bytes.
+      */
+    public void clearKey()
+    {
+        Arrays.fill(theKey, (byte) 0);
+    }
+
+    /**
+     * Obtain the salt.
+     * @return the salt
+     */
+    public byte[] getContext()
+    {
+        return Arrays.clone(theContext);
+    }
+}
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/params/FPEParameters.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/params/FPEParameters.java
new file mode 100644
index 0000000..91b8dea
--- /dev/null
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/params/FPEParameters.java
@@ -0,0 +1,61 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.crypto.params;
+
+import com.android.internal.org.bouncycastle.crypto.CipherParameters;
+import com.android.internal.org.bouncycastle.crypto.util.RadixConverter;
+import com.android.internal.org.bouncycastle.util.Arrays;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+public final class FPEParameters
+    implements CipherParameters
+{
+    private final KeyParameter key;
+    private final RadixConverter radixConverter;
+    private final byte[] tweak;
+    private final boolean useInverse;
+
+    public FPEParameters(KeyParameter key, int radix, byte[] tweak)
+    {
+        this(key, radix, tweak, false);
+    }
+
+    public FPEParameters(KeyParameter key, int radix, byte[] tweak, boolean useInverse)
+    {
+        this(key, new RadixConverter(radix), tweak, useInverse);
+    }
+
+    public FPEParameters(KeyParameter key, RadixConverter radixConverter, byte[] tweak, boolean useInverse)
+    {
+        this.key = key;
+        this.radixConverter = radixConverter;
+        this.tweak = Arrays.clone(tweak);
+        this.useInverse = useInverse;
+    }
+
+    public KeyParameter getKey()
+    {
+        return key;
+    }
+
+    public int getRadix()
+    {
+        return radixConverter.getRadix();
+    }
+
+    public RadixConverter getRadixConverter()
+    {
+        return radixConverter;
+    }
+
+    public byte[] getTweak()
+    {
+        return Arrays.clone(tweak);
+    }
+
+    public boolean isUsingInverseFunction()
+    {
+        return useInverse;
+    }
+}
\ No newline at end of file
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/params/KeyParameter.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/params/KeyParameter.java
index 210e7d6..aebfacd 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/params/KeyParameter.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/params/KeyParameter.java
@@ -2,6 +2,7 @@
 package com.android.internal.org.bouncycastle.crypto.params;
 
 import com.android.internal.org.bouncycastle.crypto.CipherParameters;
+import com.android.internal.org.bouncycastle.util.Arrays;
 
 /**
  * @hide This class is not part of the Android public SDK API
@@ -22,13 +23,38 @@
         int     keyOff,
         int     keyLen)
     {
-        this.key = new byte[keyLen];
+        this(keyLen);
 
         System.arraycopy(key, keyOff, this.key, 0, keyLen);
     }
 
+    private KeyParameter(int length)
+    {
+        this.key = new byte[length];
+    }
+
+    public void copyTo(byte[] buf, int off, int len)
+    {
+        if (key.length != len)
+            throw new IllegalArgumentException("len");
+
+        System.arraycopy(key, 0, buf, off, len);
+    }
+
     public byte[] getKey()
     {
         return key;
     }
+
+    public int getKeyLength()
+    {
+        return key.length;
+    }
+
+    public KeyParameter reverse()
+    {
+        KeyParameter reversed = new KeyParameter(key.length);
+        Arrays.reverse(this.key, reversed.key);
+        return reversed;
+    }
 }
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/params/RSAKeyParameters.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/params/RSAKeyParameters.java
index c23d9f5..4f76182 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/params/RSAKeyParameters.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/params/RSAKeyParameters.java
@@ -3,6 +3,9 @@
 
 import java.math.BigInteger;
 
+import com.android.internal.org.bouncycastle.crypto.CryptoServicesRegistrar;
+import com.android.internal.org.bouncycastle.math.Primes;
+import com.android.internal.org.bouncycastle.util.BigIntegers;
 import com.android.internal.org.bouncycastle.util.Properties;
 
 /**
@@ -11,6 +14,8 @@
 public class RSAKeyParameters
     extends AsymmetricKeyParameter
 {
+    private static final BigIntegers.Cache validated = new BigIntegers.Cache();
+
     // Hexadecimal value of the product of the 131 smallest odd primes from 3 to 743
     private static final BigInteger SMALL_PRIMES_PRODUCT = new BigInteger(
               "8138e8a0fcf3a4e84a771d40fd305d7f4aa59306d7251de54d98af8fe95729a1f"
@@ -29,6 +34,15 @@
         BigInteger  modulus,
         BigInteger  exponent)
     {
+        this(isPrivate, modulus, exponent, false);
+    }   
+
+    public RSAKeyParameters(
+        boolean     isPrivate,
+        BigInteger  modulus,
+        BigInteger  exponent,
+        boolean     isInternal)
+    {
         super(isPrivate);
 
         if (!isPrivate)
@@ -38,13 +52,20 @@
                 throw new IllegalArgumentException("RSA publicExponent is even");
             }
         }
-
-        this.modulus = validate(modulus);
+  
+        this.modulus = validated.contains(modulus) ? modulus : validate(modulus, isInternal);
         this.exponent = exponent;
-    }   
+    }
 
-    private BigInteger validate(BigInteger modulus)
+    private BigInteger validate(BigInteger modulus, boolean isInternal)
     {
+        if (isInternal)
+        {
+            validated.add(modulus);
+
+            return modulus;
+        }
+
         if ((modulus.intValue() & 1) == 0)
         {
             throw new IllegalArgumentException("RSA modulus is even");
@@ -57,16 +78,45 @@
             return modulus;
         }
 
+        int maxBitLength = Properties.asInteger("com.android.internal.org.bouncycastle.rsa.max_size", 15360);
+
+        int modBitLength = modulus.bitLength();
+        if (maxBitLength < modBitLength)
+        {
+            throw new IllegalArgumentException("modulus value out of range");
+        }
+
         if (!modulus.gcd(SMALL_PRIMES_PRODUCT).equals(ONE))
         {
             throw new IllegalArgumentException("RSA modulus has a small prime factor");
         }
 
-        // TODO: add additional primePower/Composite test - expensive!!
+        int bits = modulus.bitLength() / 2;
+        int iterations = Properties.asInteger("com.android.internal.org.bouncycastle.rsa.max_mr_tests", getMRIterations(bits));
 
+        if (iterations > 0)
+        {
+            Primes.MROutput mr = Primes.enhancedMRProbablePrimeTest(modulus, CryptoServicesRegistrar.getSecureRandom(), iterations);
+            if (!mr.isProvablyComposite())
+            {
+                throw new IllegalArgumentException("RSA modulus is not composite");
+            }
+        }
+
+        validated.add(modulus);
+        
         return modulus;
     }
 
+    private static int getMRIterations(int bits)
+    {
+        int iterations = bits >= 1536 ? 3
+            : bits >= 1024 ? 4
+            : bits >= 512 ? 7
+            : 50;
+        return iterations;
+    }
+
     public BigInteger getModulus()
     {
         return modulus;
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/params/RSAPrivateCrtKeyParameters.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/params/RSAPrivateCrtKeyParameters.java
index 1fc5f05..f69b232 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/params/RSAPrivateCrtKeyParameters.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/params/RSAPrivateCrtKeyParameters.java
@@ -20,6 +20,19 @@
      * 
      */
     public RSAPrivateCrtKeyParameters(
+         BigInteger  modulus,
+         BigInteger  publicExponent,
+         BigInteger  privateExponent,
+         BigInteger  p,
+         BigInteger  q,
+         BigInteger  dP,
+         BigInteger  dQ,
+         BigInteger  qInv)
+     {
+         this(modulus, publicExponent, privateExponent, p, q, dP, dQ, qInv, false);
+     }
+
+    public RSAPrivateCrtKeyParameters(
         BigInteger  modulus,
         BigInteger  publicExponent,
         BigInteger  privateExponent,
@@ -27,9 +40,10 @@
         BigInteger  q,
         BigInteger  dP,
         BigInteger  dQ,
-        BigInteger  qInv)
+        BigInteger  qInv,
+        boolean     isInternal)
     {
-        super(true, modulus, privateExponent);
+        super(true, modulus, privateExponent, isInternal);
 
         this.e = publicExponent;
         this.p = p;
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/signers/DSASigner.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/signers/DSASigner.java
index 0be6a4a..a0de433 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/signers/DSASigner.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/signers/DSASigner.java
@@ -70,6 +70,8 @@
             this.key = (DSAPublicKeyParameters)param;
         }
 
+        CryptoServicesRegistrar.checkConstraints(Utils.getDefaultProperties("DSA", key, forSigning));
+
         this.random = initSecureRandom(forSigning && !kCalculator.isDeterministic(), providedRandom);
     }
 
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/signers/ECDSASigner.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/signers/ECDSASigner.java
index 0216020..d854536 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/signers/ECDSASigner.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/signers/ECDSASigner.java
@@ -76,6 +76,8 @@
             this.key = (ECPublicKeyParameters)param;
         }
 
+        CryptoServicesRegistrar.checkConstraints(Utils.getDefaultProperties("ECDSA", key, forSigning));
+
         this.random = initSecureRandom(forSigning && !kCalculator.isDeterministic(), providedRandom);
     }
 
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/signers/Utils.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/signers/Utils.java
new file mode 100644
index 0000000..8f969c5
--- /dev/null
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/signers/Utils.java
@@ -0,0 +1,41 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.crypto.signers;
+
+import com.android.internal.org.bouncycastle.crypto.CipherParameters;
+import com.android.internal.org.bouncycastle.crypto.CryptoServiceProperties;
+import com.android.internal.org.bouncycastle.crypto.CryptoServicePurpose;
+import com.android.internal.org.bouncycastle.crypto.constraints.ConstraintUtils;
+import com.android.internal.org.bouncycastle.crypto.constraints.DefaultServiceProperties;
+import com.android.internal.org.bouncycastle.crypto.params.DSAKeyParameters;
+import com.android.internal.org.bouncycastle.crypto.params.ECKeyParameters;
+// Android-removed: unsupported algorithm
+// import org.bouncycastle.crypto.params.GOST3410KeyParameters;
+
+class Utils
+{
+    static CryptoServiceProperties getDefaultProperties(String algorithm, DSAKeyParameters k, boolean forSigning)
+    {
+        return new DefaultServiceProperties(algorithm, ConstraintUtils.bitsOfSecurityFor(k.getParameters().getP()), k, getPurpose(forSigning));
+    }
+
+    // Android-removed: unsupported algorithm
+    // static CryptoServiceProperties getDefaultProperties(String algorithm, GOST3410KeyParameters k, boolean forSigning)
+    // {
+    //     return new DefaultServiceProperties(algorithm, ConstraintUtils.bitsOfSecurityFor(k.getParameters().getP()), k, getPurpose(forSigning));
+    // }
+
+    static CryptoServiceProperties getDefaultProperties(String algorithm, ECKeyParameters k, boolean forSigning)
+    {
+        return new DefaultServiceProperties(algorithm, ConstraintUtils.bitsOfSecurityFor(k.getParameters().getCurve()), k, getPurpose(forSigning));
+    }
+
+    static CryptoServiceProperties getDefaultProperties(String algorithm, int bitsOfSecurity, CipherParameters k, boolean forSigning)
+    {
+        return new DefaultServiceProperties(algorithm, bitsOfSecurity, k, getPurpose(forSigning));
+    }
+
+    static CryptoServicePurpose getPurpose(boolean forSigning)
+    {
+        return forSigning ? CryptoServicePurpose.SIGNING : CryptoServicePurpose.VERIFYING;
+    }
+}
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/util/BasicAlphabetMapper.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/util/BasicAlphabetMapper.java
new file mode 100644
index 0000000..1e077c0
--- /dev/null
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/util/BasicAlphabetMapper.java
@@ -0,0 +1,107 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.crypto.util;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import com.android.internal.org.bouncycastle.crypto.AlphabetMapper;
+
+/**
+ * A basic alphabet mapper that just creates a mapper based on the
+ * passed in array of characters.
+ * @hide This class is not part of the Android public SDK API
+ */
+public class BasicAlphabetMapper
+    implements AlphabetMapper
+{
+    private Map<Character, Integer> indexMap = new HashMap<Character, Integer>();
+    private Map<Integer, Character> charMap = new HashMap<Integer, Character>();
+
+    /**
+     * Base constructor.
+     *
+     * @param alphabet a String of characters making up the alphabet.
+     */
+    public BasicAlphabetMapper(String alphabet)
+    {
+        this(alphabet.toCharArray());
+    }
+
+    /**
+     * Base constructor.
+     *
+     * @param alphabet an array of characters making up the alphabet.
+     */
+    public BasicAlphabetMapper(char[] alphabet)
+    {
+        for (int i = 0; i != alphabet.length; i++)
+        {
+            if (indexMap.containsKey(alphabet[i]))
+            {
+                throw new IllegalArgumentException("duplicate key detected in alphabet: " + alphabet[i]);
+            }
+            indexMap.put(alphabet[i], i);
+            charMap.put(i, alphabet[i]);
+        }
+    }
+
+    public int getRadix()
+    {
+        return indexMap.size();
+    }
+
+    public byte[] convertToIndexes(char[] input)
+    {
+        byte[] out;
+
+        if (indexMap.size() <= 256)
+        {
+            out = new byte[input.length];
+            for (int i = 0; i != input.length; i++)
+            {
+                out[i] = indexMap.get(input[i]).byteValue();
+            }
+        }
+        else
+        {
+            out = new byte[input.length * 2];
+            for (int i = 0; i != input.length; i++)
+            {
+                int idx = indexMap.get(input[i]);
+                out[i * 2] = (byte)((idx >> 8) & 0xff);
+                out[i * 2  + 1] = (byte)(idx & 0xff);
+            }
+        }
+
+        return out;
+    }
+
+    public char[] convertToChars(byte[] input)
+    {
+        char[] out;
+
+        if (charMap.size() <= 256)
+        {
+            out = new char[input.length];
+            for (int i = 0; i != input.length; i++)
+            {
+                out[i] = charMap.get(input[i] & 0xff);
+            }
+        }
+        else
+        {
+            if ((input.length & 0x1) != 0)
+            {
+                throw new IllegalArgumentException("two byte radix and input string odd length");
+            }
+            
+            out = new char[input.length / 2];
+            for (int i = 0; i != input.length; i += 2)
+            {
+                out[i / 2] = charMap.get(((input[i] << 8) & 0xff00) | (input[i + 1] & 0xff));
+            }
+        }
+
+        return out;
+    }
+}
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/util/PrivateKeyFactory.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/util/PrivateKeyFactory.java
index 323d2c5..0a296f9 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/util/PrivateKeyFactory.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/util/PrivateKeyFactory.java
@@ -70,6 +70,14 @@
     public static AsymmetricKeyParameter createKey(byte[] privateKeyInfoData)
         throws IOException
     {
+        if (privateKeyInfoData == null)
+        {
+            throw new IllegalArgumentException("privateKeyInfoData array null");
+        }
+        if (privateKeyInfoData.length == 0)
+        {
+            throw new IllegalArgumentException("privateKeyInfoData array empty");
+        }
         return createKey(PrivateKeyInfo.getInstance(ASN1Primitive.fromByteArray(privateKeyInfoData)));
     }
 
@@ -97,6 +105,11 @@
     public static AsymmetricKeyParameter createKey(PrivateKeyInfo keyInfo)
         throws IOException
     {
+        if (keyInfo == null)
+        {
+            throw new IllegalArgumentException("keyInfo argument null");
+        }
+
         AlgorithmIdentifier algId = keyInfo.getPrivateKeyAlgorithm();
         ASN1ObjectIdentifier algOID = algId.getAlgorithm();
 
@@ -139,12 +152,12 @@
         else if (algOID.equals(X9ObjectIdentifiers.id_dsa))
         {
             ASN1Integer derX = (ASN1Integer)keyInfo.parsePrivateKey();
-            ASN1Encodable de = algId.getParameters();
+            ASN1Encodable algParameters = algId.getParameters();
 
             DSAParameters parameters = null;
-            if (de != null)
+            if (algParameters != null)
             {
-                DSAParameter params = DSAParameter.getInstance(de.toASN1Primitive());
+                DSAParameter params = DSAParameter.getInstance(algParameters.toASN1Primitive());
                 parameters = new DSAParameters(params.getP(), params.getQ(), params.getG());
             }
 
@@ -184,32 +197,44 @@
         /*
         else if (algOID.equals(EdECObjectIdentifiers.id_X25519))
         {
-            return new X25519PrivateKeyParameters(getRawKey(keyInfo, X25519PrivateKeyParameters.KEY_SIZE), 0);
+            // Java 11 bug: exact length of X25519/X448 secret used in Java 11
+            if (X25519PrivateKeyParameters.KEY_SIZE == keyInfo.getPrivateKeyLength())
+            {
+                return new X25519PrivateKeyParameters(keyInfo.getPrivateKey().getOctets());
+            }
+
+            return new X25519PrivateKeyParameters(getRawKey(keyInfo));
         }
         else if (algOID.equals(EdECObjectIdentifiers.id_X448))
         {
-            return new X448PrivateKeyParameters(getRawKey(keyInfo, X448PrivateKeyParameters.KEY_SIZE), 0);
+            // Java 11 bug: exact length of X25519/X448 secret used in Java 11
+            if (X448PrivateKeyParameters.KEY_SIZE == keyInfo.getPrivateKeyLength())
+            {
+                return new X448PrivateKeyParameters(keyInfo.getPrivateKey().getOctets());
+            }
+
+            return new X448PrivateKeyParameters(getRawKey(keyInfo));
         }
         else if (algOID.equals(EdECObjectIdentifiers.id_Ed25519))
         {
-            return new Ed25519PrivateKeyParameters(getRawKey(keyInfo, Ed25519PrivateKeyParameters.KEY_SIZE), 0);
+            return new Ed25519PrivateKeyParameters(getRawKey(keyInfo));
         }
         else if (algOID.equals(EdECObjectIdentifiers.id_Ed448))
         {
-            return new Ed448PrivateKeyParameters(getRawKey(keyInfo, Ed448PrivateKeyParameters.KEY_SIZE), 0);
+            return new Ed448PrivateKeyParameters(getRawKey(keyInfo));
         }
         else if (
             algOID.equals(CryptoProObjectIdentifiers.gostR3410_2001) ||
                 algOID.equals(RosstandartObjectIdentifiers.id_tc26_gost_3410_12_512) ||
                 algOID.equals(RosstandartObjectIdentifiers.id_tc26_gost_3410_12_256))
         {
-            GOST3410PublicKeyAlgParameters gostParams = GOST3410PublicKeyAlgParameters.getInstance(keyInfo.getPrivateKeyAlgorithm().getParameters());
+            ASN1Encodable algParameters = algId.getParameters();
+            GOST3410PublicKeyAlgParameters gostParams = GOST3410PublicKeyAlgParameters.getInstance(algParameters);
             ECGOST3410Parameters ecSpec = null;
             BigInteger d = null;
-            ASN1Primitive p = keyInfo.getPrivateKeyAlgorithm().getParameters().toASN1Primitive();
+            ASN1Primitive p = algParameters.toASN1Primitive();
             if (p instanceof ASN1Sequence && (ASN1Sequence.getInstance(p).size() == 2 || ASN1Sequence.getInstance(p).size() == 3))
             {
-
                 X9ECParameters ecP = ECGOST3410NamedCurves.getByOIDX9(gostParams.getPublicKeyParamSet());
 
                 ecSpec = new ECGOST3410Parameters(
@@ -218,11 +243,12 @@
                     gostParams.getPublicKeyParamSet(),
                     gostParams.getDigestParamSet(),
                     gostParams.getEncryptionParamSet());
-                ASN1OctetString privEnc = keyInfo.getPrivateKey();
 
-                if (privEnc.getOctets().length == 32 || privEnc.getOctets().length == 64)
+                int privateKeyLength = keyInfo.getPrivateKeyLength();
+
+                if (privateKeyLength == 32 || privateKeyLength == 64)
                 {
-                    d = new BigInteger(1, Arrays.reverse(privEnc.getOctets()));
+                    d = new BigInteger(1, Arrays.reverse(keyInfo.getPrivateKey().getOctets()));
                 }
                 else
                 {
@@ -240,7 +266,7 @@
             }
             else
             {
-                X962Parameters params = X962Parameters.getInstance(keyInfo.getPrivateKeyAlgorithm().getParameters());
+                X962Parameters params = X962Parameters.getInstance(algId.getParameters());
 
                 if (params.isNamedCurve())
                 {
@@ -296,14 +322,8 @@
         }
     }
 
-    private static byte[] getRawKey(PrivateKeyInfo keyInfo, int expectedSize)
-        throws IOException
+    private static byte[] getRawKey(PrivateKeyInfo keyInfo) throws IOException
     {
-        byte[] result = ASN1OctetString.getInstance(keyInfo.parsePrivateKey()).getOctets();
-        if (expectedSize != result.length)
-        {
-            throw new RuntimeException("private key encoding has incorrect length");
-        }
-        return result;
+        return ASN1OctetString.getInstance(keyInfo.parsePrivateKey()).getOctets();
     }
 }
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/util/PublicKeyFactory.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/util/PublicKeyFactory.java
index 9e1f295..1b44a17 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/util/PublicKeyFactory.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/util/PublicKeyFactory.java
@@ -7,13 +7,13 @@
 import java.util.HashMap;
 import java.util.Map;
 
+import com.android.internal.org.bouncycastle.asn1.ASN1BitString;
 import com.android.internal.org.bouncycastle.asn1.ASN1Encodable;
 import com.android.internal.org.bouncycastle.asn1.ASN1InputStream;
 import com.android.internal.org.bouncycastle.asn1.ASN1Integer;
 import com.android.internal.org.bouncycastle.asn1.ASN1ObjectIdentifier;
 import com.android.internal.org.bouncycastle.asn1.ASN1OctetString;
 import com.android.internal.org.bouncycastle.asn1.ASN1Primitive;
-import com.android.internal.org.bouncycastle.asn1.DERBitString;
 import com.android.internal.org.bouncycastle.asn1.DEROctetString;
 // Android-removed: Unsupported algorithms
 // import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers;
@@ -115,6 +115,14 @@
     public static AsymmetricKeyParameter createKey(byte[] keyInfoData)
         throws IOException
     {
+        if (keyInfoData == null)
+        {
+            throw new IllegalArgumentException("keyInfoData array null");
+        }
+        if (keyInfoData.length == 0)
+        {
+            throw new IllegalArgumentException("keyInfoData array empty");
+        }
         return createKey(SubjectPublicKeyInfo.getInstance(ASN1Primitive.fromByteArray(keyInfoData)));
     }
 
@@ -141,6 +149,10 @@
     public static AsymmetricKeyParameter createKey(SubjectPublicKeyInfo keyInfo)
         throws IOException
     {
+        if (keyInfo == null)
+        {
+            throw new IllegalArgumentException("keyInfo argument null");
+        }
         return createKey(keyInfo, null);
     }
 
@@ -155,6 +167,11 @@
     public static AsymmetricKeyParameter createKey(SubjectPublicKeyInfo keyInfo, Object defaultParams)
         throws IOException
     {
+        if (keyInfo == null)
+        {
+            throw new IllegalArgumentException("keyInfo argument null");
+        }
+
         AlgorithmIdentifier algID = keyInfo.getAlgorithm();
 
         SubjectPublicKeyInfoConverter converter = (SubjectPublicKeyInfoConverter)converters.get(algID.getAlgorithm());
@@ -306,7 +323,7 @@
                 dParams = new ECDomainParameters(x9);
             }
 
-            DERBitString bits = keyInfo.getPublicKeyData();
+            ASN1BitString bits = keyInfo.getPublicKeyData();
             byte[] data = bits.getBytes();
             ASN1OctetString key = new DEROctetString(data);
 
@@ -483,7 +500,7 @@
                 }
                 BigInteger b = new BigInteger(1, b_bytes);
                 DSTU4145BinaryField field = binary.getField();
-                ECCurve curve = new ECCurve.F2m(field.getM(), field.getK1(), field.getK2(), field.getK3(), binary.getA(), b);
+                ECCurve curve = new ECCurve.F2m(field.getM(), field.getK1(), field.getK2(), field.getK3(), binary.getA(), b, null, null);
                 byte[] g_bytes = binary.getG();
                 if (algOid.equals(UAObjectIdentifiers.dstu4145le))
                 {
@@ -516,7 +533,7 @@
     {
         AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Object defaultParams)
         {
-            return new X25519PublicKeyParameters(getRawKey(keyInfo, defaultParams, X25519PublicKeyParameters.KEY_SIZE), 0);
+            return new X25519PublicKeyParameters(getRawKey(keyInfo, defaultParams));
         }
     }
 
@@ -525,7 +542,7 @@
     {
         AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Object defaultParams)
         {
-            return new X448PublicKeyParameters(getRawKey(keyInfo, defaultParams, X448PublicKeyParameters.KEY_SIZE), 0);
+            return new X448PublicKeyParameters(getRawKey(keyInfo, defaultParams));
         }
     }
 
@@ -534,7 +551,7 @@
     {
         AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Object defaultParams)
         {
-            return new Ed25519PublicKeyParameters(getRawKey(keyInfo, defaultParams, Ed25519PublicKeyParameters.KEY_SIZE), 0);
+            return new Ed25519PublicKeyParameters(getRawKey(keyInfo, defaultParams));
         }
     }
 
@@ -543,24 +560,19 @@
     {
         AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Object defaultParams)
         {
-            return new Ed448PublicKeyParameters(getRawKey(keyInfo, defaultParams, Ed448PublicKeyParameters.KEY_SIZE), 0);
+            return new Ed448PublicKeyParameters(getRawKey(keyInfo, defaultParams));
         }
     }
     */
     // END Android-removed: Unsupported algorithms
 
-    private static byte[] getRawKey(SubjectPublicKeyInfo keyInfo, Object defaultParams, int expectedSize)
+    private static byte[] getRawKey(SubjectPublicKeyInfo keyInfo, Object defaultParams)
     {
         /*
          * TODO[RFC 8422]
          * - Require defaultParams == null?
          * - Require keyInfo.getAlgorithm().getParameters() == null?
          */
-        byte[] result = keyInfo.getPublicKeyData().getOctets();
-        if (expectedSize != result.length)
-        {
-            throw new RuntimeException("public key encoding has incorrect length");
-        }
-        return result;
+        return keyInfo.getPublicKeyData().getOctets();
     }
 }
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/util/RadixConverter.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/util/RadixConverter.java
new file mode 100644
index 0000000..dc8492e
--- /dev/null
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/util/RadixConverter.java
@@ -0,0 +1,190 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.crypto.util;
+
+import java.math.BigInteger;
+
+import com.android.internal.org.bouncycastle.util.BigIntegers;
+
+/**
+ * Utility class to convert decimal numbers (BigInteger) into a number in the base provided and the other way round.
+ * <p>For an application of this see the FPE parameter classes.</p>
+ * @hide This class is not part of the Android public SDK API
+ */
+public class RadixConverter
+{
+
+    /*
+    The conversions in this class are more complex than the standard ways of converting between basis because we want to improve the performance by limiting the
+    operations on BigInteger which are not very efficient.
+    The general idea is to perform math operations on primitive long as much as we can and just work with BigInteger when necessary.
+    Converting between basis uses the fact that a number in base 'B' have a unique representation in polynomial form.
+
+    num = ... + r8B^8 + r7B^7 + r6B^6 + r5B^5 + r4B^4 + r3B^3 + r2B^2 + r1B + r0
+
+    We can compute how many digits in base 'B' can fit in a long. For example, for a radix R=2^16 the number of digits 'n' that we can fit into a long is the
+    max 'n' that still satisfies: R^n < Long.MAX_VALUE (i.e. 2^63 -1). In this case 'n' is 3. To convert 'num' from its decimal representation to base 'B'
+    representation we can write down 'num' in a polynomial form of B^3:
+
+    num = (((...)B^3 + r8B^2 + r7B + r6)B^3 + r5B^2 + r4B + r3)B^3 + (r2B^2 + r1B + r0)
+
+    B^3 would be our intermediate base. We can convert numbers in base B^3 while operating on primitive long.
+    To convert a decimal num to its representation in base B we can first build its B^3 representation and then figure out the digits in base B from the single
+    digit in base B^3. num % B^3 gives us a single digit in base B^3 which corresponds to a group of 3 digits in base B.
+
+    An equivalent way of writing the polynomial form of num would be:
+
+    num = (...)B^9 + (r8B^8 + r7B^7 + r6B^6)B^6 + (r5B^5 + r4B^4 + r3)B^3 + (r2B^2 + r1B + r0)
+
+    In this form it becomes clear that to obtain 'num' from a sequence of digits, one can group the digits in group of 3 and compute the corresponding decimal
+    number for the group in base B. We can then multiply the decimal numbers by the corresponding power of B^3 and sum up the result to obtain the decimal
+    representation of num in base B,
+     */
+    private static final double LOG_LONG_MAX_VALUE = Math.log(Long.MAX_VALUE);
+    private static final int DEFAULT_POWERS_TO_CACHE = 10;
+    // the max number of digits in base 'radix' that fits in a long
+    private final int digitsGroupLength;
+    // the total number of digits combination in a group. radix ^ digitsGroupLength
+    private final BigInteger digitsGroupSpaceSize;
+    private final int radix;
+    private final BigInteger[] digitsGroupSpacePowers;
+
+    /**
+     * @param radix                the radix to use for base conversions
+     * @param numberOfCachedPowers number of intermediate base powers to precompute and cache.
+     */
+    public RadixConverter(int radix, int numberOfCachedPowers)
+    {
+        this.radix = radix;
+        // solves radix^n < Long.MAX_VALUE to find n (maxDigitsFitsInLong)
+        this.digitsGroupLength = (int)Math.floor(LOG_LONG_MAX_VALUE / Math.log(radix));
+        this.digitsGroupSpaceSize = BigInteger.valueOf(radix).pow(digitsGroupLength);
+        this.digitsGroupSpacePowers = precomputeDigitsGroupPowers(numberOfCachedPowers, digitsGroupSpaceSize);
+    }
+
+    /**
+     * @param radix the radix to use for base conversions.
+     */
+    public RadixConverter(int radix)
+    {
+        this(radix, DEFAULT_POWERS_TO_CACHE);
+    }
+
+    public int getRadix()
+    {
+        return radix;
+    }
+
+    public void toEncoding(BigInteger number, int messageLength, short[] out)
+    {
+        if (number.signum() < 0)
+        {
+            throw new IllegalArgumentException();
+        }
+        // convert number into its representation in base 'radix'.
+        // writes leading '0' if the messageLength is greater than the number of digits required to encode in base 'radix'
+        int digitIndex = messageLength - 1;
+        do
+        {
+            if (number.equals(BigInteger.ZERO))
+            {
+                out[digitIndex--] = 0;
+                continue;
+            }
+            BigInteger[] quotientAndRemainder = number.divideAndRemainder(digitsGroupSpaceSize);
+            number = quotientAndRemainder[0];
+            digitIndex = toEncoding(quotientAndRemainder[1].longValue(), digitIndex, out);
+        }
+        while (digitIndex >= 0);
+        if (number.signum() != 0)
+        {
+            throw new IllegalArgumentException();
+        }
+    }
+
+    private int toEncoding(long number, int digitIndex, short[] out)
+    {
+        for (int i = 0; i < digitsGroupLength && digitIndex >= 0; i++)
+        {
+            if (number == 0)
+            {
+                out[digitIndex--] = 0;
+                continue;
+            }
+            out[digitIndex--] = (short)(number % radix);
+            number = number / radix;
+        }
+        if (number != 0)
+        {
+            throw new IllegalStateException("Failed to convert decimal number");
+        }
+        return digitIndex;
+    }
+
+    public BigInteger fromEncoding(short[] digits)
+    {
+        // from a sequence of digits in base 'radix' to a decimal number
+        // iterate through groups of digits right to left
+        // digitsGroupLength = 2;  digits: [22, 45, 11, 31, 24]
+        // groups are, in order of iteration: [31, 24], [45, 11], [22]
+        BigInteger currentGroupCardinality = BigIntegers.ONE;
+        BigInteger res = null;
+        int indexGroup = 0;
+        int numberOfDigits = digits.length;
+        for (int groupStartDigitIndex = numberOfDigits - digitsGroupLength;
+             groupStartDigitIndex > -digitsGroupLength;
+             groupStartDigitIndex = groupStartDigitIndex - digitsGroupLength)
+        {
+            int actualDigitsInGroup = digitsGroupLength;
+            if (groupStartDigitIndex < 0)
+            {
+                // last group might contain fewer digits so adjust offsets
+                actualDigitsInGroup = digitsGroupLength + groupStartDigitIndex;
+                groupStartDigitIndex = 0;
+            }
+            int groupEndDigitIndex = Math.min(groupStartDigitIndex + actualDigitsInGroup, numberOfDigits);
+            long groupInBaseRadix = fromEncoding(groupStartDigitIndex, groupEndDigitIndex, digits);
+            BigInteger bigInteger = BigInteger.valueOf(groupInBaseRadix);
+            if (indexGroup == 0)
+            {
+                res = bigInteger;
+            }
+            else
+            {
+                currentGroupCardinality =
+                    indexGroup <= digitsGroupSpacePowers.length
+                        ? digitsGroupSpacePowers[indexGroup - 1]
+                        : currentGroupCardinality.multiply(digitsGroupSpaceSize);
+                res = res.add(bigInteger.multiply(currentGroupCardinality));
+            }
+            indexGroup++;
+        }
+        return res;
+    }
+
+    public int getDigitsGroupLength()
+    {
+        return digitsGroupLength;
+    }
+
+    private long fromEncoding(int groupStartDigitIndex, int groupEndDigitIndex, short[] digits)
+    {
+        long decimalNumber = 0;
+        for (int digitIndex = groupStartDigitIndex; digitIndex < groupEndDigitIndex; digitIndex++)
+        {
+            decimalNumber = (decimalNumber * radix) + (digits[digitIndex] & 0xFFFF);
+        }
+        return decimalNumber;
+    }
+
+    private BigInteger[] precomputeDigitsGroupPowers(int numberOfCachedPowers, BigInteger digitsGroupSpaceSize)
+    {
+        BigInteger[] cachedPowers = new BigInteger[numberOfCachedPowers];
+        BigInteger currentPower = digitsGroupSpaceSize;
+        for (int i = 0; i < numberOfCachedPowers; i++)
+        {
+            cachedPowers[i] = currentPower;
+            currentPower = currentPower.multiply(digitsGroupSpaceSize);
+        }
+        return cachedPowers;
+    }
+}
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/util/SSHNamedCurves.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/util/SSHNamedCurves.java
index 9853bf2..9498637 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/util/SSHNamedCurves.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/crypto/util/SSHNamedCurves.java
@@ -73,8 +73,8 @@
             while (e.hasMoreElements())
             {
                 String name = (String)e.nextElement();
-                X9ECParameters parameters = CustomNamedCurves.getByName(name);
-                put(parameters.getCurve(), name);
+                ECCurve curve = CustomNamedCurves.getByNameLazy(name).getCurve();
+                put(curve, name);
             }
 
         }
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/cms/GCMParameters.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/internal/asn1/cms/GCMParameters.java
similarity index 89%
rename from repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/cms/GCMParameters.java
rename to repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/internal/asn1/cms/GCMParameters.java
index e08c5a2..3bc6dd9 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/cms/GCMParameters.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/internal/asn1/cms/GCMParameters.java
@@ -1,5 +1,5 @@
 /* GENERATED SOURCE. DO NOT MODIFY. */
-package com.android.internal.org.bouncycastle.asn1.cms;
+package com.android.internal.org.bouncycastle.internal.asn1.cms;
 
 import com.android.internal.org.bouncycastle.asn1.ASN1EncodableVector;
 import com.android.internal.org.bouncycastle.asn1.ASN1Integer;
@@ -33,8 +33,8 @@
      * Accepted inputs:
      * <ul>
      * <li> null &rarr; null
-     * <li> {@link com.android.internal.org.bouncycastle.asn1.cms.GCMParameters} object
-     * <li> {@link com.android.internal.org.bouncycastle.asn1.ASN1Sequence#getInstance(Object) ASN1Sequence} input formats with GCMParameters structure inside
+     * <li> {@link GCMParameters} object
+     * <li> {@link ASN1Sequence#getInstance(Object) ASN1Sequence} input formats with GCMParameters structure inside
      * </ul>
      *
      * @param obj the object we want converted.
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/isismtt/ISISMTTObjectIdentifiers.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/internal/asn1/isismtt/ISISMTTObjectIdentifiers.java
similarity index 94%
rename from repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/isismtt/ISISMTTObjectIdentifiers.java
rename to repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/internal/asn1/isismtt/ISISMTTObjectIdentifiers.java
index 9bfcbfc..3ef4699 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/asn1/isismtt/ISISMTTObjectIdentifiers.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/internal/asn1/isismtt/ISISMTTObjectIdentifiers.java
@@ -1,5 +1,5 @@
 /* GENERATED SOURCE. DO NOT MODIFY. */
-package com.android.internal.org.bouncycastle.asn1.isismtt;
+package com.android.internal.org.bouncycastle.internal.asn1.isismtt;
 
 import com.android.internal.org.bouncycastle.asn1.ASN1ObjectIdentifier;
 
@@ -105,8 +105,6 @@
      * </pre>
      * <p>
      * OID: 1.3.36.8.3.8
-     * 
-     * @see com.android.internal.org.bouncycastle.asn1.isismtt.x509.Restriction
      */
     static final ASN1ObjectIdentifier id_isismtt_at_restriction = id_isismtt_at.branch("8");
 
@@ -131,8 +129,6 @@
      * returned in this extension.
      * <p>
      * OID: 1.3.36.8.3.10
-     * 
-     * @see com.android.internal.org.bouncycastle.asn1.isismtt.ocsp.RequestedCertificate
      */
     static final ASN1ObjectIdentifier id_isismtt_at_requestedCertificate = id_isismtt_at.branch("10");
 
@@ -161,8 +157,7 @@
      * Hash of a certificate in OCSP.
      * <p>
      * OID: 1.3.36.8.3.13
-     * 
-     * @see com.android.internal.org.bouncycastle.asn1.isismtt.ocsp.CertHash
+     *
      */
     static final ASN1ObjectIdentifier id_isismtt_at_certHash = id_isismtt_at.branch("13");
 
@@ -188,8 +183,7 @@
      * </pre>
      * <p>
      * OID: 1.3.36.8.3.15
-     * 
-     * @see com.android.internal.org.bouncycastle.asn1.isismtt.x509.AdditionalInformationSyntax
+     *
      */
     static final ASN1ObjectIdentifier id_isismtt_at_additionalInformation = id_isismtt_at.branch("15");
 
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/its/asn1/EndEntityType.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/its/asn1/EndEntityType.java
deleted file mode 100644
index dac4a9e..0000000
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/its/asn1/EndEntityType.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/* GENERATED SOURCE. DO NOT MODIFY. */
-package com.android.internal.org.bouncycastle.its.asn1;
-
-import com.android.internal.org.bouncycastle.asn1.ASN1BitString;
-import com.android.internal.org.bouncycastle.asn1.ASN1Object;
-import com.android.internal.org.bouncycastle.asn1.ASN1Primitive;
-import com.android.internal.org.bouncycastle.asn1.DERBitString;
-
-/**
- * <pre>
- *     EndEntityType ::= BIT STRING { app(0), enrol(1) } (SIZE (8)) (ALL EXCEPT ())
- * </pre>
- * @hide This class is not part of the Android public SDK API
- */
-public class EndEntityType
-    extends ASN1Object
-{
-    public static final int        app = (1 << 7);
-    public static final int        enrol = (1 << 6);
-
-    private final ASN1BitString type;
-
-    public EndEntityType(int type)
-    {
-        if (type != app && type != enrol)
-        {
-            throw new IllegalArgumentException("value out of range");
-        }
-
-        this.type = new DERBitString(type);
-    }
-
-    private EndEntityType(DERBitString str)
-    {
-        this.type = str;
-    }
-
-    public static EndEntityType getInstance(Object src)
-    {
-        if (src instanceof EndEntityType)
-        {
-            return (EndEntityType)src;
-        }
-        else if (src != null)
-        {
-            return new EndEntityType(DERBitString.getInstance(src));
-        }
-
-        return null;
-    }
-
-    public ASN1Primitive toASN1Primitive()
-    {
-        return type;
-    }
-}
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/its/asn1/PsidGroupPermissions.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/its/asn1/PsidGroupPermissions.java
deleted file mode 100644
index 72173fc..0000000
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/its/asn1/PsidGroupPermissions.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/* GENERATED SOURCE. DO NOT MODIFY. */
-package com.android.internal.org.bouncycastle.its.asn1;
-
-import java.math.BigInteger;
-
-import com.android.internal.org.bouncycastle.asn1.ASN1Integer;
-import com.android.internal.org.bouncycastle.asn1.ASN1Object;
-import com.android.internal.org.bouncycastle.asn1.ASN1Primitive;
-import com.android.internal.org.bouncycastle.asn1.ASN1Sequence;
-
-/**
- * <pre>
- *     PsidGroupPermissions ::= SEQUENCE {
- *         subjectPermissions SubjectPermissions,
- *         minChainLength INTEGER DEFAULT 1,
- *         chainLengthRange INTEGER DEFAULT 0,
- *         eeType EndEntityType DEFAULT (app)
- *     }
- * </pre>
- * @hide This class is not part of the Android public SDK API
- */
-public class PsidGroupPermissions
-    extends ASN1Object
-{
-    private final SubjectPermissions subjectPermissions;
-    private final BigInteger minChainLength;
-    private final BigInteger chainLengthRange;
-    private final Object eeType;
-
-    private PsidGroupPermissions(ASN1Sequence seq)
-    {
-        if (seq.size() != 2)
-        {
-            throw new IllegalArgumentException("sequence not length 2");
-        }
-
-        this.subjectPermissions = SubjectPermissions.getInstance(seq.getObjectAt(0));
-        this.minChainLength = ASN1Integer.getInstance(seq.getObjectAt(1)).getValue();
-        this.chainLengthRange = ASN1Integer.getInstance(seq.getObjectAt(2)).getValue();
-        this.eeType = EndEntityType.getInstance(seq.getObjectAt(3));
-    }
-
-    public static PsidGroupPermissions getInstance(Object src)
-    {
-        if (src instanceof PsidGroupPermissions)
-        {
-            return (PsidGroupPermissions)src;
-        }
-        else if (src != null)
-        {
-            return new PsidGroupPermissions(ASN1Sequence.getInstance(src));
-        }
-
-        return null;
-    }
-
-    public ASN1Primitive toASN1Primitive()
-    {
-        return null;
-    }
-}
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/BCLoadStoreParameter.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/BCLoadStoreParameter.java
new file mode 100644
index 0000000..da84300
--- /dev/null
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/BCLoadStoreParameter.java
@@ -0,0 +1,75 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.jcajce;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.security.KeyStore;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+public class BCLoadStoreParameter
+    implements KeyStore.LoadStoreParameter
+{
+    private final InputStream in;
+    private final OutputStream out;
+    private final KeyStore.ProtectionParameter protectionParameter;
+
+    /**
+     * Base constructor for
+     *
+     * @param out
+     * @param password
+     */
+    public BCLoadStoreParameter(OutputStream out, char[] password)
+    {
+        this(out, new KeyStore.PasswordProtection(password));
+    }
+
+    public BCLoadStoreParameter(InputStream in, char[] password)
+    {
+        this(in, new KeyStore.PasswordProtection(password));
+    }
+
+    public BCLoadStoreParameter(InputStream in, KeyStore.ProtectionParameter protectionParameter)
+    {
+        this(in, null, protectionParameter);
+    }
+
+    public BCLoadStoreParameter(OutputStream out, KeyStore.ProtectionParameter protectionParameter)
+    {
+        this(null, out, protectionParameter);
+    }
+
+    BCLoadStoreParameter(InputStream in, OutputStream out, KeyStore.ProtectionParameter protectionParameter)
+    {
+        this.in = in;
+        this.out = out;
+        this.protectionParameter = protectionParameter;
+    }
+
+    public KeyStore.ProtectionParameter getProtectionParameter()
+    {
+        return protectionParameter;
+    }
+
+    public OutputStream getOutputStream()
+    {
+        if (out == null)
+        {
+            throw new UnsupportedOperationException("parameter not configured for storage - no OutputStream");
+        }
+
+        return out;
+    }
+
+    public InputStream getInputStream()
+    {
+        if (out != null)
+        {
+            throw new UnsupportedOperationException("parameter configured for storage OutputStream present");
+        }
+
+        return in;
+    }
+}
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/CompositePrivateKey.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/CompositePrivateKey.java
index 594a05b..06a247b 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/CompositePrivateKey.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/CompositePrivateKey.java
@@ -75,7 +75,7 @@
         try
         {
             return new PrivateKeyInfo(
-                new AlgorithmIdentifier(MiscObjectIdentifiers.id_alg_composite), new DERSequence(v)).getEncoded(ASN1Encoding.DER);
+                new AlgorithmIdentifier(MiscObjectIdentifiers.id_composite_key), new DERSequence(v)).getEncoded(ASN1Encoding.DER);
         }
         catch (IOException e)
         {
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/CompositePublicKey.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/CompositePublicKey.java
index e9af6e5..1f73e33 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/CompositePublicKey.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/CompositePublicKey.java
@@ -75,7 +75,7 @@
         try
         {
             return new SubjectPublicKeyInfo(
-                new AlgorithmIdentifier(MiscObjectIdentifiers.id_alg_composite), new DERSequence(v)).getEncoded(ASN1Encoding.DER);
+                new AlgorithmIdentifier(MiscObjectIdentifiers.id_composite_key), new DERSequence(v)).getEncoded(ASN1Encoding.DER);
         }
         catch (IOException e)
         {
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/ExternalPublicKey.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/ExternalPublicKey.java
new file mode 100644
index 0000000..03f56fe
--- /dev/null
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/ExternalPublicKey.java
@@ -0,0 +1,105 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.jcajce;
+
+import java.io.IOException;
+import java.security.MessageDigest;
+import java.security.PublicKey;
+
+import com.android.internal.org.bouncycastle.asn1.ASN1Encoding;
+import com.android.internal.org.bouncycastle.asn1.bc.BCObjectIdentifiers;
+import com.android.internal.org.bouncycastle.asn1.bc.ExternalValue;
+import com.android.internal.org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import com.android.internal.org.bouncycastle.asn1.x509.GeneralName;
+import com.android.internal.org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import com.android.internal.org.bouncycastle.jcajce.util.MessageDigestUtils;
+import com.android.internal.org.bouncycastle.util.Arrays;
+
+/**
+ * Wrapper class which returns an "ExternalValue" for the public key encoding. In this case
+ * the key encoding is a hash and the actual key needs to be looked up somewhere else. Useful
+ * for where the public keys are really large but it's required to keep certificates small.
+ * @hide This class is not part of the Android public SDK API
+ */
+public class ExternalPublicKey
+    implements PublicKey
+{
+    private final GeneralName location;
+    private final AlgorithmIdentifier digestAlg;
+    private final byte[] digest;
+
+    /**
+     * Base constructor with fundamental contents.
+     *
+     * @param location location URI for the actual public key.
+     * @param digestAlg hashing algorithm used to hash the actual public key encoding.
+     * @param digest digest of the actual public key.
+     */
+    public ExternalPublicKey(GeneralName location, AlgorithmIdentifier digestAlg, byte[] digest)
+    {
+        this.location = location;
+        this.digestAlg = digestAlg;
+        this.digest = Arrays.clone(digest);
+    }
+
+    /**
+     * Helper constructor with JCA contents.
+     *
+     * @param key the public key we are externalising.
+     * @param location location URI for the actual public key.
+     * @param digest digest to use for hashing the key.
+     */
+    // Android-removed: unsupported
+    // public ExternalPublicKey(PublicKey key, GeneralName location, MessageDigest digest)
+    // {
+    //     this(location, MessageDigestUtils.getDigestAlgID(digest.getAlgorithm()), digest.digest(key.getEncoded()));
+    // }
+
+    /**
+     * Base constructor with ASN.1 structure.
+     *
+     * @param extKey structure with location, hashing algorithm and hash for the public key.
+     */
+    public ExternalPublicKey(ExternalValue extKey)
+    {
+        this(extKey.getLocation(), extKey.getHashAlg(), extKey.getHashValue());
+    }
+
+    /**
+     * Return "ExternalKey"
+     *
+     * @return  "ExternalKey"
+     */
+    public String getAlgorithm()
+    {
+        return "ExternalKey";
+    }
+
+    /**
+     * Return "X.509" (DER encoded SubjectPublicKeyInfo)
+     *
+     * @return  "X.509"
+     */
+    public String getFormat()
+    {
+        return "X.509";
+    }
+
+    /**
+     * Return a SubjectPublicKeyInfo structure containing an ExternalValue encoding for the key.
+     *
+     * @return a DER encoding of SubjectPublicKeyInfo containing an ExternalValue structure.
+     */
+    public byte[] getEncoded()
+    {
+        try
+        {
+            return new SubjectPublicKeyInfo(
+                new AlgorithmIdentifier(BCObjectIdentifiers.external_value),
+                    new ExternalValue(location, digestAlg, digest)).getEncoded(ASN1Encoding.DER);
+        }
+        catch (IOException e)
+        {
+            throw new IllegalStateException("unable to encode composite key: " + e.getMessage());
+        }
+    }
+}
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/SecretKeyWithEncapsulation.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/SecretKeyWithEncapsulation.java
new file mode 100644
index 0000000..edcd039
--- /dev/null
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/SecretKeyWithEncapsulation.java
@@ -0,0 +1,79 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.jcajce;
+
+import javax.crypto.SecretKey;
+
+import com.android.internal.org.bouncycastle.util.Arrays;
+
+/**
+ * Carrier class for a KEM/KTS secret key plus its encapsulation.
+ * @hide This class is not part of the Android public SDK API
+ */
+public final class SecretKeyWithEncapsulation
+    implements SecretKey
+{
+    private final SecretKey secretKey;
+    private final byte[] encapsulation;
+
+    /**
+     * Basic constructor.
+     *
+     * @param secretKey the secret key that was arrived at.
+     * @param encapsulation the encapsulation the key data was carried in.
+     */
+    public SecretKeyWithEncapsulation(SecretKey secretKey, byte[] encapsulation)
+    {
+        this.secretKey = secretKey;
+        this.encapsulation = Arrays.clone(encapsulation);
+    }
+
+    /**
+     * Return the algorithm for the agreed secret key.
+     *
+     * @return the secret key value.
+     */
+    public String getAlgorithm()
+    {
+        return secretKey.getAlgorithm();
+    }
+
+    /**
+     * Return the format for the agreed secret key.
+     *
+     * @return the secret key format.
+     */
+    public String getFormat()
+    {
+        return secretKey.getFormat();
+    }
+
+    /**
+     * Return the encoding of the agreed secret key.
+     *
+     * @return the secret key encoding.
+     */
+    public byte[] getEncoded()
+    {
+        return secretKey.getEncoded();
+    }
+
+    /**
+     * Return the encapsulation that carried the key material used in creating the agreed secret key.
+     *
+     * @return the encrypted encapsulation of the agreed secret key.
+     */
+    public byte[] getEncapsulation()
+    {
+        return Arrays.clone(encapsulation);
+    }
+
+    public boolean equals(Object o)
+    {
+        return secretKey.equals(o);
+    }
+
+    public int hashCode()
+    {
+        return secretKey.hashCode();
+    }
+}
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/io/SignatureUpdatingOutputStream.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/io/SignatureUpdatingOutputStream.java
index 8127e4d..2c288d5 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/io/SignatureUpdatingOutputStream.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/io/SignatureUpdatingOutputStream.java
@@ -6,6 +6,8 @@
 import java.security.Signature;
 import java.security.SignatureException;
 
+import com.android.internal.org.bouncycastle.util.Exceptions;
+
 class SignatureUpdatingOutputStream
     extends OutputStream
 {
@@ -25,7 +27,7 @@
         }
         catch (SignatureException e)
         {
-            throw new IOException(e.getMessage());
+            throw Exceptions.ioException(e.getMessage(), e);
         }
     }
 
@@ -38,7 +40,7 @@
         }
         catch (SignatureException e)
         {
-            throw new IOException(e.getMessage());
+            throw Exceptions.ioException(e.getMessage(), e);
         }
     }
 
@@ -51,7 +53,7 @@
         }
         catch (SignatureException e)
         {
-            throw new IOException(e.getMessage());
+            throw Exceptions.ioException(e.getMessage(), e);
         }
     }
 }
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/asymmetric/COMPOSITE.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/asymmetric/COMPOSITE.java
new file mode 100644
index 0000000..0826695
--- /dev/null
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/asymmetric/COMPOSITE.java
@@ -0,0 +1,149 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.jcajce.provider.asymmetric;
+
+import java.io.IOException;
+import java.security.InvalidKeyException;
+import java.security.Key;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.util.HashMap;
+import java.util.Map;
+
+import com.android.internal.org.bouncycastle.asn1.ASN1Sequence;
+import com.android.internal.org.bouncycastle.asn1.misc.MiscObjectIdentifiers;
+import com.android.internal.org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
+import com.android.internal.org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import com.android.internal.org.bouncycastle.jcajce.CompositePrivateKey;
+import com.android.internal.org.bouncycastle.jcajce.CompositePublicKey;
+import com.android.internal.org.bouncycastle.jcajce.provider.asymmetric.util.BaseKeyFactorySpi;
+import com.android.internal.org.bouncycastle.jcajce.provider.config.ConfigurableProvider;
+import com.android.internal.org.bouncycastle.jcajce.provider.util.AsymmetricAlgorithmProvider;
+import com.android.internal.org.bouncycastle.jcajce.provider.util.AsymmetricKeyInfoConverter;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+public class COMPOSITE
+{
+    private static final String PREFIX = "com.android.internal.org.bouncycastle.jcajce.provider.asymmetric.COMPOSITE";
+
+    private static final Map<String, String> compositeAttributes = new HashMap<String, String>();
+
+    static
+    {
+        compositeAttributes.put("SupportedKeyClasses", "com.android.internal.org.bouncycastle.jcajce.CompositePublicKey|com.android.internal.org.bouncycastle.jcajce.CompositePrivateKey");
+        compositeAttributes.put("SupportedKeyFormats", "PKCS#8|X.509");
+    }
+
+    private static AsymmetricKeyInfoConverter baseConverter;
+
+    /**
+     * @hide This class is not part of the Android public SDK API
+     */
+    public static class KeyFactory
+        extends BaseKeyFactorySpi
+    {
+        protected Key engineTranslateKey(Key key)
+            throws InvalidKeyException
+        {
+            try
+            {
+                if (key instanceof PrivateKey)
+                {
+                    return generatePrivate(PrivateKeyInfo.getInstance(key.getEncoded()));
+                }
+                else if (key instanceof PublicKey)
+                {
+                    return generatePublic(SubjectPublicKeyInfo.getInstance(key.getEncoded()));
+                }
+            }
+            catch (IOException e)
+            {
+                throw new InvalidKeyException("key could not be parsed: " + e.getMessage());
+            }
+
+            throw new InvalidKeyException("key not recognized");
+        }
+
+        public PrivateKey generatePrivate(PrivateKeyInfo keyInfo)
+            throws IOException
+        {
+            return baseConverter.generatePrivate(keyInfo);
+        }
+
+        public PublicKey generatePublic(SubjectPublicKeyInfo keyInfo)
+            throws IOException
+        {
+            return baseConverter.generatePublic(keyInfo);
+        }
+    }
+
+    private static class CompositeKeyInfoConverter
+        implements AsymmetricKeyInfoConverter
+    {
+        private final ConfigurableProvider provider;
+
+        public CompositeKeyInfoConverter(ConfigurableProvider provider)
+        {
+            this.provider = provider;
+        }
+
+        public PrivateKey generatePrivate(PrivateKeyInfo keyInfo)
+            throws IOException
+        {
+            ASN1Sequence keySeq = ASN1Sequence.getInstance(keyInfo.parsePrivateKey());
+            PrivateKey[] privKeys = new PrivateKey[keySeq.size()];
+
+            for (int i = 0; i != keySeq.size(); i++)
+            {
+                PrivateKeyInfo privInfo = PrivateKeyInfo.getInstance(keySeq.getObjectAt(i));
+
+                privKeys[i] = provider.getKeyInfoConverter(
+                    privInfo.getPrivateKeyAlgorithm().getAlgorithm()).generatePrivate(privInfo);
+            }
+
+            return new CompositePrivateKey(privKeys);
+        }
+
+        public PublicKey generatePublic(SubjectPublicKeyInfo keyInfo)
+            throws IOException
+        {
+            ASN1Sequence keySeq = ASN1Sequence.getInstance(keyInfo.getPublicKeyData().getBytes());
+            PublicKey[] pubKeys = new PublicKey[keySeq.size()];
+
+            for (int i = 0; i != keySeq.size(); i++)
+            {
+                SubjectPublicKeyInfo pubInfo = SubjectPublicKeyInfo.getInstance(keySeq.getObjectAt(i));
+
+                pubKeys[i] = provider.getKeyInfoConverter((pubInfo.getAlgorithm().getAlgorithm())).generatePublic(pubInfo);
+            }
+
+            return new CompositePublicKey(pubKeys);
+        }
+    }
+
+    /**
+     * @hide This class is not part of the Android public SDK API
+     */
+    public static class Mappings
+        extends AsymmetricAlgorithmProvider
+    {
+        public Mappings()
+        {
+        }
+
+        public void configure(ConfigurableProvider provider)
+        {
+            provider.addAlgorithm("KeyFactory.COMPOSITE", PREFIX + "$KeyFactory");
+            provider.addAlgorithm("KeyFactory." + MiscObjectIdentifiers.id_alg_composite, PREFIX + "$KeyFactory");
+            provider.addAlgorithm("KeyFactory.OID." + MiscObjectIdentifiers.id_alg_composite, PREFIX + "$KeyFactory");
+            provider.addAlgorithm("KeyFactory." + MiscObjectIdentifiers.id_composite_key, PREFIX + "$KeyFactory");
+            provider.addAlgorithm("KeyFactory.OID." + MiscObjectIdentifiers.id_composite_key, PREFIX + "$KeyFactory");
+
+            baseConverter = new CompositeKeyInfoConverter(provider);
+
+            provider.addKeyInfoConverter(MiscObjectIdentifiers.id_alg_composite, baseConverter);
+            provider.addKeyInfoConverter(MiscObjectIdentifiers.id_composite_key, baseConverter);
+        }
+    }
+}
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/asymmetric/DSA.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/asymmetric/DSA.java
index 869ed54..aa19a8a 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/asymmetric/DSA.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/asymmetric/DSA.java
@@ -24,7 +24,7 @@
         public Mappings()
         {
         }
-        
+
         public void configure(ConfigurableProvider provider)
         {
             provider.addAlgorithm("AlgorithmParameters.DSA", PREFIX + "AlgorithmParametersSpi");
@@ -91,6 +91,8 @@
             provider.addAlgorithm("Alg.Alias.Signature.DSAWithSHA1", "SHA1withDSA");
             // END Android-changed: Change primary ID from DSA to SHA1withDSA
 
+            // addSignatureAlgorithm(provider, "RIPEMD160", "DSA", PREFIX + "DSASigner$dsaRMD160");
+
             AsymmetricKeyInfoConverter keyFact = new KeyFactorySpi();
 
             for (int i = 0; i != DSAUtil.dsaOids.length; i++)
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/asymmetric/EC.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/asymmetric/EC.java
index fa92c48..d9ae497 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/asymmetric/EC.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/asymmetric/EC.java
@@ -12,6 +12,9 @@
 // import org.bouncycastle.asn1.teletrust.TeleTrusTObjectIdentifiers;
 // END Android-removed: Unsupported algorithms
 import com.android.internal.org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
+// import org.bouncycastle.internal.asn1.bsi.BSIObjectIdentifiers;
+// import org.bouncycastle.internal.asn1.cms.CMSObjectIdentifiers;
+// import org.bouncycastle.internal.asn1.eac.EACObjectIdentifiers;
 import com.android.internal.org.bouncycastle.jcajce.provider.asymmetric.ec.KeyFactorySpi;
 import com.android.internal.org.bouncycastle.jcajce.provider.config.ConfigurableProvider;
 import com.android.internal.org.bouncycastle.jcajce.provider.util.AsymmetricAlgorithmProvider;
@@ -50,77 +53,73 @@
             /*
             provider.addAlgorithm("AlgorithmParameters.EC", PREFIX + "AlgorithmParametersSpi");
 
-            provider.addAttributes("KeyAgreement.ECDH", generalEcAttributes);
-            provider.addAlgorithm("KeyAgreement.ECDH", PREFIX + "KeyAgreementSpi$DH");
-            provider.addAttributes("KeyAgreement.ECDHC", generalEcAttributes);
-            provider.addAlgorithm("KeyAgreement.ECDHC", PREFIX + "KeyAgreementSpi$DHC");
-            provider.addAttributes("KeyAgreement.ECCDH", generalEcAttributes);
-            provider.addAlgorithm("KeyAgreement.ECCDH", PREFIX + "KeyAgreementSpi$DHC");
+            provider.addAlgorithm("KeyAgreement.ECDH", PREFIX + "KeyAgreementSpi$DH", generalEcAttributes);
+            provider.addAlgorithm("KeyAgreement.ECDHC", PREFIX + "KeyAgreementSpi$DHC", generalEcAttributes);
+            provider.addAlgorithm("KeyAgreement.ECCDH", PREFIX + "KeyAgreementSpi$DHC", generalEcAttributes);
+            
+            provider.addAlgorithm("KeyAgreement.ECCDHU", PREFIX + "KeyAgreementSpi$DHUC", generalEcAttributes);
 
-            provider.addAttributes("KeyAgreement.ECCDHU", generalEcAttributes);
-            provider.addAlgorithm("KeyAgreement.ECCDHU", PREFIX + "KeyAgreementSpi$DHUC");
+            provider.addAlgorithm("KeyAgreement.ECDHWITHSHA1KDF", PREFIX + "KeyAgreementSpi$DHwithSHA1KDFAndSharedInfo", generalEcAttributes);
+            provider.addAlgorithm("KeyAgreement.ECCDHWITHSHA1KDF", PREFIX + "KeyAgreementSpi$CDHwithSHA1KDFAndSharedInfo", generalEcAttributes);
 
-            provider.addAlgorithm("KeyAgreement.ECDHWITHSHA1KDF", PREFIX + "KeyAgreementSpi$DHwithSHA1KDFAndSharedInfo");
-            provider.addAlgorithm("KeyAgreement.ECCDHWITHSHA1KDF", PREFIX + "KeyAgreementSpi$CDHwithSHA1KDFAndSharedInfo");
+            provider.addAlgorithm("KeyAgreement.ECDHWITHSHA224KDF", PREFIX + "KeyAgreementSpi$DHwithSHA224KDFAndSharedInfo", generalEcAttributes);
+            provider.addAlgorithm("KeyAgreement.ECCDHWITHSHA224KDF", PREFIX + "KeyAgreementSpi$CDHwithSHA224KDFAndSharedInfo", generalEcAttributes);
 
-            provider.addAlgorithm("KeyAgreement.ECDHWITHSHA224KDF", PREFIX + "KeyAgreementSpi$DHwithSHA224KDFAndSharedInfo");
-            provider.addAlgorithm("KeyAgreement.ECCDHWITHSHA224KDF", PREFIX + "KeyAgreementSpi$CDHwithSHA224KDFAndSharedInfo");
+            provider.addAlgorithm("KeyAgreement.ECDHWITHSHA256KDF", PREFIX + "KeyAgreementSpi$DHwithSHA256KDFAndSharedInfo", generalEcAttributes);
+            provider.addAlgorithm("KeyAgreement.ECCDHWITHSHA256KDF", PREFIX + "KeyAgreementSpi$CDHwithSHA256KDFAndSharedInfo", generalEcAttributes);
 
-            provider.addAlgorithm("KeyAgreement.ECDHWITHSHA256KDF", PREFIX + "KeyAgreementSpi$DHwithSHA256KDFAndSharedInfo");
-            provider.addAlgorithm("KeyAgreement.ECCDHWITHSHA256KDF", PREFIX + "KeyAgreementSpi$CDHwithSHA256KDFAndSharedInfo");
+            provider.addAlgorithm("KeyAgreement.ECDHWITHSHA384KDF", PREFIX + "KeyAgreementSpi$DHwithSHA384KDFAndSharedInfo", generalEcAttributes);
+            provider.addAlgorithm("KeyAgreement.ECCDHWITHSHA384KDF", PREFIX + "KeyAgreementSpi$CDHwithSHA384KDFAndSharedInfo", generalEcAttributes);
 
-            provider.addAlgorithm("KeyAgreement.ECDHWITHSHA384KDF", PREFIX + "KeyAgreementSpi$DHwithSHA384KDFAndSharedInfo");
-            provider.addAlgorithm("KeyAgreement.ECCDHWITHSHA384KDF", PREFIX + "KeyAgreementSpi$CDHwithSHA384KDFAndSharedInfo");
+            provider.addAlgorithm("KeyAgreement.ECDHWITHSHA512KDF", PREFIX + "KeyAgreementSpi$DHwithSHA512KDFAndSharedInfo", generalEcAttributes);
+            provider.addAlgorithm("KeyAgreement.ECCDHWITHSHA512KDF", PREFIX + "KeyAgreementSpi$CDHwithSHA512KDFAndSharedInfo", generalEcAttributes);
 
-            provider.addAlgorithm("KeyAgreement.ECDHWITHSHA512KDF", PREFIX + "KeyAgreementSpi$DHwithSHA512KDFAndSharedInfo");
-            provider.addAlgorithm("KeyAgreement.ECCDHWITHSHA512KDF", PREFIX + "KeyAgreementSpi$CDHwithSHA512KDFAndSharedInfo");
+            provider.addAlgorithm("KeyAgreement", X9ObjectIdentifiers.dhSinglePass_stdDH_sha1kdf_scheme, PREFIX + "KeyAgreementSpi$DHwithSHA1KDFAndSharedInfo", generalEcAttributes);
+            provider.addAlgorithm("KeyAgreement", X9ObjectIdentifiers.dhSinglePass_cofactorDH_sha1kdf_scheme, PREFIX + "KeyAgreementSpi$CDHwithSHA1KDFAndSharedInfo", generalEcAttributes);
 
-            provider.addAlgorithm("KeyAgreement", X9ObjectIdentifiers.dhSinglePass_stdDH_sha1kdf_scheme, PREFIX + "KeyAgreementSpi$DHwithSHA1KDFAndSharedInfo");
-            provider.addAlgorithm("KeyAgreement", X9ObjectIdentifiers.dhSinglePass_cofactorDH_sha1kdf_scheme, PREFIX + "KeyAgreementSpi$CDHwithSHA1KDFAndSharedInfo");
+            provider.addAlgorithm("KeyAgreement", SECObjectIdentifiers.dhSinglePass_stdDH_sha224kdf_scheme, PREFIX + "KeyAgreementSpi$DHwithSHA224KDFAndSharedInfo", generalEcAttributes);
+            provider.addAlgorithm("KeyAgreement", SECObjectIdentifiers.dhSinglePass_cofactorDH_sha224kdf_scheme, PREFIX + "KeyAgreementSpi$CDHwithSHA224KDFAndSharedInfo", generalEcAttributes);
 
-            provider.addAlgorithm("KeyAgreement", SECObjectIdentifiers.dhSinglePass_stdDH_sha224kdf_scheme, PREFIX + "KeyAgreementSpi$DHwithSHA224KDFAndSharedInfo");
-            provider.addAlgorithm("KeyAgreement", SECObjectIdentifiers.dhSinglePass_cofactorDH_sha224kdf_scheme, PREFIX + "KeyAgreementSpi$CDHwithSHA224KDFAndSharedInfo");
+            provider.addAlgorithm("KeyAgreement", SECObjectIdentifiers.dhSinglePass_stdDH_sha256kdf_scheme, PREFIX + "KeyAgreementSpi$DHwithSHA256KDFAndSharedInfo", generalEcAttributes);
+            provider.addAlgorithm("KeyAgreement", SECObjectIdentifiers.dhSinglePass_cofactorDH_sha256kdf_scheme, PREFIX + "KeyAgreementSpi$CDHwithSHA256KDFAndSharedInfo", generalEcAttributes);
 
-            provider.addAlgorithm("KeyAgreement", SECObjectIdentifiers.dhSinglePass_stdDH_sha256kdf_scheme, PREFIX + "KeyAgreementSpi$DHwithSHA256KDFAndSharedInfo");
-            provider.addAlgorithm("KeyAgreement", SECObjectIdentifiers.dhSinglePass_cofactorDH_sha256kdf_scheme, PREFIX + "KeyAgreementSpi$CDHwithSHA256KDFAndSharedInfo");
+            provider.addAlgorithm("KeyAgreement", SECObjectIdentifiers.dhSinglePass_stdDH_sha384kdf_scheme, PREFIX + "KeyAgreementSpi$DHwithSHA384KDFAndSharedInfo", generalEcAttributes);
+            provider.addAlgorithm("KeyAgreement", SECObjectIdentifiers.dhSinglePass_cofactorDH_sha384kdf_scheme, PREFIX + "KeyAgreementSpi$CDHwithSHA384KDFAndSharedInfo", generalEcAttributes);
 
-            provider.addAlgorithm("KeyAgreement", SECObjectIdentifiers.dhSinglePass_stdDH_sha384kdf_scheme, PREFIX + "KeyAgreementSpi$DHwithSHA384KDFAndSharedInfo");
-            provider.addAlgorithm("KeyAgreement", SECObjectIdentifiers.dhSinglePass_cofactorDH_sha384kdf_scheme, PREFIX + "KeyAgreementSpi$CDHwithSHA384KDFAndSharedInfo");
+            provider.addAlgorithm("KeyAgreement", SECObjectIdentifiers.dhSinglePass_stdDH_sha512kdf_scheme, PREFIX + "KeyAgreementSpi$DHwithSHA512KDFAndSharedInfo", generalEcAttributes);
+            provider.addAlgorithm("KeyAgreement", SECObjectIdentifiers.dhSinglePass_cofactorDH_sha512kdf_scheme, PREFIX + "KeyAgreementSpi$CDHwithSHA512KDFAndSharedInfo", generalEcAttributes);
 
-            provider.addAlgorithm("KeyAgreement", SECObjectIdentifiers.dhSinglePass_stdDH_sha512kdf_scheme, PREFIX + "KeyAgreementSpi$DHwithSHA512KDFAndSharedInfo");
-            provider.addAlgorithm("KeyAgreement", SECObjectIdentifiers.dhSinglePass_cofactorDH_sha512kdf_scheme, PREFIX + "KeyAgreementSpi$CDHwithSHA512KDFAndSharedInfo");
+            provider.addAlgorithm("KeyAgreement.ECCDHWITHSHA1CKDF", PREFIX + "KeyAgreementSpi$DHwithSHA1CKDF", generalEcAttributes);
+            provider.addAlgorithm("KeyAgreement.ECCDHWITHSHA256CKDF", PREFIX + "KeyAgreementSpi$DHwithSHA256CKDF", generalEcAttributes);
+            provider.addAlgorithm("KeyAgreement.ECCDHWITHSHA384CKDF", PREFIX + "KeyAgreementSpi$DHwithSHA384CKDF", generalEcAttributes);
+            provider.addAlgorithm("KeyAgreement.ECCDHWITHSHA512CKDF", PREFIX + "KeyAgreementSpi$DHwithSHA512CKDF", generalEcAttributes);
 
-            provider.addAlgorithm("KeyAgreement.ECCDHWITHSHA1CKDF", PREFIX + "KeyAgreementSpi$DHwithSHA1CKDF");
-            provider.addAlgorithm("KeyAgreement.ECCDHWITHSHA256CKDF", PREFIX + "KeyAgreementSpi$DHwithSHA256CKDF");
-            provider.addAlgorithm("KeyAgreement.ECCDHWITHSHA384CKDF", PREFIX + "KeyAgreementSpi$DHwithSHA384CKDF");
-            provider.addAlgorithm("KeyAgreement.ECCDHWITHSHA512CKDF", PREFIX + "KeyAgreementSpi$DHwithSHA512CKDF");
+            provider.addAlgorithm("KeyAgreement.ECCDHUWITHSHA1CKDF", PREFIX + "KeyAgreementSpi$DHUwithSHA1CKDF", generalEcAttributes);
+            provider.addAlgorithm("KeyAgreement.ECCDHUWITHSHA224CKDF", PREFIX + "KeyAgreementSpi$DHUwithSHA224CKDF", generalEcAttributes);
+            provider.addAlgorithm("KeyAgreement.ECCDHUWITHSHA256CKDF", PREFIX + "KeyAgreementSpi$DHUwithSHA256CKDF", generalEcAttributes);
+            provider.addAlgorithm("KeyAgreement.ECCDHUWITHSHA384CKDF", PREFIX + "KeyAgreementSpi$DHUwithSHA384CKDF", generalEcAttributes);
+            provider.addAlgorithm("KeyAgreement.ECCDHUWITHSHA512CKDF", PREFIX + "KeyAgreementSpi$DHUwithSHA512CKDF", generalEcAttributes);
 
-            provider.addAlgorithm("KeyAgreement.ECCDHUWITHSHA1CKDF", PREFIX + "KeyAgreementSpi$DHUwithSHA1CKDF");
-            provider.addAlgorithm("KeyAgreement.ECCDHUWITHSHA224CKDF", PREFIX + "KeyAgreementSpi$DHUwithSHA224CKDF");
-            provider.addAlgorithm("KeyAgreement.ECCDHUWITHSHA256CKDF", PREFIX + "KeyAgreementSpi$DHUwithSHA256CKDF");
-            provider.addAlgorithm("KeyAgreement.ECCDHUWITHSHA384CKDF", PREFIX + "KeyAgreementSpi$DHUwithSHA384CKDF");
-            provider.addAlgorithm("KeyAgreement.ECCDHUWITHSHA512CKDF", PREFIX + "KeyAgreementSpi$DHUwithSHA512CKDF");
+            provider.addAlgorithm("KeyAgreement.ECCDHUWITHSHA1KDF", PREFIX + "KeyAgreementSpi$DHUwithSHA1KDF", generalEcAttributes);
+            provider.addAlgorithm("KeyAgreement.ECCDHUWITHSHA224KDF", PREFIX + "KeyAgreementSpi$DHUwithSHA224KDF", generalEcAttributes);
+            provider.addAlgorithm("KeyAgreement.ECCDHUWITHSHA256KDF", PREFIX + "KeyAgreementSpi$DHUwithSHA256KDF", generalEcAttributes);
+            provider.addAlgorithm("KeyAgreement.ECCDHUWITHSHA384KDF", PREFIX + "KeyAgreementSpi$DHUwithSHA384KDF", generalEcAttributes);
+            provider.addAlgorithm("KeyAgreement.ECCDHUWITHSHA512KDF", PREFIX + "KeyAgreementSpi$DHUwithSHA512KDF", generalEcAttributes);
 
-            provider.addAlgorithm("KeyAgreement.ECCDHUWITHSHA1KDF", PREFIX + "KeyAgreementSpi$DHUwithSHA1KDF");
-            provider.addAlgorithm("KeyAgreement.ECCDHUWITHSHA224KDF", PREFIX + "KeyAgreementSpi$DHUwithSHA224KDF");
-            provider.addAlgorithm("KeyAgreement.ECCDHUWITHSHA256KDF", PREFIX + "KeyAgreementSpi$DHUwithSHA256KDF");
-            provider.addAlgorithm("KeyAgreement.ECCDHUWITHSHA384KDF", PREFIX + "KeyAgreementSpi$DHUwithSHA384KDF");
-            provider.addAlgorithm("KeyAgreement.ECCDHUWITHSHA512KDF", PREFIX + "KeyAgreementSpi$DHUwithSHA512KDF");
+            provider.addAlgorithm("KeyAgreement.ECKAEGWITHSHA1KDF", PREFIX + "KeyAgreementSpi$ECKAEGwithSHA1KDF", generalEcAttributes);
+            provider.addAlgorithm("KeyAgreement.ECKAEGWITHSHA224KDF", PREFIX + "KeyAgreementSpi$ECKAEGwithSHA224KDF", generalEcAttributes);
+            provider.addAlgorithm("KeyAgreement.ECKAEGWITHSHA256KDF", PREFIX + "KeyAgreementSpi$ECKAEGwithSHA256KDF", generalEcAttributes);
+            provider.addAlgorithm("KeyAgreement.ECKAEGWITHSHA384KDF", PREFIX + "KeyAgreementSpi$ECKAEGwithSHA384KDF", generalEcAttributes);
+            provider.addAlgorithm("KeyAgreement.ECKAEGWITHSHA512KDF", PREFIX + "KeyAgreementSpi$ECKAEGwithSHA512KDF", generalEcAttributes);
 
-            provider.addAlgorithm("KeyAgreement.ECKAEGWITHSHA1KDF", PREFIX + "KeyAgreementSpi$ECKAEGwithSHA1KDF");
-            provider.addAlgorithm("KeyAgreement.ECKAEGWITHSHA224KDF", PREFIX + "KeyAgreementSpi$ECKAEGwithSHA224KDF");
-            provider.addAlgorithm("KeyAgreement.ECKAEGWITHSHA256KDF", PREFIX + "KeyAgreementSpi$ECKAEGwithSHA256KDF");
-            provider.addAlgorithm("KeyAgreement.ECKAEGWITHSHA384KDF", PREFIX + "KeyAgreementSpi$ECKAEGwithSHA384KDF");
-            provider.addAlgorithm("KeyAgreement.ECKAEGWITHSHA512KDF", PREFIX + "KeyAgreementSpi$ECKAEGwithSHA512KDF");
+            provider.addAlgorithm("KeyAgreement", BSIObjectIdentifiers.ecka_eg_X963kdf_SHA1, PREFIX + "KeyAgreementSpi$ECKAEGwithSHA1KDF", generalEcAttributes);
+            provider.addAlgorithm("KeyAgreement", BSIObjectIdentifiers.ecka_eg_X963kdf_SHA224, PREFIX + "KeyAgreementSpi$ECKAEGwithSHA224KDF", generalEcAttributes);
+            provider.addAlgorithm("KeyAgreement", BSIObjectIdentifiers.ecka_eg_X963kdf_SHA256, PREFIX + "KeyAgreementSpi$ECKAEGwithSHA256KDF", generalEcAttributes);
+            provider.addAlgorithm("KeyAgreement", BSIObjectIdentifiers.ecka_eg_X963kdf_SHA384, PREFIX + "KeyAgreementSpi$ECKAEGwithSHA384KDF", generalEcAttributes);
+            provider.addAlgorithm("KeyAgreement", BSIObjectIdentifiers.ecka_eg_X963kdf_SHA512, PREFIX + "KeyAgreementSpi$ECKAEGwithSHA512KDF", generalEcAttributes);
 
-            provider.addAlgorithm("KeyAgreement", BSIObjectIdentifiers.ecka_eg_X963kdf_SHA1, PREFIX + "KeyAgreementSpi$ECKAEGwithSHA1KDF");
-            provider.addAlgorithm("KeyAgreement", BSIObjectIdentifiers.ecka_eg_X963kdf_SHA224, PREFIX + "KeyAgreementSpi$ECKAEGwithSHA224KDF");
-            provider.addAlgorithm("KeyAgreement", BSIObjectIdentifiers.ecka_eg_X963kdf_SHA256, PREFIX + "KeyAgreementSpi$ECKAEGwithSHA256KDF");
-            provider.addAlgorithm("KeyAgreement", BSIObjectIdentifiers.ecka_eg_X963kdf_SHA384, PREFIX + "KeyAgreementSpi$ECKAEGwithSHA384KDF");
-            provider.addAlgorithm("KeyAgreement", BSIObjectIdentifiers.ecka_eg_X963kdf_SHA512, PREFIX + "KeyAgreementSpi$ECKAEGwithSHA512KDF");
-
-            provider.addAlgorithm("KeyAgreement", BSIObjectIdentifiers.ecka_eg_X963kdf_RIPEMD160, PREFIX + "KeyAgreementSpi$ECKAEGwithRIPEMD160KDF");
-            provider.addAlgorithm("KeyAgreement.ECKAEGWITHRIPEMD160KDF", PREFIX + "KeyAgreementSpi$ECKAEGwithRIPEMD160KDF");
+            provider.addAlgorithm("KeyAgreement", BSIObjectIdentifiers.ecka_eg_X963kdf_RIPEMD160, PREFIX + "KeyAgreementSpi$ECKAEGwithRIPEMD160KDF", generalEcAttributes);
+            provider.addAlgorithm("KeyAgreement.ECKAEGWITHRIPEMD160KDF", PREFIX + "KeyAgreementSpi$ECKAEGwithRIPEMD160KDF", generalEcAttributes);
 
             registerOid(provider, X9ObjectIdentifiers.id_ecPublicKey, "EC", new KeyFactorySpi.EC());
 
@@ -158,25 +157,25 @@
 
             if (!Properties.isOverrideSet("org.bouncycastle.ec.disable_mqv"))
             {
-                provider.addAlgorithm("KeyAgreement.ECMQV", PREFIX + "KeyAgreementSpi$MQV");
+                provider.addAlgorithm("KeyAgreement.ECMQV", PREFIX + "KeyAgreementSpi$MQV", generalEcAttributes);
 
-                provider.addAlgorithm("KeyAgreement.ECMQVWITHSHA1CKDF", PREFIX + "KeyAgreementSpi$MQVwithSHA1CKDF");
-                provider.addAlgorithm("KeyAgreement.ECMQVWITHSHA224CKDF", PREFIX + "KeyAgreementSpi$MQVwithSHA224CKDF");
-                provider.addAlgorithm("KeyAgreement.ECMQVWITHSHA256CKDF", PREFIX + "KeyAgreementSpi$MQVwithSHA256CKDF");
-                provider.addAlgorithm("KeyAgreement.ECMQVWITHSHA384CKDF", PREFIX + "KeyAgreementSpi$MQVwithSHA384CKDF");
-                provider.addAlgorithm("KeyAgreement.ECMQVWITHSHA512CKDF", PREFIX + "KeyAgreementSpi$MQVwithSHA512CKDF");
+                provider.addAlgorithm("KeyAgreement.ECMQVWITHSHA1CKDF", PREFIX + "KeyAgreementSpi$MQVwithSHA1CKDF", generalEcAttributes);
+                provider.addAlgorithm("KeyAgreement.ECMQVWITHSHA224CKDF", PREFIX + "KeyAgreementSpi$MQVwithSHA224CKDF", generalEcAttributes);
+                provider.addAlgorithm("KeyAgreement.ECMQVWITHSHA256CKDF", PREFIX + "KeyAgreementSpi$MQVwithSHA256CKDF", generalEcAttributes);
+                provider.addAlgorithm("KeyAgreement.ECMQVWITHSHA384CKDF", PREFIX + "KeyAgreementSpi$MQVwithSHA384CKDF", generalEcAttributes);
+                provider.addAlgorithm("KeyAgreement.ECMQVWITHSHA512CKDF", PREFIX + "KeyAgreementSpi$MQVwithSHA512CKDF", generalEcAttributes);
 
-                provider.addAlgorithm("KeyAgreement.ECMQVWITHSHA1KDF", PREFIX + "KeyAgreementSpi$MQVwithSHA1KDF");
-                provider.addAlgorithm("KeyAgreement.ECMQVWITHSHA224KDF", PREFIX + "KeyAgreementSpi$MQVwithSHA224KDF");
-                provider.addAlgorithm("KeyAgreement.ECMQVWITHSHA256KDF", PREFIX + "KeyAgreementSpi$MQVwithSHA256KDF");
-                provider.addAlgorithm("KeyAgreement.ECMQVWITHSHA384KDF", PREFIX + "KeyAgreementSpi$MQVwithSHA384KDF");
-                provider.addAlgorithm("KeyAgreement.ECMQVWITHSHA512KDF", PREFIX + "KeyAgreementSpi$MQVwithSHA512KDF");
+                provider.addAlgorithm("KeyAgreement.ECMQVWITHSHA1KDF", PREFIX + "KeyAgreementSpi$MQVwithSHA1KDF", generalEcAttributes);
+                provider.addAlgorithm("KeyAgreement.ECMQVWITHSHA224KDF", PREFIX + "KeyAgreementSpi$MQVwithSHA224KDF", generalEcAttributes);
+                provider.addAlgorithm("KeyAgreement.ECMQVWITHSHA256KDF", PREFIX + "KeyAgreementSpi$MQVwithSHA256KDF", generalEcAttributes);
+                provider.addAlgorithm("KeyAgreement.ECMQVWITHSHA384KDF", PREFIX + "KeyAgreementSpi$MQVwithSHA384KDF", generalEcAttributes);
+                provider.addAlgorithm("KeyAgreement.ECMQVWITHSHA512KDF", PREFIX + "KeyAgreementSpi$MQVwithSHA512KDF", generalEcAttributes);
 
-                provider.addAlgorithm("KeyAgreement." + X9ObjectIdentifiers.mqvSinglePass_sha1kdf_scheme, PREFIX + "KeyAgreementSpi$MQVwithSHA1KDFAndSharedInfo");
-                provider.addAlgorithm("KeyAgreement." + SECObjectIdentifiers.mqvSinglePass_sha224kdf_scheme, PREFIX + "KeyAgreementSpi$MQVwithSHA224KDFAndSharedInfo");
-                provider.addAlgorithm("KeyAgreement." + SECObjectIdentifiers.mqvSinglePass_sha256kdf_scheme, PREFIX + "KeyAgreementSpi$MQVwithSHA256KDFAndSharedInfo");
-                provider.addAlgorithm("KeyAgreement." + SECObjectIdentifiers.mqvSinglePass_sha384kdf_scheme, PREFIX + "KeyAgreementSpi$MQVwithSHA384KDFAndSharedInfo");
-                provider.addAlgorithm("KeyAgreement." + SECObjectIdentifiers.mqvSinglePass_sha512kdf_scheme, PREFIX + "KeyAgreementSpi$MQVwithSHA512KDFAndSharedInfo");
+                provider.addAlgorithm("KeyAgreement." + X9ObjectIdentifiers.mqvSinglePass_sha1kdf_scheme, PREFIX + "KeyAgreementSpi$MQVwithSHA1KDFAndSharedInfo", generalEcAttributes);
+                provider.addAlgorithm("KeyAgreement." + SECObjectIdentifiers.mqvSinglePass_sha224kdf_scheme, PREFIX + "KeyAgreementSpi$MQVwithSHA224KDFAndSharedInfo", generalEcAttributes);
+                provider.addAlgorithm("KeyAgreement." + SECObjectIdentifiers.mqvSinglePass_sha256kdf_scheme, PREFIX + "KeyAgreementSpi$MQVwithSHA256KDFAndSharedInfo", generalEcAttributes);
+                provider.addAlgorithm("KeyAgreement." + SECObjectIdentifiers.mqvSinglePass_sha384kdf_scheme, PREFIX + "KeyAgreementSpi$MQVwithSHA384KDFAndSharedInfo", generalEcAttributes);
+                provider.addAlgorithm("KeyAgreement." + SECObjectIdentifiers.mqvSinglePass_sha512kdf_scheme, PREFIX + "KeyAgreementSpi$MQVwithSHA512KDFAndSharedInfo", generalEcAttributes);
 
                 registerOid(provider, X9ObjectIdentifiers.dhSinglePass_stdDH_sha1kdf_scheme, "EC", new KeyFactorySpi.EC());
             
@@ -210,17 +209,42 @@
             provider.addAlgorithm("KeyPairGenerator.ECDHC", PREFIX + "KeyPairGeneratorSpi$ECDHC");
             provider.addAlgorithm("KeyPairGenerator.ECIES", PREFIX + "KeyPairGeneratorSpi$ECDH");
 
-            provider.addAlgorithm("Cipher.ECIES", PREFIX + "IESCipher$ECIES");
+            provider.addAlgorithm("Cipher.ECIES", PREFIX + "IESCipher$ECIES", generalEcAttributes);
+            provider.addAlgorithm("Cipher.ECIESwithSHA1", PREFIX + "IESCipher$ECIES", generalEcAttributes);
+            provider.addAlgorithm("Cipher.ECIESWITHSHA1", PREFIX + "IESCipher$ECIES", generalEcAttributes);
+            provider.addAlgorithm("Cipher.ECIESwithSHA256", PREFIX + "IESCipher$ECIESwithSHA256", generalEcAttributes);
+            provider.addAlgorithm("Cipher.ECIESWITHSHA256", PREFIX + "IESCipher$ECIESwithSHA256", generalEcAttributes);
+            provider.addAlgorithm("Cipher.ECIESwithSHA384", PREFIX + "IESCipher$ECIESwithSHA384", generalEcAttributes);
+            provider.addAlgorithm("Cipher.ECIESWITHSHA384", PREFIX + "IESCipher$ECIESwithSHA384", generalEcAttributes);
+            provider.addAlgorithm("Cipher.ECIESwithSHA512", PREFIX + "IESCipher$ECIESwithSHA512", generalEcAttributes);
+            provider.addAlgorithm("Cipher.ECIESWITHSHA512", PREFIX + "IESCipher$ECIESwithSHA512", generalEcAttributes);
 
-            provider.addAlgorithm("Cipher.ECIESwithAES-CBC", PREFIX + "IESCipher$ECIESwithAESCBC");
-            provider.addAlgorithm("Cipher.ECIESWITHAES-CBC", PREFIX + "IESCipher$ECIESwithAESCBC");
-            provider.addAlgorithm("Cipher.ECIESwithDESEDE-CBC", PREFIX + "IESCipher$ECIESwithDESedeCBC");
-            provider.addAlgorithm("Cipher.ECIESWITHDESEDE-CBC", PREFIX + "IESCipher$ECIESwithDESedeCBC");
+            provider.addAlgorithm("Cipher.ECIESwithAES-CBC", PREFIX + "IESCipher$ECIESwithAESCBC", generalEcAttributes);
+            provider.addAlgorithm("Cipher.ECIESWITHAES-CBC", PREFIX + "IESCipher$ECIESwithAESCBC", generalEcAttributes);
+            provider.addAlgorithm("Cipher.ECIESwithSHA1andAES-CBC", PREFIX + "IESCipher$ECIESwithAESCBC", generalEcAttributes);
+            provider.addAlgorithm("Cipher.ECIESWITHSHA1ANDAES-CBC", PREFIX + "IESCipher$ECIESwithAESCBC", generalEcAttributes);
+            provider.addAlgorithm("Cipher.ECIESwithSHA256andAES-CBC", PREFIX + "IESCipher$ECIESwithSHA256andAESCBC", generalEcAttributes);
+            provider.addAlgorithm("Cipher.ECIESWITHSHA256ANDAES-CBC", PREFIX + "IESCipher$ECIESwithSHA256andAESCBC", generalEcAttributes);
+            provider.addAlgorithm("Cipher.ECIESwithSHA384andAES-CBC", PREFIX + "IESCipher$ECIESwithSHA384andAESCBC", generalEcAttributes);
+            provider.addAlgorithm("Cipher.ECIESWITHSHA384ANDAES-CBC", PREFIX + "IESCipher$ECIESwithSHA384andAESCBC", generalEcAttributes);
+            provider.addAlgorithm("Cipher.ECIESwithSHA512andAES-CBC", PREFIX + "IESCipher$ECIESwithSHA512andAESCBC", generalEcAttributes);
+            provider.addAlgorithm("Cipher.ECIESWITHSHA512ANDAES-CBC", PREFIX + "IESCipher$ECIESwithSHA512andAESCBC", generalEcAttributes);
 
-            provider.addAlgorithm("Signature.ECDSA", PREFIX + "SignatureSpi$ecDSA");
+            provider.addAlgorithm("Cipher.ECIESwithDESEDE-CBC", PREFIX + "IESCipher$ECIESwithDESedeCBC", generalEcAttributes);
+            provider.addAlgorithm("Cipher.ECIESWITHDESEDE-CBC", PREFIX + "IESCipher$ECIESwithDESedeCBC", generalEcAttributes);
+            provider.addAlgorithm("Cipher.ECIESwithSHA1andDESEDE-CBC", PREFIX + "IESCipher$ECIESwithDESedeCBC", generalEcAttributes);
+            provider.addAlgorithm("Cipher.ECIESWITHSHA1ANDDESEDE-CBC", PREFIX + "IESCipher$ECIESwithDESedeCBC", generalEcAttributes);
+            provider.addAlgorithm("Cipher.ECIESwithSHA256andDESEDE-CBC", PREFIX + "IESCipher$ECIESwithSHA256andDESedeCBC", generalEcAttributes);
+            provider.addAlgorithm("Cipher.ECIESWITHSHA256ANDDESEDE-CBC", PREFIX + "IESCipher$ECIESwithSHA256andDESedeCBC", generalEcAttributes);
+            provider.addAlgorithm("Cipher.ECIESwithSHA384andDESEDE-CBC", PREFIX + "IESCipher$ECIESwithSHA384andDESedeCBC", generalEcAttributes);
+            provider.addAlgorithm("Cipher.ECIESWITHSHA384ANDDESEDE-CBC", PREFIX + "IESCipher$ECIESwithSHA384andDESedeCBC", generalEcAttributes);
+            provider.addAlgorithm("Cipher.ECIESwithSHA512andDESEDE-CBC", PREFIX + "IESCipher$ECIESwithSHA512andDESedeCBC", generalEcAttributes);
+            provider.addAlgorithm("Cipher.ECIESWITHSHA512ANDDESEDE-CBC", PREFIX + "IESCipher$ECIESwithSHA512andDESedeCBC", generalEcAttributes);
 
-            provider.addAlgorithm("Signature.SHA1withECDSA", PREFIX + "SignatureSpi$ecDSA");
-            provider.addAlgorithm("Signature.NONEwithECDSA", PREFIX + "SignatureSpi$ecDSAnone");
+            provider.addAlgorithm("Cipher.ETSIKEMWITHSHA256", PREFIX + "IESKEMCipher$KEMwithSHA256", generalEcAttributes);
+
+            provider.addAlgorithm("Signature.ECDSA", PREFIX + "SignatureSpi$ecDSA", generalEcAttributes);
+            provider.addAlgorithm("Signature.NONEwithECDSA", PREFIX + "SignatureSpi$ecDSAnone", generalEcAttributes);
 
             provider.addAlgorithm("Alg.Alias.Signature.ECDSA", "SHA1withECDSA");
             provider.addAlgorithm("Alg.Alias.Signature.ECDSAwithSHA1", "SHA1withECDSA");
@@ -231,16 +255,16 @@
             provider.addAlgorithm("Alg.Alias.Signature.1.2.840.10045.4.1", "SHA1withECDSA");
             provider.addAlgorithm("Alg.Alias.Signature." + TeleTrusTObjectIdentifiers.ecSignWithSha1, "ECDSA");
 
-            provider.addAlgorithm("Signature.ECDDSA", PREFIX + "SignatureSpi$ecDetDSA");
-            provider.addAlgorithm("Signature.SHA1WITHECDDSA", PREFIX + "SignatureSpi$ecDetDSA");
-            provider.addAlgorithm("Signature.SHA224WITHECDDSA", PREFIX + "SignatureSpi$ecDetDSA224");
-            provider.addAlgorithm("Signature.SHA256WITHECDDSA", PREFIX + "SignatureSpi$ecDetDSA256");
-            provider.addAlgorithm("Signature.SHA384WITHECDDSA", PREFIX + "SignatureSpi$ecDetDSA384");
-            provider.addAlgorithm("Signature.SHA512WITHECDDSA", PREFIX + "SignatureSpi$ecDetDSA512");
-            provider.addAlgorithm("Signature.SHA3-224WITHECDDSA", PREFIX + "SignatureSpi$ecDetDSASha3_224");
-            provider.addAlgorithm("Signature.SHA3-256WITHECDDSA", PREFIX + "SignatureSpi$ecDetDSASha3_256");
-            provider.addAlgorithm("Signature.SHA3-384WITHECDDSA", PREFIX + "SignatureSpi$ecDetDSASha3_384");
-            provider.addAlgorithm("Signature.SHA3-512WITHECDDSA", PREFIX + "SignatureSpi$ecDetDSASha3_512");
+            provider.addAlgorithm("Signature.ECDDSA", PREFIX + "SignatureSpi$ecDetDSA", generalEcAttributes);
+            provider.addAlgorithm("Signature.SHA1WITHECDDSA", PREFIX + "SignatureSpi$ecDetDSA", generalEcAttributes);
+            provider.addAlgorithm("Signature.SHA224WITHECDDSA", PREFIX + "SignatureSpi$ecDetDSA224", generalEcAttributes);
+            provider.addAlgorithm("Signature.SHA256WITHECDDSA", PREFIX + "SignatureSpi$ecDetDSA256", generalEcAttributes);
+            provider.addAlgorithm("Signature.SHA384WITHECDDSA", PREFIX + "SignatureSpi$ecDetDSA384", generalEcAttributes);
+            provider.addAlgorithm("Signature.SHA512WITHECDDSA", PREFIX + "SignatureSpi$ecDetDSA512", generalEcAttributes);
+            provider.addAlgorithm("Signature.SHA3-224WITHECDDSA", PREFIX + "SignatureSpi$ecDetDSASha3_224", generalEcAttributes);
+            provider.addAlgorithm("Signature.SHA3-256WITHECDDSA", PREFIX + "SignatureSpi$ecDetDSASha3_256", generalEcAttributes);
+            provider.addAlgorithm("Signature.SHA3-384WITHECDDSA", PREFIX + "SignatureSpi$ecDetDSASha3_384", generalEcAttributes);
+            provider.addAlgorithm("Signature.SHA3-512WITHECDDSA", PREFIX + "SignatureSpi$ecDetDSASha3_512", generalEcAttributes);
 
             provider.addAlgorithm("Alg.Alias.Signature.DETECDSA", "ECDDSA");
             provider.addAlgorithm("Alg.Alias.Signature.SHA1WITHDETECDSA", "SHA1WITHECDDSA");
@@ -249,22 +273,29 @@
             provider.addAlgorithm("Alg.Alias.Signature.SHA384WITHDETECDSA", "SHA384WITHECDDSA");
             provider.addAlgorithm("Alg.Alias.Signature.SHA512WITHDETECDSA", "SHA512WITHECDDSA");
 
-            addSignatureAlgorithm(provider, "SHA224", "ECDSA", PREFIX + "SignatureSpi$ecDSA224", X9ObjectIdentifiers.ecdsa_with_SHA224);
-            addSignatureAlgorithm(provider, "SHA256", "ECDSA", PREFIX + "SignatureSpi$ecDSA256", X9ObjectIdentifiers.ecdsa_with_SHA256);
-            addSignatureAlgorithm(provider, "SHA384", "ECDSA", PREFIX + "SignatureSpi$ecDSA384", X9ObjectIdentifiers.ecdsa_with_SHA384);
-            addSignatureAlgorithm(provider, "SHA512", "ECDSA", PREFIX + "SignatureSpi$ecDSA512", X9ObjectIdentifiers.ecdsa_with_SHA512);
-            addSignatureAlgorithm(provider, "SHA3-224", "ECDSA", PREFIX + "SignatureSpi$ecDSASha3_224", NISTObjectIdentifiers.id_ecdsa_with_sha3_224);
-            addSignatureAlgorithm(provider, "SHA3-256", "ECDSA", PREFIX + "SignatureSpi$ecDSASha3_256", NISTObjectIdentifiers.id_ecdsa_with_sha3_256);
-            addSignatureAlgorithm(provider, "SHA3-384", "ECDSA", PREFIX + "SignatureSpi$ecDSASha3_384", NISTObjectIdentifiers.id_ecdsa_with_sha3_384);
-            addSignatureAlgorithm(provider, "SHA3-512", "ECDSA", PREFIX + "SignatureSpi$ecDSASha3_512", NISTObjectIdentifiers.id_ecdsa_with_sha3_512);
+            addSignatureAlgorithm(provider, "SHA224", "ECDSA", PREFIX + "SignatureSpi$ecDSA224", X9ObjectIdentifiers.ecdsa_with_SHA224, generalEcAttributes);
+            addSignatureAlgorithm(provider, "SHA256", "ECDSA", PREFIX + "SignatureSpi$ecDSA256", X9ObjectIdentifiers.ecdsa_with_SHA256, generalEcAttributes);
+            addSignatureAlgorithm(provider, "SHA384", "ECDSA", PREFIX + "SignatureSpi$ecDSA384", X9ObjectIdentifiers.ecdsa_with_SHA384, generalEcAttributes);
+            addSignatureAlgorithm(provider, "SHA512", "ECDSA", PREFIX + "SignatureSpi$ecDSA512", X9ObjectIdentifiers.ecdsa_with_SHA512, generalEcAttributes);
+            addSignatureAlgorithm(provider, "SHA3-224", "ECDSA", PREFIX + "SignatureSpi$ecDSASha3_224", NISTObjectIdentifiers.id_ecdsa_with_sha3_224, generalEcAttributes);
+            addSignatureAlgorithm(provider, "SHA3-256", "ECDSA", PREFIX + "SignatureSpi$ecDSASha3_256", NISTObjectIdentifiers.id_ecdsa_with_sha3_256, generalEcAttributes);
+            addSignatureAlgorithm(provider, "SHA3-384", "ECDSA", PREFIX + "SignatureSpi$ecDSASha3_384", NISTObjectIdentifiers.id_ecdsa_with_sha3_384, generalEcAttributes);
+            addSignatureAlgorithm(provider, "SHA3-512", "ECDSA", PREFIX + "SignatureSpi$ecDSASha3_512", NISTObjectIdentifiers.id_ecdsa_with_sha3_512, generalEcAttributes);
+            addSignatureAlgorithm(provider, "SHAKE128", "ECDSA", PREFIX + "SignatureSpi$ecDSAShake128", CMSObjectIdentifiers.id_ecdsa_with_shake128, generalEcAttributes);
+            addSignatureAlgorithm(provider, "SHAKE256", "ECDSA", PREFIX + "SignatureSpi$ecDSAShake256", CMSObjectIdentifiers.id_ecdsa_with_shake256, generalEcAttributes);
+            addSignatureAlgorithm(provider, "RIPEMD160", "ECDSA", PREFIX + "SignatureSpi$ecDSARipeMD160",TeleTrusTObjectIdentifiers.ecSignWithRipemd160, generalEcAttributes);
 
-            addSignatureAlgorithm(provider, "RIPEMD160", "ECDSA", PREFIX + "SignatureSpi$ecDSARipeMD160",TeleTrusTObjectIdentifiers.ecSignWithRipemd160);
+            provider.addAlgorithm("Signature.SHA1WITHECNR", PREFIX + "SignatureSpi$ecNR", generalEcAttributes);
+            provider.addAlgorithm("Signature.SHA224WITHECNR", PREFIX + "SignatureSpi$ecNR224", generalEcAttributes);
+            provider.addAlgorithm("Signature.SHA256WITHECNR", PREFIX + "SignatureSpi$ecNR256", generalEcAttributes);
+            provider.addAlgorithm("Signature.SHA384WITHECNR", PREFIX + "SignatureSpi$ecNR384", generalEcAttributes);
+            provider.addAlgorithm("Signature.SHA512WITHECNR", PREFIX + "SignatureSpi$ecNR512", generalEcAttributes);
 
-            provider.addAlgorithm("Signature.SHA1WITHECNR", PREFIX + "SignatureSpi$ecNR");
-            provider.addAlgorithm("Signature.SHA224WITHECNR", PREFIX + "SignatureSpi$ecNR224");
-            provider.addAlgorithm("Signature.SHA256WITHECNR", PREFIX + "SignatureSpi$ecNR256");
-            provider.addAlgorithm("Signature.SHA384WITHECNR", PREFIX + "SignatureSpi$ecNR384");
-            provider.addAlgorithm("Signature.SHA512WITHECNR", PREFIX + "SignatureSpi$ecNR512");
+            addSignatureAlgorithm(provider, "SHA1", "CVC-ECDSA", PREFIX + "SignatureSpi$ecCVCDSA", EACObjectIdentifiers.id_TA_ECDSA_SHA_1, generalEcAttributes);
+            addSignatureAlgorithm(provider, "SHA224", "CVC-ECDSA", PREFIX + "SignatureSpi$ecCVCDSA224", EACObjectIdentifiers.id_TA_ECDSA_SHA_224, generalEcAttributes);
+            addSignatureAlgorithm(provider, "SHA256", "CVC-ECDSA", PREFIX + "SignatureSpi$ecCVCDSA256", EACObjectIdentifiers.id_TA_ECDSA_SHA_256, generalEcAttributes);
+            addSignatureAlgorithm(provider, "SHA384", "CVC-ECDSA", PREFIX + "SignatureSpi$ecCVCDSA384", EACObjectIdentifiers.id_TA_ECDSA_SHA_384, generalEcAttributes);
+            addSignatureAlgorithm(provider, "SHA512", "CVC-ECDSA", PREFIX + "SignatureSpi$ecCVCDSA512", EACObjectIdentifiers.id_TA_ECDSA_SHA_512, generalEcAttributes);
 
             addSignatureAlgorithm(provider, "SHA1", "CVC-ECDSA", PREFIX + "SignatureSpi$ecCVCDSA", EACObjectIdentifiers.id_TA_ECDSA_SHA_1);
             addSignatureAlgorithm(provider, "SHA224", "CVC-ECDSA", PREFIX + "SignatureSpi$ecCVCDSA224", EACObjectIdentifiers.id_TA_ECDSA_SHA_224);
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/asymmetric/EXTERNAL.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/asymmetric/EXTERNAL.java
new file mode 100644
index 0000000..277ada3
--- /dev/null
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/asymmetric/EXTERNAL.java
@@ -0,0 +1,128 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.jcajce.provider.asymmetric;
+
+import java.io.IOException;
+import java.security.InvalidKeyException;
+import java.security.Key;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.util.HashMap;
+import java.util.Map;
+
+import com.android.internal.org.bouncycastle.asn1.bc.BCObjectIdentifiers;
+import com.android.internal.org.bouncycastle.asn1.bc.ExternalValue;
+import com.android.internal.org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
+import com.android.internal.org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import com.android.internal.org.bouncycastle.jcajce.ExternalPublicKey;
+import com.android.internal.org.bouncycastle.jcajce.provider.asymmetric.util.BaseKeyFactorySpi;
+import com.android.internal.org.bouncycastle.jcajce.provider.config.ConfigurableProvider;
+import com.android.internal.org.bouncycastle.jcajce.provider.util.AsymmetricAlgorithmProvider;
+import com.android.internal.org.bouncycastle.jcajce.provider.util.AsymmetricKeyInfoConverter;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+public class EXTERNAL
+{
+    private static final String PREFIX = "com.android.internal.org.bouncycastle.jcajce.provider.asymmetric.EXTERNAL";
+
+    private static final Map<String, String> externalAttributes = new HashMap<String, String>();
+
+    static
+    {
+        externalAttributes.put("SupportedKeyClasses", "com.android.internal.org.bouncycastle.jcajce.ExternalPublicKey");
+        externalAttributes.put("SupportedKeyFormats", "X.509");
+    }
+
+    private static AsymmetricKeyInfoConverter baseConverter;
+
+    /**
+     * @hide This class is not part of the Android public SDK API
+     */
+    public static class KeyFactory
+        extends BaseKeyFactorySpi
+    {
+        protected Key engineTranslateKey(Key key)
+            throws InvalidKeyException
+        {
+            try
+            {
+                if (key instanceof PrivateKey)
+                {
+                    return generatePrivate(PrivateKeyInfo.getInstance(key.getEncoded()));
+                }
+                else if (key instanceof PublicKey)
+                {
+                    return generatePublic(SubjectPublicKeyInfo.getInstance(key.getEncoded()));
+                }
+            }
+            catch (IOException e)
+            {
+                throw new InvalidKeyException("key could not be parsed: " + e.getMessage());
+            }
+
+            throw new InvalidKeyException("key not recognized");
+        }
+
+        public PrivateKey generatePrivate(PrivateKeyInfo keyInfo)
+            throws IOException
+        {
+            return baseConverter.generatePrivate(keyInfo);
+        }
+
+        public PublicKey generatePublic(SubjectPublicKeyInfo keyInfo)
+            throws IOException
+        {
+            return baseConverter.generatePublic(keyInfo);
+        }
+    }
+
+    private static class ExternalKeyInfoConverter
+        implements AsymmetricKeyInfoConverter
+    {
+        private final ConfigurableProvider provider;
+
+        public ExternalKeyInfoConverter(ConfigurableProvider provider)
+        {
+            this.provider = provider;
+        }
+
+        public PrivateKey generatePrivate(PrivateKeyInfo keyInfo)
+            throws IOException
+        {
+            throw new UnsupportedOperationException("no support for private key");
+        }
+
+        public PublicKey generatePublic(SubjectPublicKeyInfo keyInfo)
+            throws IOException
+        {
+            ExternalValue extKey = ExternalValue.getInstance(keyInfo.parsePublicKey());
+
+            // TODO: maybe implement some sort of cache lookup?
+
+            return new ExternalPublicKey(extKey);
+        }
+    }
+
+    /**
+     * @hide This class is not part of the Android public SDK API
+     */
+    public static class Mappings
+        extends AsymmetricAlgorithmProvider
+    {
+        public Mappings()
+        {
+        }
+
+        public void configure(ConfigurableProvider provider)
+        {
+            provider.addAlgorithm("KeyFactory.EXTERNAL", PREFIX + "$KeyFactory");
+            provider.addAlgorithm("KeyFactory." + BCObjectIdentifiers.external_value, PREFIX + "$KeyFactory");
+            provider.addAlgorithm("KeyFactory.OID." + BCObjectIdentifiers.external_value, PREFIX + "$KeyFactory");
+
+            baseConverter = new ExternalKeyInfoConverter(provider);
+
+            provider.addKeyInfoConverter(BCObjectIdentifiers.external_value, baseConverter);
+        }
+    }
+}
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/asymmetric/LMS.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/asymmetric/LMS.java
new file mode 100644
index 0000000..0f3eece
--- /dev/null
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/asymmetric/LMS.java
@@ -0,0 +1,37 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.jcajce.provider.asymmetric;
+
+import com.android.internal.org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import com.android.internal.org.bouncycastle.jcajce.provider.config.ConfigurableProvider;
+import com.android.internal.org.bouncycastle.jcajce.provider.util.AsymmetricAlgorithmProvider;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+public class LMS
+{
+    private static final String PREFIX = "com.android.internal.org.bouncycastle.pqc.jcajce.provider" + ".lms.";
+
+    /**
+     * @hide This class is not part of the Android public SDK API
+     */
+    public static class Mappings
+        extends AsymmetricAlgorithmProvider
+    {
+        public Mappings()
+        {
+        }
+
+        public void configure(ConfigurableProvider provider)
+        {
+            provider.addAlgorithm("KeyFactory.LMS", PREFIX + "LMSKeyFactorySpi");
+            provider.addAlgorithm("Alg.Alias.KeyFactory." + PKCSObjectIdentifiers.id_alg_hss_lms_hashsig, "LMS");
+
+            provider.addAlgorithm("KeyPairGenerator.LMS", PREFIX + "LMSKeyPairGeneratorSpi");
+            provider.addAlgorithm("Alg.Alias.KeyPairGenerator." + PKCSObjectIdentifiers.id_alg_hss_lms_hashsig, "LMS");
+
+            provider.addAlgorithm("Signature.LMS", PREFIX + "LMSSignatureSpi$generic");
+            provider.addAlgorithm("Alg.Alias.Signature." + PKCSObjectIdentifiers.id_alg_hss_lms_hashsig, "LMS");
+        }
+    }
+}
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/asymmetric/RSA.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/asymmetric/RSA.java
index 40c585e..4ea65a1 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/asymmetric/RSA.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/asymmetric/RSA.java
@@ -11,6 +11,7 @@
 // Android-removed: Unsupported algorithms
 // import org.bouncycastle.asn1.teletrust.TeleTrusTObjectIdentifiers;
 import com.android.internal.org.bouncycastle.asn1.x509.X509ObjectIdentifiers;
+// import org.bouncycastle.internal.asn1.cms.CMSObjectIdentifiers;
 import com.android.internal.org.bouncycastle.jcajce.provider.asymmetric.rsa.KeyFactorySpi;
 import com.android.internal.org.bouncycastle.jcajce.provider.config.ConfigurableProvider;
 import com.android.internal.org.bouncycastle.jcajce.provider.util.AsymmetricAlgorithmProvider;
@@ -27,7 +28,7 @@
 
     static
     {
-        generalRsaAttributes.put("SupportedKeyClasses", "javax.crypto.interfaces.RSAPublicKey|javax.crypto.interfaces.RSAPrivateKey");
+        generalRsaAttributes.put("SupportedKeyClasses", "java.security.interfaces.RSAPublicKey|java.security.interfaces.RSAPrivateKey");
         generalRsaAttributes.put("SupportedKeyFormats", "PKCS#8|X.509");
     }
 
@@ -80,8 +81,8 @@
             // BEGIN Android-removed: Unsupported algorithms
             /*
             provider.addAlgorithm("Cipher.RSA/PKCS1", PREFIX + "CipherSpi$PKCS1v1_5Padding");
-            provider.addAlgorithm("Cipher", PKCSObjectIdentifiers.rsaEncryption, PREFIX + "CipherSpi$PKCS1v1_5Padding");
-            provider.addAlgorithm("Cipher", X509ObjectIdentifiers.id_ea_rsa, PREFIX + "CipherSpi$PKCS1v1_5Padding");
+            provider.addAlgorithm("Cipher", PKCSObjectIdentifiers.rsaEncryption, PREFIX + "CipherSpi$PKCS1v1_5Padding", generalRsaAttributes);
+            provider.addAlgorithm("Cipher", X509ObjectIdentifiers.id_ea_rsa, PREFIX + "CipherSpi$PKCS1v1_5Padding", generalRsaAttributes);
             provider.addAlgorithm("Cipher.RSA/1", PREFIX + "CipherSpi$PKCS1v1_5Padding_PrivateOnly");
             provider.addAlgorithm("Cipher.RSA/2", PREFIX + "CipherSpi$PKCS1v1_5Padding_PublicOnly");
             provider.addAlgorithm("Cipher.RSA/OAEP", PREFIX + "CipherSpi$OAEPPadding");
@@ -122,12 +123,12 @@
             registerOidAlgorithmParameters(provider, PKCSObjectIdentifiers.id_RSAES_OAEP, "OAEP");
             registerOidAlgorithmParameters(provider, PKCSObjectIdentifiers.id_RSASSA_PSS, "PSS");
 
-            provider.addAlgorithm("Signature.RSASSA-PSS", PREFIX + "PSSSignatureSpi$PSSwithRSA");
-            provider.addAlgorithm("Signature." + PKCSObjectIdentifiers.id_RSASSA_PSS, PREFIX + "PSSSignatureSpi$PSSwithRSA");
-            provider.addAlgorithm("Signature.OID." + PKCSObjectIdentifiers.id_RSASSA_PSS, PREFIX + "PSSSignatureSpi$PSSwithRSA");
+            provider.addAlgorithm("Signature.RSASSA-PSS", PREFIX + "PSSSignatureSpi$PSSwithRSA", generalRsaAttributes);
+            provider.addAlgorithm("Alg.Alias.Signature." + PKCSObjectIdentifiers.id_RSASSA_PSS, "RSASSA-PSS");
+            provider.addAlgorithm("Alg.Alias.Signature.OID." + PKCSObjectIdentifiers.id_RSASSA_PSS, "RSASSA-PSS");
 
-            provider.addAlgorithm("Signature.RSA", PREFIX + "DigestSignatureSpi$noneRSA");
-            provider.addAlgorithm("Signature.RAWRSASSA-PSS", PREFIX + "PSSSignatureSpi$nonePSS");
+            provider.addAlgorithm("Signature.RSA", PREFIX + "DigestSignatureSpi$noneRSA", generalRsaAttributes);
+            provider.addAlgorithm("Signature.RAWRSASSA-PSS", PREFIX + "PSSSignatureSpi$nonePSS", generalRsaAttributes);
 
             provider.addAlgorithm("Alg.Alias.Signature.RAWRSA", "RSA");
             provider.addAlgorithm("Alg.Alias.Signature.NONEWITHRSA", "RSA");
@@ -137,17 +138,43 @@
             provider.addAlgorithm("Alg.Alias.Signature.NONEWITHRSAANDMGF1", "RAWRSASSA-PSS");
             provider.addAlgorithm("Alg.Alias.Signature.RSAPSS", "RSASSA-PSS");
 
-            addPSSSignature(provider, "SHA224", PREFIX + "PSSSignatureSpi$SHA224withRSA");
-            addPSSSignature(provider, "SHA256", PREFIX + "PSSSignatureSpi$SHA256withRSA");
-            addPSSSignature(provider, "SHA384", PREFIX + "PSSSignatureSpi$SHA384withRSA");
-            addPSSSignature(provider, "SHA512", PREFIX + "PSSSignatureSpi$SHA512withRSA");
-            addPSSSignature(provider, "SHA512(224)", PREFIX + "PSSSignatureSpi$SHA512_224withRSA");
-            addPSSSignature(provider, "SHA512(256)", PREFIX + "PSSSignatureSpi$SHA512_256withRSA");
+            addPSSSignature(provider, "SHA224", "MGF1", PREFIX + "PSSSignatureSpi$SHA224withRSA");
+            addPSSSignature(provider, "SHA256", "MGF1", PREFIX + "PSSSignatureSpi$SHA256withRSA");
+            addPSSSignature(provider, "SHA384", "MGF1", PREFIX + "PSSSignatureSpi$SHA384withRSA");
+            addPSSSignature(provider, "SHA512", "MGF1", PREFIX + "PSSSignatureSpi$SHA512withRSA");
+            addPSSSignature(provider, "SHA512(224)", "MGF1", PREFIX + "PSSSignatureSpi$SHA512_224withRSA");
+            addPSSSignature(provider, "SHA512(256)", "MGF1", PREFIX + "PSSSignatureSpi$SHA512_256withRSA");
 
-            addPSSSignature(provider, "SHA3-224", PREFIX + "PSSSignatureSpi$SHA3_224withRSA");
-            addPSSSignature(provider, "SHA3-256", PREFIX + "PSSSignatureSpi$SHA3_256withRSA");
-            addPSSSignature(provider, "SHA3-384", PREFIX + "PSSSignatureSpi$SHA3_384withRSA");
-            addPSSSignature(provider, "SHA3-512", PREFIX + "PSSSignatureSpi$SHA3_512withRSA");
+            addPSSSignature(provider, "SHA3-224", "MGF1", PREFIX + "PSSSignatureSpi$SHA3_224withRSA");
+            addPSSSignature(provider, "SHA3-256", "MGF1", PREFIX + "PSSSignatureSpi$SHA3_256withRSA");
+            addPSSSignature(provider, "SHA3-384", "MGF1", PREFIX + "PSSSignatureSpi$SHA3_384withRSA");
+            addPSSSignature(provider, "SHA3-512", "MGF1", PREFIX + "PSSSignatureSpi$SHA3_512withRSA");
+            addPSSSignature(provider, "SHAKE128", PREFIX + "PSSSignatureSpi$SHAKE128WithRSAPSS", CMSObjectIdentifiers.id_RSASSA_PSS_SHAKE128);
+            addPSSSignature(provider, "SHAKE256", PREFIX + "PSSSignatureSpi$SHAKE256WithRSAPSS", CMSObjectIdentifiers.id_RSASSA_PSS_SHAKE256);
+
+            addPSSSignature(provider, "SHA224", "SHAKE128", PREFIX + "PSSSignatureSpi$SHA224withRSAandSHAKE128");
+            addPSSSignature(provider, "SHA256", "SHAKE128", PREFIX + "PSSSignatureSpi$SHA256withRSAandSHAKE128");
+            addPSSSignature(provider, "SHA384", "SHAKE128", PREFIX + "PSSSignatureSpi$SHA384withRSAandSHAKE128");
+            addPSSSignature(provider, "SHA512", "SHAKE128", PREFIX + "PSSSignatureSpi$SHA512withRSAandSHAKE128");
+            addPSSSignature(provider, "SHA512(224)", "SHAKE128", PREFIX + "PSSSignatureSpi$SHA512_224withRSAandSHAKE128");
+            addPSSSignature(provider, "SHA512(256)", "SHAKE128", PREFIX + "PSSSignatureSpi$SHA512_256withRSAandSHAKE128");
+
+            addPSSSignature(provider, "SHA224", "SHAKE256", PREFIX + "PSSSignatureSpi$SHA224withRSAandSHAKE256");
+            addPSSSignature(provider, "SHA256", "SHAKE256", PREFIX + "PSSSignatureSpi$SHA256withRSAandSHAKE256");
+            addPSSSignature(provider, "SHA384", "SHAKE256", PREFIX + "PSSSignatureSpi$SHA384withRSAandSHAKE256");
+            addPSSSignature(provider, "SHA512", "SHAKE256", PREFIX + "PSSSignatureSpi$SHA512withRSAandSHAKE256");
+            addPSSSignature(provider, "SHA512(224)", "SHAKE256", PREFIX + "PSSSignatureSpi$SHA512_224withRSAandSHAKE256");
+            addPSSSignature(provider, "SHA512(256)", "SHAKE256", PREFIX + "PSSSignatureSpi$SHA512_256withRSAandSHAKE256");
+
+            addPSSSignature(provider, "SHA3-224", "SHAKE128", PREFIX + "PSSSignatureSpi$SHA3_224withRSAandSHAKE128");
+            addPSSSignature(provider, "SHA3-256", "SHAKE128", PREFIX + "PSSSignatureSpi$SHA3_256withRSAandSHAKE128");
+            addPSSSignature(provider, "SHA3-384", "SHAKE128", PREFIX + "PSSSignatureSpi$SHA3_384withRSAandSHAKE128");
+            addPSSSignature(provider, "SHA3-512", "SHAKE128", PREFIX + "PSSSignatureSpi$SHA3_512withRSAandSHAKE128");
+
+            addPSSSignature(provider, "SHA3-224", "SHAKE256", PREFIX + "PSSSignatureSpi$SHA3_224withRSAandSHAKE256");
+            addPSSSignature(provider, "SHA3-256", "SHAKE256", PREFIX + "PSSSignatureSpi$SHA3_256withRSAandSHAKE256");
+            addPSSSignature(provider, "SHA3-384", "SHAKE256", PREFIX + "PSSSignatureSpi$SHA3_384withRSAandSHAKE256");
+            addPSSSignature(provider, "SHA3-512", "SHAKE256", PREFIX + "PSSSignatureSpi$SHA3_512withRSAandSHAKE256");
 
             if (provider.hasAlgorithm("MessageDigest", "MD2"))
             {
@@ -170,7 +197,9 @@
                 provider.addAlgorithm("Alg.Alias.AlgorithmParameters.SHA1withRSA/PSS", "PSS");
                 provider.addAlgorithm("Alg.Alias.AlgorithmParameters.SHA1WITHRSAANDMGF1", "PSS");
 
-                addPSSSignature(provider, "SHA1", PREFIX + "PSSSignatureSpi$SHA1withRSA");
+                addPSSSignature(provider, "SHA1", "MGF1", PREFIX + "PSSSignatureSpi$SHA1withRSA");
+                addPSSSignature(provider, "SHA1", "SHAKE128", PREFIX + "PSSSignatureSpi$SHA1withRSAandSHAKE128");
+                addPSSSignature(provider, "SHA1", "SHAKE256", PREFIX + "PSSSignatureSpi$SHA1withRSAandSHAKE256");
                 addDigestSignature(provider, "SHA1", PREFIX + "DigestSignatureSpi$SHA1", PKCSObjectIdentifiers.sha1WithRSAEncryption);
                 addISO9796Signature(provider, "SHA1", PREFIX + "ISOSignatureSpi$SHA1WithRSAEncryption");
 
@@ -270,6 +299,7 @@
                 provider.addAlgorithm("Alg.Alias.Signature." + oid, mainName);
                 provider.addAlgorithm("Alg.Alias.Signature.OID." + oid, mainName);
             }
+            provider.addAttributes("Signature." + mainName, generalRsaAttributes);
         }
 
         private void addISO9796Signature(
@@ -280,12 +310,37 @@
             provider.addAlgorithm("Alg.Alias.Signature." + digest + "withRSA/ISO9796-2", digest + "WITHRSA/ISO9796-2");
             provider.addAlgorithm("Alg.Alias.Signature." + digest + "WithRSA/ISO9796-2", digest + "WITHRSA/ISO9796-2");
             provider.addAlgorithm("Signature." + digest + "WITHRSA/ISO9796-2", className);
+            provider.addAttributes("Signature." + digest + "WITHRSA/ISO9796-2", generalRsaAttributes);
+        }
+
+        private void addPSSSignature(
+            ConfigurableProvider provider,
+            String digest, String mgf,
+            String className)
+        {
+            String stem = "WITHRSAAND" + mgf;
+            if (mgf.equals("MGF1"))
+            {
+                provider.addAlgorithm("Alg.Alias.Signature." + digest + "withRSA/PSS", digest + stem);
+                provider.addAlgorithm("Alg.Alias.Signature." + digest + "WithRSA/PSS", digest + stem);
+                provider.addAlgorithm("Alg.Alias.Signature." + digest + "WITHRSA/PSS", digest + stem);
+                provider.addAlgorithm("Alg.Alias.Signature." + digest + "withRSASSA-PSS", digest + stem);
+                provider.addAlgorithm("Alg.Alias.Signature." + digest + "WithRSASSA-PSS", digest + stem);
+                provider.addAlgorithm("Alg.Alias.Signature." + digest + "WITHRSASSA-PSS", digest + stem);
+            }
+
+            provider.addAlgorithm("Alg.Alias.Signature." + digest + "withRSAand" + mgf, digest + stem);
+            provider.addAlgorithm("Alg.Alias.Signature." + digest + "WithRSAAnd" + mgf, digest + stem);
+
+            provider.addAlgorithm("Signature." + digest + "WITHRSAAND" + mgf, className);
+            provider.addAttributes("Signature." + digest + "WITHRSAAND" + mgf, generalRsaAttributes);
         }
 
         private void addPSSSignature(
             ConfigurableProvider provider,
             String digest,
-            String className)
+            String className,
+            ASN1ObjectIdentifier sigOid)
         {
             provider.addAlgorithm("Alg.Alias.Signature." + digest + "withRSA/PSS", digest + "WITHRSAANDMGF1");
             provider.addAlgorithm("Alg.Alias.Signature." + digest + "WithRSA/PSS", digest + "WITHRSAANDMGF1");
@@ -306,6 +361,7 @@
             provider.addAlgorithm("Alg.Alias.Signature." + digest + "withRSA/X9.31", digest + "WITHRSA/X9.31");
             provider.addAlgorithm("Alg.Alias.Signature." + digest + "WithRSA/X9.31", digest + "WITHRSA/X9.31");
             provider.addAlgorithm("Signature." + digest + "WITHRSA/X9.31", className);
+            provider.addAttributes("Signature." + digest + "WITHRSA/X9.31", generalRsaAttributes);
         }
     }
 }
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/asymmetric/dh/KeyAgreementSpi.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/asymmetric/dh/KeyAgreementSpi.java
index 97a1e6c..f88a335 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/asymmetric/dh/KeyAgreementSpi.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/asymmetric/dh/KeyAgreementSpi.java
@@ -41,6 +41,7 @@
 // import org.bouncycastle.jcajce.spec.DHUParameterSpec;
 // import org.bouncycastle.jcajce.spec.MQVParameterSpec;
 import com.android.internal.org.bouncycastle.jcajce.spec.UserKeyingMaterialSpec;
+import com.android.internal.org.bouncycastle.util.BigIntegers;
 
 /**
  * Diffie-Hellman key agreement. There's actually a better way of doing this
@@ -117,30 +118,9 @@
         //
         int expectedLength = (p.bitLength() + 7) / 8;
 
-        byte[]    tmp = r.toByteArray();
-
-        if (tmp.length == expectedLength)
-        {
-            return tmp;
-        }
-
-        if (tmp[0] == 0 && tmp.length == expectedLength + 1)
-        {
-            byte[]    rv = new byte[tmp.length - 1];
-            
-            System.arraycopy(tmp, 1, rv, 0, rv.length);
-            return rv;
-        }
-
-        // tmp must be shorter than expectedLength
-        // pad to the left with zeros.
-        byte[]    rv = new byte[expectedLength];
-
-        System.arraycopy(tmp, 0, rv, rv.length - tmp.length, tmp.length);
-
-        return rv;
+        return BigIntegers.asUnsignedByteArray(expectedLength, r);
     }
-    
+
     protected Key engineDoPhase(
         Key     key,
         boolean lastPhase) 
@@ -268,7 +248,7 @@
         return super.engineGenerateSecret(algorithm);
     }
 
-    protected void engineInit(
+    protected void doInitFromKey(
         Key                     key,
         AlgorithmParameterSpec  params,
         SecureRandom            random) 
@@ -387,7 +367,7 @@
         this.result = bigIntToBytes(x);
     }
 
-    protected byte[] calcSecret()
+    protected byte[] doCalcSecret()
     {
         return result;
     }
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/asymmetric/dsa/AlgorithmParameterGeneratorSpi.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/asymmetric/dsa/AlgorithmParameterGeneratorSpi.java
index e8a2fb1..f8ddbf4 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/asymmetric/dsa/AlgorithmParameterGeneratorSpi.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/asymmetric/dsa/AlgorithmParameterGeneratorSpi.java
@@ -71,7 +71,7 @@
         }
         else
         {
-            pGen = new DSAParametersGenerator(new SHA256Digest());
+            pGen = new DSAParametersGenerator(SHA256Digest.newInstance());
         }
 
         if (random == null)
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/asymmetric/dsa/DSASigner.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/asymmetric/dsa/DSASigner.java
index cc30d17..9601735 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/asymmetric/dsa/DSASigner.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/asymmetric/dsa/DSASigner.java
@@ -1,6 +1,6 @@
 /* GENERATED SOURCE. DO NOT MODIFY. */
-package com.android.internal.org.bouncycastle.jcajce.provider.asymmetric.dsa;
 
+package com.android.internal.org.bouncycastle.jcajce.provider.asymmetric.dsa;
 import java.math.BigInteger;
 import java.security.AlgorithmParameters;
 import java.security.InvalidKeyException;
@@ -10,7 +10,6 @@
 import java.security.SignatureException;
 import java.security.SignatureSpi;
 import java.security.spec.AlgorithmParameterSpec;
-
 import com.android.internal.org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
 import com.android.internal.org.bouncycastle.asn1.x509.X509ObjectIdentifiers;
 import com.android.internal.org.bouncycastle.crypto.CipherParameters;
@@ -29,7 +28,6 @@
 // import org.bouncycastle.crypto.util.DigestFactory;
 import com.android.internal.org.bouncycastle.crypto.digests.AndroidDigestFactory;
 import com.android.internal.org.bouncycastle.util.Arrays;
-
 /**
  * @hide This class is not part of the Android public SDK API
  */
@@ -41,7 +39,6 @@
     private DSAExt                  signer;
     private DSAEncoding             encoding = StandardDSAEncoding.INSTANCE;
     private SecureRandom            random;
-
     protected DSASigner(
         Digest digest,
         DSAExt signer)
@@ -49,17 +46,14 @@
         this.digest = digest;
         this.signer = signer;
     }
-
     protected void engineInitVerify(
         PublicKey   publicKey)
         throws InvalidKeyException
     {
         CipherParameters    param = DSAUtil.generatePublicKeyParameter(publicKey);
-
         digest.reset();
         signer.init(false, param);
     }
-
     protected void engineInitSign(
         PrivateKey      privateKey,
         SecureRandom    random)
@@ -68,53 +62,43 @@
         this.random = random;
         engineInitSign(privateKey);
     }
-
     protected void engineInitSign(
         PrivateKey  privateKey)
         throws InvalidKeyException
     {
         CipherParameters    param = DSAUtil.generatePrivateKeyParameter(privateKey);
-
         // Android-added: Check DSA keys when generated
         DSAParameters dsaParam = ((DSAKeyParameters) param).getParameters();
         checkKey(dsaParam);
-
         if (random != null)
         {
             param = new ParametersWithRandom(param, random);
         }
-
         digest.reset();
         signer.init(true, param);
     }
-
     protected void engineUpdate(
         byte    b)
         throws SignatureException
     {
         digest.update(b);
     }
-
     protected void engineUpdate(
         byte[]  b,
         int     off,
-        int     len) 
+        int     len)
         throws SignatureException
     {
         digest.update(b, off, len);
     }
-
     protected byte[] engineSign()
         throws SignatureException
     {
         byte[]  hash = new byte[digest.getDigestSize()];
-
         digest.doFinal(hash, 0);
-
         try
         {
             BigInteger[] sig = signer.generateSignature(hash);
-
             return encoding.encode(signer.getOrder(), sig[0], sig[1]);
         }
         catch (Exception e)
@@ -122,17 +106,13 @@
             throw new SignatureException(e.toString());
         }
     }
-
     protected boolean engineVerify(
-        byte[]  sigBytes) 
+        byte[]  sigBytes)
         throws SignatureException
     {
         byte[]  hash = new byte[digest.getDigestSize()];
-
         digest.doFinal(hash, 0);
-
         BigInteger[] sig;
-
         try
         {
             sig = encoding.decode(signer.getOrder(), sigBytes);
@@ -141,27 +121,22 @@
         {
             throw new SignatureException("error decoding signature bytes.");
         }
-
         return signer.verifySignature(hash, sig[0], sig[1]);
     }
-
     protected AlgorithmParameters engineGetParameters()
     {
         return null;
     }
-
     protected void engineSetParameter(
         AlgorithmParameterSpec params)
     {
         throw new UnsupportedOperationException("engineSetParameter unsupported");
     }
-
     // BEGIN Android-added: Check DSA keys when generated
     protected void checkKey(DSAParameters params) throws InvalidKeyException {
         int valueL = params.getP().bitLength();
         int valueN = params.getQ().bitLength();
         int digestSize = digest.getDigestSize();
-
         // The checks are consistent with DSAParametersGenerator's init method.
         if ((valueL < 1024 || valueL > 3072) || valueL % 1024 != 0) {
             throw new InvalidKeyException("valueL values must be between 1024 and 3072 and a multiple of 1024");
@@ -176,7 +151,6 @@
             throw new InvalidKeyException("Key is too strong for this signature algorithm");
         }
     }
-
     // END Android-added: Check DSA keys when generated
     /**
      * @deprecated replaced with #engineSetParameter(java.security.spec.AlgorithmParameterSpec)
@@ -187,7 +161,6 @@
     {
         throw new UnsupportedOperationException("engineSetParameter unsupported");
     }
-
     /**
      * @deprecated
      */
@@ -196,7 +169,6 @@
     {
         throw new UnsupportedOperationException("engineGetParameter unsupported");
     }
-
     /**
      * @hide This class is not part of the Android public SDK API
      */
@@ -210,7 +182,6 @@
             super(AndroidDigestFactory.getSHA1(), new com.android.internal.org.bouncycastle.crypto.signers.DSASigner());
         }
     }
-
     // BEGIN Android-removed: Unsupported algorithm
     /*
     static public class detDSA
@@ -221,6 +192,14 @@
             super(DigestFactory.createSHA1(), new org.bouncycastle.crypto.signers.DSASigner(new HMacDSAKCalculator(DigestFactory.createSHA1())));
         }
     }
+    static public class dsaRMD160
+        extends DSASigner
+    {
+        public dsaRMD160()
+        {
+            super(new RIPEMD160Digest(), new org.bouncycastle.crypto.signers.DSASigner());
+        }
+    }
     */
     // END Android-removed: Unsupported algorithm
 
@@ -237,7 +216,6 @@
             super(AndroidDigestFactory.getSHA224(), new com.android.internal.org.bouncycastle.crypto.signers.DSASigner());
         }
     }
-
     // BEGIN Android-removed: Unsupported algorithm
     /*
     static public class detDSA224
@@ -250,7 +228,6 @@
     }
     */
     // END Android-removed: Unsupported algorithm
-
     /**
      * @hide This class is not part of the Android public SDK API
      */
@@ -264,7 +241,6 @@
             super(AndroidDigestFactory.getSHA256(), new com.android.internal.org.bouncycastle.crypto.signers.DSASigner());
         }
     }
-
     // BEGIN Android-removed: Unsupported algorithms
     /*
     static public class detDSA256
@@ -275,7 +251,6 @@
             super(DigestFactory.createSHA256(), new org.bouncycastle.crypto.signers.DSASigner(new HMacDSAKCalculator(DigestFactory.createSHA256())));
         }
     }
-
     static public class dsa384
         extends DSASigner
     {
@@ -284,7 +259,6 @@
             super(DigestFactory.createSHA384(), new org.bouncycastle.crypto.signers.DSASigner());
         }
     }
-
     static public class detDSA384
         extends DSASigner
     {
@@ -293,7 +267,6 @@
             super(DigestFactory.createSHA384(), new org.bouncycastle.crypto.signers.DSASigner(new HMacDSAKCalculator(DigestFactory.createSHA384())));
         }
     }
-
     static public class dsa512
         extends DSASigner
     {
@@ -302,7 +275,6 @@
             super(DigestFactory.createSHA512(), new org.bouncycastle.crypto.signers.DSASigner());
         }
     }
-
     static public class detDSA512
         extends DSASigner
     {
@@ -311,7 +283,6 @@
             super(DigestFactory.createSHA512(), new org.bouncycastle.crypto.signers.DSASigner(new HMacDSAKCalculator(DigestFactory.createSHA512())));
         }
     }
-
     static public class dsaSha3_224
         extends DSASigner
     {
@@ -320,7 +291,6 @@
             super(DigestFactory.createSHA3_224(), new org.bouncycastle.crypto.signers.DSASigner());
         }
     }
-
     static public class detDSASha3_224
         extends DSASigner
     {
@@ -329,7 +299,6 @@
             super(DigestFactory.createSHA3_224(), new org.bouncycastle.crypto.signers.DSASigner(new HMacDSAKCalculator(DigestFactory.createSHA3_224())));
         }
     }
-
     static public class dsaSha3_256
         extends DSASigner
     {
@@ -338,7 +307,6 @@
             super(DigestFactory.createSHA3_256(), new org.bouncycastle.crypto.signers.DSASigner());
         }
     }
-
     static public class detDSASha3_256
         extends DSASigner
     {
@@ -347,7 +315,6 @@
             super(DigestFactory.createSHA3_256(), new org.bouncycastle.crypto.signers.DSASigner(new HMacDSAKCalculator(DigestFactory.createSHA3_256())));
         }
     }
-
     static public class dsaSha3_384
         extends DSASigner
     {
@@ -356,7 +323,6 @@
             super(DigestFactory.createSHA3_384(), new org.bouncycastle.crypto.signers.DSASigner());
         }
     }
-
     static public class detDSASha3_384
         extends DSASigner
     {
@@ -365,7 +331,6 @@
             super(DigestFactory.createSHA3_384(), new org.bouncycastle.crypto.signers.DSASigner(new HMacDSAKCalculator(DigestFactory.createSHA3_384())));
         }
     }
-
     static public class dsaSha3_512
         extends DSASigner
     {
@@ -374,7 +339,6 @@
             super(DigestFactory.createSHA3_512(), new org.bouncycastle.crypto.signers.DSASigner());
         }
     }
-
     static public class detDSASha3_512
         extends DSASigner
     {
@@ -385,7 +349,6 @@
     }
     */
     // END Android-removed: Unsupported algorithms
-
     /**
      * @hide This class is not part of the Android public SDK API
      */
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/asymmetric/dsa/KeyPairGeneratorSpi.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/asymmetric/dsa/KeyPairGeneratorSpi.java
index 5541f66..996c7be 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/asymmetric/dsa/KeyPairGeneratorSpi.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/asymmetric/dsa/KeyPairGeneratorSpi.java
@@ -152,7 +152,7 @@
                         else if (strength > 1024)
                         {
                             dsaParams = new DSAParameterGenerationParameters(strength, 256, certainty, random);
-                            pGen = new DSAParametersGenerator(new SHA256Digest());
+                            pGen = new DSAParametersGenerator(SHA256Digest.newInstance());
                             pGen.init(dsaParams);
                         }
                         else
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/asymmetric/ec/AlgorithmParametersSpi.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/asymmetric/ec/AlgorithmParametersSpi.java
index e9fe33f..3b6ecea 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/asymmetric/ec/AlgorithmParametersSpi.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/asymmetric/ec/AlgorithmParametersSpi.java
@@ -16,6 +16,7 @@
 import com.android.internal.org.bouncycastle.asn1.x9.X9ECPoint;
 import com.android.internal.org.bouncycastle.jcajce.provider.asymmetric.util.EC5Util;
 import com.android.internal.org.bouncycastle.jcajce.provider.asymmetric.util.ECUtil;
+import com.android.internal.org.bouncycastle.jcajce.provider.config.ProviderConfiguration;
 import com.android.internal.org.bouncycastle.jce.provider.BouncyCastleProvider;
 import com.android.internal.org.bouncycastle.jce.spec.ECNamedCurveSpec;
 import com.android.internal.org.bouncycastle.math.ec.ECCurve;
@@ -41,12 +42,14 @@
         if (algorithmParameterSpec instanceof ECGenParameterSpec)
         {
             ECGenParameterSpec ecGenParameterSpec = (ECGenParameterSpec)algorithmParameterSpec;
-            X9ECParameters params = ECUtils.getDomainParametersFromGenSpec(ecGenParameterSpec);
+            ProviderConfiguration configuration = BouncyCastleProvider.CONFIGURATION;
 
-            if (params == null)
+            X9ECParameters params = ECUtils.getDomainParametersFromGenSpec(ecGenParameterSpec, configuration);
+            if (null == params)
             {
                 throw new InvalidParameterSpecException("EC curve name not recognized: " + ecGenParameterSpec.getName());
             }
+
             curveName = ecGenParameterSpec.getName();
             ECParameterSpec baseSpec = EC5Util.convertToSpec(params);
             ecParameterSpec = new ECNamedCurveSpec(curveName,
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/asymmetric/ec/BCECPrivateKey.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/asymmetric/ec/BCECPrivateKey.java
index 051e0b6..f08909a 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/asymmetric/ec/BCECPrivateKey.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/asymmetric/ec/BCECPrivateKey.java
@@ -11,18 +11,20 @@
 import java.security.spec.EllipticCurve;
 import java.util.Enumeration;
 
+import com.android.internal.org.bouncycastle.asn1.ASN1BitString;
 import com.android.internal.org.bouncycastle.asn1.ASN1Encodable;
 import com.android.internal.org.bouncycastle.asn1.ASN1Encoding;
 import com.android.internal.org.bouncycastle.asn1.ASN1Integer;
 import com.android.internal.org.bouncycastle.asn1.ASN1ObjectIdentifier;
 import com.android.internal.org.bouncycastle.asn1.ASN1Primitive;
-import com.android.internal.org.bouncycastle.asn1.DERBitString;
 import com.android.internal.org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
 import com.android.internal.org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 import com.android.internal.org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import com.android.internal.org.bouncycastle.asn1.x9.ECNamedCurveTable;
 import com.android.internal.org.bouncycastle.asn1.x9.X962Parameters;
 import com.android.internal.org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
 import com.android.internal.org.bouncycastle.crypto.params.ECDomainParameters;
+import com.android.internal.org.bouncycastle.crypto.params.ECNamedDomainParameters;
 import com.android.internal.org.bouncycastle.crypto.params.ECPrivateKeyParameters;
 import com.android.internal.org.bouncycastle.jcajce.provider.asymmetric.util.EC5Util;
 import com.android.internal.org.bouncycastle.jcajce.provider.asymmetric.util.ECUtil;
@@ -31,7 +33,9 @@
 import com.android.internal.org.bouncycastle.jce.interfaces.ECPointEncoder;
 import com.android.internal.org.bouncycastle.jce.interfaces.PKCS12BagAttributeCarrier;
 import com.android.internal.org.bouncycastle.jce.provider.BouncyCastleProvider;
+import com.android.internal.org.bouncycastle.jce.spec.ECNamedCurveParameterSpec;
 import com.android.internal.org.bouncycastle.math.ec.ECCurve;
+import com.android.internal.org.bouncycastle.util.Arrays;
 
 /**
  * @hide This class is not part of the Android public SDK API
@@ -41,16 +45,20 @@
 {
     static final long serialVersionUID = 994553197664784084L;
 
-    private String          algorithm = "EC";
-    private boolean         withCompression;
+    private String algorithm = "EC";
+    private boolean withCompression;
 
-    private transient BigInteger              d;
-    private transient ECParameterSpec         ecSpec;
-    private transient ProviderConfiguration   configuration;
-    private transient DERBitString            publicKey;
+    private transient BigInteger d;
+    private transient ECParameterSpec ecSpec;
+    private transient ProviderConfiguration configuration;
+    private transient ASN1BitString publicKey;
+    private transient PrivateKeyInfo privateKeyInfo;
+    private transient byte[] encoding;
 
+    private transient ECPrivateKeyParameters baseKey;
     private transient PKCS12BagAttributeCarrierImpl attrCarrier = new PKCS12BagAttributeCarrierImpl();
 
+
     protected BCECPrivateKey()
     {
     }
@@ -63,6 +71,7 @@
         this.algorithm = key.getAlgorithm();
         this.ecSpec = key.getParams();
         this.configuration = configuration;
+        this.baseKey = convertToBaseKey(this);
     }
 
     public BCECPrivateKey(
@@ -88,6 +97,7 @@
         }
 
         this.configuration = configuration;
+        this.baseKey = convertToBaseKey(this);
     }
 
 
@@ -100,6 +110,7 @@
         this.d = spec.getS();
         this.ecSpec = spec.getParams();
         this.configuration = configuration;
+        this.baseKey = convertToBaseKey(this);
     }
 
     public BCECPrivateKey(
@@ -113,6 +124,7 @@
         this.attrCarrier = key.attrCarrier;
         this.publicKey = key.publicKey;
         this.configuration = key.configuration;
+        this.baseKey = key.baseKey;
     }
 
     public BCECPrivateKey(
@@ -125,6 +137,7 @@
         this.algorithm = algorithm;
         this.d = params.getD();
         this.configuration = configuration;
+        this.baseKey = params;
 
         if (spec == null)
         {
@@ -155,6 +168,7 @@
         this.algorithm = algorithm;
         this.d = params.getD();
         this.configuration = configuration;
+        this.baseKey = params;
 
         if (spec == null)
         {
@@ -193,10 +207,11 @@
         this.d = params.getD();
         this.ecSpec = null;
         this.configuration = configuration;
+        this.baseKey = params;
     }
 
     BCECPrivateKey(
-        String         algorithm,
+        String algorithm,
         PrivateKeyInfo info,
         ProviderConfiguration configuration)
         throws IOException
@@ -217,7 +232,7 @@
         ASN1Encodable privKey = info.parsePrivateKey();
         if (privKey instanceof ASN1Integer)
         {
-            ASN1Integer          derD = ASN1Integer.getInstance(privKey);
+            ASN1Integer derD = ASN1Integer.getInstance(privKey);
 
             this.d = derD.getValue();
         }
@@ -228,6 +243,7 @@
             this.d = ec.getKey();
             this.publicKey = ec.getPublicKey();
         }
+        this.baseKey = convertToBaseKey(this);
     }
 
     public String getAlgorithm()
@@ -253,40 +269,71 @@
      */
     public byte[] getEncoded()
     {
-        X962Parameters  params = ECUtils.getDomainParametersFromName(ecSpec, withCompression);
+        if (encoding == null)
+        {
+            PrivateKeyInfo info = getPrivateKeyInfo();
 
-        int orderBitLength;
-        if (ecSpec == null)
-        {
-            orderBitLength = ECUtil.getOrderBitLength(configuration, null, this.getS());
-        }
-        else
-        {
-            orderBitLength = ECUtil.getOrderBitLength(configuration, ecSpec.getOrder(), this.getS());
-        }
-        
-        PrivateKeyInfo          info;
-        com.android.internal.org.bouncycastle.asn1.sec.ECPrivateKey            keyStructure;
+            if (info == null)
+            {
+                return null;
+            }
 
-        if (publicKey != null)
-        {
-            keyStructure = new com.android.internal.org.bouncycastle.asn1.sec.ECPrivateKey(orderBitLength, this.getS(), publicKey, params);
-        }
-        else
-        {
-            keyStructure = new com.android.internal.org.bouncycastle.asn1.sec.ECPrivateKey(orderBitLength, this.getS(), params);
+            try
+            {
+                encoding = info.getEncoded(ASN1Encoding.DER);
+            }
+            catch (IOException e)
+            {
+                return null;
+            }
         }
 
-        try
-        {
-            info = new PrivateKeyInfo(new AlgorithmIdentifier(X9ObjectIdentifiers.id_ecPublicKey, params), keyStructure);
+        return Arrays.clone(encoding);
+    }
 
-            return info.getEncoded(ASN1Encoding.DER);
-        }
-        catch (IOException e)
+    private PrivateKeyInfo getPrivateKeyInfo()
+    {
+        if (privateKeyInfo == null)
         {
-            return null;
+            X962Parameters params = ECUtils.getDomainParametersFromName(ecSpec, withCompression);
+
+            int orderBitLength;
+            if (ecSpec == null)
+            {
+                orderBitLength = ECUtil.getOrderBitLength(configuration, null, this.getS());
+            }
+            else
+            {
+                orderBitLength = ECUtil.getOrderBitLength(configuration, ecSpec.getOrder(), this.getS());
+            }
+
+            com.android.internal.org.bouncycastle.asn1.sec.ECPrivateKey keyStructure;
+
+            if (publicKey != null)
+            {
+                keyStructure = new com.android.internal.org.bouncycastle.asn1.sec.ECPrivateKey(orderBitLength, this.getS(), publicKey, params);
+            }
+            else
+            {
+                keyStructure = new com.android.internal.org.bouncycastle.asn1.sec.ECPrivateKey(orderBitLength, this.getS(), params);
+            }
+
+            try
+            {
+                privateKeyInfo = new PrivateKeyInfo(new AlgorithmIdentifier(X9ObjectIdentifiers.id_ecPublicKey, params), keyStructure);
+            }
+            catch (IOException e)
+            {
+                return null;
+            }
         }
+
+        return privateKeyInfo;
+    }
+
+    public ECPrivateKeyParameters engineGetKeyParameters()
+    {
+        return baseKey;
     }
 
     public ECParameterSpec getParams()
@@ -300,7 +347,6 @@
         {
             return null;
         }
-        
         return EC5Util.convertSpec(ecSpec);
     }
 
@@ -323,10 +369,10 @@
     {
         return d;
     }
-    
+
     public void setBagAttribute(
         ASN1ObjectIdentifier oid,
-        ASN1Encodable        attribute)
+        ASN1Encodable attribute)
     {
         attrCarrier.setBagAttribute(oid, attribute);
     }
@@ -344,19 +390,37 @@
 
     public void setPointFormat(String style)
     {
-       withCompression = !("UNCOMPRESSED".equalsIgnoreCase(style));
+        withCompression = !("UNCOMPRESSED".equalsIgnoreCase(style));
     }
 
     public boolean equals(Object o)
     {
-        if (!(o instanceof BCECPrivateKey))
+        if (o instanceof ECPrivateKey)
         {
-            return false;
+            ECPrivateKey other = (ECPrivateKey)o;
+
+            PrivateKeyInfo info = this.getPrivateKeyInfo();
+            PrivateKeyInfo otherInfo = (other instanceof BCECPrivateKey) ? ((BCECPrivateKey)other).getPrivateKeyInfo() : PrivateKeyInfo.getInstance(other.getEncoded());
+
+            if (info == null || otherInfo == null)
+            {
+                return false;
+            }
+
+            try
+            {
+                boolean algEquals = Arrays.constantTimeAreEqual(info.getPrivateKeyAlgorithm().getEncoded(), otherInfo.getPrivateKeyAlgorithm().getEncoded());
+                boolean keyEquals = Arrays.constantTimeAreEqual(this.getS().toByteArray(), other.getS().toByteArray());
+
+                return algEquals & keyEquals;
+            }
+            catch (IOException e)
+            {
+                return false;
+            }
         }
 
-        BCECPrivateKey other = (BCECPrivateKey)o;
-
-        return getD().equals(other.getD()) && (engineGetSpec().equals(other.engineGetSpec()));
+        return false;
     }
 
     public int hashCode()
@@ -369,7 +433,7 @@
         return ECUtil.privateKeyToString("EC", d, engineGetSpec());
     }
 
-    private DERBitString getPublicKeyDetails(BCECPublicKey pub)
+    private ASN1BitString getPublicKeyDetails(BCECPublicKey pub)
     {
         try
         {
@@ -406,4 +470,31 @@
 
         out.writeObject(this.getEncoded());
     }
+
+    private static ECPrivateKeyParameters convertToBaseKey(BCECPrivateKey key)
+    {
+        com.android.internal.org.bouncycastle.jce.interfaces.ECPrivateKey k = (com.android.internal.org.bouncycastle.jce.interfaces.ECPrivateKey)key;
+        com.android.internal.org.bouncycastle.jce.spec.ECParameterSpec s = k.getParameters();
+
+        if (s == null)
+        {
+            s = BouncyCastleProvider.CONFIGURATION.getEcImplicitlyCa();
+        }
+
+        if (k.getParameters() instanceof ECNamedCurveParameterSpec)
+        {
+            String name = ((ECNamedCurveParameterSpec)k.getParameters()).getName();
+            if (name != null)
+            {
+                return new ECPrivateKeyParameters(
+                    k.getD(),
+                    new ECNamedDomainParameters(ECNamedCurveTable.getOID(name),
+                        s.getCurve(), s.getG(), s.getN(), s.getH(), s.getSeed()));
+            }
+        }
+
+        return new ECPrivateKeyParameters(
+                k.getD(),
+                new ECDomainParameters(s.getCurve(), s.getG(), s.getN(), s.getH(), s.getSeed()));
+    }
 }
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/asymmetric/ec/BCECPublicKey.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/asymmetric/ec/BCECPublicKey.java
index 13e42f8..3d5d552 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/asymmetric/ec/BCECPublicKey.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/asymmetric/ec/BCECPublicKey.java
@@ -10,9 +10,9 @@
 import java.security.spec.ECPublicKeySpec;
 import java.security.spec.EllipticCurve;
 
+import com.android.internal.org.bouncycastle.asn1.ASN1BitString;
 import com.android.internal.org.bouncycastle.asn1.ASN1OctetString;
 import com.android.internal.org.bouncycastle.asn1.ASN1Primitive;
-import com.android.internal.org.bouncycastle.asn1.DERBitString;
 import com.android.internal.org.bouncycastle.asn1.DEROctetString;
 import com.android.internal.org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 import com.android.internal.org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
@@ -29,6 +29,7 @@
 import com.android.internal.org.bouncycastle.jce.interfaces.ECPointEncoder;
 import com.android.internal.org.bouncycastle.jce.provider.BouncyCastleProvider;
 import com.android.internal.org.bouncycastle.math.ec.ECCurve;
+import com.android.internal.org.bouncycastle.util.Arrays;
 import com.android.internal.org.bouncycastle.util.Properties;
 
 /**
@@ -45,6 +46,8 @@
     private transient ECPublicKeyParameters   ecPublicKey;
     private transient ECParameterSpec         ecSpec;
     private transient ProviderConfiguration   configuration;
+    private transient byte[]                  encoding;
+    private transient boolean                 oldPcSet;
 
     public BCECPublicKey(
         String algorithm,
@@ -197,7 +200,7 @@
         ECCurve curve = EC5Util.getCurve(configuration, params);
         ecSpec = EC5Util.convertToSpec(params, curve);
 
-        DERBitString    bits = info.getPublicKeyData();
+        ASN1BitString   bits = info.getPublicKeyData();
         byte[]          data = bits.getBytes();
         ASN1OctetString key = new DEROctetString(data);
 
@@ -239,16 +242,23 @@
 
     public byte[] getEncoded()
     {
-        boolean compress = withCompression || Properties.isOverrideSet("com.android.internal.org.bouncycastle.ec.enable_pc");
+        boolean pcSet = Properties.isOverrideSet("com.android.internal.org.bouncycastle.ec.enable_pc");
+        if (encoding == null || oldPcSet != pcSet)
+        {
+            boolean compress = withCompression || pcSet;
 
-        AlgorithmIdentifier algId = new AlgorithmIdentifier(
-            X9ObjectIdentifiers.id_ecPublicKey,
-            ECUtils.getDomainParametersFromName(ecSpec, compress));
+            AlgorithmIdentifier algId = new AlgorithmIdentifier(
+                X9ObjectIdentifiers.id_ecPublicKey,
+                ECUtils.getDomainParametersFromName(ecSpec, compress));
 
-        byte[] pubKeyOctets = ecPublicKey.getQ().getEncoded(compress);
+            byte[] pubKeyOctets = ecPublicKey.getQ().getEncoded(compress);
 
-        // stored curve is null if ImplicitlyCa
-        return KeyUtil.getEncodedSubjectPublicKeyInfo(algId, pubKeyOctets);
+            // stored curve is null if ImplicitlyCa
+            encoding = KeyUtil.getEncodedSubjectPublicKeyInfo(algId, pubKeyOctets);
+            oldPcSet = pcSet;
+        }
+
+        return Arrays.clone(encoding);
     }
 
     public ECParameterSpec getParams()
@@ -306,18 +316,26 @@
     public void setPointFormat(String style)
     {
        withCompression = !("UNCOMPRESSED".equalsIgnoreCase(style));
+       encoding = null;
     }
 
     public boolean equals(Object o)
     {
-        if (!(o instanceof BCECPublicKey))
+        if (o instanceof BCECPublicKey)
         {
-            return false;
+            BCECPublicKey other = (BCECPublicKey)o;
+
+            return ecPublicKey.getQ().equals(other.ecPublicKey.getQ()) && (engineGetSpec().equals(other.engineGetSpec()));
         }
 
-        BCECPublicKey other = (BCECPublicKey)o;
+        if (o instanceof ECPublicKey)
+        {
+            ECPublicKey other = (ECPublicKey)o;
 
-        return ecPublicKey.getQ().equals(other.ecPublicKey.getQ()) && (engineGetSpec().equals(other.engineGetSpec()));
+            return Arrays.areEqual(getEncoded(), other.getEncoded());
+        }
+
+        return false;
     }
 
     public int hashCode()
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/asymmetric/ec/ECUtils.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/asymmetric/ec/ECUtils.java
index d07bebf..f06afa2 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/asymmetric/ec/ECUtils.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/asymmetric/ec/ECUtils.java
@@ -3,9 +3,11 @@
 
 import java.math.BigInteger;
 import java.security.InvalidKeyException;
+import java.security.PrivateKey;
 import java.security.PublicKey;
 import java.security.spec.ECGenParameterSpec;
 import java.security.spec.ECParameterSpec;
+import java.util.Map;
 
 import com.android.internal.org.bouncycastle.asn1.ASN1ObjectIdentifier;
 import com.android.internal.org.bouncycastle.asn1.DERNull;
@@ -15,6 +17,7 @@
 import com.android.internal.org.bouncycastle.crypto.params.AsymmetricKeyParameter;
 import com.android.internal.org.bouncycastle.jcajce.provider.asymmetric.util.EC5Util;
 import com.android.internal.org.bouncycastle.jcajce.provider.asymmetric.util.ECUtil;
+import com.android.internal.org.bouncycastle.jcajce.provider.config.ProviderConfiguration;
 import com.android.internal.org.bouncycastle.jce.spec.ECNamedCurveSpec;
 import com.android.internal.org.bouncycastle.math.ec.ECCurve;
 
@@ -27,39 +30,49 @@
         return (key instanceof BCECPublicKey) ? ((BCECPublicKey)key).engineGetKeyParameters() : ECUtil.generatePublicKeyParameter(key);
     }
 
-    static X9ECParameters getDomainParametersFromGenSpec(ECGenParameterSpec genSpec)
+    static AsymmetricKeyParameter generatePrivateKeyParameter(
+            PrivateKey key)
+        throws InvalidKeyException
     {
-        return getDomainParametersFromName(genSpec.getName());
+        return (key instanceof BCECPrivateKey) ? ((BCECPrivateKey)key).engineGetKeyParameters() : ECUtil.generatePrivateKeyParameter(key);
     }
 
-    static X9ECParameters getDomainParametersFromName(String curveName)
+    static X9ECParameters getDomainParametersFromGenSpec(ECGenParameterSpec genSpec, ProviderConfiguration configuration)
     {
-        X9ECParameters domainParameters;
-        try
+        return getDomainParametersFromName(genSpec.getName(), configuration);
+    }
+
+    static X9ECParameters getDomainParametersFromName(String curveName, ProviderConfiguration configuration)
+    {
+        if (null == curveName || curveName.length() < 1)
         {
-            if (curveName.charAt(0) >= '0' && curveName.charAt(0) <= '2')
+            return null;
+        }
+
+        int spacePos = curveName.indexOf(' ');
+        if (spacePos > 0)
+        {
+            curveName = curveName.substring(spacePos + 1);
+        }
+
+        ASN1ObjectIdentifier oid = getOID(curveName);
+        if (null == oid)
+        {
+            return ECUtil.getNamedCurveByName(curveName);
+        }
+
+        X9ECParameters x9 = ECUtil.getNamedCurveByOid(oid);
+        if (null == x9)
+        {
+            if (null != configuration)
             {
-                ASN1ObjectIdentifier oidID = new ASN1ObjectIdentifier(curveName);
-                domainParameters = ECUtil.getNamedCurveByOid(oidID);
-            }
-            else
-            {
-                if (curveName.indexOf(' ') > 0)
-                {
-                    curveName = curveName.substring(curveName.indexOf(' ') + 1);
-                    domainParameters = ECUtil.getNamedCurveByName(curveName);
-                }
-                else
-                {
-                    domainParameters = ECUtil.getNamedCurveByName(curveName);
-                }
+                Map extraCurves = configuration.getAdditionalECParameters();
+
+                x9 = (X9ECParameters)extraCurves.get(oid);
             }
         }
-        catch (IllegalArgumentException ex)
-        {
-            domainParameters = ECUtil.getNamedCurveByName(curveName);
-        }
-        return domainParameters;
+
+        return x9;
     }
 
     static X962Parameters getDomainParametersFromName(ECParameterSpec ecSpec, boolean withCompression)
@@ -95,4 +108,20 @@
 
         return params;
     }
+
+    private static ASN1ObjectIdentifier getOID(String curveName)
+    {
+        char firstChar = curveName.charAt(0);
+        if (firstChar >= '0' && firstChar <= '2')
+        {
+            try
+            {
+                return new ASN1ObjectIdentifier(curveName);
+            }
+            catch (Exception e)
+            {
+            }
+        }
+        return null;
+    }
 }
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/asymmetric/ec/GMKeyPairGeneratorSpi.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/asymmetric/ec/GMKeyPairGeneratorSpi.java
new file mode 100644
index 0000000..fdde6c2
--- /dev/null
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/asymmetric/ec/GMKeyPairGeneratorSpi.java
@@ -0,0 +1,259 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.jcajce.provider.asymmetric.ec;
+
+import java.math.BigInteger;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidParameterException;
+import java.security.KeyPair;
+import java.security.SecureRandom;
+import java.security.spec.AlgorithmParameterSpec;
+import java.security.spec.ECGenParameterSpec;
+import java.util.Hashtable;
+
+import com.android.internal.org.bouncycastle.asn1.x9.X9ECParameters;
+import com.android.internal.org.bouncycastle.crypto.AsymmetricCipherKeyPair;
+import com.android.internal.org.bouncycastle.crypto.CryptoServicesRegistrar;
+import com.android.internal.org.bouncycastle.crypto.generators.ECKeyPairGenerator;
+import com.android.internal.org.bouncycastle.crypto.params.ECDomainParameters;
+import com.android.internal.org.bouncycastle.crypto.params.ECKeyGenerationParameters;
+import com.android.internal.org.bouncycastle.crypto.params.ECPrivateKeyParameters;
+import com.android.internal.org.bouncycastle.crypto.params.ECPublicKeyParameters;
+import com.android.internal.org.bouncycastle.jcajce.provider.asymmetric.util.EC5Util;
+import com.android.internal.org.bouncycastle.jcajce.provider.asymmetric.util.ECUtil;
+import com.android.internal.org.bouncycastle.jcajce.provider.config.ProviderConfiguration;
+import com.android.internal.org.bouncycastle.jce.provider.BouncyCastleProvider;
+import com.android.internal.org.bouncycastle.jce.spec.ECNamedCurveGenParameterSpec;
+import com.android.internal.org.bouncycastle.jce.spec.ECNamedCurveSpec;
+import com.android.internal.org.bouncycastle.jce.spec.ECParameterSpec;
+import com.android.internal.org.bouncycastle.math.ec.ECCurve;
+import com.android.internal.org.bouncycastle.math.ec.ECPoint;
+import com.android.internal.org.bouncycastle.util.Integers;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+public abstract class GMKeyPairGeneratorSpi
+    extends java.security.KeyPairGenerator
+{
+    public GMKeyPairGeneratorSpi(String algorithmName)
+    {
+        super(algorithmName);
+    }
+
+    /**
+     * @hide This class is not part of the Android public SDK API
+     */
+    public static class BaseSM2
+        extends GMKeyPairGeneratorSpi
+    {
+        ECKeyGenerationParameters   param;
+        ECKeyPairGenerator          engine = new ECKeyPairGenerator();
+        Object                      ecParams = null;
+        int                         strength = 239;
+        SecureRandom                random = CryptoServicesRegistrar.getSecureRandom();
+        boolean                     initialised = false;
+        String                      algorithm;
+        ProviderConfiguration       configuration;
+
+        static private Hashtable    ecParameters;
+
+        static
+        {
+            ecParameters = new Hashtable();
+
+            ecParameters.put(Integers.valueOf(192), new ECNamedCurveGenParameterSpec("prime192v1")); // a.k.a P-192
+            ecParameters.put(Integers.valueOf(239), new ECNamedCurveGenParameterSpec("prime239v1"));
+            ecParameters.put(Integers.valueOf(256), new ECNamedCurveGenParameterSpec("prime256v1")); // a.k.a P-256
+
+            ecParameters.put(Integers.valueOf(224), new ECNamedCurveGenParameterSpec("P-224"));
+            ecParameters.put(Integers.valueOf(384), new ECNamedCurveGenParameterSpec("P-384"));
+            ecParameters.put(Integers.valueOf(521), new ECNamedCurveGenParameterSpec("P-521"));
+        }
+
+        public BaseSM2()
+        {
+            super("EC");
+            this.algorithm = "EC";
+            this.configuration = BouncyCastleProvider.CONFIGURATION;
+        }
+
+        public BaseSM2(
+            String  algorithm,
+            ProviderConfiguration configuration)
+        {
+            super(algorithm);
+            this.algorithm = algorithm;
+            this.configuration = configuration;
+        }
+
+        public void initialize(
+            int             strength,
+            SecureRandom    random)
+        {
+            this.strength = strength;
+            this.random = random;
+
+            ECNamedCurveGenParameterSpec ecParams = (ECNamedCurveGenParameterSpec)ecParameters.get(Integers.valueOf(strength));
+            if (ecParams == null)
+            {
+                throw new InvalidParameterException("unknown key size.");
+            }
+
+            try
+            {
+                initialize(ecParams, random);
+            }
+            catch (InvalidAlgorithmParameterException e)
+            {
+                throw new InvalidParameterException("key size not configurable.");
+            }
+        }
+
+        public void initialize(
+            AlgorithmParameterSpec  params,
+            SecureRandom            random)
+            throws InvalidAlgorithmParameterException
+        {
+            if (params == null)
+            {
+                ECParameterSpec implicitCA = configuration.getEcImplicitlyCa();
+                if (implicitCA == null)
+                {
+                    throw new InvalidAlgorithmParameterException("null parameter passed but no implicitCA set");
+                }
+
+                this.ecParams = null;
+                this.param = createKeyGenParamsBC(implicitCA, random);
+            }
+            else if (params instanceof ECParameterSpec)
+            {
+                this.ecParams = params;
+                this.param = createKeyGenParamsBC((ECParameterSpec)params, random);
+            }
+            else if (params instanceof java.security.spec.ECParameterSpec)
+            {
+                this.ecParams = params;
+                this.param = createKeyGenParamsJCE((java.security.spec.ECParameterSpec)params, random);
+            }
+            else if (params instanceof ECGenParameterSpec)
+            {
+                initializeNamedCurve(((ECGenParameterSpec)params).getName(), random);
+            }
+            else if (params instanceof ECNamedCurveGenParameterSpec)
+            {
+                initializeNamedCurve(((ECNamedCurveGenParameterSpec)params).getName(), random);
+            }
+            else
+            {
+                String name = ECUtil.getNameFrom(params);
+
+                if (name != null)
+                {
+                    initializeNamedCurve(name, random);
+                }
+                else
+                {
+                    throw new InvalidAlgorithmParameterException("invalid parameterSpec: " + params);
+                }
+            }
+
+            engine.init(param);
+            initialised = true;
+        }
+
+        public KeyPair generateKeyPair()
+        {
+            if (!initialised)
+            {
+                initialize(strength, new SecureRandom());
+            }
+
+            AsymmetricCipherKeyPair     pair = engine.generateKeyPair();
+            ECPublicKeyParameters       pub = (ECPublicKeyParameters)pair.getPublic();
+            ECPrivateKeyParameters      priv = (ECPrivateKeyParameters)pair.getPrivate();
+
+            if (ecParams instanceof ECParameterSpec)
+            {
+                ECParameterSpec p = (ECParameterSpec)ecParams;
+
+                BCECPublicKey pubKey = new BCECPublicKey(algorithm, pub, p, configuration);
+                return new KeyPair(pubKey,
+                                   new BCECPrivateKey(algorithm, priv, pubKey, p, configuration));
+            }
+            else if (ecParams == null)
+            {
+               return new KeyPair(new BCECPublicKey(algorithm, pub, configuration),
+                                   new BCECPrivateKey(algorithm, priv, configuration));
+            }
+            else
+            {
+                java.security.spec.ECParameterSpec p = (java.security.spec.ECParameterSpec)ecParams;
+
+                BCECPublicKey pubKey = new BCECPublicKey(algorithm, pub, p, configuration);
+                
+                return new KeyPair(pubKey, new BCECPrivateKey(algorithm, priv, pubKey, p, configuration));
+            }
+        }
+
+        protected ECKeyGenerationParameters createKeyGenParamsBC(ECParameterSpec p, SecureRandom r)
+        {
+            return new ECKeyGenerationParameters(new ECDomainParameters(p.getCurve(), p.getG(), p.getN(), p.getH()), r);
+        }
+
+        protected ECKeyGenerationParameters createKeyGenParamsJCE(java.security.spec.ECParameterSpec p, SecureRandom r)
+        {
+            if (p instanceof ECNamedCurveSpec)
+            {
+                String curveName = ((ECNamedCurveSpec)p).getName();
+
+                X9ECParameters x9 = ECUtils.getDomainParametersFromName(curveName, configuration);
+                if (null != x9)
+                {
+                    return createKeyGenParamsJCE(x9, r);
+                }
+            }
+
+            ECCurve curve = EC5Util.convertCurve(p.getCurve());
+            ECPoint g = EC5Util.convertPoint(curve, p.getGenerator());
+            BigInteger n = p.getOrder();
+            BigInteger h = BigInteger.valueOf(p.getCofactor());
+            ECDomainParameters dp = new ECDomainParameters(curve, g, n, h);
+            return new ECKeyGenerationParameters(dp, r);
+        }
+
+        protected ECKeyGenerationParameters createKeyGenParamsJCE(X9ECParameters x9, SecureRandom r)
+        {
+            ECDomainParameters dp = new ECDomainParameters(x9.getCurve(), x9.getG(), x9.getN(), x9.getH());
+
+            return new ECKeyGenerationParameters(dp, r);
+        }
+
+        protected void initializeNamedCurve(String curveName, SecureRandom random)
+            throws InvalidAlgorithmParameterException
+        {
+            X9ECParameters x9 = ECUtils.getDomainParametersFromName(curveName, configuration);
+            if (null == x9)
+            {
+                throw new InvalidAlgorithmParameterException("unknown curve name: " + curveName);
+            }
+
+            // Work-around for JDK bug -- it won't look up named curves properly if seed is present
+            byte[] seed = null; //p.getSeed();
+
+            this.ecParams = new ECNamedCurveSpec(curveName, x9.getCurve(), x9.getG(), x9.getN(), x9.getH(), seed);
+            this.param = createKeyGenParamsJCE(x9, random);
+        }
+    }
+
+    /**
+     * @hide This class is not part of the Android public SDK API
+     */
+    public static class SM2
+        extends BaseSM2
+    {
+        public SM2()
+        {
+            super("SM2", BouncyCastleProvider.CONFIGURATION);
+        }
+    }
+}
\ No newline at end of file
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/asymmetric/ec/KeyAgreementSpi.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/asymmetric/ec/KeyAgreementSpi.java
index 324623c..b981962 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/asymmetric/ec/KeyAgreementSpi.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/asymmetric/ec/KeyAgreementSpi.java
@@ -197,21 +197,18 @@
         return null;
     }
 
-    protected void engineInit(
-        Key key,
-        AlgorithmParameterSpec params,
-        SecureRandom random)
+    protected void doInitFromKey(Key key, AlgorithmParameterSpec parameterSpec, SecureRandom random)
         throws InvalidKeyException, InvalidAlgorithmParameterException
     {
         // Android-removed: Unsupported algorithms
-        // if (params != null &&
-        //     !(params instanceof MQVParameterSpec || params instanceof UserKeyingMaterialSpec || params instanceof DHUParameterSpec))
-        if (params != null && !(params instanceof UserKeyingMaterialSpec))
+        // if (parameterSpec != null &&
+        //     !(parameterSpec instanceof MQVParameterSpec || parameterSpec instanceof UserKeyingMaterialSpec || params instanceof DHUParameterSpec))
+        if (parameterSpec != null && !(parameterSpec instanceof UserKeyingMaterialSpec))
         {
             throw new InvalidAlgorithmParameterException("No algorithm parameters supported");
         }
 
-        initFromKey(key, params);
+        initFromKey(key, parameterSpec);
     }
 
     protected void engineInit(
@@ -251,9 +248,9 @@
             {
                 MQVPrivateKey mqvPrivKey = (MQVPrivateKey)key;
                 staticPrivKey = (ECPrivateKeyParameters)
-                    ECUtil.generatePrivateKeyParameter(mqvPrivKey.getStaticPrivateKey());
+                    ECUtils.generatePrivateKeyParameter(mqvPrivKey.getStaticPrivateKey());
                 ephemPrivKey = (ECPrivateKeyParameters)
-                    ECUtil.generatePrivateKeyParameter(mqvPrivKey.getEphemeralPrivateKey());
+                    ECUtils.generatePrivateKeyParameter(mqvPrivKey.getEphemeralPrivateKey());
 
                 ephemPubKey = null;
                 if (mqvPrivKey.getEphemeralPublicKey() != null)
@@ -267,9 +264,9 @@
                 MQVParameterSpec mqvParameterSpec = (MQVParameterSpec)parameterSpec;
 
                 staticPrivKey = (ECPrivateKeyParameters)
-                    ECUtil.generatePrivateKeyParameter((PrivateKey)key);
+                    ECUtils.generatePrivateKeyParameter((PrivateKey)key);
                 ephemPrivKey = (ECPrivateKeyParameters)
-                    ECUtil.generatePrivateKeyParameter(mqvParameterSpec.getEphemeralPrivateKey());
+                    ECUtils.generatePrivateKeyParameter(mqvParameterSpec.getEphemeralPrivateKey());
 
                 ephemPubKey = null;
                 if (mqvParameterSpec.getEphemeralPublicKey() != null)
@@ -301,9 +298,9 @@
             ECPublicKeyParameters ephemPubKey;
 
             staticPrivKey = (ECPrivateKeyParameters)
-                ECUtil.generatePrivateKeyParameter((PrivateKey)key);
+                ECUtils.generatePrivateKeyParameter((PrivateKey)key);
             ephemPrivKey = (ECPrivateKeyParameters)
-                ECUtil.generatePrivateKeyParameter(dheParameterSpec.getEphemeralPrivateKey());
+                ECUtils.generatePrivateKeyParameter(dheParameterSpec.getEphemeralPrivateKey());
 
             ephemPubKey = null;
             if (dheParameterSpec.getEphemeralPublicKey() != null)
@@ -332,7 +329,7 @@
             {
                 throw new InvalidAlgorithmParameterException("no KDF specified for UserKeyingMaterialSpec");
             }
-            ECPrivateKeyParameters privKey = (ECPrivateKeyParameters)ECUtil.generatePrivateKeyParameter((PrivateKey)key);
+            ECPrivateKeyParameters privKey = (ECPrivateKeyParameters)ECUtils.generatePrivateKeyParameter((PrivateKey)key);
             this.parameters = privKey.getParameters();
             ukmParameters = (parameterSpec instanceof UserKeyingMaterialSpec) ? ((UserKeyingMaterialSpec)parameterSpec).getUserKeyingMaterial() : null;
             ((BasicAgreement)agreement).init(privKey);
@@ -346,7 +343,7 @@
         return fullName.substring(fullName.lastIndexOf('.') + 1);
     }
     
-    protected byte[] calcSecret()
+    protected byte[] doCalcSecret()
     {
         return Arrays.clone(result);
     }
@@ -762,8 +759,8 @@
    		{
    			super("ECKAEGwithSHA1KDF", new ECDHBasicAgreement(),
                    new KDF2BytesGenerator(DigestFactory.createSHA1()));
-   		}
-   	}
+           }
+       }
 
     /**
    	 * KeyAgreement according to BSI TR-03111 chapter 4.3.1
@@ -775,8 +772,8 @@
    		{
    			super("ECKAEGwithRIPEMD160KDF", new ECDHBasicAgreement(),
                    new KDF2BytesGenerator(new RIPEMD160Digest()));
-   		}
-   	}
+           }
+       }
 
     /**
    	 * KeyAgreement according to BSI TR-03111 chapter 4.3.1
@@ -788,8 +785,8 @@
    		{
    			super("ECKAEGwithSHA224KDF", new ECDHBasicAgreement(),
                    new KDF2BytesGenerator(DigestFactory.createSHA224()));
-   		}
-   	}
+           }
+       }
 
 	/**
 	 * KeyAgreement according to BSI TR-03111 chapter 4.3.1
@@ -801,8 +798,8 @@
 		{
 			super("ECKAEGwithSHA256KDF", new ECDHBasicAgreement(),
                 new KDF2BytesGenerator(DigestFactory.createSHA256()));
-		}
-	}
+        }
+    }
 
 	/**
 	 * KeyAgreement according to BSI TR-03111 chapter 4.3.1
@@ -814,8 +811,8 @@
 		{
 			super("ECKAEGwithSHA384KDF", new ECDHBasicAgreement(),
                 new KDF2BytesGenerator(DigestFactory.createSHA384()));
-		}
-	}
+        }
+    }
 
 	/**
 	 * KeyAgreement according to BSI TR-03111 chapter 4.3.1
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/asymmetric/ec/KeyFactorySpi.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/asymmetric/ec/KeyFactorySpi.java
index 87ea479..b2fdef1 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/asymmetric/ec/KeyFactorySpi.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/asymmetric/ec/KeyFactorySpi.java
@@ -241,7 +241,11 @@
 
             try
             {
-                return new BCECPrivateKey(algorithm, new PrivateKeyInfo(new AlgorithmIdentifier(X9ObjectIdentifiers.id_ecPublicKey, ecKey.getParameters()), ecKey), configuration);
+                return new BCECPrivateKey(algorithm,
+                    new PrivateKeyInfo(
+                        new AlgorithmIdentifier(X9ObjectIdentifiers.id_ecPublicKey, ecKey.getParametersObject()),
+                        ecKey),
+                    configuration);
             }
             catch (IOException e)
             {
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/asymmetric/ec/KeyPairGeneratorSpi.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/asymmetric/ec/KeyPairGeneratorSpi.java
index 0892390..f024d75 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/asymmetric/ec/KeyPairGeneratorSpi.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/asymmetric/ec/KeyPairGeneratorSpi.java
@@ -9,10 +9,7 @@
 import java.security.spec.AlgorithmParameterSpec;
 import java.security.spec.ECGenParameterSpec;
 import java.util.Hashtable;
-import java.util.Map;
 
-import com.android.internal.org.bouncycastle.asn1.ASN1ObjectIdentifier;
-import com.android.internal.org.bouncycastle.asn1.x9.ECNamedCurveTable;
 import com.android.internal.org.bouncycastle.asn1.x9.X9ECParameters;
 import com.android.internal.org.bouncycastle.crypto.AsymmetricCipherKeyPair;
 import com.android.internal.org.bouncycastle.crypto.CryptoServicesRegistrar;
@@ -63,7 +60,8 @@
 
         static private Hashtable    ecParameters;
 
-        static {
+        static
+        {
             ecParameters = new Hashtable();
 
             ecParameters.put(Integers.valueOf(192), new ECGenParameterSpec("prime192v1")); // a.k.a P-192
@@ -220,13 +218,12 @@
         {
             if (p instanceof ECNamedCurveSpec)
             {
-                X9ECParameters x9P = ECUtils.getDomainParametersFromName(((ECNamedCurveSpec)p).getName());
+                String curveName = ((ECNamedCurveSpec)p).getName();
 
-                if (x9P != null)
+                X9ECParameters x9 = ECUtils.getDomainParametersFromName(curveName, configuration);
+                if (null != x9)
                 {
-                    ECDomainParameters dp = new ECDomainParameters(x9P.getCurve(), x9P.getG(), x9P.getN(), x9P.getH());
-
-                    return new ECKeyGenerationParameters(dp, r);
+                    return createKeyGenParamsJCE(x9, r);
                 }
             }
 
@@ -238,48 +235,27 @@
             return new ECKeyGenerationParameters(dp, r);
         }
 
-        protected ECNamedCurveSpec createNamedCurveSpec(String curveName)
-            throws InvalidAlgorithmParameterException
+        protected ECKeyGenerationParameters createKeyGenParamsJCE(X9ECParameters x9, SecureRandom r)
         {
-            // NOTE: Don't bother with custom curves here as the curve will be converted to JCE type shortly
+            ECDomainParameters dp = new ECDomainParameters(x9.getCurve(), x9.getG(), x9.getN(), x9.getH());
 
-            X9ECParameters p = ECUtils.getDomainParametersFromName(curveName);
-            if (p == null)
-            {
-                try
-                {
-                    // Check whether it's actually an OID string (SunJSSE ServerHandshaker setupEphemeralECDHKeys bug)
-                    p = ECNamedCurveTable.getByOID(new ASN1ObjectIdentifier(curveName));
-                    if (p == null)
-                    {
-                        Map extraCurves = configuration.getAdditionalECParameters();
-
-                        p = (X9ECParameters)extraCurves.get(new ASN1ObjectIdentifier(curveName));
-
-                        if (p == null)
-                        {
-                            throw new InvalidAlgorithmParameterException("unknown curve OID: " + curveName);
-                        }
-                    }
-                }
-                catch (IllegalArgumentException ex)
-                {
-                    throw new InvalidAlgorithmParameterException("unknown curve name: " + curveName);
-                }
-            }
-
-            // Work-around for JDK bug -- it won't look up named curves properly if seed is present
-            byte[] seed = null; //p.getSeed();
-
-            return new ECNamedCurveSpec(curveName, p.getCurve(), p.getG(), p.getN(), p.getH(), seed);
+            return new ECKeyGenerationParameters(dp, r);
         }
 
         protected void initializeNamedCurve(String curveName, SecureRandom random)
             throws InvalidAlgorithmParameterException
         {
-            ECNamedCurveSpec namedCurve = createNamedCurveSpec(curveName);
-            this.ecParams = namedCurve;
-            this.param = createKeyGenParamsJCE(namedCurve, random);
+            X9ECParameters x9 = ECUtils.getDomainParametersFromName(curveName, configuration);
+            if (null == x9)
+            {
+                throw new InvalidAlgorithmParameterException("unknown curve name: " + curveName);
+            }
+
+            // Work-around for JDK bug -- it won't look up named curves properly if seed is present
+            byte[] seed = null; //p.getSeed();
+
+            this.ecParams = new ECNamedCurveSpec(curveName, x9.getCurve(), x9.getG(), x9.getN(), x9.getH(), seed);
+            this.param = createKeyGenParamsJCE(x9, random);
         }
     }
 
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/asymmetric/ec/SignatureSpi.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/asymmetric/ec/SignatureSpi.java
index 87ad765..50ce091 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/asymmetric/ec/SignatureSpi.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/asymmetric/ec/SignatureSpi.java
@@ -12,6 +12,7 @@
 import com.android.internal.org.bouncycastle.crypto.digests.NullDigest;
 // BEGIN Android-removed: Unsupported algorithms
 // import org.bouncycastle.crypto.digests.RIPEMD160Digest;
+// import org.bouncycastle.crypto.digests.SHAKEDigest;
 // END Android-removed: Unsupported algorithms
 import com.android.internal.org.bouncycastle.crypto.params.ParametersWithRandom;
 import com.android.internal.org.bouncycastle.crypto.signers.DSAEncoding;
@@ -26,7 +27,6 @@
 // import org.bouncycastle.crypto.util.DigestFactory;
 import com.android.internal.org.bouncycastle.crypto.digests.AndroidDigestFactory;
 import com.android.internal.org.bouncycastle.jcajce.provider.asymmetric.util.DSABase;
-import com.android.internal.org.bouncycastle.jcajce.provider.asymmetric.util.ECUtil;
 
 /**
  * @hide This class is not part of the Android public SDK API
@@ -52,7 +52,7 @@
         PrivateKey privateKey)
         throws InvalidKeyException
     {
-        CipherParameters param = ECUtil.generatePrivateKeyParameter(privateKey);
+        CipherParameters param = ECUtils.generatePrivateKeyParameter(privateKey);
 
         digest.reset();
 
@@ -297,6 +297,26 @@
         }
     }
 
+    static public class ecDSAShake128
+         extends SignatureSpi
+    {
+        public ecDSAShake128()
+        {
+            // RFC 8702 specifies deterministic DSA
+            super(new SHAKEDigest(128), new ECDSASigner(new HMacDSAKCalculator(new SHAKEDigest(128))), StandardDSAEncoding.INSTANCE);
+        }
+    }
+
+    static public class ecDSAShake256
+        extends SignatureSpi
+    {
+        public ecDSAShake256()
+        {
+            // RFC 8702 specifies deterministic DSA
+            super(new SHAKEDigest(256), new ECDSASigner(new HMacDSAKCalculator(new SHAKEDigest(256))), StandardDSAEncoding.INSTANCE);
+        }
+    }
+
     static public class ecNR
         extends SignatureSpi
     {
@@ -395,6 +415,41 @@
             super(new RIPEMD160Digest(), new ECDSASigner(), PlainDSAEncoding.INSTANCE);
         }
     }
+    static public class ecCVCDSA3_224
+        extends SignatureSpi
+    {
+        public ecCVCDSA3_224()
+        {
+            super(DigestFactory.createSHA3_224(), new ECDSASigner(), PlainDSAEncoding.INSTANCE);
+        }
+    }
+
+    static public class ecCVCDSA3_256
+        extends SignatureSpi
+    {
+        public ecCVCDSA3_256()
+        {
+            super(DigestFactory.createSHA3_256(), new ECDSASigner(), PlainDSAEncoding.INSTANCE);
+        }
+    }
+
+    static public class ecCVCDSA3_384
+        extends SignatureSpi
+    {
+        public ecCVCDSA3_384()
+        {
+            super(DigestFactory.createSHA3_384(), new ECDSASigner(), PlainDSAEncoding.INSTANCE);
+        }
+    }
+
+    static public class ecCVCDSA3_512
+        extends SignatureSpi
+    {
+        public ecCVCDSA3_512()
+        {
+            super(DigestFactory.createSHA3_512(), new ECDSASigner(), PlainDSAEncoding.INSTANCE);
+        }
+    }
     */
     // END Android-removed: Unsupported algorithms
 }
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/asymmetric/rsa/AlgorithmParametersSpi.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/asymmetric/rsa/AlgorithmParametersSpi.java
index 8381d95..4420aea 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/asymmetric/rsa/AlgorithmParametersSpi.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/asymmetric/rsa/AlgorithmParametersSpi.java
@@ -12,9 +12,11 @@
 
 import com.android.internal.org.bouncycastle.asn1.ASN1Encoding;
 import com.android.internal.org.bouncycastle.asn1.ASN1Integer;
+import com.android.internal.org.bouncycastle.asn1.ASN1ObjectIdentifier;
 import com.android.internal.org.bouncycastle.asn1.ASN1OctetString;
 import com.android.internal.org.bouncycastle.asn1.DERNull;
 import com.android.internal.org.bouncycastle.asn1.DEROctetString;
+import com.android.internal.org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
 import com.android.internal.org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
 import com.android.internal.org.bouncycastle.asn1.pkcs.RSAESOAEPparams;
 import com.android.internal.org.bouncycastle.asn1.pkcs.RSASSAPSSparams;
@@ -184,16 +186,36 @@
             throws IOException
         {
             PSSParameterSpec pssSpec = currentSpec;
-            AlgorithmIdentifier hashAlgorithm = new AlgorithmIdentifier(
-                                                DigestFactory.getOID(pssSpec.getDigestAlgorithm()),
-                                                DERNull.INSTANCE);
-            MGF1ParameterSpec mgfSpec = (MGF1ParameterSpec)pssSpec.getMGFParameters();
-            AlgorithmIdentifier maskGenAlgorithm = new AlgorithmIdentifier(
-                                                PKCSObjectIdentifiers.id_mgf1,
-                                                new AlgorithmIdentifier(DigestFactory.getOID(mgfSpec.getDigestAlgorithm()), DERNull.INSTANCE));
-            RSASSAPSSparams pssP = new RSASSAPSSparams(hashAlgorithm, maskGenAlgorithm, new ASN1Integer(pssSpec.getSaltLength()), new ASN1Integer(pssSpec.getTrailerField()));
+            ASN1ObjectIdentifier digOid = DigestFactory.getOID(pssSpec.getDigestAlgorithm());
+            AlgorithmIdentifier hashAlgorithm;
+            // RFC 8072
+            if (NISTObjectIdentifiers.id_shake128.equals(digOid) || NISTObjectIdentifiers.id_shake256.equals(digOid))
+            {
+                hashAlgorithm = new AlgorithmIdentifier(digOid);
+            }
+            else
+            {
+                hashAlgorithm = new AlgorithmIdentifier(digOid, DERNull.INSTANCE);
+            }
             
-            return pssP.getEncoded("DER");
+            MGF1ParameterSpec mgfSpec = (MGF1ParameterSpec)pssSpec.getMGFParameters();
+            if (mgfSpec != null)
+            {
+                AlgorithmIdentifier maskGenAlgorithm = new AlgorithmIdentifier(
+                    PKCSObjectIdentifiers.id_mgf1,
+                    new AlgorithmIdentifier(DigestFactory.getOID(mgfSpec.getDigestAlgorithm()), DERNull.INSTANCE));
+                RSASSAPSSparams pssP = new RSASSAPSSparams(hashAlgorithm, maskGenAlgorithm, new ASN1Integer(pssSpec.getSaltLength()), new ASN1Integer(pssSpec.getTrailerField()));
+
+                return pssP.getEncoded("DER");
+            }
+            else
+            {
+                AlgorithmIdentifier maskGenAlgorithm = new AlgorithmIdentifier(
+                    pssSpec.getMGFAlgorithm().equals("SHAKE128") ? NISTObjectIdentifiers.id_shake128 : NISTObjectIdentifiers.id_shake256);
+                RSASSAPSSparams pssP = new RSASSAPSSparams(hashAlgorithm, maskGenAlgorithm, new ASN1Integer(pssSpec.getSaltLength()), new ASN1Integer(pssSpec.getTrailerField()));
+
+                return pssP.getEncoded("DER");
+            }
         }
     
         protected byte[] engineGetEncoded(
@@ -241,17 +263,29 @@
             {
                 RSASSAPSSparams pssP = RSASSAPSSparams.getInstance(params);
 
-                if (!pssP.getMaskGenAlgorithm().getAlgorithm().equals(PKCSObjectIdentifiers.id_mgf1))
+                ASN1ObjectIdentifier mgfOid = pssP.getMaskGenAlgorithm().getAlgorithm();
+                if (mgfOid.equals(PKCSObjectIdentifiers.id_mgf1))
+                {
+                    currentSpec = new PSSParameterSpec(
+                        MessageDigestUtils.getDigestName(pssP.getHashAlgorithm().getAlgorithm()),
+                        PSSParameterSpec.DEFAULT.getMGFAlgorithm(),
+                        new MGF1ParameterSpec(MessageDigestUtils.getDigestName(AlgorithmIdentifier.getInstance(pssP.getMaskGenAlgorithm().getParameters()).getAlgorithm())),
+                        pssP.getSaltLength().intValue(),
+                        pssP.getTrailerField().intValue());
+                }
+                else if (mgfOid.equals(NISTObjectIdentifiers.id_shake128) || mgfOid.equals(NISTObjectIdentifiers.id_shake256))
+                {
+                    currentSpec = new PSSParameterSpec(
+                        MessageDigestUtils.getDigestName(pssP.getHashAlgorithm().getAlgorithm()),
+                        mgfOid.equals(NISTObjectIdentifiers.id_shake128) ? "SHAKE128" : "SHAKE256",
+                        null,
+                        pssP.getSaltLength().intValue(),
+                        pssP.getTrailerField().intValue());
+                }
+                else
                 {
                     throw new IOException("unknown mask generation function: " + pssP.getMaskGenAlgorithm().getAlgorithm());
                 }
-
-                currentSpec = new PSSParameterSpec(
-                                       MessageDigestUtils.getDigestName(pssP.getHashAlgorithm().getAlgorithm()),
-                                       PSSParameterSpec.DEFAULT.getMGFAlgorithm(),
-                                       new MGF1ParameterSpec(MessageDigestUtils.getDigestName(AlgorithmIdentifier.getInstance(pssP.getMaskGenAlgorithm().getParameters()).getAlgorithm())),
-                                       pssP.getSaltLength().intValue(),
-                                       pssP.getTrailerField().intValue());
             }
             catch (ClassCastException e)
             {
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/asymmetric/rsa/CipherSpi.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/asymmetric/rsa/CipherSpi.java
index 8a9a86c..f6d0b00 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/asymmetric/rsa/CipherSpi.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/asymmetric/rsa/CipherSpi.java
@@ -31,7 +31,6 @@
 // Android-removed: Unsupported algorithm
 // import org.bouncycastle.crypto.encodings.ISO9796d1Encoding;
 import com.android.internal.org.bouncycastle.crypto.encodings.OAEPEncoding;
-import com.android.internal.org.bouncycastle.crypto.encodings.PKCS1Encoding;
 import com.android.internal.org.bouncycastle.crypto.engines.RSABlindedEngine;
 import com.android.internal.org.bouncycastle.crypto.params.ParametersWithRandom;
 import com.android.internal.org.bouncycastle.jcajce.provider.asymmetric.util.BaseCipherSpi;
@@ -209,7 +208,7 @@
         }
         else if (pad.equals("PKCS1PADDING"))
         {
-            cipher = new PKCS1Encoding(new RSABlindedEngine());
+            cipher = new CustomPKCS1Encoding(new RSABlindedEngine());
         }
         // BEGIN Android-removed: Unsupported algorithm
         // else if (pad.equals("ISO9796-1PADDING"))
@@ -546,15 +545,26 @@
     {
         try
         {
-            return cipher.processBlock(bOut.getBuf(), 0, bOut.size());
-        }
-        catch (InvalidCipherTextException e)
-        {
-            throw new BadBlockException("unable to decrypt block", e);
-        }
-        catch (ArrayIndexOutOfBoundsException e)
-        {
-            throw new BadBlockException("unable to decrypt block", e);
+            byte[] output;
+            try
+            {
+                output = cipher.processBlock(bOut.getBuf(), 0, bOut.size());
+            }
+            catch (InvalidCipherTextException e)
+            {
+                throw new BadBlockException("unable to decrypt block", e);
+            }
+            catch (ArrayIndexOutOfBoundsException e)
+            {
+                throw new BadBlockException("unable to decrypt block", e);
+            }
+
+            if (output == null)
+            {
+                throw new BadBlockException("unable to decrypt block", null);
+            }
+
+            return output;
         }
         finally
         {
@@ -583,7 +593,7 @@
     {
         public PKCS1v1_5Padding()
         {
-            super(new PKCS1Encoding(new RSABlindedEngine()));
+            super(new CustomPKCS1Encoding(new RSABlindedEngine()));
         }
     }
 
@@ -592,7 +602,7 @@
     {
         public PKCS1v1_5Padding_PrivateOnly()
         {
-            super(false, true, new PKCS1Encoding(new RSABlindedEngine()));
+            super(false, true, new CustomPKCS1Encoding(new RSABlindedEngine()));
         }
     }
 
@@ -601,7 +611,7 @@
     {
         public PKCS1v1_5Padding_PublicOnly()
         {
-            super(true, false, new PKCS1Encoding(new RSABlindedEngine()));
+            super(true, false, new CustomPKCS1Encoding(new RSABlindedEngine()));
         }
     }
 
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/asymmetric/rsa/CustomPkcs1Encoding.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/asymmetric/rsa/CustomPkcs1Encoding.java
new file mode 100644
index 0000000..371dd19
--- /dev/null
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/asymmetric/rsa/CustomPkcs1Encoding.java
@@ -0,0 +1,264 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.jcajce.provider.asymmetric.rsa;
+
+import java.security.SecureRandom;
+
+import com.android.internal.org.bouncycastle.crypto.AsymmetricBlockCipher;
+import com.android.internal.org.bouncycastle.crypto.CipherParameters;
+import com.android.internal.org.bouncycastle.crypto.CryptoServicesRegistrar;
+import com.android.internal.org.bouncycastle.crypto.InvalidCipherTextException;
+import com.android.internal.org.bouncycastle.crypto.encodings.PKCS1Encoding;
+import com.android.internal.org.bouncycastle.crypto.params.AsymmetricKeyParameter;
+import com.android.internal.org.bouncycastle.crypto.params.ParametersWithRandom;
+import com.android.internal.org.bouncycastle.util.Arrays;
+import com.android.internal.org.bouncycastle.util.Properties;
+
+/**
+ * this does your basic PKCS 1 v1.5 padding - whether or not you should be using this
+ * depends on your application - see PKCS1 Version 2 for details.
+ */
+class CustomPKCS1Encoding
+    implements AsymmetricBlockCipher
+{
+    private static final int HEADER_LENGTH = 10;
+
+    private SecureRandom random;
+    private AsymmetricBlockCipher engine;
+    private boolean forEncryption;
+    private boolean forPrivateKey;
+    private boolean useStrictLength;
+    private byte[] blockBuffer;
+
+    /**
+     * Basic constructor.
+     *
+     * @param cipher
+     */
+    CustomPKCS1Encoding(AsymmetricBlockCipher cipher)
+    {
+        this.engine = cipher;
+        this.useStrictLength = useStrict();
+    }
+
+    //
+    // for J2ME compatibility
+    //
+    private boolean useStrict()
+    {
+        if (Properties.isOverrideSetTo(PKCS1Encoding.NOT_STRICT_LENGTH_ENABLED_PROPERTY, true))
+        {
+            return false;
+        }
+
+        return !Properties.isOverrideSetTo(PKCS1Encoding.STRICT_LENGTH_ENABLED_PROPERTY, false);
+    }
+
+    public AsymmetricBlockCipher getUnderlyingCipher()
+    {
+        return engine;
+    }
+
+    public void init(boolean forEncryption, CipherParameters param)
+    {
+        AsymmetricKeyParameter kParam;
+
+        if (param instanceof ParametersWithRandom)
+        {
+            ParametersWithRandom rParam = (ParametersWithRandom)param;
+
+            this.random = rParam.getRandom();
+            kParam = (AsymmetricKeyParameter)rParam.getParameters();
+        }
+        else
+        {
+            kParam = (AsymmetricKeyParameter)param;
+            if (!kParam.isPrivate() && forEncryption)
+            {
+                this.random = CryptoServicesRegistrar.getSecureRandom();
+            }
+        }
+
+        engine.init(forEncryption, param);
+
+        this.forPrivateKey = kParam.isPrivate();
+        this.forEncryption = forEncryption;
+        this.blockBuffer = new byte[engine.getOutputBlockSize()];
+    }
+
+    public int getInputBlockSize()
+    {
+        int baseBlockSize = engine.getInputBlockSize();
+
+        if (forEncryption)
+        {
+            return baseBlockSize - HEADER_LENGTH;
+        }
+        else
+        {
+            return baseBlockSize;
+        }
+    }
+
+    public int getOutputBlockSize()
+    {
+        int baseBlockSize = engine.getOutputBlockSize();
+
+        if (forEncryption)
+        {
+            return baseBlockSize;
+        }
+        else
+        {
+            return baseBlockSize - HEADER_LENGTH;
+        }
+    }
+
+    public byte[] processBlock(byte[] in, int inOff, int inLen) throws InvalidCipherTextException
+    {
+        if (forEncryption)
+        {
+            return encodeBlock(in, inOff, inLen);
+        }
+        else
+        {
+            return decodeBlock(in, inOff, inLen);
+        }
+    }
+
+    private byte[] encodeBlock(byte[] in, int inOff, int inLen) throws InvalidCipherTextException
+    {
+        if (inLen > getInputBlockSize())
+        {
+            throw new IllegalArgumentException("input data too large");
+        }
+
+        byte[] block = new byte[engine.getInputBlockSize()];
+
+        if (forPrivateKey)
+        {
+            block[0] = 0x01;                        // type code 1
+
+            for (int i = 1; i != block.length - inLen - 1; i++)
+            {
+                block[i] = (byte)0xFF;
+            }
+        }
+        else
+        {
+            random.nextBytes(block);                // random fill
+
+            block[0] = 0x02;                        // type code 2
+
+            //
+            // a zero byte marks the end of the padding, so all
+            // the pad bytes must be non-zero.
+            //
+            for (int i = 1; i != block.length - inLen - 1; i++)
+            {
+                while (block[i] == 0)
+                {
+                    block[i] = (byte)random.nextInt();
+                }
+            }
+        }
+
+        block[block.length - inLen - 1] = 0x00;       // mark the end of the padding
+        System.arraycopy(in, inOff, block, block.length - inLen, inLen);
+
+        return engine.processBlock(block, 0, block.length);
+    }
+
+    /**
+     * Check the argument is a valid encoding with type 1. Returns the plaintext length if valid, or -1 if invalid.
+     */
+    private static int checkPkcs1Encoding1(byte[] buf)
+    {
+        int foundZeroMask = 0;
+        int lastPadPos = 0;
+
+        // The first byte should be 0x01
+        int badPadSign = -((buf[0] & 0xFF) ^ 0x01);
+
+        // There must be a zero terminator for the padding somewhere
+        for (int i = 1; i < buf.length; ++i)
+        {
+            int padByte = buf[i] & 0xFF;
+            int is0x00Mask = ((padByte ^ 0x00) - 1) >> 31;
+            int is0xFFMask = ((padByte ^ 0xFF) - 1) >> 31;
+            lastPadPos ^= i & ~foundZeroMask & is0x00Mask;
+            foundZeroMask |= is0x00Mask;
+            badPadSign |= ~(foundZeroMask | is0xFFMask);
+        }
+
+        // The header should be at least 10 bytes
+        badPadSign |= lastPadPos - 9;
+
+        int plaintextLength = buf.length - 1 - lastPadPos;
+        return plaintextLength | badPadSign >> 31;
+    }
+
+    /**
+     * Check the argument is a valid encoding with type 2. Returns the plaintext length if valid, or -1 if invalid.
+     */
+    private static int checkPkcs1Encoding2(byte[] buf)
+    {
+        int foundZeroMask = 0;
+        int lastPadPos = 0;
+
+        // The first byte should be 0x02
+        int badPadSign = -((buf[0] & 0xFF) ^ 0x02);
+
+        // There must be a zero terminator for the padding somewhere
+        for (int i = 1; i < buf.length; ++i)
+        {
+            int padByte = buf[i] & 0xFF;
+            int is0x00Mask = ((padByte ^ 0x00) - 1) >> 31;
+            lastPadPos ^= i & ~foundZeroMask & is0x00Mask;
+            foundZeroMask |= is0x00Mask;
+        }
+
+        // The header should be at least 10 bytes
+        badPadSign |= lastPadPos - 9;
+
+        int plaintextLength = buf.length - 1 - lastPadPos;
+        return plaintextLength | badPadSign >> 31;
+    }
+
+    /**
+     * @throws InvalidCipherTextException if the decrypted block is not in PKCS1 format.
+     */
+    private byte[] decodeBlock(byte[] in, int inOff, int inLen)
+        throws InvalidCipherTextException
+    {
+        int strictBlockSize = engine.getOutputBlockSize();
+        byte[] block = engine.processBlock(in, inOff, inLen);
+
+        boolean incorrectLength = useStrictLength & (block.length != strictBlockSize);
+
+        byte[] data = block;
+        if (block.length < strictBlockSize)
+        {
+            data = blockBuffer;
+        }
+
+        int plaintextLength = forPrivateKey ? checkPkcs1Encoding2(data) : checkPkcs1Encoding1(data);
+
+        try
+        {
+            if (plaintextLength < 0 | incorrectLength)
+            {
+                // Special behaviour to avoid throw/catch/throw in CipherSpi
+                return null;
+            }
+
+            byte[] result = new byte[plaintextLength];
+            System.arraycopy(data, data.length - plaintextLength, result, 0, plaintextLength);
+            return result;
+        }
+        finally
+        {
+            Arrays.fill(block, (byte)0);
+            Arrays.fill(blockBuffer, 0, Math.max(0, blockBuffer.length - block.length), (byte)0);
+        }
+    }
+}
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/asymmetric/util/BaseAgreementSpi.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/asymmetric/util/BaseAgreementSpi.java
index b89cd73..9f0afce 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/asymmetric/util/BaseAgreementSpi.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/asymmetric/util/BaseAgreementSpi.java
@@ -1,7 +1,12 @@
 /* GENERATED SOURCE. DO NOT MODIFY. */
 package com.android.internal.org.bouncycastle.jcajce.provider.asymmetric.util;
 
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.Key;
 import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.security.spec.AlgorithmParameterSpec;
 import java.util.HashMap;
 import java.util.Hashtable;
 import java.util.Map;
@@ -27,6 +32,7 @@
 // import org.bouncycastle.crypto.agreement.kdf.DHKEKGenerator;
 import com.android.internal.org.bouncycastle.crypto.params.DESParameters;
 import com.android.internal.org.bouncycastle.crypto.params.KDFParameters;
+import com.android.internal.org.bouncycastle.jcajce.spec.HybridValueParameterSpec;
 import com.android.internal.org.bouncycastle.util.Arrays;
 import com.android.internal.org.bouncycastle.util.Integers;
 import com.android.internal.org.bouncycastle.util.Strings;
@@ -152,6 +158,7 @@
     protected final DerivationFunction kdf;
 
     protected byte[]     ukmParameters;
+    private HybridValueParameterSpec hybridSpec;
 
     public BaseAgreementSpi(String kaAlgorithm, DerivationFunction kdf)
     {
@@ -225,6 +232,40 @@
         }
     }
 
+    protected void engineInit(
+        Key             key,
+        SecureRandom    random)
+        throws InvalidKeyException
+    {
+        try
+        {
+            doInitFromKey(key, null, random);
+        }
+        catch (InvalidAlgorithmParameterException e)
+        {
+            // this should never occur.
+            throw new InvalidKeyException(e.getMessage());
+        }
+    }
+
+    protected void engineInit(
+        Key key,
+        AlgorithmParameterSpec params,
+        SecureRandom random)
+        throws InvalidKeyException, InvalidAlgorithmParameterException
+    {
+        if (params instanceof HybridValueParameterSpec)
+        {
+            this.hybridSpec = (HybridValueParameterSpec)params;
+            doInitFromKey(key, hybridSpec.getBaseParameterSpec(), random);
+        }
+        else
+        {
+            this.hybridSpec = null;
+            doInitFromKey(key, params, random);
+        }
+    }
+
     protected byte[] engineGenerateSecret()
         throws IllegalStateException
     {
@@ -351,5 +392,26 @@
         }
     }
 
-    protected abstract byte[] calcSecret();
+    private byte[] calcSecret()
+    {
+        if (hybridSpec != null)
+        {
+            // Set Z' to Z || T
+            byte[] s = doCalcSecret();
+            byte[] sec = Arrays.concatenate(s, hybridSpec.getT());
+
+            Arrays.clear(s);
+
+            return sec;
+        }
+        else
+        {
+            return doCalcSecret();
+        }
+    }
+
+    protected abstract byte[] doCalcSecret();
+
+    protected abstract void doInitFromKey(Key key, AlgorithmParameterSpec parameterSpec, SecureRandom random)
+        throws InvalidKeyException, InvalidAlgorithmParameterException;
 }
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/asymmetric/util/EC5Util.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/asymmetric/util/EC5Util.java
index 7eaea81..3beccd4 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/asymmetric/util/EC5Util.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/asymmetric/util/EC5Util.java
@@ -20,6 +20,7 @@
 import com.android.internal.org.bouncycastle.asn1.x9.ECNamedCurveTable;
 import com.android.internal.org.bouncycastle.asn1.x9.X962Parameters;
 import com.android.internal.org.bouncycastle.asn1.x9.X9ECParameters;
+import com.android.internal.org.bouncycastle.asn1.x9.X9ECParametersHolder;
 import com.android.internal.org.bouncycastle.crypto.ec.CustomNamedCurves;
 import com.android.internal.org.bouncycastle.crypto.params.ECDomainParameters;
 import com.android.internal.org.bouncycastle.jcajce.provider.config.ProviderConfiguration;
@@ -39,20 +40,41 @@
  */
 public class EC5Util
 {
-    private static Map customCurves = new HashMap();
-
-    static
+    private static class CustomCurves
     {
-        Enumeration e = CustomNamedCurves.getNames();
-        while (e.hasMoreElements())
-        {
-            String name = (String)e.nextElement();
+        private static Map CURVE_MAP = createCurveMap();
 
-            X9ECParameters curveParams = ECNamedCurveTable.getByName(name);
-            if (curveParams != null)  // there may not be a regular curve, may just be a custom curve.
+        private static Map createCurveMap()
+        {
+            Map map = new HashMap();
+
+            Enumeration e = CustomNamedCurves.getNames();
+            while (e.hasMoreElements())
             {
-                customCurves.put(curveParams.getCurve(), CustomNamedCurves.getByName(name).getCurve());
+                String name = (String)e.nextElement();
+
+                X9ECParametersHolder curveParams = ECNamedCurveTable.getByNameLazy(name);
+                if (curveParams != null)  // there may not be a regular curve, may just be a custom curve.
+                {
+                    ECCurve curve = curveParams.getCurve();
+                    if (ECAlgorithms.isFpCurve(curve))
+                    {
+                        map.put(curve, CustomNamedCurves.getByNameLazy(name).getCurve());
+                    }
+                }
             }
+
+            ECCurve c_25519 = CustomNamedCurves.getByNameLazy("Curve25519").getCurve();
+
+            map.put(new ECCurve.Fp(
+                c_25519.getField().getCharacteristic(),
+                c_25519.getA().toBigInteger(),
+                c_25519.getB().toBigInteger(),
+                c_25519.getOrder(),
+                c_25519.getCofactor(),
+                true), c_25519);
+
+            return map;
         }
 
         // BEGIN Android-removed: Unsupported curves
@@ -69,6 +91,11 @@
             ), c_25519);
         */
         // END Android-removed: Unsupported curves
+        static ECCurve substitute(ECCurve c)
+        {
+            ECCurve custom = (ECCurve)CURVE_MAP.get(c);
+            return null != custom ? custom : c;
+        }
     }
 
     public static ECCurve getCurve(
@@ -280,21 +307,14 @@
 
         if (field instanceof ECFieldFp)
         {
-            ECCurve.Fp curve = new ECCurve.Fp(((ECFieldFp)field).getP(), a, b);
-
-            if (customCurves.containsKey(curve))
-            {
-                return (ECCurve)customCurves.get(curve);
-            }
-
-            return curve;
+            return CustomCurves.substitute(new ECCurve.Fp(((ECFieldFp)field).getP(), a, b, null, null));
         }
         else
         {
             ECFieldF2m fieldF2m = (ECFieldF2m)field;
             int m = fieldF2m.getM();
             int ks[] = ECUtil.convertMidTerms(fieldF2m.getMidTermsOfReductionPolynomial());
-            return new ECCurve.F2m(m, ks[0], ks[1], ks[2], a, b); 
+            return new ECCurve.F2m(m, ks[0], ks[1], ks[2], a, b, null, null);
         }
     }
 
@@ -308,7 +328,7 @@
         {
             Polynomial poly = ((PolynomialExtensionField)field).getMinimalPolynomial();
             int[] exponents = poly.getExponentsPresent();
-            int[] ks = Arrays.reverse(Arrays.copyOfRange(exponents, 1, exponents.length - 1));
+            int[] ks = Arrays.reverseInPlace(Arrays.copyOfRange(exponents, 1, exponents.length - 1));
             return new ECFieldF2m(poly.getDegree(), ks);
         }
     }
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/asymmetric/util/ECUtil.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/asymmetric/util/ECUtil.java
index 5d0ebfe..75f275f 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/asymmetric/util/ECUtil.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/asymmetric/util/ECUtil.java
@@ -293,6 +293,11 @@
     {
         if (order == null)     // implicitly CA
         {
+            if (configuration == null)
+            {
+                return privateValue.bitLength();   // a guess but better than an exception!
+            }
+
             ECParameterSpec implicitCA = configuration.getEcImplicitlyCa();
 
             if (implicitCA == null)
@@ -311,26 +316,24 @@
     public static ASN1ObjectIdentifier getNamedCurveOid(
         String curveName)
     {
-        String name = curveName;
+        if (null == curveName || curveName.length() < 1)
+        {
+            return null;
+        }
 
-        int spacePos = name.indexOf(' ');
+        int spacePos = curveName.indexOf(' ');
         if (spacePos > 0)
         {
-            name = name.substring(spacePos + 1);
+            curveName = curveName.substring(spacePos + 1);
         }
 
-        try
+        ASN1ObjectIdentifier oid = getOID(curveName);
+        if (null != oid)
         {
-            if (name.charAt(0) >= '0' && name.charAt(0) <= '2')
-            {
-                return new ASN1ObjectIdentifier(name);
-            }
-        }
-        catch (IllegalArgumentException ex)
-        {
+            return oid;
         }
 
-        return ECNamedCurveTable.getOID(name);
+        return ECNamedCurveTable.getOID(curveName);
     }
 
     public static ASN1ObjectIdentifier getNamedCurveOid(
@@ -448,4 +451,20 @@
             }
         });
     }
+
+    private static ASN1ObjectIdentifier getOID(String curveName)
+    {
+        char firstChar = curveName.charAt(0);
+        if (firstChar >= '0' && firstChar <= '2')
+        {
+            try
+            {
+                return new ASN1ObjectIdentifier(curveName);
+            }
+            catch (Exception e)
+            {
+            }
+        }
+        return null;
+    }
 }
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/asymmetric/x509/CertificateFactory.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/asymmetric/x509/CertificateFactory.java
index 1de0527..3b3db1a 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/asymmetric/x509/CertificateFactory.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/asymmetric/x509/CertificateFactory.java
@@ -65,10 +65,11 @@
     }
 
     private java.security.cert.Certificate readPEMCertificate(
-        InputStream in)
+        InputStream in,
+        boolean isFirst)
         throws IOException, CertificateParsingException
     {
-        return getCertificate(PEM_CERT_PARSER.readPEMObject(in));
+        return getCertificate(PEM_CERT_PARSER.readPEMObject(in, isFirst));
     }
 
     private java.security.cert.Certificate getCertificate(ASN1Sequence seq)
@@ -123,10 +124,11 @@
     }
     
     private CRL readPEMCRL(
-        InputStream in)
+        InputStream in,
+        boolean isFirst)
         throws IOException, CRLException
     {
-        return getCRL(PEM_CRL_PARSER.readPEMObject(in));
+        return getCRL(PEM_CRL_PARSER.readPEMObject(in, isFirst));
     }
 
     private CRL readDERCRL(
@@ -181,6 +183,14 @@
         InputStream in)
         throws CertificateException
     {
+        return doGenerateCertificate(in, true);
+    }
+
+   private java.security.cert.Certificate doGenerateCertificate(
+            InputStream in,
+            boolean isFirst)
+            throws CertificateException
+   {
         if (currentStream == null)
         {
             currentStream = in;
@@ -254,7 +264,7 @@
 
             if (tag != 0x30)  // assume ascii PEM encoded.
             {
-                return readPEMCertificate(pis);
+                return readPEMCertificate(pis, isFirst);
             }
             else
             {
@@ -286,7 +296,8 @@
 
         // Android-changed: Read from original stream
         // while ((cert = engineGenerateCertificate(in)) != null)
-        while ((cert = engineGenerateCertificate(inStream)) != null)
+        // if we do read some certificates we'll return them even if junk at end of file
+        while ((cert = doGenerateCertificate(inStream, certs.isEmpty())) != null)
         {
             certs.add(cert);
         }
@@ -302,6 +313,18 @@
         InputStream in)
         throws CRLException
     {
+        return doGenerateCRL(in, true);
+    }
+
+    /**
+     * Generates a certificate revocation list (CRL) object and initializes
+     * it with the data read from the input stream inStream.
+     */
+    private CRL doGenerateCRL(
+        InputStream in,
+        boolean     isFirst)
+        throws CRLException
+    {
         if (currentCrlStream == null)
         {
             currentCrlStream = in;
@@ -353,7 +376,7 @@
             pis.reset();
             if (tag != 0x30)  // assume ascii PEM encoded.
             {
-                return readPEMCRL(pis);
+                return readPEMCRL(pis, isFirst);
             }
             else
             {       // lazy evaluate to help processing of large CRLs
@@ -387,7 +410,8 @@
         List crls = new ArrayList();
         BufferedInputStream in = new BufferedInputStream(inStream);
 
-        while ((crl = engineGenerateCRL(in)) != null)
+        // if we do read some certificates we'll return them even if junk at end of file
+        while ((crl = doGenerateCRL(in, crls.isEmpty())) != null)
         {
             crls.add(crl);
         }
@@ -435,7 +459,7 @@
         return new PKIXCertPath(certificates);
     }
 
-    private class ExCertificateException
+    private static class ExCertificateException
         extends CertificateException
     {
         private Throwable cause;
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/asymmetric/x509/PEMUtil.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/asymmetric/x509/PEMUtil.java
index 25c4230..12ee916 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/asymmetric/x509/PEMUtil.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/asymmetric/x509/PEMUtil.java
@@ -14,7 +14,7 @@
      * current PEM object.
      *
      */
-    private class Boundaries
+    private static class Boundaries
     {
         private final String _header;
         private final String _footer;
@@ -113,7 +113,8 @@
     }
 
     ASN1Sequence readPEMObject(
-        InputStream in)
+        InputStream in,
+        boolean     isFirst)
         throws IOException
     {
         String line;
@@ -132,6 +133,11 @@
 
         if (header == null)
         {
+            if (!isFirst)
+            {
+                // just ignore the data
+                return null;
+            }
             throw new IOException("malformed PEM data: no header found");
         }
 
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/asymmetric/x509/X509CRLImpl.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/asymmetric/x509/X509CRLImpl.java
index 755d869..cdec618 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/asymmetric/x509/X509CRLImpl.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/asymmetric/x509/X509CRLImpl.java
@@ -29,6 +29,7 @@
 
 import javax.security.auth.x500.X500Principal;
 
+import com.android.internal.org.bouncycastle.asn1.ASN1BitString;
 import com.android.internal.org.bouncycastle.asn1.ASN1Encodable;
 import com.android.internal.org.bouncycastle.asn1.ASN1Encoding;
 import com.android.internal.org.bouncycastle.asn1.ASN1InputStream;
@@ -37,7 +38,6 @@
 import com.android.internal.org.bouncycastle.asn1.ASN1OctetString;
 import com.android.internal.org.bouncycastle.asn1.ASN1Primitive;
 import com.android.internal.org.bouncycastle.asn1.ASN1Sequence;
-import com.android.internal.org.bouncycastle.asn1.DERBitString;
 import com.android.internal.org.bouncycastle.asn1.util.ASN1Dump;
 import com.android.internal.org.bouncycastle.asn1.x500.X500Name;
 import com.android.internal.org.bouncycastle.asn1.x509.AlgorithmIdentifier;
@@ -159,19 +159,6 @@
         return null;
     }
 
-    public byte[] getEncoded()
-        throws CRLException
-    {
-        try
-        {
-            return c.getEncoded(ASN1Encoding.DER);
-        }
-        catch (IOException e)
-        {
-            throw new CRLException(e.toString());
-        }
-    }
-
     public void verify(PublicKey key)
         throws CRLException, NoSuchAlgorithmException,
         InvalidKeyException, NoSuchProviderException, SignatureException
@@ -256,7 +243,7 @@
         {
             List<PublicKey> pubKeys = ((CompositePublicKey)key).getPublicKeys();
             ASN1Sequence keySeq = ASN1Sequence.getInstance(c.getSignatureAlgorithm().getParameters());
-            ASN1Sequence sigSeq = ASN1Sequence.getInstance(DERBitString.getInstance(c.getSignature()).getBytes());
+            ASN1Sequence sigSeq = ASN1Sequence.getInstance(ASN1BitString.getInstance(c.getSignature()).getBytes());
 
             boolean success = false;
             for (int i = 0; i != pubKeys.size(); i++)
@@ -278,7 +265,7 @@
                     checkSignature(
                         (PublicKey)pubKeys.get(i), signature,
                         sigAlg.getParameters(),
-                        DERBitString.getInstance(sigSeq.getObjectAt(i)).getBytes());
+                        ASN1BitString.getInstance(sigSeq.getObjectAt(i)).getBytes());
                     success = true;
                 }
                 catch (SignatureException e)
@@ -300,7 +287,7 @@
         else if (X509SignatureUtil.isCompositeAlgorithm(c.getSignatureAlgorithm()))
         {
             ASN1Sequence keySeq = ASN1Sequence.getInstance(c.getSignatureAlgorithm().getParameters());
-            ASN1Sequence sigSeq = ASN1Sequence.getInstance(DERBitString.getInstance(c.getSignature()).getBytes());
+            ASN1Sequence sigSeq = ASN1Sequence.getInstance(ASN1BitString.getInstance(c.getSignature()).getBytes());
 
             boolean success = false;
             for (int i = 0; i != sigSeq.size(); i++)
@@ -317,7 +304,7 @@
                     checkSignature(
                         key, signature,
                         sigAlg.getParameters(),
-                        DERBitString.getInstance(sigSeq.getObjectAt(i)).getBytes());
+                        ASN1BitString.getInstance(sigSeq.getObjectAt(i)).getBytes());
 
                     success = true;
                 }
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/asymmetric/x509/X509CRLInternal.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/asymmetric/x509/X509CRLInternal.java
index a3a5fed..23fbc55 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/asymmetric/x509/X509CRLInternal.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/asymmetric/x509/X509CRLInternal.java
@@ -6,25 +6,42 @@
 import com.android.internal.org.bouncycastle.asn1.x509.CertificateList;
 import com.android.internal.org.bouncycastle.jcajce.util.JcaJceHelper;
 
+/**
+ * This class exists to let {@link #equals(Object)} and {@link #hashCode()} methods be delegated efficiently
+ * to the platform default implementations (especially important for compatibility of {@link #hashCode()}
+ * calculations). Those methods fall back to calling {@link #getEncoded()} for third-party subclasses, and
+ * this class allows us to avoid cloning the return value of {@link #getEncoded()} for those callers.
+ */
 class X509CRLInternal extends X509CRLImpl
 {
     private final byte[] encoding;
+    private final CRLException exception;
 
     X509CRLInternal(JcaJceHelper bcHelper, CertificateList c, String sigAlgName, byte[] sigAlgParams, boolean isIndirect,
-        byte[] encoding)
+        byte[] encoding, CRLException exception)
     {
         super(bcHelper, c, sigAlgName, sigAlgParams, isIndirect);
 
         this.encoding = encoding;
+        this.exception = exception;
     }
 
     public byte[] getEncoded() throws CRLException
     {
+        if (null != exception)
+        {
+            throw exception;
+        }
+
         if (null == encoding)
         {
             throw new CRLException();
         }
 
+        /*
+         * NOTE: Don't clone this return value. See class javadoc for details. Any necessary cloning is
+         * handled by the X509CRLObject that is holding this instance.
+         */
         return encoding;
     }
 }
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/asymmetric/x509/X509CRLObject.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/asymmetric/x509/X509CRLObject.java
index 1eb9d66..f094cde 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/asymmetric/x509/X509CRLObject.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/asymmetric/x509/X509CRLObject.java
@@ -1,6 +1,7 @@
 /* GENERATED SOURCE. DO NOT MODIFY. */
 package com.android.internal.org.bouncycastle.jcajce.provider.asymmetric.x509;
 
+import java.io.IOException;
 import java.security.cert.CRLException;
 
 import com.android.internal.org.bouncycastle.asn1.ASN1BitString;
@@ -10,6 +11,7 @@
 import com.android.internal.org.bouncycastle.asn1.x509.Extension;
 import com.android.internal.org.bouncycastle.asn1.x509.IssuingDistributionPoint;
 import com.android.internal.org.bouncycastle.jcajce.util.JcaJceHelper;
+import com.android.internal.org.bouncycastle.util.Arrays;
 
 class X509CRLObject
     extends X509CRLImpl
@@ -25,6 +27,11 @@
         super(bcHelper, c, createSigAlgName(c), createSigAlgParams(c), isIndirectCRL(c));
     }
 
+    public byte[] getEncoded() throws CRLException
+    {
+        return Arrays.clone(getInternalCRL().getEncoded());
+    }
+
     public boolean equals(Object other)
     {
         if (this == other)
@@ -51,6 +58,8 @@
                     return false;
                 }
             }
+
+            return getInternalCRL().equals(otherBC.getInternalCRL());
         }
 
         return getInternalCRL().equals(other);
@@ -77,17 +86,19 @@
             }
         }
 
-        byte[] encoding;
+        byte[] encoding = null;
+        CRLException exception = null;
         try
         {
-            encoding = getEncoded();
+            encoding = c.getEncoded(ASN1Encoding.DER);
         }
-        catch (CRLException e)
+        catch (IOException e)
         {
-            encoding = null;
+            exception = new X509CRLException(e);
         }
 
-        X509CRLInternal temp = new X509CRLInternal(bcHelper, c, sigAlgName,sigAlgParams, isIndirect, encoding);
+        X509CRLInternal temp = new X509CRLInternal(bcHelper, c, sigAlgName,sigAlgParams, isIndirect, encoding,
+            exception);
 
         synchronized (cacheLock)
         {
@@ -108,7 +119,7 @@
         }
         catch (Exception e)
         {
-            throw new CRLException("CRL contents invalid: " + e);
+            throw new X509CRLException("CRL contents invalid: " + e.getMessage(), e);
         }
     }
 
@@ -147,4 +158,26 @@
             throw new ExtCRLException("Exception reading IssuingDistributionPoint", e);
         }
     }
+
+    private static class X509CRLException
+        extends CRLException
+    {
+        private final Throwable cause;
+
+        X509CRLException(String msg, Throwable cause)
+        {
+            super(msg);
+            this.cause = cause;
+        }
+
+        X509CRLException(Throwable cause)
+        {
+            this.cause = cause;
+        }
+
+        public Throwable getCause()
+        {
+            return cause;
+        }
+    }
 }
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/asymmetric/x509/X509CertificateImpl.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/asymmetric/x509/X509CertificateImpl.java
index 920186c..5819f16 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/asymmetric/x509/X509CertificateImpl.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/asymmetric/x509/X509CertificateImpl.java
@@ -32,16 +32,17 @@
 
 import javax.security.auth.x500.X500Principal;
 
+import com.android.internal.org.bouncycastle.asn1.ASN1BitString;
 import com.android.internal.org.bouncycastle.asn1.ASN1Encodable;
 import com.android.internal.org.bouncycastle.asn1.ASN1Encoding;
+import com.android.internal.org.bouncycastle.asn1.ASN1IA5String;
 import com.android.internal.org.bouncycastle.asn1.ASN1InputStream;
+import com.android.internal.org.bouncycastle.asn1.ASN1Integer;
 import com.android.internal.org.bouncycastle.asn1.ASN1ObjectIdentifier;
 import com.android.internal.org.bouncycastle.asn1.ASN1OctetString;
 import com.android.internal.org.bouncycastle.asn1.ASN1Primitive;
 import com.android.internal.org.bouncycastle.asn1.ASN1Sequence;
 import com.android.internal.org.bouncycastle.asn1.ASN1String;
-import com.android.internal.org.bouncycastle.asn1.DERBitString;
-import com.android.internal.org.bouncycastle.asn1.DERIA5String;
 import com.android.internal.org.bouncycastle.asn1.DERNull;
 import com.android.internal.org.bouncycastle.asn1.DEROctetString;
 import com.android.internal.org.bouncycastle.asn1.misc.MiscObjectIdentifiers;
@@ -65,6 +66,7 @@
 import com.android.internal.org.bouncycastle.jce.X509Principal;
 import com.android.internal.org.bouncycastle.jce.provider.BouncyCastleProvider;
 import com.android.internal.org.bouncycastle.util.Arrays;
+import com.android.internal.org.bouncycastle.util.Exceptions;
 import com.android.internal.org.bouncycastle.util.Integers;
 import com.android.internal.org.bouncycastle.util.Properties;
 import com.android.internal.org.bouncycastle.util.Strings;
@@ -79,7 +81,6 @@
     protected boolean[] keyUsage;
     protected String sigAlgName;
     protected byte[] sigAlgParams;
-
     X509CertificateImpl(JcaJceHelper bcHelper, com.android.internal.org.bouncycastle.asn1.x509.Certificate c,
         BasicConstraints basicConstraints, boolean[] keyUsage, String sigAlgName, byte[] sigAlgParams)
     {
@@ -230,7 +231,7 @@
 
     public boolean[] getIssuerUniqueID()
     {
-        DERBitString    id = c.getTBSCertificate().getIssuerUniqueId();
+        ASN1BitString    id = c.getTBSCertificate().getIssuerUniqueId();
 
         if (id != null)
         {
@@ -250,7 +251,7 @@
 
     public boolean[] getSubjectUniqueID()
     {
-        DERBitString    id = c.getTBSCertificate().getSubjectUniqueId();
+        ASN1BitString id = c.getTBSCertificate().getSubjectUniqueId();
 
         if (id != null)
         {
@@ -298,29 +299,21 @@
             throw new CertificateParsingException("error processing extended key usage extension");
         }
     }
-    
+
     public int getBasicConstraints()
     {
-        if (basicConstraints != null)
+        if (basicConstraints == null || !basicConstraints.isCA())
         {
-            if (basicConstraints.isCA())
-            {
-                if (basicConstraints.getPathLenConstraint() == null)
-                {
-                    return Integer.MAX_VALUE;
-                }
-                else
-                {
-                    return basicConstraints.getPathLenConstraint().intValue();
-                }
-            }
-            else
-            {
-                return -1;
-            }
+            return -1;
         }
 
-        return -1;
+        ASN1Integer pathLenConstraint = basicConstraints.getPathLenConstraintInteger();
+        if (pathLenConstraint == null)
+        {
+            return Integer.MAX_VALUE;
+        }
+
+        return pathLenConstraint.intPositiveValueExact();
     }
 
     public Collection getSubjectAlternativeNames()
@@ -375,7 +368,7 @@
             }
             catch (Exception e)
             {
-                throw new IllegalStateException("error parsing " + e.toString());
+                throw Exceptions.illegalStateException("error parsing " + e.getMessage(), e);
             }
         }
 
@@ -461,20 +454,7 @@
         }
         catch (IOException e)
         {
-            return null;   // should never happen...
-        }
-    }
-
-    public byte[] getEncoded()
-        throws CertificateEncodingException
-    {
-        try
-        {
-            return c.getEncoded(ASN1Encoding.DER);
-        }
-        catch (IOException e)
-        {
-            throw new CertificateEncodingException(e.toString());
+            throw Exceptions.illegalStateException("failed to recover public key: " + e.getMessage(), e);
         }
     }
 
@@ -527,15 +507,15 @@
                         }
                         else if (oid.equals(MiscObjectIdentifiers.netscapeCertType))
                         {
-                            buf.append(new NetscapeCertType(DERBitString.getInstance(dIn.readObject()))).append(nl);
+                            buf.append(new NetscapeCertType(ASN1BitString.getInstance(dIn.readObject()))).append(nl);
                         }
                         else if (oid.equals(MiscObjectIdentifiers.netscapeRevocationURL))
                         {
-                            buf.append(new NetscapeRevocationURL(DERIA5String.getInstance(dIn.readObject()))).append(nl);
+                            buf.append(new NetscapeRevocationURL(ASN1IA5String.getInstance(dIn.readObject()))).append(nl);
                         }
                         else if (oid.equals(MiscObjectIdentifiers.verisignCzagExtension))
                         {
-                            buf.append(new VerisignCzagExtension(DERIA5String.getInstance(dIn.readObject()))).append(nl);
+                            buf.append(new VerisignCzagExtension(ASN1IA5String.getInstance(dIn.readObject()))).append(nl);
                         }
                         else 
                         {
@@ -647,7 +627,7 @@
         {
             List<PublicKey> pubKeys = ((CompositePublicKey)key).getPublicKeys();
             ASN1Sequence keySeq = ASN1Sequence.getInstance(c.getSignatureAlgorithm().getParameters());
-            ASN1Sequence sigSeq = ASN1Sequence.getInstance(DERBitString.getInstance(c.getSignature()).getBytes());
+            ASN1Sequence sigSeq = ASN1Sequence.getInstance(ASN1BitString.getInstance(c.getSignature()).getBytes());
 
             boolean success = false;
             for (int i = 0; i != pubKeys.size(); i++)
@@ -668,7 +648,7 @@
                     checkSignature(
                         (PublicKey)pubKeys.get(i), signature,
                         sigAlg.getParameters(),
-                        DERBitString.getInstance(sigSeq.getObjectAt(i)).getBytes());
+                        ASN1BitString.getInstance(sigSeq.getObjectAt(i)).getBytes());
                     success = true;
                 }
                 catch (SignatureException e)
@@ -690,7 +670,7 @@
         else if (X509SignatureUtil.isCompositeAlgorithm(c.getSignatureAlgorithm()))
         {
             ASN1Sequence keySeq = ASN1Sequence.getInstance(c.getSignatureAlgorithm().getParameters());
-            ASN1Sequence sigSeq = ASN1Sequence.getInstance(DERBitString.getInstance(c.getSignature()).getBytes());
+            ASN1Sequence sigSeq = ASN1Sequence.getInstance(ASN1BitString.getInstance(c.getSignature()).getBytes());
 
             boolean success = false;
             for (int i = 0; i != sigSeq.size(); i++)
@@ -707,7 +687,7 @@
                     checkSignature(
                         key, signature,
                         sigAlg.getParameters(),
-                        DERBitString.getInstance(sigSeq.getObjectAt(i)).getBytes());
+                        ASN1BitString.getInstance(sigSeq.getObjectAt(i)).getBytes());
 
                     success = true;
                 }
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/asymmetric/x509/X509CertificateInternal.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/asymmetric/x509/X509CertificateInternal.java
index 6ff5ac1..7c345b6 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/asymmetric/x509/X509CertificateInternal.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/asymmetric/x509/X509CertificateInternal.java
@@ -6,25 +6,43 @@
 import com.android.internal.org.bouncycastle.asn1.x509.BasicConstraints;
 import com.android.internal.org.bouncycastle.jcajce.util.JcaJceHelper;
 
+/**
+ * This class exists to let {@link #equals(Object)} and {@link #hashCode()} methods be delegated efficiently
+ * to the platform default implementations (especially important for compatibility of {@link #hashCode()}
+ * calculations). Those methods fall back to calling {@link #getEncoded()} for third-party subclasses, and
+ * this class allows us to avoid cloning the return value of {@link #getEncoded()} for those callers.
+ */
 class X509CertificateInternal extends X509CertificateImpl
 {
     private final byte[] encoding;
+    private final CertificateEncodingException exception;
 
     X509CertificateInternal(JcaJceHelper bcHelper, com.android.internal.org.bouncycastle.asn1.x509.Certificate c,
-        BasicConstraints basicConstraints, boolean[] keyUsage, String sigAlgName, byte[] sigAlgParams, byte[] encoding)
+        BasicConstraints basicConstraints, boolean[] keyUsage, String sigAlgName, byte[] sigAlgParams, byte[] encoding,
+        CertificateEncodingException exception)
     {
         super(bcHelper, c, basicConstraints, keyUsage, sigAlgName, sigAlgParams);
 
         this.encoding = encoding;
+        this.exception = exception;
     }
 
     public byte[] getEncoded() throws CertificateEncodingException
     {
+        if (null != exception)
+        {
+            throw exception;
+        }
+
         if (null == encoding)
         {
             throw new CertificateEncodingException();
         }
 
+        /*
+         * NOTE: Don't clone this return value. See class javadoc for details. Any necessary cloning is
+         * handled by the X509CertificateObject that is holding this instance.
+         */
         return encoding;
     }
 }
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/asymmetric/x509/X509CertificateObject.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/asymmetric/x509/X509CertificateObject.java
index 39f0ff1..b69b46e 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/asymmetric/x509/X509CertificateObject.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/asymmetric/x509/X509CertificateObject.java
@@ -1,57 +1,22 @@
 /* GENERATED SOURCE. DO NOT MODIFY. */
 package com.android.internal.org.bouncycastle.jcajce.provider.asymmetric.x509;
 
-import java.io.ByteArrayOutputStream;
 import java.io.IOException;
-import java.math.BigInteger;
-import java.net.InetAddress;
-import java.net.UnknownHostException;
-import java.security.InvalidKeyException;
-import java.security.NoSuchAlgorithmException;
-import java.security.NoSuchProviderException;
-import java.security.Principal;
-import java.security.Provider;
 import java.security.PublicKey;
-import java.security.Signature;
-import java.security.SignatureException;
 import java.security.cert.CertificateEncodingException;
-import java.security.cert.CertificateException;
 import java.security.cert.CertificateExpiredException;
 import java.security.cert.CertificateNotYetValidException;
 import java.security.cert.CertificateParsingException;
-import java.security.cert.X509Certificate;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
 import java.util.Date;
 import java.util.Enumeration;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
 
 import javax.security.auth.x500.X500Principal;
 
 import com.android.internal.org.bouncycastle.asn1.ASN1BitString;
 import com.android.internal.org.bouncycastle.asn1.ASN1Encodable;
 import com.android.internal.org.bouncycastle.asn1.ASN1Encoding;
-import com.android.internal.org.bouncycastle.asn1.ASN1InputStream;
 import com.android.internal.org.bouncycastle.asn1.ASN1ObjectIdentifier;
-import com.android.internal.org.bouncycastle.asn1.ASN1OutputStream;
 import com.android.internal.org.bouncycastle.asn1.ASN1Primitive;
-import com.android.internal.org.bouncycastle.asn1.ASN1Sequence;
-import com.android.internal.org.bouncycastle.asn1.ASN1String;
-import com.android.internal.org.bouncycastle.asn1.DERBitString;
-import com.android.internal.org.bouncycastle.asn1.DERIA5String;
-import com.android.internal.org.bouncycastle.asn1.DERNull;
-import com.android.internal.org.bouncycastle.asn1.DEROctetString;
-import com.android.internal.org.bouncycastle.asn1.misc.MiscObjectIdentifiers;
-import com.android.internal.org.bouncycastle.asn1.misc.NetscapeCertType;
-import com.android.internal.org.bouncycastle.asn1.misc.NetscapeRevocationURL;
-import com.android.internal.org.bouncycastle.asn1.misc.VerisignCzagExtension;
-import com.android.internal.org.bouncycastle.asn1.util.ASN1Dump;
-import com.android.internal.org.bouncycastle.asn1.x500.X500Name;
-import com.android.internal.org.bouncycastle.asn1.x500.style.RFC4519Style;
-import com.android.internal.org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 import com.android.internal.org.bouncycastle.asn1.x509.BasicConstraints;
 import com.android.internal.org.bouncycastle.asn1.x509.Extension;
 import com.android.internal.org.bouncycastle.asn1.x509.Extensions;
@@ -62,12 +27,8 @@
 // END Android-added: Unknown reason
 import com.android.internal.org.bouncycastle.jcajce.provider.asymmetric.util.PKCS12BagAttributeCarrierImpl;
 import com.android.internal.org.bouncycastle.jcajce.util.JcaJceHelper;
-import com.android.internal.org.bouncycastle.jce.X509Principal;
 import com.android.internal.org.bouncycastle.jce.interfaces.PKCS12BagAttributeCarrier;
-import com.android.internal.org.bouncycastle.jce.provider.BouncyCastleProvider;
-import com.android.internal.org.bouncycastle.util.Integers;
-import com.android.internal.org.bouncycastle.util.Strings;
-import com.android.internal.org.bouncycastle.util.encoders.Hex;
+import com.android.internal.org.bouncycastle.util.Arrays;
 
 class X509CertificateObject
     extends X509CertificateImpl
@@ -254,6 +215,8 @@
                     return false;
                 }
             }
+
+            return getInternalCertificate().equals(otherBC.getInternalCertificate());
         }
 
         return getInternalCertificate().equals(other);
@@ -318,18 +281,19 @@
             }
         }
 
-        byte[] encoding;
+        byte[] encoding = null;
+        CertificateEncodingException exception = null;
         try
         {
-            encoding = getEncoded();
+            encoding = c.getEncoded(ASN1Encoding.DER);
         }
-        catch (CertificateEncodingException e)
+        catch (IOException e)
         {
-            encoding = null;
+            exception = new X509CertificateEncodingException(e);
         }
 
         X509CertificateInternal temp = new X509CertificateInternal(bcHelper, c, basicConstraints, keyUsage, sigAlgName,
-            sigAlgParams, encoding);
+            sigAlgParams, encoding, exception);
 
         synchronized (cacheLock)
         {
@@ -371,7 +335,7 @@
                 return null;
             }
 
-            ASN1BitString bits = DERBitString.getInstance(ASN1Primitive.fromByteArray(extOctets));
+            ASN1BitString bits = ASN1BitString.getInstance(ASN1Primitive.fromByteArray(extOctets));
 
             byte[] bytes = bits.getBytes();
             int length = (bytes.length * 8) - bits.getPadBits();
@@ -420,4 +384,20 @@
             throw new CertificateParsingException("cannot construct SigAlgParams: " + e);
         }
     }
+
+    private static class X509CertificateEncodingException
+        extends CertificateEncodingException
+    {
+        private final Throwable cause;
+
+        X509CertificateEncodingException(Throwable cause)
+        {
+            this.cause = cause;
+        }
+
+        public Throwable getCause()
+        {
+            return cause;
+        }
+    }
 }
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/asymmetric/x509/X509SignatureUtil.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/asymmetric/x509/X509SignatureUtil.java
index bcf9fb5..7265c3f 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/asymmetric/x509/X509SignatureUtil.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/asymmetric/x509/X509SignatureUtil.java
@@ -181,6 +181,11 @@
 
     static void prettyPrintSignature(byte[] sig, StringBuffer buf, String nl)
     {
+        // -DM Hex.toHexString
+        // -DM Hex.toHexString
+        // -DM Hex.toHexString
+        // -DM Hex.toHexString
+
         if (sig.length > 20)
         {
             buf.append("            Signature: ").append(Hex.toHexString(sig, 0, 20)).append(nl);
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/config/ConfigurableProvider.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/config/ConfigurableProvider.java
index cd76b86..d24420a 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/config/ConfigurableProvider.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/config/ConfigurableProvider.java
@@ -47,8 +47,12 @@
 
     void addAlgorithm(String key, String value);
 
+    void addAlgorithm(String key, String value, Map<String, String> attributes);
+
     void addAlgorithm(String type, ASN1ObjectIdentifier oid, String className);
 
+    void addAlgorithm(String type, ASN1ObjectIdentifier oid, String className, Map<String, String> attributes);
+
     boolean hasAlgorithm(String type, String name);
 
     void addKeyInfoConverter(ASN1ObjectIdentifier oid, AsymmetricKeyInfoConverter keyInfoConverter);
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/digest/BCMessageDigest.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/digest/BCMessageDigest.java
index da100c1..70f37e6 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/digest/BCMessageDigest.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/digest/BCMessageDigest.java
@@ -1,6 +1,7 @@
 /* GENERATED SOURCE. DO NOT MODIFY. */
 package com.android.internal.org.bouncycastle.jcajce.provider.digest;
 
+import java.security.DigestException;
 import java.security.MessageDigest;
 
 import com.android.internal.org.bouncycastle.crypto.Digest;
@@ -69,4 +70,16 @@
 
         return digestBytes;
     }
+
+    public int engineDigest(byte[] buf, int off, int len) throws DigestException
+    {
+        if (len < digestSize)
+            throw new DigestException("partial digests not returned");
+        if (buf.length - off < digestSize)
+            throw new DigestException("insufficient space in the output buffer to store the digest");
+
+        digest.doFinal(buf, off);
+
+        return digestSize;
+    }
 }
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/digest/DigestAlgorithmProvider.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/digest/DigestAlgorithmProvider.java
index b89a44e..89d02b7 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/digest/DigestAlgorithmProvider.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/digest/DigestAlgorithmProvider.java
@@ -24,6 +24,19 @@
         provider.addAlgorithm("Alg.Alias.KeyGenerator.HMAC/" + algorithm, mainName);
     }
 
+    protected void addKMACAlgorithm(
+        ConfigurableProvider provider,
+        String algorithm,
+        String algorithmClassName,
+        String keyGeneratorClassName)
+    {
+        String mainName = "KMAC" + algorithm;
+
+        provider.addAlgorithm("Mac." + mainName, algorithmClassName);
+        provider.addAlgorithm("KeyGenerator." + mainName, keyGeneratorClassName);
+        provider.addAlgorithm("Alg.Alias.KeyGenerator.KMAC" + algorithm, mainName);
+    }
+
     protected void addHMACAlias(
         ConfigurableProvider provider,
         String algorithm,
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/digest/Haraka.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/digest/Haraka.java
new file mode 100644
index 0000000..e281654
--- /dev/null
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/digest/Haraka.java
@@ -0,0 +1,80 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.jcajce.provider.digest;
+
+import com.android.internal.org.bouncycastle.crypto.digests.Haraka256Digest;
+import com.android.internal.org.bouncycastle.crypto.digests.Haraka512Digest;
+import com.android.internal.org.bouncycastle.jcajce.provider.config.ConfigurableProvider;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+public class Haraka
+{
+    private Haraka()
+    {
+
+    }
+
+    /**
+     * @hide This class is not part of the Android public SDK API
+     */
+    static public class Digest256
+        extends BCMessageDigest
+        implements Cloneable
+    {
+        public Digest256()
+        {
+            super(new Haraka256Digest());
+        }
+
+        public Object clone()
+            throws CloneNotSupportedException
+        {
+            Digest256 d = (Digest256)super.clone();
+            d.digest = new Haraka256Digest((Haraka256Digest)digest);
+
+            return d;
+        }
+    }
+
+    /**
+     * @hide This class is not part of the Android public SDK API
+     */
+    static public class Digest512
+        extends BCMessageDigest
+        implements Cloneable
+    {
+        public Digest512()
+        {
+            super(new Haraka512Digest());
+        }
+
+        public Object clone()
+            throws CloneNotSupportedException
+        {
+            Digest512 d = (Digest512)super.clone();
+            d.digest = new Haraka512Digest((Haraka512Digest)digest);
+
+            return d;
+        }
+    }
+
+    /**
+     * @hide This class is not part of the Android public SDK API
+     */
+    public static class Mappings
+        extends DigestAlgorithmProvider
+    {
+        private static final String PREFIX = Haraka.class.getName();
+
+        public Mappings()
+        {
+        }
+
+        public void configure(ConfigurableProvider provider)
+        {
+            provider.addAlgorithm("MessageDigest.HARAKA-256", PREFIX + "$Digest256");
+            provider.addAlgorithm("MessageDigest.HARAKA-512", PREFIX + "$Digest512");
+        }
+    }
+}
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/digest/SHA256.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/digest/SHA256.java
index a7ab409..c4a06ef 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/digest/SHA256.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/digest/SHA256.java
@@ -30,14 +30,14 @@
     {
         public Digest()
         {
-            super(new SHA256Digest());
+            super(SHA256Digest.newInstance());
         }
 
         public Object clone()
             throws CloneNotSupportedException
         {
             Digest d = (Digest)super.clone();
-            d.digest = new SHA256Digest((SHA256Digest)digest);
+            d.digest = SHA256Digest.newInstance(digest);
 
             return d;
         }
@@ -51,7 +51,7 @@
     {
         public HashMac()
         {
-            super(new HMac(new SHA256Digest()));
+            super(new HMac(SHA256Digest.newInstance()));
         }
     }
 
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/digest/SHA512.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/digest/SHA512.java
index 243a306..d9844d7 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/digest/SHA512.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/digest/SHA512.java
@@ -190,10 +190,16 @@
 
             provider.addAlgorithm("MessageDigest.SHA-512/224", PREFIX + "$DigestT224");
             provider.addAlgorithm("Alg.Alias.MessageDigest.SHA512/224", "SHA-512/224");
+            provider.addAlgorithm("Alg.Alias.MessageDigest.SHA512224", "SHA-512/224");
+            provider.addAlgorithm("Alg.Alias.MessageDigest.SHA-512(224)", "SHA-512/224");
+            provider.addAlgorithm("Alg.Alias.MessageDigest.SHA512(224)", "SHA-512/224");
             provider.addAlgorithm("Alg.Alias.MessageDigest." + NISTObjectIdentifiers.id_sha512_224, "SHA-512/224");
 
             provider.addAlgorithm("MessageDigest.SHA-512/256", PREFIX + "$DigestT256");
+            provider.addAlgorithm("Alg.Alias.MessageDigest.SHA512/256", "SHA-512/256");
             provider.addAlgorithm("Alg.Alias.MessageDigest.SHA512256", "SHA-512/256");
+            provider.addAlgorithm("Alg.Alias.MessageDigest.SHA-512(256)", "SHA-512/256");
+            provider.addAlgorithm("Alg.Alias.MessageDigest.SHA512(256)", "SHA-512/256");
             provider.addAlgorithm("Alg.Alias.MessageDigest." + NISTObjectIdentifiers.id_sha512_256, "SHA-512/256");
 
             provider.addAlgorithm("Mac.OLDHMACSHA512", PREFIX + "$OldSHA512");
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/drbg/EntropyDaemon.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/drbg/EntropyDaemon.java
new file mode 100644
index 0000000..1e16d0c
--- /dev/null
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/drbg/EntropyDaemon.java
@@ -0,0 +1,66 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.jcajce.provider.drbg;
+
+import java.util.LinkedList;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+class EntropyDaemon
+    implements Runnable
+{
+    private static final Logger LOG = Logger.getLogger(EntropyDaemon.class.getName());
+
+    private final LinkedList<Runnable> tasks = new LinkedList<Runnable>();
+
+    public EntropyDaemon()
+    {
+    }
+
+    void addTask(Runnable task)
+    {
+        synchronized (tasks)
+        {
+            tasks.add(task);
+        }
+    }
+
+    public void run()
+    {
+        while (!Thread.currentThread().isInterrupted())
+        {
+            Runnable task;
+            synchronized (tasks)
+            {
+                task = tasks.poll();
+            }
+
+            if (task != null)
+            {
+                try
+                {
+                    task.run();
+                }
+                catch (Throwable e)
+                {
+                    // ignore
+                }
+            }
+            else
+            {
+                try
+                {
+                    Thread.sleep(5000);
+                }
+                catch (InterruptedException e)
+                {
+                    Thread.currentThread().interrupt();
+                }
+            }
+        }
+
+        if (LOG.isLoggable(Level.FINE))
+        {
+            LOG.fine("entropy thread interrupted - exiting");
+        }
+    }
+}
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/keystore/BC.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/keystore/BC.java
index 1cfc616..0480ac2 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/keystore/BC.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/keystore/BC.java
@@ -3,6 +3,7 @@
 
 import com.android.internal.org.bouncycastle.jcajce.provider.config.ConfigurableProvider;
 import com.android.internal.org.bouncycastle.jcajce.provider.util.AsymmetricAlgorithmProvider;
+import com.android.internal.org.bouncycastle.util.Properties;
 
 /**
  * @hide This class is not part of the Android public SDK API
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/keystore/bc/BcKeyStoreSpi.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/keystore/bc/BcKeyStoreSpi.java
index 6c2c25b..a699cb2 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/keystore/bc/BcKeyStoreSpi.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/keystore/bc/BcKeyStoreSpi.java
@@ -58,6 +58,7 @@
 import com.android.internal.org.bouncycastle.jce.interfaces.BCKeyStore;
 import com.android.internal.org.bouncycastle.jce.provider.BouncyCastleProvider;
 import com.android.internal.org.bouncycastle.util.Arrays;
+import com.android.internal.org.bouncycastle.util.Properties;
 import com.android.internal.org.bouncycastle.util.io.Streams;
 import com.android.internal.org.bouncycastle.util.io.TeeOutputStream;
 
@@ -398,6 +399,11 @@
     {
         byte[]      enc = key.getEncoded();
 
+        if (enc == null)
+        {
+            throw new IOException("unable to store encoding of protected key");
+        }
+
         if (key instanceof PrivateKey)
         {
             dOut.write(KEY_PRIVATE);
@@ -676,9 +682,18 @@
         Certificate[]   chain) 
         throws KeyStoreException
     {
-        if ((key instanceof PrivateKey) && (chain == null))
+        if ((key instanceof PrivateKey))
         {
-            throw new KeyStoreException("no certificate chain for private key");
+            if (chain == null)
+            {
+                throw new KeyStoreException("no certificate chain for private key");
+            }
+            if (key.getEncoded() == null)
+            {
+                // we ingore the password as the key is already protected.
+                table.put(alias, new StoreEntry(alias, new Date(), KEY, key, chain));
+                return;
+            }
         }
 
         try
@@ -1129,6 +1144,10 @@
         public Version1()
         {
             super(1);
+            if (!Properties.isOverrideSet("com.android.internal.org.bouncycastle.bks.enable_v1"))
+            {
+                 throw new IllegalStateException("BKS-V1 not enabled");
+            }
         }
     }
 
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/keystore/pkcs12/PKCS12KeyStoreSpi.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/keystore/pkcs12/PKCS12KeyStoreSpi.java
index 745534d..23031b0 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/keystore/pkcs12/PKCS12KeyStoreSpi.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/keystore/pkcs12/PKCS12KeyStoreSpi.java
@@ -3,6 +3,7 @@
 
 import java.io.BufferedInputStream;
 import java.io.ByteArrayInputStream;
+import java.io.EOFException;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
@@ -47,6 +48,7 @@
 import javax.crypto.spec.PBEKeySpec;
 import javax.crypto.spec.PBEParameterSpec;
 
+import com.android.internal.org.bouncycastle.asn1.ASN1BMPString;
 import com.android.internal.org.bouncycastle.asn1.ASN1Encodable;
 import com.android.internal.org.bouncycastle.asn1.ASN1EncodableVector;
 import com.android.internal.org.bouncycastle.asn1.ASN1Encoding;
@@ -66,6 +68,7 @@
 // Android-removed: Unsupported algorithms
 // import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers;
 // import org.bouncycastle.asn1.cryptopro.GOST28147Parameters;
+// import org.bouncycastle.asn1.misc.MiscObjectIdentifiers;
 import com.android.internal.org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
 // import org.bouncycastle.asn1.ntt.NTTObjectIdentifiers;
 import com.android.internal.org.bouncycastle.asn1.oiw.OIWObjectIdentifiers;
@@ -84,9 +87,13 @@
 import com.android.internal.org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 import com.android.internal.org.bouncycastle.asn1.x509.AuthorityKeyIdentifier;
 import com.android.internal.org.bouncycastle.asn1.x509.DigestInfo;
+import com.android.internal.org.bouncycastle.asn1.x509.ExtendedKeyUsage;
 import com.android.internal.org.bouncycastle.asn1.x509.Extension;
+import com.android.internal.org.bouncycastle.asn1.x509.Extensions;
+import com.android.internal.org.bouncycastle.asn1.x509.KeyPurposeId;
 import com.android.internal.org.bouncycastle.asn1.x509.SubjectKeyIdentifier;
 import com.android.internal.org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import com.android.internal.org.bouncycastle.asn1.x509.TBSCertificate;
 import com.android.internal.org.bouncycastle.asn1.x509.X509ObjectIdentifiers;
 import com.android.internal.org.bouncycastle.crypto.CryptoServicesRegistrar;
 import com.android.internal.org.bouncycastle.crypto.Digest;
@@ -97,6 +104,9 @@
 import com.android.internal.org.bouncycastle.jcajce.PKCS12StoreParameter;
 // Android-removed: Unsupported algorithms
 // import org.bouncycastle.jcajce.spec.GOST28147ParameterSpec;
+// import org.bouncycastle.jcajce.BCLoadStoreParameter;
+import com.android.internal.org.bouncycastle.jcajce.provider.keystore.util.AdaptingKeyStoreSpi;
+import com.android.internal.org.bouncycastle.jcajce.provider.keystore.util.ParameterUtil;
 import com.android.internal.org.bouncycastle.jcajce.spec.PBKDF2KeySpec;
 import com.android.internal.org.bouncycastle.jcajce.util.BCJcaJceHelper;
 import com.android.internal.org.bouncycastle.jcajce.util.DefaultJcaJceHelper;
@@ -133,7 +143,7 @@
     private static final DefaultSecretKeyProvider keySizeProvider = new DefaultSecretKeyProvider();
 
     private IgnoresCaseHashtable keys = new IgnoresCaseHashtable();
-    private Hashtable localIds = new Hashtable();
+    private IgnoresCaseHashtable localIds = new IgnoresCaseHashtable();
     private IgnoresCaseHashtable certs = new IgnoresCaseHashtable();
     private Hashtable chainCerts = new Hashtable();
     private Hashtable keyCerts = new Hashtable();
@@ -257,6 +267,12 @@
         this.random = rand;
     }
 
+    public boolean engineProbe(InputStream stream)
+        throws IOException
+    {
+        return false;
+    }
+
     public Enumeration engineAliases()
     {
         Hashtable tab = new Hashtable();
@@ -295,25 +311,23 @@
         String alias)
         throws KeyStoreException
     {
-        Key k = (Key)keys.remove(alias);
-
-        Certificate c = (Certificate)certs.remove(alias);
-
-        if (c != null)
+        Certificate cert = (Certificate)certs.remove(alias);
+        if (cert != null)
         {
-            chainCerts.remove(new CertId(c.getPublicKey()));
+            chainCerts.remove(new CertId(cert.getPublicKey()));
         }
 
-        if (k != null)
+        Key key = (Key)keys.remove(alias);
+        if (key != null)
         {
             String id = (String)localIds.remove(alias);
             if (id != null)
             {
-                c = (Certificate)keyCerts.remove(id);
-            }
-            if (c != null)
-            {
-                chainCerts.remove(new CertId(c.getPublicKey()));
+                Certificate keyCert = (Certificate)keyCerts.remove(id);
+                if (keyCert != null)
+                {
+                    chainCerts.remove(new CertId(keyCert.getPublicKey()));
+                }
             }
         }
     }
@@ -658,7 +672,7 @@
 
         try
         {
-            SecretKeyFactory keyFact =  helper.createSecretKeyFactory(algorithm);
+            SecretKeyFactory keyFact = helper.createSecretKeyFactory(algorithm);
             PBEParameterSpec defParams = new PBEParameterSpec(
                 pbeParams.getIV(),
                 pbeParams.getIterations().intValue());
@@ -708,7 +722,7 @@
                 throw new IOException("exception decrypting data - " + e.toString());
             }
         }
-        else  if (algorithm.equals(PKCSObjectIdentifiers.id_PBES2))
+        else if (algorithm.equals(PKCSObjectIdentifiers.id_PBES2))
         {
             try
             {
@@ -771,6 +785,31 @@
         return cipher;
     }
 
+  
+    // BEGIN Android-removed: Unsupported algorithms
+    /*
+    public void engineLoad(KeyStore.LoadStoreParameter loadStoreParameter)
+        throws IOException, NoSuchAlgorithmException, CertificateException
+    {
+        if (loadStoreParameter == null)
+        {
+            engineLoad(null, null);
+        }
+        else if (loadStoreParameter instanceof BCLoadStoreParameter)
+        {
+            BCLoadStoreParameter bcParam = (BCLoadStoreParameter)loadStoreParameter;
+
+            engineLoad(bcParam.getInputStream(), ParameterUtil.extractPassword(loadStoreParameter));
+        }
+        else
+        {
+            throw new IllegalArgumentException(
+                "no support for 'param' of type " + loadStoreParameter.getClass().getName());
+        }
+    }
+    */
+    // END Android-removed: Unsupported algorithms
+
     public void engineLoad(
         InputStream stream,
         char[] password)
@@ -786,7 +825,10 @@
         bufIn.mark(10);
 
         int head = bufIn.read();
-
+        if (head < 0)
+        {
+            throw new EOFException("no data in keystore stream");
+        }
         if (head != 0x30)
         {
             throw new IOException("stream does not represent a PKCS12 key store");
@@ -795,7 +837,7 @@
         bufIn.reset();
 
         ASN1InputStream bIn = new ASN1InputStream(bufIn);
-        
+
         Pfx bag;
         try
         {
@@ -872,7 +914,7 @@
         // END Android-removed: keep v1.61 behaviour to keep backwards-compatibility
 
         keys = new IgnoresCaseHashtable();
-        localIds = new Hashtable();
+        localIds = new IgnoresCaseHashtable();
 
         if (info.getContentType().equals(data))
         {
@@ -892,86 +934,19 @@
                         SafeBag b = SafeBag.getInstance(seq.getObjectAt(j));
                         if (b.getBagId().equals(pkcs8ShroudedKeyBag))
                         {
-                            com.android.internal.org.bouncycastle.asn1.pkcs.EncryptedPrivateKeyInfo eIn = com.android.internal.org.bouncycastle.asn1.pkcs.EncryptedPrivateKeyInfo.getInstance(b.getBagValue());
-                            PrivateKey privKey = unwrapKey(eIn.getEncryptionAlgorithm(), eIn.getEncryptedData(), password, wrongPKCS12Zero);
-
-                            //
-                            // set the attributes on the key
-                            //
-                            String alias = null;
-                            ASN1OctetString localId = null;
-
-                            if (b.getBagAttributes() != null)
-                            {
-                                Enumeration e = b.getBagAttributes().getObjects();
-                                while (e.hasMoreElements())
-                                {
-                                    ASN1Sequence sq = (ASN1Sequence)e.nextElement();
-                                    ASN1ObjectIdentifier aOid = (ASN1ObjectIdentifier)sq.getObjectAt(0);
-                                    ASN1Set attrSet = (ASN1Set)sq.getObjectAt(1);
-                                    ASN1Primitive attr = null;
-
-                                    if (attrSet.size() > 0)
-                                    {
-                                        attr = (ASN1Primitive)attrSet.getObjectAt(0);
-
-                                        if (privKey instanceof PKCS12BagAttributeCarrier)
-                                        {
-                                            PKCS12BagAttributeCarrier bagAttr = (PKCS12BagAttributeCarrier)privKey;
-                                            ASN1Encodable existing = bagAttr.getBagAttribute(aOid);
-                                            if (existing != null)
-                                            {
-                                                // OK, but the value has to be the same
-                                                if (!existing.toASN1Primitive().equals(attr))
-                                                {
-                                                    throw new IOException(
-                                                        "attempt to add existing attribute with different value");
-                                                }
-                                            }
-                                            else
-                                            {
-                                                bagAttr.setBagAttribute(aOid, attr);
-                                            }
-                                        }
-                                    }
-
-                                    if (aOid.equals(pkcs_9_at_friendlyName))
-                                    {
-                                        alias = ((DERBMPString)attr).getString();
-                                        keys.put(alias, privKey);
-                                    }
-                                    else if (aOid.equals(pkcs_9_at_localKeyId))
-                                    {
-                                        localId = (ASN1OctetString)attr;
-                                    }
-                                }
-                            }
-
-                            if (localId != null)
-                            {
-                                String name = new String(Hex.encode(localId.getOctets()));
-
-                                if (alias == null)
-                                {
-                                    keys.put(name, privKey);
-                                }
-                                else
-                                {
-                                    localIds.put(alias, name);
-                                }
-                            }
-                            else
-                            {
-                                unmarkedKey = true;
-                                keys.put("unmarked", privKey);
-                            }
+                            unmarkedKey = processShroudedKeyBag(b, password, wrongPKCS12Zero);
                         }
                         else if (b.getBagId().equals(certBag))
                         {
                             chain.addElement(b);
                         }
+                        else if (b.getBagId().equals(keyBag))
+                        {
+                            processKeyBag(b);
+                        }
                         else
                         {
+                            // -DM 2 System.out.println
                             System.out.println("extra in data " + b.getBagId());
                             System.out.println(ASN1Dump.dumpAsString(b));
                         }
@@ -987,137 +962,21 @@
                     for (int j = 0; j != seq.size(); j++)
                     {
                         SafeBag b = SafeBag.getInstance(seq.getObjectAt(j));
-
                         if (b.getBagId().equals(certBag))
                         {
                             chain.addElement(b);
                         }
                         else if (b.getBagId().equals(pkcs8ShroudedKeyBag))
                         {
-                            com.android.internal.org.bouncycastle.asn1.pkcs.EncryptedPrivateKeyInfo eIn = com.android.internal.org.bouncycastle.asn1.pkcs.EncryptedPrivateKeyInfo.getInstance(b.getBagValue());
-                            PrivateKey privKey = unwrapKey(eIn.getEncryptionAlgorithm(), eIn.getEncryptedData(), password, wrongPKCS12Zero);
-
-                            //
-                            // set the attributes on the key
-                            //
-                            PKCS12BagAttributeCarrier bagAttr = (PKCS12BagAttributeCarrier)privKey;
-                            String alias = null;
-                            ASN1OctetString localId = null;
-
-                            Enumeration e = b.getBagAttributes().getObjects();
-                            while (e.hasMoreElements())
-                            {
-                                ASN1Sequence sq = (ASN1Sequence)e.nextElement();
-                                ASN1ObjectIdentifier aOid = (ASN1ObjectIdentifier)sq.getObjectAt(0);
-                                ASN1Set attrSet = (ASN1Set)sq.getObjectAt(1);
-                                ASN1Primitive attr = null;
-
-                                if (attrSet.size() > 0)
-                                {
-                                    attr = (ASN1Primitive)attrSet.getObjectAt(0);
-
-                                    ASN1Encodable existing = bagAttr.getBagAttribute(aOid);
-                                    if (existing != null)
-                                    {
-                                        // OK, but the value has to be the same
-                                        if (!existing.toASN1Primitive().equals(attr))
-                                        {
-                                            throw new IOException(
-                                                "attempt to add existing attribute with different value");
-                                        }
-                                    }
-                                    else
-                                    {
-                                        bagAttr.setBagAttribute(aOid, attr);
-                                    }
-                                }
-
-                                if (aOid.equals(pkcs_9_at_friendlyName))
-                                {
-                                    alias = ((DERBMPString)attr).getString();
-                                    keys.put(alias, privKey);
-                                }
-                                else if (aOid.equals(pkcs_9_at_localKeyId))
-                                {
-                                    localId = (ASN1OctetString)attr;
-                                }
-                            }
-
-                            String name = new String(Hex.encode(localId.getOctets()));
-
-                            if (alias == null)
-                            {
-                                keys.put(name, privKey);
-                            }
-                            else
-                            {
-                                localIds.put(alias, name);
-                            }
+                            unmarkedKey = processShroudedKeyBag(b, password, wrongPKCS12Zero);
                         }
                         else if (b.getBagId().equals(keyBag))
                         {
-                            com.android.internal.org.bouncycastle.asn1.pkcs.PrivateKeyInfo kInfo = com.android.internal.org.bouncycastle.asn1.pkcs.PrivateKeyInfo.getInstance(b.getBagValue());
-                            PrivateKey privKey = BouncyCastleProvider.getPrivateKey(kInfo);
-
-                            //
-                            // set the attributes on the key
-                            //
-                            PKCS12BagAttributeCarrier bagAttr = (PKCS12BagAttributeCarrier)privKey;
-                            String alias = null;
-                            ASN1OctetString localId = null;
-
-                            Enumeration e = b.getBagAttributes().getObjects();
-                            while (e.hasMoreElements())
-                            {
-                                ASN1Sequence sq = ASN1Sequence.getInstance(e.nextElement());
-                                ASN1ObjectIdentifier aOid = ASN1ObjectIdentifier.getInstance(sq.getObjectAt(0));
-                                ASN1Set attrSet = ASN1Set.getInstance(sq.getObjectAt(1));
-                                ASN1Primitive attr = null;
-
-                                if (attrSet.size() > 0)
-                                {
-                                    attr = (ASN1Primitive)attrSet.getObjectAt(0);
-
-                                    ASN1Encodable existing = bagAttr.getBagAttribute(aOid);
-                                    if (existing != null)
-                                    {
-                                        // OK, but the value has to be the same
-                                        if (!existing.toASN1Primitive().equals(attr))
-                                        {
-                                            throw new IOException(
-                                                "attempt to add existing attribute with different value");
-                                        }
-                                    }
-                                    else
-                                    {
-                                        bagAttr.setBagAttribute(aOid, attr);
-                                    }
-
-                                    if (aOid.equals(pkcs_9_at_friendlyName))
-                                    {
-                                        alias = ((DERBMPString)attr).getString();
-                                        keys.put(alias, privKey);
-                                    }
-                                    else if (aOid.equals(pkcs_9_at_localKeyId))
-                                    {
-                                        localId = (ASN1OctetString)attr;
-                                    }
-                                }
-                            }
-
-                            String name = new String(Hex.encode(localId.getOctets()));
-
-                            if (alias == null)
-                            {
-                                keys.put(name, privKey);
-                            }
-                            else
-                            {
-                                localIds.put(alias, name);
-                            }
+                            processKeyBag(b);
                         }
                         else
                         {
+                            // -DM 2 System.out.println
                             System.out.println("extra in encryptedData " + b.getBagId());
                             System.out.println(ASN1Dump.dumpAsString(b));
                         }
@@ -1125,6 +984,7 @@
                 }
                 else
                 {
+                    // -DM 2 System.out.println
                     System.out.println("extra " + c[i].getContentType().getId());
                     System.out.println("extra " + ASN1Dump.dumpAsString(c[i].getContent()));
                 }
@@ -1185,6 +1045,17 @@
                             ASN1Encodable existing = bagAttr.getBagAttribute(oid);
                             if (existing != null)
                             {
+                                // we've found more than one - one might be incorrect
+                                if (oid.equals(pkcs_9_at_localKeyId))
+                                {
+                                    // -DM Hex.toHexString
+                                    String id = Hex.toHexString(((ASN1OctetString)attr).getOctets());
+                                    if (!(keys.keys.containsKey(id) || localIds.keys.containsKey(id)))
+                                    {
+                                        continue; // ignore this one - it's not valid
+                                    }
+                                }
+
                                 // OK, but the value has to be the same
                                 if (!existing.toASN1Primitive().equals(attr))
                                 {
@@ -1194,13 +1065,20 @@
                             }
                             else
                             {
-                                bagAttr.setBagAttribute(oid, attr);
+                                if (attrSet.size() > 1)
+                                {
+                                    bagAttr.setBagAttribute(oid, attrSet);
+                                }
+                                else
+                                {
+                                    bagAttr.setBagAttribute(oid, attr);
+                                }
                             }
                         }
 
                         if (oid.equals(pkcs_9_at_friendlyName))
                         {
-                            alias = ((DERBMPString)attr).getString();
+                            alias = ((ASN1BMPString)attr).getString();
                         }
                         else if (oid.equals(pkcs_9_at_localKeyId))
                         {
@@ -1241,6 +1119,149 @@
         }
     }
 
+    private boolean processShroudedKeyBag(SafeBag b, char[] password, boolean wrongPKCS12Zero)
+        throws IOException
+    {
+        com.android.internal.org.bouncycastle.asn1.pkcs.EncryptedPrivateKeyInfo eIn = com.android.internal.org.bouncycastle.asn1.pkcs.EncryptedPrivateKeyInfo.getInstance(b.getBagValue());
+        PrivateKey privKey = unwrapKey(eIn.getEncryptionAlgorithm(), eIn.getEncryptedData(), password, wrongPKCS12Zero);
+
+        //
+        // set the attributes on the key
+        //
+        String alias = null;
+        ASN1OctetString localId = null;
+
+        if (b.getBagAttributes() != null)
+        {
+            Enumeration e = b.getBagAttributes().getObjects();
+            while (e.hasMoreElements())
+            {
+                ASN1Sequence sq = (ASN1Sequence)e.nextElement();
+                ASN1ObjectIdentifier aOid = (ASN1ObjectIdentifier)sq.getObjectAt(0);
+                ASN1Set attrSet = (ASN1Set)sq.getObjectAt(1);
+                ASN1Primitive attr = null;
+
+                if (attrSet.size() > 0)
+                {
+                    attr = (ASN1Primitive)attrSet.getObjectAt(0);
+
+                    if (privKey instanceof PKCS12BagAttributeCarrier)
+                    {
+                        PKCS12BagAttributeCarrier bagAttr = (PKCS12BagAttributeCarrier)privKey;
+                        ASN1Encodable existing = bagAttr.getBagAttribute(aOid);
+                        if (existing != null)
+                        {
+                            // OK, but the value has to be the same
+                            if (!existing.toASN1Primitive().equals(attr))
+                            {
+                                throw new IOException(
+                                    "attempt to add existing attribute with different value");
+                            }
+                        }
+                        else
+                        {
+                            bagAttr.setBagAttribute(aOid, attr);
+                        }
+                    }
+                }
+
+                if (aOid.equals(pkcs_9_at_friendlyName))
+                {
+                    alias = ((ASN1BMPString)attr).getString();
+                    keys.put(alias, privKey);
+                }
+                else if (aOid.equals(pkcs_9_at_localKeyId))
+                {
+                    localId = (ASN1OctetString)attr;
+                }
+            }
+        }
+
+        if (localId != null)
+        {
+            String name = new String(Hex.encode(localId.getOctets()));
+
+            if (alias == null)
+            {
+                keys.put(name, privKey);
+            }
+            else
+            {
+                localIds.put(alias, name);
+            }
+            return false;  // key properly marked
+        }
+        else
+        {
+            keys.put("unmarked", privKey);
+            return true;  // key properly marked
+        }
+    }
+
+    private void processKeyBag(SafeBag b)
+        throws IOException
+    {
+        com.android.internal.org.bouncycastle.asn1.pkcs.PrivateKeyInfo kInfo = com.android.internal.org.bouncycastle.asn1.pkcs.PrivateKeyInfo.getInstance(b.getBagValue());
+        PrivateKey privKey = BouncyCastleProvider.getPrivateKey(kInfo);
+
+        //
+        // set the attributes on the key
+        //
+        PKCS12BagAttributeCarrier bagAttr = (PKCS12BagAttributeCarrier)privKey;
+        String alias = null;
+        ASN1OctetString localId = null;
+
+        Enumeration e = b.getBagAttributes().getObjects();
+        while (e.hasMoreElements())
+        {
+            ASN1Sequence sq = ASN1Sequence.getInstance(e.nextElement());
+            ASN1ObjectIdentifier aOid = ASN1ObjectIdentifier.getInstance(sq.getObjectAt(0));
+            ASN1Set attrSet = ASN1Set.getInstance(sq.getObjectAt(1));
+            ASN1Primitive attr = null;
+
+            if (attrSet.size() > 0)
+            {
+                attr = (ASN1Primitive)attrSet.getObjectAt(0);
+
+                ASN1Encodable existing = bagAttr.getBagAttribute(aOid);
+                if (existing != null)
+                {
+                    // OK, but the value has to be the same
+                    if (!existing.toASN1Primitive().equals(attr))
+                    {
+                        throw new IOException(
+                            "attempt to add existing attribute with different value");
+                    }
+                }
+                else
+                {
+                    bagAttr.setBagAttribute(aOid, attr);
+                }
+
+                if (aOid.equals(pkcs_9_at_friendlyName))
+                {
+                    alias = ((ASN1BMPString)attr).getString();
+                    keys.put(alias, privKey);
+                }
+                else if (aOid.equals(pkcs_9_at_localKeyId))
+                {
+                    localId = (ASN1OctetString)attr;
+                }
+            }
+        }
+
+        String name = new String(Hex.encode(localId.getOctets()));
+
+        if (alias == null)
+        {
+            keys.put(name, privKey);
+        }
+        else
+        {
+            localIds.put(alias, name);
+        }
+    }
+
     private int validateIterationCount(BigInteger i)
     {
         int count = i.intValue();
@@ -1322,7 +1343,55 @@
         // See CtsKeystoreTestCases:android.keystore.cts.KeyStoreTest
         if (password == null)
         {
-            throw new NullPointerException("No password supplied for PKCS#12 KeyStore.");
+            if (password == null)
+            {
+                Enumeration cs = certs.keys();
+
+                ASN1EncodableVector certSeq = new ASN1EncodableVector();
+
+                while (cs.hasMoreElements())
+                {
+                    try
+                    {
+                        String certId = (String)cs.nextElement();
+                        Certificate cert = (Certificate)certs.get(certId);
+
+                        SafeBag sBag = createSafeBag(certId, cert);
+
+                        certSeq.add(sBag);
+                    }
+                    catch (CertificateEncodingException e)
+                    {
+                        throw new IOException("Error encoding certificate: " + e.toString());
+                    }
+                }
+
+                if (useDEREncoding)
+                {
+                    ContentInfo bagInfo = new ContentInfo(PKCSObjectIdentifiers.data, new DEROctetString(new DERSequence(certSeq).getEncoded()));
+
+                    Pfx pfx = new Pfx(new ContentInfo(PKCSObjectIdentifiers.data, new DEROctetString(new DERSequence(bagInfo).getEncoded())), null);
+
+                    pfx.encodeTo(stream, ASN1Encoding.DER);
+                }
+                else
+                {
+                    ContentInfo bagInfo = new ContentInfo(PKCSObjectIdentifiers.data, new BEROctetString(new BERSequence(certSeq).getEncoded()));
+
+                    Pfx pfx = new Pfx(new ContentInfo(PKCSObjectIdentifiers.data, new BEROctetString(new BERSequence(bagInfo).getEncoded())), null);
+
+                    pfx.encodeTo(stream, ASN1Encoding.BER);
+                }
+
+                return;
+            }
+        }
+        else
+        {
+            if (password == null)
+            {
+                throw new NullPointerException("no password supplied for PKCS#12 KeyStore");
+            }
         }
         /*
         if (keys.size() == 0)
@@ -1409,7 +1478,7 @@
                 //
                 // make sure we are using the local alias on store
                 //
-                DERBMPString nm = (DERBMPString)bagAttrs.getBagAttribute(pkcs_9_at_friendlyName);
+                ASN1BMPString nm = (ASN1BMPString)bagAttrs.getBagAttribute(pkcs_9_at_friendlyName);
                 if (nm == null || !nm.getString().equals(name))
                 {
                     bagAttrs.setBagAttribute(pkcs_9_at_friendlyName, new DERBMPString(name));
@@ -1500,7 +1569,7 @@
                     //
                     // make sure we are using the local alias on store
                     //
-                    DERBMPString nm = (DERBMPString)bagAttrs.getBagAttribute(pkcs_9_at_friendlyName);
+                    ASN1BMPString nm = (ASN1BMPString)bagAttrs.getBagAttribute(pkcs_9_at_friendlyName);
                     if (nm == null || !nm.getString().equals(name))
                     {
                         bagAttrs.setBagAttribute(pkcs_9_at_friendlyName, new DERBMPString(name));
@@ -1633,6 +1702,7 @@
                     }
                 }
 
+
                 SafeBag sBag = new SafeBag(certBag, cBag.toASN1Primitive(), new DERSet(fName));
 
                 certSeq.add(sBag);
@@ -1706,7 +1776,7 @@
             //
             // make sure we are using the local alias on store
             //
-            DERBMPString nm = (DERBMPString)bagAttrs.getBagAttribute(pkcs_9_at_friendlyName);
+            ASN1BMPString nm = (ASN1BMPString)bagAttrs.getBagAttribute(pkcs_9_at_friendlyName);
             if (nm == null || !nm.getString().equals(certId))
             {
                 if (certId != null)
@@ -1750,6 +1820,43 @@
             fName.add(new DERSequence(fSeq));
         }
 
+        // Android-removed: unsupported
+        // add the trusted usage attribute - needed for Oracle key stores
+        // if (cert instanceof X509Certificate)
+        // {
+        //     TBSCertificate tbsCert = TBSCertificate.getInstance(((X509Certificate)cert).getTBSCertificate());
+        //     Extensions exts = tbsCert.getExtensions();
+        //     if (exts != null)
+        //     {
+        //         Extension extUsage = exts.getExtension(Extension.extendedKeyUsage);
+        //         if (extUsage != null)
+        //         {
+        //             ASN1EncodableVector fSeq = new ASN1EncodableVector();
+
+        //             // oracle trusted key usage OID.
+        //             fSeq.add(MiscObjectIdentifiers.id_oracle_pkcs12_trusted_key_usage);
+        //             fSeq.add(new DERSet(ExtendedKeyUsage.getInstance(extUsage.getParsedValue()).getUsages()));
+        //             fName.add(new DERSequence(fSeq));
+        //         }
+        //         else
+        //         {
+        //             ASN1EncodableVector fSeq = new ASN1EncodableVector();
+
+        //             fSeq.add(MiscObjectIdentifiers.id_oracle_pkcs12_trusted_key_usage);
+        //             fSeq.add(new DERSet(KeyPurposeId.anyExtendedKeyUsage));
+        //             fName.add(new DERSequence(fSeq));
+        //         }
+        //     }
+        //     else
+        //     {
+        //         ASN1EncodableVector fSeq = new ASN1EncodableVector();
+
+        //         fSeq.add(MiscObjectIdentifiers.id_oracle_pkcs12_trusted_key_usage);
+        //         fSeq.add(new DERSet(KeyPurposeId.anyExtendedKeyUsage));
+        //         fName.add(new DERSequence(fSeq));
+        //     }
+        // }
+
         return new SafeBag(certBag, cBag.toASN1Primitive(), new DERSet(fName));
     }
 
@@ -1757,19 +1864,19 @@
     {
         Set usedSet = new HashSet();
 
-        for (Enumeration en = keys.keys(); en.hasMoreElements();)
+        for (Enumeration en = keys.keys(); en.hasMoreElements(); )
         {
             String alias = (String)en.nextElement();
 
-                Certificate[] certs = engineGetCertificateChain(alias);
+            Certificate[] certs = engineGetCertificateChain(alias);
 
-                for (int i = 0; i != certs.length; i++)
-                {
-                    usedSet.add(certs[i]);
-                }
+            for (int i = 0; i != certs.length; i++)
+            {
+                usedSet.add(certs[i]);
+            }
         }
 
-        for (Enumeration en = certs.keys(); en.hasMoreElements();)
+        for (Enumeration en = certs.keys(); en.hasMoreElements(); )
         {
             String alias = (String)en.nextElement();
 
@@ -1801,6 +1908,7 @@
         return mac.doFinal();
     }
 
+    // Android-changed: Use default provider for JCA algorithms instead of BC
     /**
      * @hide This class is not part of the Android public SDK API
      */
@@ -1810,7 +1918,7 @@
         public BCPKCS12KeyStore()
         {
             // Android-changed: Use default provider for JCA algorithms instead of BC
-            // Was: super(new BCJcaJceHelper(), pbeWithSHAAnd3_KeyTripleDES_CBC, pbeWithSHAAnd40BitRC2_CBC);
+            // Was: super(new BCJcaJceHelper(), new PKCS12KeyStoreSpi(new BCJcaJceHelper(), pbeWithSHAAnd3_KeyTripleDES_CBC, pbeWithSHAAnd40BitRC2_CBC));
             super(new DefaultJcaJceHelper(), pbeWithSHAAnd3_KeyTripleDES_CBC, pbeWithSHAAnd40BitRC2_CBC);
         }
     }
@@ -1818,29 +1926,29 @@
     // BEGIN Android-removed: Unsupported algorithms
     /*
     public static class BCPKCS12KeyStore3DES
-        extends PKCS12KeyStoreSpi
+        extends AdaptingKeyStoreSpi
     {
         public BCPKCS12KeyStore3DES()
         {
-            super(new BCJcaJceHelper(), pbeWithSHAAnd3_KeyTripleDES_CBC, pbeWithSHAAnd3_KeyTripleDES_CBC);
+            super(new BCJcaJceHelper(), new PKCS12KeyStoreSpi(new BCJcaJceHelper(), pbeWithSHAAnd3_KeyTripleDES_CBC, pbeWithSHAAnd3_KeyTripleDES_CBC));
         }
     }
 
     public static class DefPKCS12KeyStore
-        extends PKCS12KeyStoreSpi
+        extends AdaptingKeyStoreSpi
     {
         public DefPKCS12KeyStore()
         {
-            super(new DefaultJcaJceHelper(), pbeWithSHAAnd3_KeyTripleDES_CBC, pbeWithSHAAnd40BitRC2_CBC);
+            super(new DefaultJcaJceHelper(), new PKCS12KeyStoreSpi(new DefaultJcaJceHelper(), pbeWithSHAAnd3_KeyTripleDES_CBC, pbeWithSHAAnd40BitRC2_CBC));
         }
     }
 
     public static class DefPKCS12KeyStore3DES
-        extends PKCS12KeyStoreSpi
+        extends AdaptingKeyStoreSpi
     {
         public DefPKCS12KeyStore3DES()
         {
-            super(new DefaultJcaJceHelper(), pbeWithSHAAnd3_KeyTripleDES_CBC, pbeWithSHAAnd3_KeyTripleDES_CBC);
+            super(new DefaultJcaJceHelper(), new PKCS12KeyStoreSpi(new DefaultJcaJceHelper(), pbeWithSHAAnd3_KeyTripleDES_CBC, pbeWithSHAAnd3_KeyTripleDES_CBC));
         }
     }
     */
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/keystore/util/AdaptingKeyStoreSpi.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/keystore/util/AdaptingKeyStoreSpi.java
new file mode 100644
index 0000000..63dd613
--- /dev/null
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/keystore/util/AdaptingKeyStoreSpi.java
@@ -0,0 +1,186 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.jcajce.provider.keystore.util;
+
+import java.io.BufferedInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.security.Key;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.KeyStoreSpi;
+import java.security.NoSuchAlgorithmException;
+import java.security.UnrecoverableKeyException;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateException;
+import java.util.Date;
+import java.util.Enumeration;
+
+import com.android.internal.org.bouncycastle.jcajce.provider.keystore.pkcs12.PKCS12KeyStoreSpi;
+import com.android.internal.org.bouncycastle.jcajce.util.JcaJceHelper;
+import com.android.internal.org.bouncycastle.util.Properties;
+
+/**
+ * Implements a certificate only JKS key store.
+ * @hide This class is not part of the Android public SDK API
+ */
+public class AdaptingKeyStoreSpi
+    extends KeyStoreSpi
+{
+    public static final String COMPAT_OVERRIDE = "keystore.type.compat";
+
+    private final JKSKeyStoreSpi jksStore;
+    private final KeyStoreSpi primaryStore;
+
+    private KeyStoreSpi keyStoreSpi;
+
+    public AdaptingKeyStoreSpi(JcaJceHelper helper, KeyStoreSpi primaryStore)
+    {
+        this.jksStore = new JKSKeyStoreSpi(helper);
+        this.primaryStore = primaryStore;
+        this.keyStoreSpi = primaryStore;
+    }
+
+    public boolean engineProbe(InputStream stream)
+        throws IOException
+    {
+        if (keyStoreSpi instanceof PKCS12KeyStoreSpi)
+        {
+            return ((PKCS12KeyStoreSpi)keyStoreSpi).engineProbe(stream);
+        }
+        return false;
+    }
+
+    public Key engineGetKey(String alias, char[] password)
+        throws NoSuchAlgorithmException, UnrecoverableKeyException
+    {
+        return keyStoreSpi.engineGetKey(alias, password);
+    }
+
+    public Certificate[] engineGetCertificateChain(String alias)
+    {
+        return keyStoreSpi.engineGetCertificateChain(alias);
+    }
+
+    public Certificate engineGetCertificate(String alias)
+    {
+        return keyStoreSpi.engineGetCertificate(alias);
+    }
+
+    public Date engineGetCreationDate(String alias)
+    {
+        return keyStoreSpi.engineGetCreationDate(alias);
+    }
+
+    public void engineSetKeyEntry(String alias, Key key, char[] password, Certificate[] chain)
+        throws KeyStoreException
+    {
+        keyStoreSpi.engineSetKeyEntry(alias, key, password, chain);
+    }
+
+    public void engineSetKeyEntry(String alias, byte[] key, Certificate[] chain)
+        throws KeyStoreException
+    {
+        keyStoreSpi.engineSetKeyEntry(alias, key, chain);
+    }
+
+    public void engineSetCertificateEntry(String alias, Certificate cert)
+        throws KeyStoreException
+    {
+        keyStoreSpi.engineSetCertificateEntry(alias, cert);
+    }
+
+    public void engineDeleteEntry(String alias)
+        throws KeyStoreException
+    {
+        keyStoreSpi.engineDeleteEntry(alias);
+    }
+
+    public Enumeration<String> engineAliases()
+    {
+        return keyStoreSpi.engineAliases();
+    }
+
+    public boolean engineContainsAlias(String alias)
+    {
+        return keyStoreSpi.engineContainsAlias(alias);
+    }
+
+    public int engineSize()
+    {
+        return keyStoreSpi.engineSize();
+    }
+
+    public boolean engineIsKeyEntry(String alias)
+    {
+        return keyStoreSpi.engineIsKeyEntry(alias);
+    }
+
+    public boolean engineIsCertificateEntry(String alias)
+    {
+        return keyStoreSpi.engineIsCertificateEntry(alias);
+    }
+
+    public String engineGetCertificateAlias(Certificate cert)
+    {
+        return keyStoreSpi.engineGetCertificateAlias(cert);
+    }
+
+    public void engineStore(OutputStream stream, char[] password)
+        throws IOException, NoSuchAlgorithmException, CertificateException
+    {
+        keyStoreSpi.engineStore(stream, password);
+    }
+
+    public void engineStore(KeyStore.LoadStoreParameter parameter)
+        throws IOException, NoSuchAlgorithmException, CertificateException
+    {
+        keyStoreSpi.engineStore(parameter);
+    }
+
+    public void engineLoad(InputStream stream, char[] password)
+        throws IOException, NoSuchAlgorithmException, CertificateException
+    {
+        if (stream == null)
+        {
+            keyStoreSpi = primaryStore;
+            keyStoreSpi.engineLoad(null, password);
+        }
+        else
+        {
+            // the FIPS BCFKS/JKS compatibility is explicit and doesn't use the override.
+            if (Properties.isOverrideSet(COMPAT_OVERRIDE) || !(primaryStore instanceof PKCS12KeyStoreSpi))
+            {
+                if (!stream.markSupported())
+                {
+                    stream = new BufferedInputStream(stream);
+                }
+
+                stream.mark(8);
+                if (jksStore.engineProbe(stream))
+                {
+                    keyStoreSpi = jksStore;
+                }
+                else
+                {
+                    keyStoreSpi = primaryStore;
+                }
+
+                stream.reset();
+            }
+            else
+            {
+                keyStoreSpi = primaryStore;
+            }
+
+            keyStoreSpi.engineLoad(stream, password);
+        }
+    }
+
+    public void engineLoad(KeyStore.LoadStoreParameter parameter)
+        throws IOException, NoSuchAlgorithmException, CertificateException
+    {
+        keyStoreSpi.engineLoad(parameter);
+    }
+}
+
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/keystore/util/JKSKeyStoreSpi.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/keystore/util/JKSKeyStoreSpi.java
new file mode 100644
index 0000000..a188742
--- /dev/null
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/keystore/util/JKSKeyStoreSpi.java
@@ -0,0 +1,428 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.jcajce.provider.keystore.util;
+
+import java.io.ByteArrayInputStream;
+import java.io.DataInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.security.Key;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.KeyStoreSpi;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.UnrecoverableKeyException;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.util.Date;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.Map;
+
+import com.android.internal.org.bouncycastle.crypto.Digest;
+import com.android.internal.org.bouncycastle.jcajce.BCLoadStoreParameter;
+import com.android.internal.org.bouncycastle.jcajce.provider.util.DigestFactory;
+import com.android.internal.org.bouncycastle.jcajce.util.JcaJceHelper;
+import com.android.internal.org.bouncycastle.util.Arrays;
+import com.android.internal.org.bouncycastle.util.Strings;
+import com.android.internal.org.bouncycastle.util.io.Streams;
+
+/**
+ * Implements a certificate only JKS key store.
+ * @hide This class is not part of the Android public SDK API
+ */
+public class JKSKeyStoreSpi
+    extends KeyStoreSpi
+{
+    private static final String NOT_IMPLEMENTED_MESSAGE = "BC JKS store is read-only and only supports certificate entries";
+
+    private final Hashtable<String, BCJKSTrustedCertEntry> certificateEntries = new Hashtable<String, BCJKSTrustedCertEntry>();
+    private final JcaJceHelper helper;
+
+    public JKSKeyStoreSpi(JcaJceHelper helper)
+    {
+        this.helper = helper;
+    }
+
+    public boolean engineProbe(InputStream stream)
+        throws IOException
+    {
+        DataInputStream storeStream;
+        if (stream instanceof DataInputStream)
+        {
+            storeStream = (DataInputStream)stream;
+        }
+        else
+        {
+            storeStream = new DataInputStream(stream);
+        }
+
+        int magic = storeStream.readInt();
+        int storeVersion = storeStream.readInt();
+        return magic == (int)0x0000feedfeedL && (storeVersion == 1 || storeVersion == 2);
+    }
+
+    public Key engineGetKey(String alias, char[] password)
+        throws NoSuchAlgorithmException, UnrecoverableKeyException
+    {
+        return null;  // by definition
+    }
+
+    public Certificate[] engineGetCertificateChain(String alias)
+    {
+        return null;  // by definition
+    }
+
+    public Certificate engineGetCertificate(String alias)
+    {
+        synchronized (certificateEntries)
+        {
+            BCJKSTrustedCertEntry ent = certificateEntries.get(alias);
+            if (ent != null)
+            {
+                return ent.cert;
+            }
+        }
+        return null;
+    }
+
+    public Date engineGetCreationDate(String alias)
+    {
+        synchronized (certificateEntries)
+        {
+            BCJKSTrustedCertEntry ent = certificateEntries.get(alias);
+            if (ent != null)
+            {
+                return ent.date;
+            }
+        }
+        return null;
+    }
+
+    public void engineSetKeyEntry(String alias, Key key, char[] password, Certificate[] chain)
+        throws KeyStoreException
+    {
+        throw new KeyStoreException(NOT_IMPLEMENTED_MESSAGE);
+    }
+
+    public void engineSetKeyEntry(String alias, byte[] key, Certificate[] chain)
+        throws KeyStoreException
+    {
+        throw new KeyStoreException(NOT_IMPLEMENTED_MESSAGE);
+    }
+
+    public void engineSetCertificateEntry(String alias, Certificate cert)
+        throws KeyStoreException
+    {
+        throw new KeyStoreException(NOT_IMPLEMENTED_MESSAGE);
+    }
+
+    public void engineDeleteEntry(String alias)
+        throws KeyStoreException
+    {
+        throw new KeyStoreException(NOT_IMPLEMENTED_MESSAGE);
+    }
+
+    public Enumeration<String> engineAliases()
+    {
+        synchronized (certificateEntries)
+        {
+            return certificateEntries.keys();
+        }
+    }
+
+    public boolean engineContainsAlias(String alias)
+    {
+        if (alias == null)
+        {
+            throw new NullPointerException("alias value is null");
+        }
+
+        synchronized (certificateEntries)
+        {
+            return certificateEntries.containsKey(alias);
+        }
+    }
+
+    public int engineSize()
+    {
+        return certificateEntries.size();
+    }
+
+    public boolean engineIsKeyEntry(String alias)
+    {
+        return false;    // by definition
+    }
+
+    public boolean engineIsCertificateEntry(String alias)
+    {
+        synchronized (certificateEntries)
+        {
+            return certificateEntries.containsKey(alias);
+        }
+    }
+
+    public String engineGetCertificateAlias(Certificate cert)
+    {
+        synchronized (certificateEntries)
+        {
+            for (Iterator<Map.Entry<String, BCJKSTrustedCertEntry>> it = certificateEntries.entrySet().iterator(); it.hasNext(); )
+            {
+                Map.Entry<String, BCJKSTrustedCertEntry> entry = it.next();
+                if (entry.getValue().cert.equals(cert))
+                {
+                    return entry.getKey();
+                }
+            }
+            return null;
+        }
+    }
+
+    public void engineStore(OutputStream stream, char[] password)
+        throws IOException, NoSuchAlgorithmException, CertificateException
+    {
+        throw new IOException(NOT_IMPLEMENTED_MESSAGE);
+    }
+
+    public void engineLoad(KeyStore.LoadStoreParameter loadStoreParameter)
+        throws IOException, NoSuchAlgorithmException, CertificateException
+    {
+        if (loadStoreParameter == null)
+        {
+            engineLoad(null, null);
+        }
+        else if (loadStoreParameter instanceof BCLoadStoreParameter)
+        {
+            BCLoadStoreParameter bcParam = (BCLoadStoreParameter)loadStoreParameter;
+
+            engineLoad(bcParam.getInputStream(), ParameterUtil.extractPassword(loadStoreParameter));
+        }
+        else
+        {
+            throw new IllegalArgumentException(
+                "no support for 'param' of type " + loadStoreParameter.getClass().getName());
+        }
+    }
+
+    public void engineLoad(InputStream stream, char[] password)
+        throws IOException, NoSuchAlgorithmException, CertificateException
+    {
+        if (stream == null)
+        {
+            return;
+        }
+
+        ErasableByteStream storeStream = validateStream(stream, password);
+
+        synchronized (certificateEntries)
+        {
+            try
+            {
+                DataInputStream dIn = new DataInputStream(storeStream);
+
+                int magic = dIn.readInt();
+                int storeVersion = dIn.readInt();
+                if (magic == (int)0x0000feedfeedL)
+                {
+                    CertificateFactory certFact = null;
+                    Hashtable certFactories = null;
+
+                    switch (storeVersion)
+                    {
+                    case 1:  // all certs X.509
+                        certFact = createCertFactory("X.509");
+                        break;
+                    case 2:  // provision for format in store.
+                        certFactories = new Hashtable();
+                        break;
+                    default:
+                        throw new IllegalStateException("unable to discern store version");
+                    }
+
+                    int numEntries = dIn.readInt();
+                    for (int t = 0; t < numEntries; t++)
+                    {
+                        int tag = dIn.readInt();
+                        switch (tag)
+                        {
+                        case 1: // we can't process keys
+                            throw new IOException(NOT_IMPLEMENTED_MESSAGE);
+                        case 2: // certificate
+                            String alias = dIn.readUTF();
+                            Date date = new Date(dIn.readLong());
+
+                            if (storeVersion == 2)
+                            {
+                                String certFormat = dIn.readUTF();
+                                if (certFactories.containsKey(certFormat))
+                                {
+                                    certFact = (CertificateFactory)certFactories.get(certFormat);
+                                }
+                                else
+                                {
+                                    certFact = createCertFactory(certFormat);
+                                    certFactories.put(certFormat, certFact);
+                                }
+                            }
+
+                            int l = dIn.readInt();
+                            byte[] certData = new byte[l];
+                            dIn.readFully(certData);
+
+                            ErasableByteStream certStream = new ErasableByteStream(certData, 0, certData.length);
+                            Certificate cert;
+                            try
+                            {
+                                cert = certFact.generateCertificate(certStream);
+
+                                if (certStream.available() != 0)
+                                {
+                                    throw new IOException("password incorrect or store tampered with");
+                                }
+                            }
+                            finally
+                            {
+                                certStream.erase();
+                            }
+
+                            certificateEntries.put(alias, new BCJKSTrustedCertEntry(date, cert));
+                            break;
+                        default:
+                            throw new IllegalStateException("unable to discern entry type");
+                        }
+                    }
+                }
+
+                if (storeStream.available() != 0)
+                {
+                    throw new IOException("password incorrect or store tampered with");
+                }
+            }
+            finally
+            {
+                storeStream.erase();
+            }
+        }
+    }
+
+    private CertificateFactory createCertFactory(String certFormat)
+        throws CertificateException
+    {
+        if (helper != null)
+        {
+            try
+            {
+                return helper.createCertificateFactory(certFormat);
+            }
+            catch (NoSuchProviderException e)
+            {
+                throw new CertificateException(e.toString());
+            }
+        }
+        else
+        {
+            return CertificateFactory.getInstance(certFormat);
+        }
+    }
+
+    /**
+     * Process password updates the digest with the password.
+     *
+     * @param digest   The digest instance.
+     * @param password The password.
+     */
+    private void addPassword(Digest digest, char[] password)
+        throws IOException
+    {
+        for (int i = 0; i < password.length; ++i)
+        {
+            digest.update((byte)(password[i] >> 8));
+            digest.update((byte)password[i]);
+        }
+
+        //
+        // This "Mighty Aphrodite" string goes all the way back to the
+        // first java betas in the mid 90's, why who knows? But see
+        // https://cryptosense.com/mighty-aphrodite-dark-secrets-of-the-java-keystore/
+        //
+        digest.update(Strings.toByteArray("Mighty Aphrodite"), 0, 16);
+    }
+
+    /**
+     * Validate password takes the checksum of the store and will either.
+     * 1. If password is null, load the store into memory, return the result.
+     * 2. If password is not null, load the store into memory, test the checksum, and if successful return
+     * a new input stream instance of the store.
+     * 3. Fail if there is a password and an invalid checksum.
+     *
+     * @param inputStream The input stream.
+     * @param password    the password.
+     * @return Either the passed in input stream or a new input stream.
+     * @throws IOException
+     */
+    private ErasableByteStream validateStream(InputStream inputStream, char[] password)
+        throws IOException
+    {
+        Digest checksumCalculator = DigestFactory.getDigest("SHA-1");
+        byte[] rawStore = Streams.readAll(inputStream);
+
+        if (password != null)
+        {
+            addPassword(checksumCalculator, password);
+            checksumCalculator.update(rawStore, 0, rawStore.length - checksumCalculator.getDigestSize());
+
+            byte[] checksum = new byte[checksumCalculator.getDigestSize()];
+
+            checksumCalculator.doFinal(checksum, 0);
+
+            byte[] streamChecksum = new byte[checksum.length];
+            System.arraycopy(rawStore, rawStore.length - checksum.length, streamChecksum, 0, checksum.length);
+
+            if (!Arrays.constantTimeAreEqual(checksum, streamChecksum))
+            {
+                Arrays.fill(rawStore, (byte)0);
+                throw new IOException("password incorrect or store tampered with");
+            }
+
+            return new ErasableByteStream(rawStore, 0, rawStore.length - checksum.length);
+        }
+
+        return new ErasableByteStream(rawStore, 0, rawStore.length - checksumCalculator.getDigestSize());
+    }
+
+    /**
+     * BCJKSTrustedCertEntry is a internal container for the certificate entry.
+     */
+    private static final class BCJKSTrustedCertEntry
+    {
+        final Date date;
+        final Certificate cert;
+
+        public BCJKSTrustedCertEntry(Date date, Certificate cert)
+        {
+            this.date = date;
+            this.cert = cert;
+        }
+    }
+
+    private static final class ErasableByteStream
+        extends ByteArrayInputStream
+    {
+        public ErasableByteStream(byte[] buf, int offSet, int length)
+        {
+            super(buf, offSet, length);
+        }
+
+        public void erase()
+        {
+            // this will also erase the checksum from memory.
+            Arrays.fill(buf, (byte)0);
+        }
+    }
+
+
+}
+
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/keystore/util/ParameterUtil.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/keystore/util/ParameterUtil.java
new file mode 100644
index 0000000..f2a20c9
--- /dev/null
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/keystore/util/ParameterUtil.java
@@ -0,0 +1,53 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.jcajce.provider.keystore.util;
+
+import java.io.IOException;
+import java.security.KeyStore;
+
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.callback.PasswordCallback;
+import javax.security.auth.callback.UnsupportedCallbackException;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+public class ParameterUtil
+{
+    public static char[] extractPassword(KeyStore.LoadStoreParameter bcParam)
+        throws IOException
+    {
+        KeyStore.ProtectionParameter protParam = bcParam.getProtectionParameter();
+
+        if (protParam == null)
+        {
+            return null;
+        }
+        else if (protParam instanceof KeyStore.PasswordProtection)
+        {
+            return ((KeyStore.PasswordProtection)protParam).getPassword();
+        }
+        else if (protParam instanceof KeyStore.CallbackHandlerProtection)
+        {
+            CallbackHandler handler = ((KeyStore.CallbackHandlerProtection)protParam).getCallbackHandler();
+
+            PasswordCallback passwordCallback = new PasswordCallback("password: ", false);
+
+            try
+            {
+                handler.handle(new Callback[]{passwordCallback});
+
+                return passwordCallback.getPassword();
+            }
+            catch (UnsupportedCallbackException e)
+            {
+                throw new IllegalArgumentException("PasswordCallback not recognised: " + e.getMessage(), e);
+            }
+        }
+        else
+        {
+            throw new IllegalArgumentException(
+                "no support for protection parameter of type " + protParam.getClass().getName());
+        }
+    }
+}
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/symmetric/AES.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/symmetric/AES.java
index 056faae..7daa5d2 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/symmetric/AES.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/symmetric/AES.java
@@ -19,16 +19,13 @@
 import javax.crypto.NoSuchPaddingException;
 // END Android-added: Needed for setting padding with GCM
 import com.android.internal.org.bouncycastle.asn1.bc.BCObjectIdentifiers;
-// Android-removed: Unsupported algorithms
-// import org.bouncycastle.asn1.cms.CCMParameters;
-import com.android.internal.org.bouncycastle.asn1.cms.GCMParameters;
 import com.android.internal.org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
 import com.android.internal.org.bouncycastle.crypto.BlockCipher;
-import com.android.internal.org.bouncycastle.crypto.BufferedBlockCipher;
 import com.android.internal.org.bouncycastle.crypto.CipherKeyGenerator;
 import com.android.internal.org.bouncycastle.crypto.CipherParameters;
 import com.android.internal.org.bouncycastle.crypto.CryptoServicesRegistrar;
 import com.android.internal.org.bouncycastle.crypto.DataLengthException;
+import com.android.internal.org.bouncycastle.crypto.DefaultBufferedBlockCipher;
 import com.android.internal.org.bouncycastle.crypto.InvalidCipherTextException;
 import com.android.internal.org.bouncycastle.crypto.Mac;
 import com.android.internal.org.bouncycastle.crypto.engines.AESEngine;
@@ -43,9 +40,13 @@
 import com.android.internal.org.bouncycastle.crypto.modes.CBCBlockCipher;
 // Android-removed: Unsupported algorithms
 // import org.bouncycastle.crypto.modes.CCMBlockCipher;
+// import org.bouncycastle.crypto.modes.CCMModeCipher;
 import com.android.internal.org.bouncycastle.crypto.modes.CFBBlockCipher;
 import com.android.internal.org.bouncycastle.crypto.modes.GCMBlockCipher;
 import com.android.internal.org.bouncycastle.crypto.modes.OFBBlockCipher;
+// Android-removed: Unsupported algorithms
+// import org.bouncycastle.internal.asn1.cms.CCMParameters;
+import com.android.internal.org.bouncycastle.internal.asn1.cms.GCMParameters;
 import com.android.internal.org.bouncycastle.jcajce.provider.config.ConfigurableProvider;
 // Android-removed: Unsupported algorithms
 // import org.bouncycastle.jcajce.provider.symmetric.util.BaseAlgorithmParameterGenerator;
@@ -78,7 +79,7 @@
     private AES()
     {
     }
-    
+
     /**
      * @hide This class is not part of the Android public SDK API
      */
@@ -91,7 +92,7 @@
             {
                 public BlockCipher get()
                 {
-                    return new AESEngine();
+                    return AESEngine.newInstance();
                 }
             });
         }
@@ -101,11 +102,11 @@
      * @hide This class is not part of the Android public SDK API
      */
     public static class CBC
-       extends BaseBlockCipher
+        extends BaseBlockCipher
     {
         public CBC()
         {
-            super(new CBCBlockCipher(new AESEngine()), 128);
+            super(CBCBlockCipher.newInstance(AESEngine.newInstance()), 128);
         }
     }
 
@@ -117,7 +118,7 @@
     {
         public CFB()
         {
-            super(new BufferedBlockCipher(new CFBBlockCipher(new AESEngine(), 128)), 128);
+            super(new DefaultBufferedBlockCipher(CFBBlockCipher.newInstance(AESEngine.newInstance(), 128)), 128);
         }
     }
 
@@ -129,7 +130,7 @@
     {
         public OFB()
         {
-            super(new BufferedBlockCipher(new OFBBlockCipher(new AESEngine(), 128)), 128);
+            super(new DefaultBufferedBlockCipher(new OFBBlockCipher(AESEngine.newInstance(), 128)), 128);
         }
     }
 
@@ -161,7 +162,7 @@
     {
         public CCM()
         {
-            super(new CCMBlockCipher(new AESEngine()), false, 12);
+            super(CCMBlockCipher.newInstance(AESEngine.newInstance()), false, 12);
         }
     }
 
@@ -170,7 +171,7 @@
     {
         public AESCMAC()
         {
-            super(new CMac(new AESEngine()));
+            super(new CMac(AESEngine.newInstance()));
         }
     }
 
@@ -179,7 +180,7 @@
     {
         public AESGMAC()
         {
-            super(new GMac(new GCMBlockCipher(new AESEngine())));
+            super(new GMac(GCMBlockCipher.newInstance(AESEngine.newInstance())));
         }
     }
 
@@ -194,7 +195,7 @@
         private static class CCMMac
             implements Mac
         {
-            private final CCMBlockCipher ccm = new CCMBlockCipher(new AESEngine());
+            private final CCMModeCipher ccm = CCMBlockCipher.newInstance(AESEngine.newInstance());
 
             private int macLength = 8;
 
@@ -249,7 +250,7 @@
     }
 
     static public class KeyFactory
-         extends BaseSecretKeyFactory
+        extends BaseSecretKeyFactory
     {
         public KeyFactory()
         {
@@ -262,7 +263,7 @@
     {
         public Poly1305()
         {
-            super(new org.bouncycastle.crypto.macs.Poly1305(new AESEngine()));
+            super(new org.bouncycastle.crypto.macs.Poly1305(AESEngine.newInstance()));
         }
     }
 
@@ -305,7 +306,7 @@
     {
         public RFC3211Wrap()
         {
-            super(new RFC3211WrapEngine(new AESEngine()), 16);
+            super(new RFC3211WrapEngine(AESEngine.newInstance()), 16);
         }
     }
 
@@ -314,7 +315,7 @@
     {
         public RFC5649Wrap()
         {
-            super(new RFC5649WrapEngine(new AESEngine()));
+            super(new RFC5649WrapEngine(AESEngine.newInstance()));
         }
     }
     */
@@ -329,7 +330,7 @@
     {
         public PBEWithAESCBC()
         {
-            super(new CBCBlockCipher(new AESEngine()));
+            super(CBCBlockCipher.newInstance(AESEngine.newInstance()));
         }
     }
 
@@ -342,7 +343,7 @@
     {
         public PBEWithSHA1AESCBC128()
         {
-            super(new CBCBlockCipher(new AESEngine()), PKCS12, SHA1, 128, 16);
+            super(CBCBlockCipher.newInstance(AESEngine.newInstance()), PKCS12, SHA1, 128, 16);
         }
     }
 
@@ -354,7 +355,7 @@
     {
         public PBEWithSHA1AESCBC192()
         {
-            super(new CBCBlockCipher(new AESEngine()), PKCS12, SHA1, 192, 16);
+            super(CBCBlockCipher.newInstance(AESEngine.newInstance()), PKCS12, SHA1, 192, 16);
         }
     }
 
@@ -366,7 +367,7 @@
     {
         public PBEWithSHA1AESCBC256()
         {
-            super(new CBCBlockCipher(new AESEngine()), PKCS12, SHA1, 256, 16);
+            super(CBCBlockCipher.newInstance(AESEngine.newInstance()), PKCS12, SHA1, 256, 16);
         }
     }
 
@@ -379,7 +380,7 @@
     {
         public PBEWithSHA256AESCBC128()
         {
-            super(new CBCBlockCipher(new AESEngine()), PKCS12, SHA256, 128, 16);
+            super(CBCBlockCipher.newInstance(AESEngine.newInstance()), PKCS12, SHA256, 128, 16);
         }
     }
 
@@ -391,7 +392,7 @@
     {
         public PBEWithSHA256AESCBC192()
         {
-            super(new CBCBlockCipher(new AESEngine()), PKCS12, SHA256, 192, 16);
+            super(CBCBlockCipher.newInstance(AESEngine.newInstance()), PKCS12, SHA256, 192, 16);
         }
     }
 
@@ -403,7 +404,7 @@
     {
         public PBEWithSHA256AESCBC256()
         {
-            super(new CBCBlockCipher(new AESEngine()), PKCS12, SHA256, 256, 16);
+            super(CBCBlockCipher.newInstance(AESEngine.newInstance()), PKCS12, SHA256, 256, 16);
         }
     }
 
@@ -472,7 +473,7 @@
             super("PBEWithSHA1And128BitAES-CBC-BC", null, true, PKCS12, SHA1, 128, 128);
         }
     }
-    
+
     /**
      * PBEWithSHA1And192BitAES-BC
      * @hide This class is not part of the Android public SDK API
@@ -485,7 +486,7 @@
             super("PBEWithSHA1And192BitAES-CBC-BC", null, true, PKCS12, SHA1, 192, 128);
         }
     }
-    
+
     /**
      * PBEWithSHA1And256BitAES-BC
      * @hide This class is not part of the Android public SDK API
@@ -498,7 +499,7 @@
             super("PBEWithSHA1And256BitAES-CBC-BC", null, true, PKCS12, SHA1, 256, 128);
         }
     }
-    
+
     /**
      * PBEWithSHA256And128BitAES-BC
      * @hide This class is not part of the Android public SDK API
@@ -511,7 +512,7 @@
             super("PBEWithSHA256And128BitAES-CBC-BC", null, true, PKCS12, SHA256, 128, 128);
         }
     }
-    
+
     /**
      * PBEWithSHA256And192BitAES-BC
      * @hide This class is not part of the Android public SDK API
@@ -524,7 +525,7 @@
             super("PBEWithSHA256And192BitAES-CBC-BC", null, true, PKCS12, SHA256, 192, 128);
         }
     }
-    
+
     /**
      * PBEWithSHA256And256BitAES-BC
      * @hide This class is not part of the Android public SDK API
@@ -537,7 +538,7 @@
             super("PBEWithSHA256And256BitAES-CBC-BC", null, true, PKCS12, SHA256, 256, 128);
         }
     }
-    
+
     /**
      * PBEWithMD5And128BitAES-OpenSSL
      * @hide This class is not part of the Android public SDK API
@@ -550,7 +551,7 @@
             super("PBEWithMD5And128BitAES-CBC-OpenSSL", null, true, OPENSSL, MD5, 128, 128);
         }
     }
-    
+
     /**
      * PBEWithMD5And192BitAES-OpenSSL
      * @hide This class is not part of the Android public SDK API
@@ -563,7 +564,7 @@
             super("PBEWithMD5And192BitAES-CBC-OpenSSL", null, true, OPENSSL, MD5, 192, 128);
         }
     }
-    
+
     /**
      * PBEWithMD5And256BitAES-OpenSSL
      * @hide This class is not part of the Android public SDK API
@@ -592,7 +593,7 @@
 
         protected AlgorithmParameters engineGenerateParameters()
         {
-            byte[]  iv = new byte[16];
+            byte[] iv = new byte[16];
 
             if (random == null)
             {
@@ -631,7 +632,7 @@
 
         protected AlgorithmParameters engineGenerateParameters()
         {
-            byte[]  iv = new byte[12];
+            byte[] iv = new byte[12];
 
             if (random == null)
             {
@@ -670,7 +671,7 @@
 
         protected AlgorithmParameters engineGenerateParameters()
         {
-            byte[]  nonce = new byte[12];
+            byte[] nonce = new byte[12];
 
             if (random == null)
             {
@@ -778,7 +779,7 @@
         {
             if (paramSpec == AlgorithmParameterSpec.class || GcmSpecUtil.isGcmSpec(paramSpec))
             {
-                if (GcmSpecUtil.gcmSpecExists())
+                if (GcmSpecUtil.gcmSpecExtractable())
                 {
                     return GcmSpecUtil.extractGcmSpec(gcmParams.toASN1Primitive());
                 }
@@ -865,7 +866,7 @@
         {
             if (paramSpec == AlgorithmParameterSpec.class || GcmSpecUtil.isGcmSpec(paramSpec))
             {
-                if (GcmSpecUtil.gcmSpecExists())
+                if (GcmSpecUtil.gcmSpecExtractable())
                 {
                     return GcmSpecUtil.extractGcmSpec(ccmParams.toASN1Primitive());
                 }
@@ -893,7 +894,7 @@
         extends SymmetricAlgorithmProvider
     {
         private static final String PREFIX = AES.class.getName();
-        
+
         /**
          * These three got introduced in some messages as a result of a typo in an
          * early document. We don't produce anything using these OID values, but we'll
@@ -1055,31 +1056,31 @@
             provider.addAlgorithm("Cipher.PBEWITHSHA256AND128BITAES-CBC-BC", PREFIX + "$PBEWithSHA256AESCBC128");
             provider.addAlgorithm("Cipher.PBEWITHSHA256AND192BITAES-CBC-BC", PREFIX + "$PBEWithSHA256AESCBC192");
             provider.addAlgorithm("Cipher.PBEWITHSHA256AND256BITAES-CBC-BC", PREFIX + "$PBEWithSHA256AESCBC256");
-            
-            provider.addAlgorithm("Alg.Alias.Cipher.PBEWITHSHA1AND128BITAES-CBC-BC","PBEWITHSHAAND128BITAES-CBC-BC");
-            provider.addAlgorithm("Alg.Alias.Cipher.PBEWITHSHA1AND192BITAES-CBC-BC","PBEWITHSHAAND192BITAES-CBC-BC");
-            provider.addAlgorithm("Alg.Alias.Cipher.PBEWITHSHA1AND256BITAES-CBC-BC","PBEWITHSHAAND256BITAES-CBC-BC");
-            provider.addAlgorithm("Alg.Alias.Cipher.PBEWITHSHA-1AND128BITAES-CBC-BC","PBEWITHSHAAND128BITAES-CBC-BC");
-            provider.addAlgorithm("Alg.Alias.Cipher.PBEWITHSHA-1AND192BITAES-CBC-BC","PBEWITHSHAAND192BITAES-CBC-BC");
-            provider.addAlgorithm("Alg.Alias.Cipher.PBEWITHSHA-1AND256BITAES-CBC-BC","PBEWITHSHAAND256BITAES-CBC-BC");
-            provider.addAlgorithm("Alg.Alias.Cipher.PBEWITHSHAAND128BITAES-BC","PBEWITHSHAAND128BITAES-CBC-BC");
+
+            provider.addAlgorithm("Alg.Alias.Cipher.PBEWITHSHA1AND128BITAES-CBC-BC", "PBEWITHSHAAND128BITAES-CBC-BC");
+            provider.addAlgorithm("Alg.Alias.Cipher.PBEWITHSHA1AND192BITAES-CBC-BC", "PBEWITHSHAAND192BITAES-CBC-BC");
+            provider.addAlgorithm("Alg.Alias.Cipher.PBEWITHSHA1AND256BITAES-CBC-BC", "PBEWITHSHAAND256BITAES-CBC-BC");
+            provider.addAlgorithm("Alg.Alias.Cipher.PBEWITHSHA-1AND128BITAES-CBC-BC", "PBEWITHSHAAND128BITAES-CBC-BC");
+            provider.addAlgorithm("Alg.Alias.Cipher.PBEWITHSHA-1AND192BITAES-CBC-BC", "PBEWITHSHAAND192BITAES-CBC-BC");
+            provider.addAlgorithm("Alg.Alias.Cipher.PBEWITHSHA-1AND256BITAES-CBC-BC", "PBEWITHSHAAND256BITAES-CBC-BC");
+            provider.addAlgorithm("Alg.Alias.Cipher.PBEWITHSHAAND128BITAES-BC", "PBEWITHSHAAND128BITAES-CBC-BC");
             provider.addAlgorithm("Alg.Alias.Cipher.PBEWITHSHAAND192BITAES-BC", "PBEWITHSHAAND192BITAES-CBC-BC");
             provider.addAlgorithm("Alg.Alias.Cipher.PBEWITHSHAAND256BITAES-BC", "PBEWITHSHAAND256BITAES-CBC-BC");
-            provider.addAlgorithm("Alg.Alias.Cipher.PBEWITHSHA1AND128BITAES-BC","PBEWITHSHAAND128BITAES-CBC-BC");
-            provider.addAlgorithm("Alg.Alias.Cipher.PBEWITHSHA1AND192BITAES-BC","PBEWITHSHAAND192BITAES-CBC-BC");
-            provider.addAlgorithm("Alg.Alias.Cipher.PBEWITHSHA1AND256BITAES-BC","PBEWITHSHAAND256BITAES-CBC-BC");
-            provider.addAlgorithm("Alg.Alias.Cipher.PBEWITHSHA-1AND128BITAES-BC","PBEWITHSHAAND128BITAES-CBC-BC");
-            provider.addAlgorithm("Alg.Alias.Cipher.PBEWITHSHA-1AND192BITAES-BC","PBEWITHSHAAND192BITAES-CBC-BC");
-            provider.addAlgorithm("Alg.Alias.Cipher.PBEWITHSHA-1AND256BITAES-BC","PBEWITHSHAAND256BITAES-CBC-BC");
-            provider.addAlgorithm("Alg.Alias.Cipher.PBEWITHSHA-256AND128BITAES-CBC-BC","PBEWITHSHA256AND128BITAES-CBC-BC");
-            provider.addAlgorithm("Alg.Alias.Cipher.PBEWITHSHA-256AND192BITAES-CBC-BC","PBEWITHSHA256AND192BITAES-CBC-BC");
-            provider.addAlgorithm("Alg.Alias.Cipher.PBEWITHSHA-256AND256BITAES-CBC-BC","PBEWITHSHA256AND256BITAES-CBC-BC");
-            provider.addAlgorithm("Alg.Alias.Cipher.PBEWITHSHA256AND128BITAES-BC","PBEWITHSHA256AND128BITAES-CBC-BC");
-            provider.addAlgorithm("Alg.Alias.Cipher.PBEWITHSHA256AND192BITAES-BC","PBEWITHSHA256AND192BITAES-CBC-BC");
-            provider.addAlgorithm("Alg.Alias.Cipher.PBEWITHSHA256AND256BITAES-BC","PBEWITHSHA256AND256BITAES-CBC-BC");
-            provider.addAlgorithm("Alg.Alias.Cipher.PBEWITHSHA-256AND128BITAES-BC","PBEWITHSHA256AND128BITAES-CBC-BC");
-            provider.addAlgorithm("Alg.Alias.Cipher.PBEWITHSHA-256AND192BITAES-BC","PBEWITHSHA256AND192BITAES-CBC-BC");
-            provider.addAlgorithm("Alg.Alias.Cipher.PBEWITHSHA-256AND256BITAES-BC","PBEWITHSHA256AND256BITAES-CBC-BC");
+            provider.addAlgorithm("Alg.Alias.Cipher.PBEWITHSHA1AND128BITAES-BC", "PBEWITHSHAAND128BITAES-CBC-BC");
+            provider.addAlgorithm("Alg.Alias.Cipher.PBEWITHSHA1AND192BITAES-BC", "PBEWITHSHAAND192BITAES-CBC-BC");
+            provider.addAlgorithm("Alg.Alias.Cipher.PBEWITHSHA1AND256BITAES-BC", "PBEWITHSHAAND256BITAES-CBC-BC");
+            provider.addAlgorithm("Alg.Alias.Cipher.PBEWITHSHA-1AND128BITAES-BC", "PBEWITHSHAAND128BITAES-CBC-BC");
+            provider.addAlgorithm("Alg.Alias.Cipher.PBEWITHSHA-1AND192BITAES-BC", "PBEWITHSHAAND192BITAES-CBC-BC");
+            provider.addAlgorithm("Alg.Alias.Cipher.PBEWITHSHA-1AND256BITAES-BC", "PBEWITHSHAAND256BITAES-CBC-BC");
+            provider.addAlgorithm("Alg.Alias.Cipher.PBEWITHSHA-256AND128BITAES-CBC-BC", "PBEWITHSHA256AND128BITAES-CBC-BC");
+            provider.addAlgorithm("Alg.Alias.Cipher.PBEWITHSHA-256AND192BITAES-CBC-BC", "PBEWITHSHA256AND192BITAES-CBC-BC");
+            provider.addAlgorithm("Alg.Alias.Cipher.PBEWITHSHA-256AND256BITAES-CBC-BC", "PBEWITHSHA256AND256BITAES-CBC-BC");
+            provider.addAlgorithm("Alg.Alias.Cipher.PBEWITHSHA256AND128BITAES-BC", "PBEWITHSHA256AND128BITAES-CBC-BC");
+            provider.addAlgorithm("Alg.Alias.Cipher.PBEWITHSHA256AND192BITAES-BC", "PBEWITHSHA256AND192BITAES-CBC-BC");
+            provider.addAlgorithm("Alg.Alias.Cipher.PBEWITHSHA256AND256BITAES-BC", "PBEWITHSHA256AND256BITAES-CBC-BC");
+            provider.addAlgorithm("Alg.Alias.Cipher.PBEWITHSHA-256AND128BITAES-BC", "PBEWITHSHA256AND128BITAES-CBC-BC");
+            provider.addAlgorithm("Alg.Alias.Cipher.PBEWITHSHA-256AND192BITAES-BC", "PBEWITHSHA256AND192BITAES-CBC-BC");
+            provider.addAlgorithm("Alg.Alias.Cipher.PBEWITHSHA-256AND256BITAES-BC", "PBEWITHSHA256AND256BITAES-CBC-BC");
 
             provider.addAlgorithm("Cipher.PBEWITHMD5AND128BITAES-CBC-OPENSSL", PREFIX + "$PBEWithAESCBC");
             provider.addAlgorithm("Cipher.PBEWITHMD5AND192BITAES-CBC-OPENSSL", PREFIX + "$PBEWithAESCBC");
@@ -1093,48 +1094,48 @@
             provider.addAlgorithm("SecretKeyFactory.PBEWITHMD5AND128BITAES-CBC-OPENSSL", PREFIX + "$PBEWithMD5And128BitAESCBCOpenSSL");
             provider.addAlgorithm("SecretKeyFactory.PBEWITHMD5AND192BITAES-CBC-OPENSSL", PREFIX + "$PBEWithMD5And192BitAESCBCOpenSSL");
             provider.addAlgorithm("SecretKeyFactory.PBEWITHMD5AND256BITAES-CBC-OPENSSL", PREFIX + "$PBEWithMD5And256BitAESCBCOpenSSL");
-            
+
             provider.addAlgorithm("SecretKeyFactory.PBEWITHSHAAND128BITAES-CBC-BC", PREFIX + "$PBEWithSHAAnd128BitAESBC");
             provider.addAlgorithm("SecretKeyFactory.PBEWITHSHAAND192BITAES-CBC-BC", PREFIX + "$PBEWithSHAAnd192BitAESBC");
             provider.addAlgorithm("SecretKeyFactory.PBEWITHSHAAND256BITAES-CBC-BC", PREFIX + "$PBEWithSHAAnd256BitAESBC");
             provider.addAlgorithm("SecretKeyFactory.PBEWITHSHA256AND128BITAES-CBC-BC", PREFIX + "$PBEWithSHA256And128BitAESBC");
             provider.addAlgorithm("SecretKeyFactory.PBEWITHSHA256AND192BITAES-CBC-BC", PREFIX + "$PBEWithSHA256And192BitAESBC");
             provider.addAlgorithm("SecretKeyFactory.PBEWITHSHA256AND256BITAES-CBC-BC", PREFIX + "$PBEWithSHA256And256BitAESBC");
-            provider.addAlgorithm("Alg.Alias.SecretKeyFactory.PBEWITHSHA1AND128BITAES-CBC-BC","PBEWITHSHAAND128BITAES-CBC-BC");
-            provider.addAlgorithm("Alg.Alias.SecretKeyFactory.PBEWITHSHA1AND192BITAES-CBC-BC","PBEWITHSHAAND192BITAES-CBC-BC");
-            provider.addAlgorithm("Alg.Alias.SecretKeyFactory.PBEWITHSHA1AND256BITAES-CBC-BC","PBEWITHSHAAND256BITAES-CBC-BC");
-            provider.addAlgorithm("Alg.Alias.SecretKeyFactory.PBEWITHSHA-1AND128BITAES-CBC-BC","PBEWITHSHAAND128BITAES-CBC-BC");
-            provider.addAlgorithm("Alg.Alias.SecretKeyFactory.PBEWITHSHA-1AND192BITAES-CBC-BC","PBEWITHSHAAND192BITAES-CBC-BC");
-            provider.addAlgorithm("Alg.Alias.SecretKeyFactory.PBEWITHSHA-1AND256BITAES-CBC-BC","PBEWITHSHAAND256BITAES-CBC-BC");
-            provider.addAlgorithm("Alg.Alias.SecretKeyFactory.PBEWITHSHA-256AND128BITAES-CBC-BC","PBEWITHSHA256AND128BITAES-CBC-BC");
-            provider.addAlgorithm("Alg.Alias.SecretKeyFactory.PBEWITHSHA-256AND192BITAES-CBC-BC","PBEWITHSHA256AND192BITAES-CBC-BC");
-            provider.addAlgorithm("Alg.Alias.SecretKeyFactory.PBEWITHSHA-256AND256BITAES-CBC-BC","PBEWITHSHA256AND256BITAES-CBC-BC");
-            provider.addAlgorithm("Alg.Alias.SecretKeyFactory.PBEWITHSHA-256AND128BITAES-BC","PBEWITHSHA256AND128BITAES-CBC-BC");
-            provider.addAlgorithm("Alg.Alias.SecretKeyFactory.PBEWITHSHA-256AND192BITAES-BC","PBEWITHSHA256AND192BITAES-CBC-BC");
-            provider.addAlgorithm("Alg.Alias.SecretKeyFactory.PBEWITHSHA-256AND256BITAES-BC","PBEWITHSHA256AND256BITAES-CBC-BC");
+            provider.addAlgorithm("Alg.Alias.SecretKeyFactory.PBEWITHSHA1AND128BITAES-CBC-BC", "PBEWITHSHAAND128BITAES-CBC-BC");
+            provider.addAlgorithm("Alg.Alias.SecretKeyFactory.PBEWITHSHA1AND192BITAES-CBC-BC", "PBEWITHSHAAND192BITAES-CBC-BC");
+            provider.addAlgorithm("Alg.Alias.SecretKeyFactory.PBEWITHSHA1AND256BITAES-CBC-BC", "PBEWITHSHAAND256BITAES-CBC-BC");
+            provider.addAlgorithm("Alg.Alias.SecretKeyFactory.PBEWITHSHA-1AND128BITAES-CBC-BC", "PBEWITHSHAAND128BITAES-CBC-BC");
+            provider.addAlgorithm("Alg.Alias.SecretKeyFactory.PBEWITHSHA-1AND192BITAES-CBC-BC", "PBEWITHSHAAND192BITAES-CBC-BC");
+            provider.addAlgorithm("Alg.Alias.SecretKeyFactory.PBEWITHSHA-1AND256BITAES-CBC-BC", "PBEWITHSHAAND256BITAES-CBC-BC");
+            provider.addAlgorithm("Alg.Alias.SecretKeyFactory.PBEWITHSHA-256AND128BITAES-CBC-BC", "PBEWITHSHA256AND128BITAES-CBC-BC");
+            provider.addAlgorithm("Alg.Alias.SecretKeyFactory.PBEWITHSHA-256AND192BITAES-CBC-BC", "PBEWITHSHA256AND192BITAES-CBC-BC");
+            provider.addAlgorithm("Alg.Alias.SecretKeyFactory.PBEWITHSHA-256AND256BITAES-CBC-BC", "PBEWITHSHA256AND256BITAES-CBC-BC");
+            provider.addAlgorithm("Alg.Alias.SecretKeyFactory.PBEWITHSHA-256AND128BITAES-BC", "PBEWITHSHA256AND128BITAES-CBC-BC");
+            provider.addAlgorithm("Alg.Alias.SecretKeyFactory.PBEWITHSHA-256AND192BITAES-BC", "PBEWITHSHA256AND192BITAES-CBC-BC");
+            provider.addAlgorithm("Alg.Alias.SecretKeyFactory.PBEWITHSHA-256AND256BITAES-BC", "PBEWITHSHA256AND256BITAES-CBC-BC");
             provider.addAlgorithm("Alg.Alias.SecretKeyFactory", BCObjectIdentifiers.bc_pbe_sha1_pkcs12_aes128_cbc, "PBEWITHSHAAND128BITAES-CBC-BC");
             provider.addAlgorithm("Alg.Alias.SecretKeyFactory", BCObjectIdentifiers.bc_pbe_sha1_pkcs12_aes192_cbc, "PBEWITHSHAAND192BITAES-CBC-BC");
             provider.addAlgorithm("Alg.Alias.SecretKeyFactory", BCObjectIdentifiers.bc_pbe_sha1_pkcs12_aes256_cbc, "PBEWITHSHAAND256BITAES-CBC-BC");
             provider.addAlgorithm("Alg.Alias.SecretKeyFactory", BCObjectIdentifiers.bc_pbe_sha256_pkcs12_aes128_cbc, "PBEWITHSHA256AND128BITAES-CBC-BC");
             provider.addAlgorithm("Alg.Alias.SecretKeyFactory", BCObjectIdentifiers.bc_pbe_sha256_pkcs12_aes192_cbc, "PBEWITHSHA256AND192BITAES-CBC-BC");
             provider.addAlgorithm("Alg.Alias.SecretKeyFactory", BCObjectIdentifiers.bc_pbe_sha256_pkcs12_aes256_cbc, "PBEWITHSHA256AND256BITAES-CBC-BC");
-            
+
             provider.addAlgorithm("Alg.Alias.AlgorithmParameters.PBEWITHSHAAND128BITAES-CBC-BC", "PKCS12PBE");
             provider.addAlgorithm("Alg.Alias.AlgorithmParameters.PBEWITHSHAAND192BITAES-CBC-BC", "PKCS12PBE");
             provider.addAlgorithm("Alg.Alias.AlgorithmParameters.PBEWITHSHAAND256BITAES-CBC-BC", "PKCS12PBE");
             provider.addAlgorithm("Alg.Alias.AlgorithmParameters.PBEWITHSHA256AND128BITAES-CBC-BC", "PKCS12PBE");
             provider.addAlgorithm("Alg.Alias.AlgorithmParameters.PBEWITHSHA256AND192BITAES-CBC-BC", "PKCS12PBE");
             provider.addAlgorithm("Alg.Alias.AlgorithmParameters.PBEWITHSHA256AND256BITAES-CBC-BC", "PKCS12PBE");
-            provider.addAlgorithm("Alg.Alias.AlgorithmParameters.PBEWITHSHA1AND128BITAES-CBC-BC","PKCS12PBE");
-            provider.addAlgorithm("Alg.Alias.AlgorithmParameters.PBEWITHSHA1AND192BITAES-CBC-BC","PKCS12PBE");
-            provider.addAlgorithm("Alg.Alias.AlgorithmParameters.PBEWITHSHA1AND256BITAES-CBC-BC","PKCS12PBE");
-            provider.addAlgorithm("Alg.Alias.AlgorithmParameters.PBEWITHSHA-1AND128BITAES-CBC-BC","PKCS12PBE");
-            provider.addAlgorithm("Alg.Alias.AlgorithmParameters.PBEWITHSHA-1AND192BITAES-CBC-BC","PKCS12PBE");
-            provider.addAlgorithm("Alg.Alias.AlgorithmParameters.PBEWITHSHA-1AND256BITAES-CBC-BC","PKCS12PBE");
-            provider.addAlgorithm("Alg.Alias.AlgorithmParameters.PBEWITHSHA-256AND128BITAES-CBC-BC","PKCS12PBE");
-            provider.addAlgorithm("Alg.Alias.AlgorithmParameters.PBEWITHSHA-256AND192BITAES-CBC-BC","PKCS12PBE");
-            provider.addAlgorithm("Alg.Alias.AlgorithmParameters.PBEWITHSHA-256AND256BITAES-CBC-BC","PKCS12PBE"); 
-            
+            provider.addAlgorithm("Alg.Alias.AlgorithmParameters.PBEWITHSHA1AND128BITAES-CBC-BC", "PKCS12PBE");
+            provider.addAlgorithm("Alg.Alias.AlgorithmParameters.PBEWITHSHA1AND192BITAES-CBC-BC", "PKCS12PBE");
+            provider.addAlgorithm("Alg.Alias.AlgorithmParameters.PBEWITHSHA1AND256BITAES-CBC-BC", "PKCS12PBE");
+            provider.addAlgorithm("Alg.Alias.AlgorithmParameters.PBEWITHSHA-1AND128BITAES-CBC-BC", "PKCS12PBE");
+            provider.addAlgorithm("Alg.Alias.AlgorithmParameters.PBEWITHSHA-1AND192BITAES-CBC-BC", "PKCS12PBE");
+            provider.addAlgorithm("Alg.Alias.AlgorithmParameters.PBEWITHSHA-1AND256BITAES-CBC-BC", "PKCS12PBE");
+            provider.addAlgorithm("Alg.Alias.AlgorithmParameters.PBEWITHSHA-256AND128BITAES-CBC-BC", "PKCS12PBE");
+            provider.addAlgorithm("Alg.Alias.AlgorithmParameters.PBEWITHSHA-256AND192BITAES-CBC-BC", "PKCS12PBE");
+            provider.addAlgorithm("Alg.Alias.AlgorithmParameters.PBEWITHSHA-256AND256BITAES-CBC-BC", "PKCS12PBE");
+
             provider.addAlgorithm("Alg.Alias.AlgorithmParameters." + BCObjectIdentifiers.bc_pbe_sha1_pkcs12_aes128_cbc.getId(), "PKCS12PBE");
             provider.addAlgorithm("Alg.Alias.AlgorithmParameters." + BCObjectIdentifiers.bc_pbe_sha1_pkcs12_aes192_cbc.getId(), "PKCS12PBE");
             provider.addAlgorithm("Alg.Alias.AlgorithmParameters." + BCObjectIdentifiers.bc_pbe_sha1_pkcs12_aes256_cbc.getId(), "PKCS12PBE");
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/symmetric/GcmSpecUtil.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/symmetric/GcmSpecUtil.java
deleted file mode 100644
index 55feea6..0000000
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/symmetric/GcmSpecUtil.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/* GENERATED SOURCE. DO NOT MODIFY. */
-package com.android.internal.org.bouncycastle.jcajce.provider.symmetric;
-
-import java.lang.reflect.Constructor;
-import java.lang.reflect.Method;
-import java.security.spec.AlgorithmParameterSpec;
-import java.security.spec.InvalidParameterSpecException;
-
-import com.android.internal.org.bouncycastle.asn1.ASN1Primitive;
-import com.android.internal.org.bouncycastle.asn1.cms.GCMParameters;
-import com.android.internal.org.bouncycastle.jcajce.provider.symmetric.util.ClassUtil;
-import com.android.internal.org.bouncycastle.util.Integers;
-
-class GcmSpecUtil
-{
-    static final Class gcmSpecClass = ClassUtil.loadClass(GcmSpecUtil.class, "javax.crypto.spec.GCMParameterSpec");
-
-    static boolean gcmSpecExists()
-    {
-        return gcmSpecClass != null;
-    }
-
-    static boolean isGcmSpec(AlgorithmParameterSpec paramSpec)
-    {
-        return gcmSpecClass != null && gcmSpecClass.isInstance(paramSpec);
-    }
-
-    static boolean isGcmSpec(Class paramSpecClass)
-    {
-        return gcmSpecClass == paramSpecClass;
-    }
-
-    static AlgorithmParameterSpec extractGcmSpec(ASN1Primitive spec)
-        throws InvalidParameterSpecException
-    {
-        try
-        {
-            GCMParameters gcmParams = GCMParameters.getInstance(spec);
-            Constructor constructor = gcmSpecClass.getConstructor(new Class[]{Integer.TYPE, byte[].class});
-
-            return (AlgorithmParameterSpec)constructor.newInstance(new Object[] { Integers.valueOf(gcmParams.getIcvLen() * 8), gcmParams.getNonce() });
-        }
-        catch (NoSuchMethodException e)
-        {
-            throw new InvalidParameterSpecException("No constructor found!");   // should never happen
-        }
-        catch (Exception e)
-        {
-            throw new InvalidParameterSpecException("Construction failed: " + e.getMessage());   // should never happen
-        }
-    }
-
-    static GCMParameters extractGcmParameters(AlgorithmParameterSpec paramSpec)
-        throws InvalidParameterSpecException
-    {
-        try
-        {
-            Method tLen = gcmSpecClass.getDeclaredMethod("getTLen", new Class[0]);
-            Method iv= gcmSpecClass.getDeclaredMethod("getIV", new Class[0]);
-
-            return new GCMParameters((byte[])iv.invoke(paramSpec, new Object[0]), ((Integer)tLen.invoke(paramSpec, new Object[0])).intValue() / 8);
-        }
-        catch (Exception e)
-        {
-            throw new InvalidParameterSpecException("Cannot process GCMParameterSpec");
-        }
-    }
-}
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/symmetric/PBEPBKDF2.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/symmetric/PBEPBKDF2.java
index 63824db..904febf 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/symmetric/PBEPBKDF2.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/symmetric/PBEPBKDF2.java
@@ -621,6 +621,15 @@
             super("PBKDF2", PKCS5S2_UTF8, SM3);
         }
     }
+
+    public static class PBKDF2withSM3
+        extends BasePBKDF2
+    {
+        public PBKDF2withSM3()
+        {
+            super("PBKDF2", PKCS5S2_UTF8, SM3);
+        }
+    }
     */
     // END Android-removed: Unsupported algorithms
 
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/symmetric/util/BCPBEKey.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/symmetric/util/BCPBEKey.java
index de35216..f7c0889 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/symmetric/util/BCPBEKey.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/symmetric/util/BCPBEKey.java
@@ -73,19 +73,23 @@
 
     public String getAlgorithm()
     {
+        String rv = this.algorithm;
+
         checkDestroyed(this);
 
-        return algorithm;
+        return rv;
     }
 
     public String getFormat()
     {
+        checkDestroyed(this);
+
         return "RAW";
     }
 
     public byte[] getEncoded()
     {
-        checkDestroyed(this);
+        byte[] enc;
 
         if (param != null)
         {
@@ -100,58 +104,72 @@
                 kParam = (KeyParameter)param;
             }
             
-            return kParam.getKey();
+            enc = kParam.getKey();
         }
         else
         {
             if (type == PBE.PKCS12)
             {
-                return PBEParametersGenerator.PKCS12PasswordToBytes(password);
+                enc = PBEParametersGenerator.PKCS12PasswordToBytes(password);
             }
             else if (type == PBE.PKCS5S2_UTF8)
             {
-                return PBEParametersGenerator.PKCS5PasswordToUTF8Bytes(password);
+                enc = PBEParametersGenerator.PKCS5PasswordToUTF8Bytes(password);
             }
             else
             {   
-                return PBEParametersGenerator.PKCS5PasswordToBytes(password);
+                enc = PBEParametersGenerator.PKCS5PasswordToBytes(password);
             }
         }
+
+        checkDestroyed(this);
+
+        return enc;
     }
     
     int getType()
     {
+        int rv = type;
+
         checkDestroyed(this);
 
-        return type;
+        return rv;
     }
     
     int getDigest()
     {
+        int rv = digest;
+
         checkDestroyed(this);
 
-        return digest;
+        return rv;
     }
     
     int getKeySize()
     {
+        int rv = keySize;
+
         checkDestroyed(this);
 
-        return keySize;
+        return rv;
     }
     
     public int getIvSize()
     {
+        int rv = ivSize;
+
         checkDestroyed(this);
 
-        return ivSize;
+        return rv;
     }
     
     public CipherParameters getParam()
     {
+        CipherParameters rv = param;
+
         checkDestroyed(this);
 
-        return param;
+        return rv;
     }
 
     /* (non-Javadoc)
@@ -159,14 +177,16 @@
      */
     public char[] getPassword()
     {
+        char[] clone = Arrays.clone(password);
+
         checkDestroyed(this);
 
-        if (password == null)
+        if (clone == null)
         {
             throw new IllegalStateException("no password available");
         }
 
-        return Arrays.clone(password);
+        return clone;
     }
 
     /* (non-Javadoc)
@@ -174,9 +194,11 @@
      */
     public byte[] getSalt()
     {
+        byte[] clone = Arrays.clone(salt);
+
         checkDestroyed(this);
 
-        return Arrays.clone(salt);
+        return clone;
     }
 
     /* (non-Javadoc)
@@ -184,16 +206,20 @@
      */
     public int getIterationCount()
     {
+        int rv = iterationCount;
+
         checkDestroyed(this);
 
-        return iterationCount;
+        return rv;
     }
     
     public ASN1ObjectIdentifier getOID()
     {
+        ASN1ObjectIdentifier rv = oid;
+
         checkDestroyed(this);
 
-        return oid;
+        return rv;
     }
     
     public void setTryWrongPKCS12Zero(boolean tryWrong)
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/symmetric/util/BaseBlockCipher.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/symmetric/util/BaseBlockCipher.java
index 50417c7..c3d967a 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/symmetric/util/BaseBlockCipher.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/symmetric/util/BaseBlockCipher.java
@@ -29,17 +29,20 @@
 // import javax.crypto.spec.RC5ParameterSpec;
 
 import com.android.internal.org.bouncycastle.asn1.DEROctetString;
-import com.android.internal.org.bouncycastle.asn1.cms.GCMParameters;
 import com.android.internal.org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
 import com.android.internal.org.bouncycastle.crypto.BlockCipher;
 import com.android.internal.org.bouncycastle.crypto.BufferedBlockCipher;
 import com.android.internal.org.bouncycastle.crypto.CipherParameters;
 import com.android.internal.org.bouncycastle.crypto.CryptoServicesRegistrar;
 import com.android.internal.org.bouncycastle.crypto.DataLengthException;
+import com.android.internal.org.bouncycastle.crypto.DefaultBufferedBlockCipher;
 import com.android.internal.org.bouncycastle.crypto.InvalidCipherTextException;
 import com.android.internal.org.bouncycastle.crypto.OutputLengthException;
 // Android-removed: Unsupported algorithms
 // import org.bouncycastle.crypto.engines.DSTU7624Engine;
+// import org.bouncycastle.crypto.fpe.FPEEngine;
+// import org.bouncycastle.crypto.fpe.FPEFF1Engine;
+// import org.bouncycastle.crypto.fpe.FPEFF3_1Engine;
 import com.android.internal.org.bouncycastle.crypto.modes.AEADBlockCipher;
 import com.android.internal.org.bouncycastle.crypto.modes.AEADCipher;
 import com.android.internal.org.bouncycastle.crypto.modes.CBCBlockCipher;
@@ -51,6 +54,7 @@
 // import org.bouncycastle.crypto.modes.GCFBBlockCipher;
 import com.android.internal.org.bouncycastle.crypto.modes.GCMBlockCipher;
 // Android-removed: Unsupported algorithms
+// import org.bouncycastle.crypto.modes.GCMSIVBlockCipher;
 // import org.bouncycastle.crypto.modes.GOFBBlockCipher;
 // import org.bouncycastle.crypto.modes.KCCMBlockCipher;
 // import org.bouncycastle.crypto.modes.KCTRBlockCipher;
@@ -60,6 +64,8 @@
 // Android-removed: Unsupported algorithms
 // import org.bouncycastle.crypto.modes.OpenPGPCFBBlockCipher;
 // import org.bouncycastle.crypto.modes.PGPCFBBlockCipher;
+import com.android.internal.org.bouncycastle.crypto.paddings.PKCS7Padding;
+// import org.bouncycastle.crypto.params.FPEParameters;
 import com.android.internal.org.bouncycastle.crypto.modes.SICBlockCipher;
 import com.android.internal.org.bouncycastle.crypto.paddings.BlockCipherPadding;
 import com.android.internal.org.bouncycastle.crypto.paddings.ISO10126d2Padding;
@@ -85,6 +91,8 @@
 // Android-removed: Unsupported algorithms
 // import org.bouncycastle.jcajce.spec.GOST28147ParameterSpec;
 // import org.bouncycastle.jcajce.spec.RepeatedSecretKeySpec;
+import com.android.internal.org.bouncycastle.internal.asn1.cms.GCMParameters;
+// import org.bouncycastle.jcajce.spec.FPEParameterSpec;
 import com.android.internal.org.bouncycastle.util.Arrays;
 import com.android.internal.org.bouncycastle.util.Strings;
 
@@ -96,7 +104,6 @@
     implements PBE
 {
     private static final int BUF_SIZE = 512;
-    private static final Class gcmSpecClass = ClassUtil.loadClass(BaseBlockCipher.class, "javax.crypto.spec.GCMParameterSpec");
 
     //
     // specs we can handle.
@@ -106,7 +113,7 @@
             // Android-removed: Unsupported alhorithms
             // RC2ParameterSpec.class,
             // RC5ParameterSpec.class,
-            gcmSpecClass,
+            GcmSpecUtil.gcmSpecClass,
             // Android-removed: unsupported algorithms
             // GOST28147ParameterSpec.class,
             IvParameterSpec.class,
@@ -170,7 +177,14 @@
         AEADBlockCipher engine)
     {
         this.baseEngine = engine.getUnderlyingCipher();
-        this.ivLength = baseEngine.getBlockSize();
+        if (engine.getAlgorithmName().indexOf("GCM") >= 0)
+        {
+            this.ivLength = 12;
+        }
+        else
+        {
+            this.ivLength = baseEngine.getBlockSize();
+        }
         this.cipher = new AEADGenericBlockCipher(engine);
     }
 
@@ -352,7 +366,7 @@
         {
             ivLength = baseEngine.getBlockSize();
             cipher = new BufferedGenericBlockCipher(
-                new CBCBlockCipher(baseEngine));
+                CBCBlockCipher.newInstance(baseEngine));
         }
         else if (modeName.startsWith("OFB"))
         {
@@ -378,12 +392,12 @@
                 int wordSize = Integer.parseInt(modeName.substring(3));
 
                 cipher = new BufferedGenericBlockCipher(
-                    new CFBBlockCipher(baseEngine, wordSize));
+                    CFBBlockCipher.newInstance(baseEngine, wordSize));
             }
             else
             {
                 cipher = new BufferedGenericBlockCipher(
-                    new CFBBlockCipher(baseEngine, 8 * baseEngine.getBlockSize()));
+                    CFBBlockCipher.newInstance(baseEngine, 8 * baseEngine.getBlockSize()));
             }
         }
         // BEGIN Android-removed: Unsupported modes
@@ -407,6 +421,18 @@
             cipher = new BufferedGenericBlockCipher(
                 new OpenPGPCFBBlockCipher(baseEngine));
         }
+        else if (modeName.equals("FF1"))
+        {
+            ivLength = 0;
+            cipher = new BufferedFPEBlockCipher(
+                new FPEFF1Engine(baseEngine));
+        }
+        else if (modeName.equals("FF3-1"))
+        {
+            ivLength = 0;
+            cipher = new BufferedFPEBlockCipher(
+                new FPEFF3_1Engine(baseEngine));
+        }
         else if (modeName.equals("SIC"))
         {
             ivLength = baseEngine.getBlockSize();
@@ -415,8 +441,8 @@
                 throw new IllegalArgumentException("Warning: SIC-Mode can become a twotime-pad if the blocksize of the cipher is too small. Use a cipher with a block size of at least 128 bits (e.g. AES)");
             }
             fixedIv = false;
-            cipher = new BufferedGenericBlockCipher(new BufferedBlockCipher(
-                new SICBlockCipher(baseEngine)));
+            cipher = new BufferedGenericBlockCipher(new DefaultBufferedBlockCipher(
+                SICBlockCipher.newInstance(baseEngine)));
         }
         */
         // END Android-removed: Unsupported modes
@@ -428,14 +454,14 @@
             /*
             if (baseEngine instanceof DSTU7624Engine)
             {
-                cipher = new BufferedGenericBlockCipher(new BufferedBlockCipher(
+                cipher = new BufferedGenericBlockCipher(new DefaultBufferedBlockCipher(
                     new KCTRBlockCipher(baseEngine)));
             }
             else
             {
             */
-                cipher = new BufferedGenericBlockCipher(new BufferedBlockCipher(
-                    new SICBlockCipher(baseEngine)));
+                cipher = new BufferedGenericBlockCipher(new DefaultBufferedBlockCipher(
+                    SICBlockCipher.newInstance(baseEngine)));
             /*
             }
             */
@@ -445,13 +471,13 @@
         else if (modeName.equals("GOFB"))
         {
             ivLength = baseEngine.getBlockSize();
-            cipher = new BufferedGenericBlockCipher(new BufferedBlockCipher(
+            cipher = new BufferedGenericBlockCipher(new DefaultBufferedBlockCipher(
                 new GOFBBlockCipher(baseEngine)));
         }
         else if (modeName.equals("GCFB"))
         {
             ivLength = baseEngine.getBlockSize();
-            cipher = new BufferedGenericBlockCipher(new BufferedBlockCipher(
+            cipher = new BufferedGenericBlockCipher(new DefaultBufferedBlockCipher(
                 new GCFBBlockCipher(baseEngine)));
         }
         */
@@ -459,7 +485,7 @@
         else if (modeName.equals("CTS"))
         {
             ivLength = baseEngine.getBlockSize();
-            cipher = new BufferedGenericBlockCipher(new CTSBlockCipher(new CBCBlockCipher(baseEngine)));
+            cipher = new BufferedGenericBlockCipher(new CTSBlockCipher(CBCBlockCipher.newInstance(baseEngine)));
         }
         else if (modeName.equals("CCM"))
         {
@@ -473,7 +499,7 @@
             else
             {
             */
-                cipher = new AEADGenericBlockCipher(new CCMBlockCipher(baseEngine));
+                cipher = new AEADGenericBlockCipher(CCMBlockCipher.newInstance(baseEngine));
             /*
             }
             */
@@ -509,12 +535,13 @@
             /*
             if (baseEngine instanceof DSTU7624Engine)
             {
+                ivLength = baseEngine.getBlockSize();
                 cipher = new AEADGenericBlockCipher(new KGCMBlockCipher(baseEngine));
             }
             else
             {
             */
-                cipher = new AEADGenericBlockCipher(new GCMBlockCipher(baseEngine));
+                cipher = new AEADGenericBlockCipher(GCMBlockCipher.newInstance(baseEngine));
             /*
             }
             */
@@ -540,7 +567,7 @@
         {
             if (cipher.wrapOnNoPadding())
             {
-                cipher = new BufferedGenericBlockCipher(new BufferedBlockCipher(cipher.getUnderlyingCipher()));
+                cipher = new BufferedGenericBlockCipher(new DefaultBufferedBlockCipher(cipher.getUnderlyingCipher()));
             }
         }
         else if (paddingName.equals("WITHCTS") || paddingName.equals("CTSPADDING") || paddingName.equals("CS3PADDING"))
@@ -849,7 +876,7 @@
             GOST28147ParameterSpec gost28147Param = (GOST28147ParameterSpec)params;
 
             param = new ParametersWithSBox(
-                new KeyParameter(key.getEncoded()), ((GOST28147ParameterSpec)params).getSbox());
+                new KeyParameter(key.getEncoded()), ((GOST28147ParameterSpec)params).getSBox());
 
             if (gost28147Param.getIV() != null && ivLength != 0)
             {
@@ -922,9 +949,15 @@
                 ivParam = (ParametersWithIV)param;
             }
         }
+        else if (params instanceof FPEParameterSpec)
+        {
+            FPEParameterSpec spec = (FPEParameterSpec)params;
+
+            param = new FPEParameters((KeyParameter)param, spec.getRadixConverter(), spec.getTweak(), spec.isUsingInverseFunction());
+        }
         */
         // END Android-removed: Unsupported algorithms
-        else if (gcmSpecClass != null && gcmSpecClass.isInstance(params))
+        else if (GcmSpecUtil.isGcmSpec(params))
         {
             if (!isAEADModeName(modeName) && !(cipher instanceof AEADGenericBlockCipher))
             {
@@ -1048,7 +1081,7 @@
                 // need to pick up IV and SBox.
                 GOST28147ParameterSpec gost28147Param = (GOST28147ParameterSpec)params;
 
-                param = new ParametersWithSBox(param, gost28147Param.getSbox());
+                param = new ParametersWithSBox(param, gost28147Param.getSBox());
 
                 if (gost28147Param.getIV() != null && ivLength != 0)
                 {
@@ -1075,7 +1108,7 @@
                 // need to pick up IV and SBox.
                 GOST28147ParameterSpec gost28147Param = (GOST28147ParameterSpec)params;
 
-                param = new ParametersWithSBox(param, gost28147Param.getSbox());
+                param = new ParametersWithSBox(param, gost28147Param.getSBox());
 
                 if (gost28147Param.getIV() != null && ivLength != 0)
                 {
@@ -1351,7 +1384,7 @@
 
         BufferedGenericBlockCipher(com.android.internal.org.bouncycastle.crypto.BlockCipher cipher)
         {
-            this.cipher = new PaddedBufferedBlockCipher(cipher);
+            this(cipher, new PKCS7Padding());
         }
 
         BufferedGenericBlockCipher(com.android.internal.org.bouncycastle.crypto.BlockCipher cipher, BlockCipherPadding padding)
@@ -1421,6 +1454,85 @@
         }
     }
 
+    // BEGIN Android-removed: unsupported
+    // private static class BufferedFPEBlockCipher
+    //     implements GenericBlockCipher
+    // {
+    //     private FPEEngine cipher;
+    //     private ErasableOutputStream eOut = new ErasableOutputStream();
+
+    //     BufferedFPEBlockCipher(FPEEngine cipher)
+    //     {
+    //         this.cipher = cipher;
+    //     }
+
+    //     public void init(boolean forEncryption, CipherParameters params)
+    //         throws IllegalArgumentException
+    //     {
+    //         cipher.init(forEncryption, params);
+    //     }
+
+    //     public boolean wrapOnNoPadding()
+    //     {
+    //         return false;
+    //     }
+
+    //     public String getAlgorithmName()
+    //     {
+    //         return cipher.getAlgorithmName();
+    //     }
+
+    //     public org.bouncycastle.crypto.BlockCipher getUnderlyingCipher()
+    //     {
+    //         throw new IllegalStateException("not applicable for FPE");
+    //     }
+
+    //     public int getOutputSize(int len)
+    //     {
+    //         return eOut.size() + len;
+    //     }
+
+    //     public int getUpdateOutputSize(int len)
+    //     {
+    //         return 0;
+    //     }
+
+    //     public void updateAAD(byte[] input, int offset, int length)
+    //     {
+    //         throw new UnsupportedOperationException("AAD is not supported in the current mode.");
+    //     }
+
+    //     public int processByte(byte in, byte[] out, int outOff)
+    //         throws DataLengthException
+    //     {
+    //         eOut.write(in);
+
+    //         return 0;
+    //     }
+
+    //     public int processBytes(byte[] in, int inOff, int len, byte[] out, int outOff)
+    //         throws DataLengthException
+    //     {
+    //         eOut.write(in, inOff, len);
+
+    //         return 0;
+    //     }
+
+    //     public int doFinal(byte[] out, int outOff)
+    //         throws IllegalStateException, BadPaddingException
+    //     {
+    //         try
+    //         {
+    //             return cipher.processBlock(eOut.getBuf(), 0, eOut.size(), out, outOff);
+    //         }
+    //         finally
+    //         {
+    //             eOut.erase();
+    //         }
+    //     }
+    // }
+    // END Android-removed: unsupported
+
     private static class AEADGenericBlockCipher
         implements GenericBlockCipher
     {
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/symmetric/util/BaseMac.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/symmetric/util/BaseMac.java
index c142c38..d6f08e4 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/symmetric/util/BaseMac.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/symmetric/util/BaseMac.java
@@ -36,8 +36,6 @@
 public class BaseMac
     extends MacSpi implements PBE
 {
-    private static final Class gcmSpecClass = ClassUtil.loadClass(BaseMac.class, "javax.crypto.spec.GCMParameterSpec");
-
     private Mac macEngine;
 
     private int scheme = PKCS12;
@@ -215,7 +213,7 @@
         {
             param = new KeyParameter(key.getEncoded());
         }
-        else if (gcmSpecClass != null && gcmSpecClass.isAssignableFrom(params.getClass()))
+        else if (GcmSpecUtil.isGcmSpec(params))
         {
             param = GcmSpecUtil.extractAeadParameters(keyParam, params);
         }
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/symmetric/util/ClassUtil.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/symmetric/util/ClassUtil.java
index 5582be7..d205733 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/symmetric/util/ClassUtil.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/symmetric/util/ClassUtil.java
@@ -27,7 +27,9 @@
                     {
                         try
                         {
-                            return Class.forName(className);
+                            ClassLoader classLoader = ClassLoader.getSystemClassLoader();
+
+                            return classLoader.loadClass(className);
                         }
                         catch (Exception e)
                         {
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/symmetric/util/GcmSpecUtil.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/symmetric/util/GcmSpecUtil.java
index 242306b..9bd37f5 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/symmetric/util/GcmSpecUtil.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/symmetric/util/GcmSpecUtil.java
@@ -11,9 +11,9 @@
 import java.security.spec.InvalidParameterSpecException;
 
 import com.android.internal.org.bouncycastle.asn1.ASN1Primitive;
-import com.android.internal.org.bouncycastle.asn1.cms.GCMParameters;
 import com.android.internal.org.bouncycastle.crypto.params.AEADParameters;
 import com.android.internal.org.bouncycastle.crypto.params.KeyParameter;
+import com.android.internal.org.bouncycastle.internal.asn1.cms.GCMParameters;
 import com.android.internal.org.bouncycastle.util.Integers;
 
 /**
@@ -21,25 +21,48 @@
  */
 public class GcmSpecUtil
 {
-    static final Class gcmSpecClass = ClassUtil.loadClass(GcmSpecUtil.class, "javax.crypto.spec.GCMParameterSpec");
-
-    static final Method tLen;
-    static final Method iv;
+    static final Class gcmSpecClass;
+    private static final Constructor constructor;
+    private static final Method tLen;
+    private static final Method iv;
 
     static
     {
+        gcmSpecClass = ClassUtil.loadClass(GcmSpecUtil.class, "javax.crypto.spec.GCMParameterSpec");
+
         if (gcmSpecClass != null)
         {
+            constructor = extractConstructor();
             tLen = extractMethod("getTLen");
             iv = extractMethod("getIV");
         }
         else
         {
+            constructor = null;
             tLen = null;
             iv = null;
         }
     }
 
+    private static Constructor extractConstructor()
+    {
+        try
+        {
+            return (Constructor)AccessController.doPrivileged(new PrivilegedExceptionAction()
+            {
+                public Object run()
+                    throws Exception
+                {
+                    return gcmSpecClass.getConstructor(new Class[]{ Integer.TYPE, byte[].class });
+                }
+            });
+        }
+        catch (PrivilegedActionException e)
+        {
+            return null;
+        }
+    }
+
     private static Method extractMethod(final String name)
     {
         try
@@ -64,6 +87,11 @@
         return gcmSpecClass != null;
     }
 
+    public static boolean gcmSpecExtractable()
+    {
+        return constructor != null;
+    }
+
     public static boolean isGcmSpec(AlgorithmParameterSpec paramSpec)
     {
         return gcmSpecClass != null && gcmSpecClass.isInstance(paramSpec);
@@ -80,14 +108,9 @@
         try
         {
             GCMParameters gcmParams = GCMParameters.getInstance(spec);
-            Constructor constructor = gcmSpecClass.getConstructor(new Class[]{Integer.TYPE, byte[].class});
 
             return (AlgorithmParameterSpec)constructor.newInstance(new Object[] { Integers.valueOf(gcmParams.getIcvLen() * 8), gcmParams.getNonce() });
         }
-        catch (NoSuchMethodException e)
-        {
-            throw new InvalidParameterSpecException("No constructor found!");   // should never happen
-        }
         catch (Exception e)
         {
             throw new InvalidParameterSpecException("Construction failed: " + e.getMessage());   // should never happen
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/symmetric/util/PBE.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/symmetric/util/PBE.java
index 5255765..b93d0ca 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/symmetric/util/PBE.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/symmetric/util/PBE.java
@@ -15,6 +15,7 @@
 import javax.crypto.spec.PBEParameterSpec;
 
 import com.android.internal.org.bouncycastle.crypto.CipherParameters;
+import com.android.internal.org.bouncycastle.crypto.CryptoServicePurpose;
 import com.android.internal.org.bouncycastle.crypto.PBEParametersGenerator;
 // Android-removed: Unsupported algorithms
 // import org.bouncycastle.crypto.digests.GOST3411Digest;
@@ -107,25 +108,25 @@
                 {
                 // Android-removed: Unsupported algorithms
                 // case MD2:
-                //     generator = new PKCS5S2ParametersGenerator(new MD2Digest());
+                    // generator = new PKCS5S2ParametersGenerator(new MD2Digest(CryptoServicePurpose.PRF));
                 //     break;
                 case MD5:
                     // Android-changed: Use Android digests
-                    // generator = new PKCS5S2ParametersGenerator(DigestFactory.createMD5());
+                    // generator = new PKCS5S2ParametersGenerator(DigestFactory.createMD5PRF());
                     generator = new PKCS5S2ParametersGenerator(AndroidDigestFactory.getMD5());
                     break;
                 case SHA1:
                     // Android-changed: Use Android digests
-                    // generator = new PKCS5S2ParametersGenerator(DigestFactory.createSHA1());
+                    // generator = new PKCS5S2ParametersGenerator(DigestFactory.createSHA1PRF());
                     generator = new PKCS5S2ParametersGenerator(AndroidDigestFactory.getSHA1());
                     break;
                 // BEGIN Android-removed: Unsupported algorithms
                 /*
                 case RIPEMD160:
-                    generator = new PKCS5S2ParametersGenerator(new RIPEMD160Digest());
+                    generator = new PKCS5S2ParametersGenerator(new RIPEMD160Digest(CryptoServicePurpose.PRF));
                     break;
                 case TIGER:
-                    generator = new PKCS5S2ParametersGenerator(new TigerDigest());
+                    generator = new PKCS5S2ParametersGenerator(new TigerDigest(CryptoServicePurpose.PRF));
                     break;
                 */
                 // END Android-removed: Unsupported algorithms
@@ -136,36 +137,43 @@
                     break;
                 // Android-removed: Unsupported algorithms
                 // case GOST3411:
+                //     generator = new PKCS5S2ParametersGenerator(new GOST3411Digest(CryptoServicePurpose.PRF));
+                //     break;
+                // Android-removed: Unsupported algorithms
+                // case GOST3411:
                 //     generator = new PKCS5S2ParametersGenerator(new GOST3411Digest());
                 //     break;
                 case SHA224:
                     // Android-changed: Use Android digests
-                    // generator = new PKCS5S2ParametersGenerator(DigestFactory.createSHA224());
+                    // generator = new PKCS5S2ParametersGenerator(DigestFactory.createSHA224PRF());
                     generator = new PKCS5S2ParametersGenerator(AndroidDigestFactory.getSHA224());
                     break;
                 case SHA384:
                     // Android-changed: Use Android digests
-                    // generator = new PKCS5S2ParametersGenerator(DigestFactory.createSHA384());
+                    // generator = new PKCS5S2ParametersGenerator(DigestFactory.createSHA384PRF());
                     generator = new PKCS5S2ParametersGenerator(AndroidDigestFactory.getSHA384());
                     break;
                 case SHA512:
                     // Android-changed: Use Android digests
-                    // generator = new PKCS5S2ParametersGenerator(DigestFactory.createSHA512());
+                    // generator = new PKCS5S2ParametersGenerator(DigestFactory.createSHA512PRF());
                     generator = new PKCS5S2ParametersGenerator(AndroidDigestFactory.getSHA512());
                     break;
                 // BEGIN Android-removed: Unsupported algorithms
                 /*
                 case SHA3_224:
-                    generator = new PKCS5S2ParametersGenerator(DigestFactory.createSHA3_224());
+                    generator = new PKCS5S2ParametersGenerator(DigestFactory.createSHA3_224PRF());
                     break;
                 case SHA3_256:
-                     generator = new PKCS5S2ParametersGenerator(DigestFactory.createSHA3_256());
+                     generator = new PKCS5S2ParametersGenerator(DigestFactory.createSHA3_256PRF());
                      break;
                 case SHA3_384:
-                    generator = new PKCS5S2ParametersGenerator(DigestFactory.createSHA3_384());
+                    generator = new PKCS5S2ParametersGenerator(DigestFactory.createSHA3_384PRF());
                     break;
                 case SHA3_512:
-                    generator = new PKCS5S2ParametersGenerator(DigestFactory.createSHA3_512());
+                    generator = new PKCS5S2ParametersGenerator(DigestFactory.createSHA3_512PRF());
+                    break;
+                case SM3:
+                    generator = new PKCS5S2ParametersGenerator(new SM3Digest(CryptoServicePurpose.PRF));
                     break;
                 case SM3:
                     generator = new PKCS5S2ParametersGenerator(new SM3Digest());
@@ -182,50 +190,54 @@
                 {
                 // Android-removed: Unsupported algorithms
                 // case MD2:
-                //     generator = new PKCS12ParametersGenerator(new MD2Digest());
+                    // generator = new PKCS12ParametersGenerator(new MD2Digest(CryptoServicePurpose.PRF));
                 //     break;
                 case MD5:
                     // Android-changed: Use Android digests
-                    // generator = new PKCS12ParametersGenerator(DigestFactory.createMD5());
+                    // generator = new PKCS12ParametersGenerator(DigestFactory.createMD5PRF());
                     generator = new PKCS12ParametersGenerator(AndroidDigestFactory.getMD5());
                     break;
                 case SHA1:
                     // Android-changed: Use Android digests
-                    // generator = new PKCS12ParametersGenerator(DigestFactory.createSHA1());
+                    // generator = new PKCS12ParametersGenerator(DigestFactory.createSHA1PRF());
                     generator = new PKCS12ParametersGenerator(AndroidDigestFactory.getSHA1());
                     break;
                 // BEGIN Android-removed: Unsupported algorithms
                 /*
                 case RIPEMD160:
-                    generator = new PKCS12ParametersGenerator(new RIPEMD160Digest());
+                    generator = new PKCS12ParametersGenerator(new RIPEMD160Digest(CryptoServicePurpose.PRF));
                     break;
                 case TIGER:
-                    generator = new PKCS12ParametersGenerator(new TigerDigest());
+                    generator = new PKCS12ParametersGenerator(new TigerDigest(CryptoServicePurpose.PRF));
                     break;
                 */
                 // END Android-removed: Unsupported algorithms
                 case SHA256:
                     // Android-changed: Use Android digests
-                    // generator = new PKCS12ParametersGenerator(DigestFactory.createSHA256());
+                    // generator = new PKCS12ParametersGenerator(DigestFactory.createSHA256PRF());
                     generator = new PKCS12ParametersGenerator(AndroidDigestFactory.getSHA256());
                     break;
                 // Android-removed: Unsupported algorithms
                 // case GOST3411:
+                //     generator = new PKCS12ParametersGenerator(new GOST3411Digest(CryptoServicePurpose.PRF));
+                //     break;
+                // Android-removed: Unsupported algorithms
+                // case GOST3411:
                 //     generator = new PKCS12ParametersGenerator(new GOST3411Digest());
                 //     break;
                 case SHA224:
                     // Android-changed: Use Android digests
-                    // generator = new PKCS12ParametersGenerator(DigestFactory.createSHA224());
+                    // generator = new PKCS12ParametersGenerator(DigestFactory.createSHA224PRF());
                     generator = new PKCS12ParametersGenerator(AndroidDigestFactory.getSHA224());
                     break;
                 case SHA384:
                     // Android-changed: Use Android digests
-                    // generator = new PKCS12ParametersGenerator(DigestFactory.createSHA384());
+                    // generator = new PKCS12ParametersGenerator(DigestFactory.createSHA384PRF());
                     generator = new PKCS12ParametersGenerator(AndroidDigestFactory.getSHA384());
                     break;
                 case SHA512:
                     // Android-changed: Use Android digests
-                    // generator = new PKCS12ParametersGenerator(DigestFactory.createSHA512());
+                    // generator = new PKCS12ParametersGenerator(DigestFactory.createSHA512PRF());
                     generator = new PKCS12ParametersGenerator(AndroidDigestFactory.getSHA512());
                     break;
                 default:
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/util/AsymmetricAlgorithmProvider.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/util/AsymmetricAlgorithmProvider.java
index f602b3d..47ad788 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/util/AsymmetricAlgorithmProvider.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/util/AsymmetricAlgorithmProvider.java
@@ -1,6 +1,8 @@
 /* GENERATED SOURCE. DO NOT MODIFY. */
 package com.android.internal.org.bouncycastle.jcajce.provider.util;
 
+import java.util.Map;
+
 import com.android.internal.org.bouncycastle.asn1.ASN1ObjectIdentifier;
 import com.android.internal.org.bouncycastle.jcajce.provider.config.ConfigurableProvider;
 
@@ -21,6 +23,24 @@
         provider.addAlgorithm("Alg.Alias.Signature.OID." + oid, algorithm);
     }
 
+    protected void addSignatureAlias(
+        ConfigurableProvider provider,
+        String algorithm,
+        ASN1ObjectIdentifier oid)
+    {
+        provider.addAlgorithm("Alg.Alias.Signature." + oid, algorithm);
+        provider.addAlgorithm("Alg.Alias.Signature.OID." + oid, algorithm);
+    }
+
+    protected void addSignatureAlgorithm(
+        ConfigurableProvider provider,
+        String digest,
+        String algorithm,
+        String className)
+    {
+        addSignatureAlgorithm(provider, digest, algorithm, className, null);
+    }
+
     protected void addSignatureAlgorithm(
         ConfigurableProvider provider,
         String digest,
@@ -37,8 +57,103 @@
         provider.addAlgorithm("Alg.Alias.Signature." + jdk11Variation1, mainName);
         provider.addAlgorithm("Alg.Alias.Signature." + jdk11Variation2, mainName);
         provider.addAlgorithm("Alg.Alias.Signature." + alias, mainName);
-        provider.addAlgorithm("Alg.Alias.Signature." + oid, mainName);
-        provider.addAlgorithm("Alg.Alias.Signature.OID." + oid, mainName);
+        if (oid != null)
+        {
+            provider.addAlgorithm("Alg.Alias.Signature." + oid, mainName);
+            provider.addAlgorithm("Alg.Alias.Signature.OID." + oid, mainName);
+        }
+    }
+
+    protected void addSignatureAlgorithm(
+        ConfigurableProvider provider,
+        String digest,
+        String algorithm,
+        String className,
+        ASN1ObjectIdentifier oid,
+        Map<String, String> attributes)
+    {
+        String mainName = digest + "WITH" + algorithm;
+        String jdk11Variation1 = digest + "with" + algorithm;
+        String jdk11Variation2 = digest + "With" + algorithm;
+        String alias = digest + "/" + algorithm;
+
+        provider.addAlgorithm("Signature." + mainName, className);
+        provider.addAlgorithm("Alg.Alias.Signature." + jdk11Variation1, mainName);
+        provider.addAlgorithm("Alg.Alias.Signature." + jdk11Variation2, mainName);
+        provider.addAlgorithm("Alg.Alias.Signature." + alias, mainName);
+        if (oid != null)
+        {
+            provider.addAlgorithm("Alg.Alias.Signature." + oid, mainName);
+            provider.addAlgorithm("Alg.Alias.Signature.OID." + oid, mainName);
+        }
+        provider.addAttributes("Signature." + mainName, attributes);
+    }
+
+    protected void addKeyPairGeneratorAlgorithm(
+        ConfigurableProvider provider,
+        String algorithm,
+        String className,
+        ASN1ObjectIdentifier oid)
+    {
+        provider.addAlgorithm("KeyPairGenerator." + algorithm, className);
+        if (oid != null)
+        {
+            provider.addAlgorithm("Alg.Alias.KeyPairGenerator." + oid, algorithm);
+            provider.addAlgorithm("Alg.Alias.KeyPairGenerator.OID." + oid, algorithm);
+        }
+    }
+
+    protected void addKeyFactoryAlgorithm(
+        ConfigurableProvider provider,
+        String algorithm,
+        String className,
+        ASN1ObjectIdentifier oid,
+        AsymmetricKeyInfoConverter keyInfoConverter)
+    {
+        provider.addAlgorithm("KeyFactory." + algorithm, className);
+        if (oid != null)
+        {
+            provider.addAlgorithm("Alg.Alias.KeyFactory." + oid, algorithm);
+            provider.addAlgorithm("Alg.Alias.KeyFactory.OID." + oid, algorithm);
+
+            provider.addKeyInfoConverter(oid, keyInfoConverter);
+        }
+    }
+
+    protected void addKeyGeneratorAlgorithm(
+        ConfigurableProvider provider,
+        String algorithm,
+        String className,
+        ASN1ObjectIdentifier oid)
+    {
+        provider.addAlgorithm("KeyGenerator." + algorithm, className);
+        if (oid != null)
+        {
+            provider.addAlgorithm("Alg.Alias.KeyGenerator." + oid, algorithm);
+            provider.addAlgorithm("Alg.Alias.KeyGenerator.OID." + oid, algorithm);
+        }
+    }
+
+    protected void addCipherAlgorithm(
+        ConfigurableProvider provider,
+        String algorithm,
+        String className,
+        ASN1ObjectIdentifier oid)
+    {
+        provider.addAlgorithm("Cipher." + algorithm, className);
+        if (oid != null)
+        {
+            provider.addAlgorithm("Alg.Alias.Cipher." + oid, algorithm);
+            provider.addAlgorithm("Alg.Alias.Cipher.OID." + oid, algorithm);
+        }
+    }
+
+    protected void registerKeyFactoryOid(ConfigurableProvider provider, ASN1ObjectIdentifier oid, String name, AsymmetricKeyInfoConverter keyFactory)
+    {
+        provider.addAlgorithm("Alg.Alias.KeyFactory." + oid, name);
+        provider.addAlgorithm("Alg.Alias.KeyFactory.OID." + oid, name);
+
+        provider.addKeyInfoConverter(oid, keyFactory);
     }
 
     protected void registerOid(ConfigurableProvider provider, ASN1ObjectIdentifier oid, String name, AsymmetricKeyInfoConverter keyFactory)
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/util/DigestFactory.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/util/DigestFactory.java
index 0a431a3..47499bf 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/util/DigestFactory.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/util/DigestFactory.java
@@ -35,6 +35,8 @@
     private static Set sha3_256 = new HashSet();
     private static Set sha3_384 = new HashSet();
     private static Set sha3_512 = new HashSet();
+    private static Set shake128 = new HashSet();
+    private static Set shake256 = new HashSet();
     */
     // END Android-removed: Unsupported algorithms
 
@@ -86,6 +88,18 @@
 
         sha3_512.add("SHA3-512");
         sha3_512.add(NISTObjectIdentifiers.id_sha3_512.getId());
+
+        shake128.add("SHAKE128");
+        shake128.add(NISTObjectIdentifiers.id_shake128.getId());
+
+        shake256.add("SHAKE256");
+        shake256.add(NISTObjectIdentifiers.id_shake256.getId());
+
+        oids.put("SHAKE128", NISTObjectIdentifiers.id_shake128);
+        oids.put(NISTObjectIdentifiers.id_shake128.getId(), NISTObjectIdentifiers.id_shake128);
+
+        oids.put("SHAKE256", NISTObjectIdentifiers.id_shake256);
+        oids.put(NISTObjectIdentifiers.id_shake256.getId(), NISTObjectIdentifiers.id_shake256);
         */
         // END Android-removed: Unsupported algorithms
 
@@ -202,6 +216,14 @@
         {
             return org.bouncycastle.crypto.util.DigestFactory.createSHA3_512();
         }
+        if (shake128.contains(digestName))
+        {
+            return org.bouncycastle.crypto.util.DigestFactory.createSHAKE128();
+        }
+        if (shake256.contains(digestName))
+        {
+            return org.bouncycastle.crypto.util.DigestFactory.createSHAKE256();
+        }
         */
         // END Android-removed: Unsupported algorithms
 
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/spec/FPEParameterSpec.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/spec/FPEParameterSpec.java
new file mode 100644
index 0000000..3aed568
--- /dev/null
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/spec/FPEParameterSpec.java
@@ -0,0 +1,55 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.jcajce.spec;
+
+import java.security.spec.AlgorithmParameterSpec;
+
+import com.android.internal.org.bouncycastle.crypto.util.RadixConverter;
+import com.android.internal.org.bouncycastle.util.Arrays;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+public class FPEParameterSpec
+    implements AlgorithmParameterSpec
+{
+    private final RadixConverter radixConverter;
+    private final byte[] tweak;
+    private final boolean useInverse;
+
+    public FPEParameterSpec(int radix, byte[] tweak)
+    {
+        this(radix, tweak, false);
+    }
+
+    public FPEParameterSpec(int radix, byte[] tweak, boolean useInverse)
+    {
+        this(new RadixConverter(radix), tweak, useInverse);
+    }
+
+    public FPEParameterSpec(RadixConverter radixConverter, byte[] tweak, boolean useInverse)
+    {
+        this.radixConverter = radixConverter;
+        this.tweak = Arrays.clone(tweak);
+        this.useInverse = useInverse;
+    }
+
+    public int getRadix()
+    {
+        return radixConverter.getRadix();
+    }
+
+    public RadixConverter getRadixConverter()
+    {
+        return radixConverter;
+    }
+
+    public byte[] getTweak()
+    {
+        return Arrays.clone(tweak);
+    }
+
+    public boolean isUsingInverseFunction()
+    {
+        return useInverse;
+    }
+}
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/spec/HybridValueParameterSpec.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/spec/HybridValueParameterSpec.java
new file mode 100644
index 0000000..3bf9bda
--- /dev/null
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/spec/HybridValueParameterSpec.java
@@ -0,0 +1,99 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.jcajce.spec;
+
+import java.security.spec.AlgorithmParameterSpec;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import javax.security.auth.Destroyable;
+
+import com.android.internal.org.bouncycastle.util.Arrays;
+
+/**
+ * SP 800-56C Hybrid Value spec, to allow the secret in a key agreement to be
+ * created as "Z | T" where T is some other secret value as described in Section 2.
+ * <p>
+ * Get methods throw IllegalStateException if destroy() is called.
+ * </p>
+ * @hide This class is not part of the Android public SDK API
+ */
+public class HybridValueParameterSpec
+    implements AlgorithmParameterSpec, Destroyable
+{
+    private final AtomicBoolean hasBeenDestroyed = new AtomicBoolean(false);
+
+    private volatile byte[] t;
+    private volatile AlgorithmParameterSpec baseSpec;
+
+    /**
+     * Create a spec with T set to t and the spec for the KDF in the agreement to baseSpec.
+     * Note: the t value is not copied.
+     *
+     * @param t a shared secret to be concatenated with the agreement's Z value.
+     * @param baseSpec the base spec for the agreements KDF.
+     */
+    public HybridValueParameterSpec(byte[] t, AlgorithmParameterSpec baseSpec)
+    {
+        this.t = t;
+        this.baseSpec = baseSpec;
+    }
+
+    /**
+     * Return a reference to the T value.
+     *
+     * @return a reference to T.
+     */
+    public byte[] getT()
+    {
+        byte[] tVal = t;
+
+        checkDestroyed();
+        
+        return tVal;
+    }
+
+    /**
+     * Return the base parameter spec.
+     *
+     * @return base spec to be applied to the KDF.
+     */
+    public AlgorithmParameterSpec getBaseParameterSpec()
+    {
+        AlgorithmParameterSpec rv = this.baseSpec;
+
+        checkDestroyed();
+
+        return rv;
+    }
+
+    /**
+     * Return true if the destroy() method is called and the contents are
+     * erased.
+     *
+     * @return true if destroyed, false otherwise.
+     */
+    public boolean isDestroyed()
+    {
+        return this.hasBeenDestroyed.get();
+    }
+
+    /**
+     * Destroy this parameter spec, explicitly erasing its contents.
+     */
+    public void destroy()
+    {
+        if (!hasBeenDestroyed.getAndSet(true))
+        {
+            Arrays.clear(t);
+            this.t = null;
+            this.baseSpec = null;
+        }
+    }
+
+    private void checkDestroyed()
+    {
+        if (isDestroyed())
+        {
+            throw new IllegalStateException("spec has been destroyed");
+        }
+    }
+}
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/spec/IESKEMParameterSpec.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/spec/IESKEMParameterSpec.java
new file mode 100644
index 0000000..94cf47f
--- /dev/null
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/spec/IESKEMParameterSpec.java
@@ -0,0 +1,53 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.jcajce.spec;
+
+import java.security.spec.AlgorithmParameterSpec;
+
+import com.android.internal.org.bouncycastle.util.Arrays;
+
+/**
+ * Parameter spec for an integrated encryptor KEM, as in IEEE_Std_1609_2
+ * @hide This class is not part of the Android public SDK API
+ */
+public class IESKEMParameterSpec
+    implements AlgorithmParameterSpec
+{
+    private final byte[] recipientInfo;
+    private final boolean usePointCompression;
+
+
+    /**
+     * Set the IESKEM parameters.
+     *
+     * @param recipientInfo recipient data.
+     */
+    public IESKEMParameterSpec(
+        byte[] recipientInfo)
+    {
+        this(recipientInfo, false);
+    }
+
+    /**
+     * Set the IESKEM parameters - specifying point compression.
+     *
+     * @param recipientInfo recipient data.
+     * @param usePointCompression use point compression on output (ignored on input).
+     */
+    public IESKEMParameterSpec(
+        byte[] recipientInfo,
+        boolean usePointCompression)
+    {
+        this.recipientInfo = Arrays.clone(recipientInfo);
+        this.usePointCompression = usePointCompression;
+    }
+
+    public byte[] getRecipientInfo()
+    {
+        return Arrays.clone(recipientInfo);
+    }
+
+    public boolean hasUsePointCompression()
+    {
+        return usePointCompression;
+    }
+}
\ No newline at end of file
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/spec/KEMExtractSpec.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/spec/KEMExtractSpec.java
new file mode 100644
index 0000000..277d4e8
--- /dev/null
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/spec/KEMExtractSpec.java
@@ -0,0 +1,52 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.jcajce.spec;
+
+import java.security.PrivateKey;
+import java.security.spec.AlgorithmParameterSpec;
+
+import com.android.internal.org.bouncycastle.util.Arrays;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+public class KEMExtractSpec
+    implements AlgorithmParameterSpec
+{
+    private final PrivateKey privateKey;
+    private final byte[] encapsulation;
+    private final String keyAlgorithmName;
+    private final int keySizeInBits;
+
+    public KEMExtractSpec(PrivateKey privateKey, byte[] encapsulation, String keyAlgorithmName)
+    {
+        this(privateKey, encapsulation, keyAlgorithmName, 256);
+    }
+
+    public KEMExtractSpec(PrivateKey privateKey, byte[] encapsulation, String keyAlgorithmName, int keySizeInBits)
+    {
+        this.privateKey = privateKey;
+        this.encapsulation = Arrays.clone(encapsulation);
+        this.keyAlgorithmName = keyAlgorithmName;
+        this.keySizeInBits = keySizeInBits;
+    }
+
+    public byte[] getEncapsulation()
+    {
+        return Arrays.clone(encapsulation);
+    }
+
+    public PrivateKey getPrivateKey()
+    {
+        return privateKey;
+    }
+
+    public String getKeyAlgorithmName()
+    {
+        return keyAlgorithmName;
+    }
+
+    public int getKeySize()
+    {
+        return keySizeInBits;
+    }
+}
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/spec/KEMGenerateSpec.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/spec/KEMGenerateSpec.java
new file mode 100644
index 0000000..24b6693
--- /dev/null
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/spec/KEMGenerateSpec.java
@@ -0,0 +1,43 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.jcajce.spec;
+
+import java.security.PublicKey;
+import java.security.spec.AlgorithmParameterSpec;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+public class KEMGenerateSpec
+    implements AlgorithmParameterSpec
+{
+    private final PublicKey publicKey;
+    private final String keyAlgorithmName;
+    private final int keySizeInBits;
+
+    public KEMGenerateSpec(PublicKey publicKey, String keyAlgorithmName)
+    {
+        this(publicKey, keyAlgorithmName, 256);
+    }
+
+    public KEMGenerateSpec(PublicKey publicKey, String keyAlgorithmName, int keySizeInBits)
+    {
+        this.publicKey = publicKey;
+        this.keyAlgorithmName = keyAlgorithmName;
+        this.keySizeInBits = keySizeInBits;
+    }
+
+    public PublicKey getPublicKey()
+    {
+        return publicKey;
+    }
+
+    public String getKeyAlgorithmName()
+    {
+        return keyAlgorithmName;
+    }
+
+    public int getKeySize()
+    {
+        return keySizeInBits;
+    }
+}
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/spec/RawEncodedKeySpec.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/spec/RawEncodedKeySpec.java
new file mode 100644
index 0000000..c20bbd9
--- /dev/null
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/spec/RawEncodedKeySpec.java
@@ -0,0 +1,27 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.jcajce.spec;
+
+import java.security.spec.EncodedKeySpec;
+
+/**
+ * An encoded key spec that just wraps the minimal data for a public/private key representation.
+ * @hide This class is not part of the Android public SDK API
+ */
+public class RawEncodedKeySpec
+    extends EncodedKeySpec
+{
+    /**
+     * Base constructor - just the minimal data.
+     *
+     * @param bytes the public/private key data.
+     */
+    public RawEncodedKeySpec(byte[] bytes)
+    {
+        super(bytes);
+    }
+
+    public String getFormat()
+    {
+        return "RAW";
+    }
+}
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/spec/UserKeyingMaterialSpec.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/spec/UserKeyingMaterialSpec.java
index 1e37d43..c99e3be 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/spec/UserKeyingMaterialSpec.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/spec/UserKeyingMaterialSpec.java
@@ -12,14 +12,47 @@
     implements AlgorithmParameterSpec
 {
     private final byte[] userKeyingMaterial;
+    private final byte[] salt;
 
+    /**
+     * Base constructor.
+     *
+     * @param userKeyingMaterial the bytes to be mixed in to the key agreement's KDF.
+     */
     public UserKeyingMaterialSpec(byte[] userKeyingMaterial)
     {
-        this.userKeyingMaterial = Arrays.clone(userKeyingMaterial);
+        this(userKeyingMaterial, null);
     }
 
+    /**
+     * Base constructor.
+     *
+     * @param userKeyingMaterial the bytes to be mixed in to the key agreement's KDF.
+     * @param salt the salt to use with the underlying KDF.
+     */
+    public UserKeyingMaterialSpec(byte[] userKeyingMaterial, byte[] salt)
+    {
+        this.userKeyingMaterial = Arrays.clone(userKeyingMaterial);
+        this.salt = Arrays.clone(salt);
+    }
+
+    /**
+     * Return a copy of the key material in this object.
+     *
+     * @return the user keying material.
+     */
     public byte[] getUserKeyingMaterial()
     {
         return Arrays.clone(userKeyingMaterial);
     }
+
+    /**
+     * Return a copy of the salt in this object.
+     *
+     * @return the KDF salt.
+     */
+    public byte[] getSalt()
+    {
+        return Arrays.clone(salt);
+    }
 }
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/util/ECKeyUtil.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/util/ECKeyUtil.java
index 4fc2ca1..c584f1b 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/util/ECKeyUtil.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/util/ECKeyUtil.java
@@ -12,6 +12,7 @@
 import com.android.internal.org.bouncycastle.asn1.x9.ECNamedCurveTable;
 import com.android.internal.org.bouncycastle.asn1.x9.X962Parameters;
 import com.android.internal.org.bouncycastle.asn1.x9.X9ECParameters;
+import com.android.internal.org.bouncycastle.asn1.x9.X9ECParametersHolder;
 import com.android.internal.org.bouncycastle.asn1.x9.X9ECPoint;
 import com.android.internal.org.bouncycastle.crypto.ec.CustomNamedCurves;
 
@@ -70,10 +71,10 @@
             {
                 ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier)params.getParameters();
 
-                X9ECParameters x9 = CustomNamedCurves.getByOID(oid);
+                X9ECParametersHolder x9 = CustomNamedCurves.getByOIDLazy(oid);
                 if (x9 == null)
                 {
-                    x9 = ECNamedCurveTable.getByOID(oid);
+                    x9 = ECNamedCurveTable.getByOIDLazy(oid);
                 }
                 curve = x9.getCurve();
             }
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/util/MessageDigestUtils.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/util/MessageDigestUtils.java
index a8fe3e7..1343075 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/util/MessageDigestUtils.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/util/MessageDigestUtils.java
@@ -15,6 +15,9 @@
 import com.android.internal.org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
 // Android-removed: Unsupported algorithms
 // import org.bouncycastle.asn1.teletrust.TeleTrusTObjectIdentifiers;
+// import org.bouncycastle.asn1.DERNull;
+// import org.bouncycastle.asn1.misc.MiscObjectIdentifiers;
+// import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 
 /**
  * @hide This class is not part of the Android public SDK API
@@ -23,6 +26,9 @@
 {
     private static Map<ASN1ObjectIdentifier, String> digestOidMap = new HashMap<ASN1ObjectIdentifier, String>();
 
+    // Android-removed: Unsupported algorithms
+    // private static Map<String, AlgorithmIdentifier> digestAlgIdMap = new HashMap<String, AlgorithmIdentifier>();
+
     static
     {
         // BEGIN Android-removed: Unsupported algorithms
@@ -37,6 +43,8 @@
         digestOidMap.put(NISTObjectIdentifiers.id_sha512, "SHA-512");
         // BEGIN Android-removed: Unsupported algorithms
         /*
+        digestOidMap.put(NISTObjectIdentifiers.id_sha512_224, "SHA-512(224)");
+        digestOidMap.put(NISTObjectIdentifiers.id_sha512_256, "SHA-512(256)");
         digestOidMap.put(TeleTrusTObjectIdentifiers.ripemd128, "RIPEMD-128");
         digestOidMap.put(TeleTrusTObjectIdentifiers.ripemd160, "RIPEMD-160");
         digestOidMap.put(TeleTrusTObjectIdentifiers.ripemd256, "RIPEMD-128");
@@ -49,11 +57,51 @@
         digestOidMap.put(NISTObjectIdentifiers.id_sha3_256, "SHA3-256");
         digestOidMap.put(NISTObjectIdentifiers.id_sha3_384, "SHA3-384");
         digestOidMap.put(NISTObjectIdentifiers.id_sha3_512, "SHA3-512");
+        digestOidMap.put(NISTObjectIdentifiers.id_shake128, "SHAKE128");
+        digestOidMap.put(NISTObjectIdentifiers.id_shake256, "SHAKE256");
         digestOidMap.put(GMObjectIdentifiers.sm3, "SM3");
+        digestOidMap.put(MiscObjectIdentifiers.blake3_256, "BLAKE3-256");
+
+        digestAlgIdMap.put("SHA-1", new AlgorithmIdentifier(OIWObjectIdentifiers.idSHA1, DERNull.INSTANCE));
+        digestAlgIdMap.put("SHA-224", new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha224));
+        digestAlgIdMap.put("SHA224", new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha224));
+        digestAlgIdMap.put("SHA-256", new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha256));
+        digestAlgIdMap.put("SHA256", new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha256));
+        digestAlgIdMap.put("SHA-384", new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha384));
+        digestAlgIdMap.put("SHA384", new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha384));
+        digestAlgIdMap.put("SHA-512", new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha512));
+        digestAlgIdMap.put("SHA512", new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha512));
+        digestAlgIdMap.put("SHA3-224", new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha3_224));
+        digestAlgIdMap.put("SHA3-256", new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha3_256));
+        digestAlgIdMap.put("SHA3-384", new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha3_384));
+        digestAlgIdMap.put("SHA3-512", new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha3_512));
+        digestAlgIdMap.put("BLAKE3-256", new AlgorithmIdentifier(MiscObjectIdentifiers.blake3_256));
         */
         // END Android-removed: Unsupported algorithms
     }
 
+    // BEGIN Android-removed: Unsupported algorithms
+    /*
+     * Attempt to find a standard JCA name for the digest represented by the passed in OID.
+     *
+     * @param digestName name of the digest algorithm of interest.
+     * @return an algorithm identifier representing the digest.
+    public static AlgorithmIdentifier getDigestAlgID(String digestName)
+    {
+        if (digestAlgIdMap.containsKey(digestName))
+        {
+            return (AlgorithmIdentifier)digestAlgIdMap.get(digestName);
+        }
+        throw new IllegalArgumentException("unknown digest: " + digestName);
+    }
+
+    public static AlgorithmIdentifier getDigestAlgID(String digestName)
+    {
+        throw new IllegalArgumentException("unknown digest: " + digestName);
+    }
+    */
+    // END Android-removed: Unsupported algorithms
+
     /**
      * Attempt to find a standard JCA name for the digest represented by the passed in OID.
      *
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jce/ECNamedCurveTable.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jce/ECNamedCurveTable.java
index aabfac9..9b7b7ba 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jce/ECNamedCurveTable.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jce/ECNamedCurveTable.java
@@ -23,32 +23,35 @@
     public static ECNamedCurveParameterSpec getParameterSpec(
         String  name)
     {
-        X9ECParameters  ecP = com.android.internal.org.bouncycastle.crypto.ec.CustomNamedCurves.getByName(name);
+        ASN1ObjectIdentifier oid;
+        try
+        {
+            oid = possibleOID(name) ? new ASN1ObjectIdentifier(name) : null;
+        }
+        catch (IllegalArgumentException e)
+        {
+            oid = null;
+        }
+
+        X9ECParameters ecP;
+        if (oid != null)
+        {
+            ecP = com.android.internal.org.bouncycastle.crypto.ec.CustomNamedCurves.getByOID(oid);
+        }
+        else
+        {
+            ecP = com.android.internal.org.bouncycastle.crypto.ec.CustomNamedCurves.getByName(name);
+        }
+
         if (ecP == null)
         {
-            try
+            if (oid != null)
             {
-                ecP = com.android.internal.org.bouncycastle.crypto.ec.CustomNamedCurves.getByOID(new ASN1ObjectIdentifier(name));
+                ecP = com.android.internal.org.bouncycastle.asn1.x9.ECNamedCurveTable.getByOID(oid);
             }
-            catch (IllegalArgumentException e)
-            {
-                // ignore - not an oid
-            }
-
-            if (ecP == null)
+            else
             {
                 ecP = com.android.internal.org.bouncycastle.asn1.x9.ECNamedCurveTable.getByName(name);
-                if (ecP == null)
-                {
-                    try
-                    {
-                        ecP = com.android.internal.org.bouncycastle.asn1.x9.ECNamedCurveTable.getByOID(new ASN1ObjectIdentifier(name));
-                    }
-                    catch (IllegalArgumentException e)
-                    {
-                        // ignore - not an oid
-                    }
-                }
             }
         }
 
@@ -75,4 +78,21 @@
     {
         return com.android.internal.org.bouncycastle.asn1.x9.ECNamedCurveTable.getNames();
     }
+
+    private static boolean possibleOID(
+        String identifier)
+    {
+        if (identifier.length() < 3 || identifier.charAt(1) != '.')
+        {
+            return false;
+        }
+
+        char first = identifier.charAt(0);
+        if (first < '0' || first > '2')
+        {
+            return false;
+        }
+
+        return true;
+    }
 }
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jce/netscape/NetscapeCertRequest.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jce/netscape/NetscapeCertRequest.java
index a77431f..0a06f6d 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jce/netscape/NetscapeCertRequest.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jce/netscape/NetscapeCertRequest.java
@@ -18,6 +18,7 @@
 
 import com.android.internal.org.bouncycastle.asn1.ASN1EncodableVector;
 import com.android.internal.org.bouncycastle.asn1.ASN1Encoding;
+import com.android.internal.org.bouncycastle.asn1.ASN1IA5String;
 import com.android.internal.org.bouncycastle.asn1.ASN1InputStream;
 import com.android.internal.org.bouncycastle.asn1.ASN1Object;
 import com.android.internal.org.bouncycastle.asn1.ASN1Primitive;
@@ -105,7 +106,7 @@
                         + pkac.size());
             }
 
-            challenge = ((DERIA5String)pkac.getObjectAt(1)).getString();
+            challenge = ((ASN1IA5String)pkac.getObjectAt(1)).getString();
 
             //this could be dangerous, as ASN.1 decoding/encoding
             //could potentially alter the bytes
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jce/provider/BouncyCastleProvider.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jce/provider/BouncyCastleProvider.java
index 2689da8..7b4d904 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jce/provider/BouncyCastleProvider.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jce/provider/BouncyCastleProvider.java
@@ -13,12 +13,20 @@
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.logging.Level;
+import java.util.logging.Logger;
 
 import com.android.internal.org.bouncycastle.asn1.ASN1ObjectIdentifier;
+// import org.bouncycastle.asn1.bc.BCObjectIdentifiers;
 // import org.bouncycastle.asn1.isara.IsaraObjectIdentifiers;
 // import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
 import com.android.internal.org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
 import com.android.internal.org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import com.android.internal.org.bouncycastle.crypto.CryptoServiceConstraintsException;
+import com.android.internal.org.bouncycastle.crypto.CryptoServiceProperties;
+import com.android.internal.org.bouncycastle.crypto.CryptoServicePurpose;
+import com.android.internal.org.bouncycastle.crypto.CryptoServicesRegistrar;
 import com.android.internal.org.bouncycastle.jcajce.provider.config.ConfigurableProvider;
 import com.android.internal.org.bouncycastle.jcajce.provider.config.ProviderConfiguration;
 import com.android.internal.org.bouncycastle.jcajce.provider.symmetric.util.ClassUtil;
@@ -35,6 +43,16 @@
 // import org.bouncycastle.pqc.jcajce.provider.xmss.XMSSKeyFactorySpi;
 // import org.bouncycastle.pqc.jcajce.provider.xmss.XMSSMTKeyFactorySpi;
 // import org.bouncycastle.pqc.jcajce.provider.lms.LMSKeyFactorySpi;
+// import org.bouncycastle.pqc.jcajce.provider.bike.BIKEKeyFactorySpi;
+// import org.bouncycastle.pqc.jcajce.provider.cmce.CMCEKeyFactorySpi;
+// import org.bouncycastle.pqc.jcajce.provider.dilithium.DilithiumKeyFactorySpi;
+// import org.bouncycastle.pqc.jcajce.provider.falcon.FalconKeyFactorySpi;
+// import org.bouncycastle.pqc.jcajce.provider.hqc.HQCKeyFactorySpi;
+// import org.bouncycastle.pqc.jcajce.provider.kyber.KyberKeyFactorySpi;
+// import org.bouncycastle.pqc.jcajce.provider.ntru.NTRUKeyFactorySpi;
+// import org.bouncycastle.pqc.jcajce.provider.picnic.PicnicKeyFactorySpi;
+// import org.bouncycastle.pqc.jcajce.provider.sphincsplus.SPHINCSPlusKeyFactorySpi;
+import com.android.internal.org.bouncycastle.util.Strings;
 
 /**
  * To add the provider at runtime use:
@@ -64,7 +82,9 @@
 public final class BouncyCastleProvider extends Provider
     implements ConfigurableProvider
 {
-    private static String info = "BouncyCastle Security Provider v1.68";
+    private static final Logger LOG = Logger.getLogger(BouncyCastleProvider.class.getName());
+
+    private static String info = "BouncyCastle Security Provider v1.77";
 
     public static final String PROVIDER_NAME = "BC";
 
@@ -89,17 +109,27 @@
     private static final String[] SYMMETRIC_MACS =
     {
         // Android-removed: Unsupported algorithms
-        // "SipHash", "Poly1305"
+        // "SipHash", "SipHash128", "Poly1305"
     };
 
-    private static final String[] SYMMETRIC_CIPHERS =
+    private static final CryptoServiceProperties[] SYMMETRIC_CIPHERS =
     {
         // Android-changed: Unsupported algorithms
-        // "AES", "ARC4", "ARIA", "Blowfish", "Camellia", "CAST5", "CAST6", "ChaCha", "DES", "DESede",
-        // "GOST28147", "Grainv1", "Grain128", "HC128", "HC256", "IDEA", "Noekeon", "RC2", "RC5",
-        // "RC6", "Rijndael", "Salsa20", "SEED", "Serpent", "Shacal2", "Skipjack", "SM4", "TEA", "Twofish", "Threefish",
-        // "VMPC", "VMPCKSA3", "XTEA", "XSalsa20", "OpenSSLPBKDF", "DSTU7624", "GOST3412_2015"
-        "AES", "ARC4", "Blowfish", "DES", "DESede", "RC2", "Twofish",
+        // TODO: these numbers need a bit more work, we cap at 256 bits.
+        // service("ARIA", 256), service("Camellia", 256), service("CAST5", 128),
+        // service("CAST6", 256), service("ChaCha", 128), service("GOST28147", 128), 
+        // service("Grainv1", 128), service("Grain128", 128), service("HC128", 128), 
+        // service("HC256", 256), service("IDEA", 128), service("Noekeon", 128), 
+        // service("RC6", 256), service("Rijndael", 256), service("Salsa20", 128), 
+        // service("SEED", 128), service("Serpent", 256), service("Shacal2", 128),
+        // service("Skipjack", 80), service("SM4", 128), service("TEA", 128), 
+        // service("RC5", 128), service("Threefish", 128), service("VMPC", 128), 
+        // service("VMPCKSA3", 128), service("XTEA", 128), service("XSalsa20", 128),
+        // service("OpenSSLPBKDF", 128), service("DSTU7624", 256), service("GOST3412_2015", 256),
+        //  service("Zuc", 128)
+        service("AES", 256), service("ARC4", 20), service("Blowfish", 128),
+        service("DES", 56),  service("DESede", 112), service("RC2", 128), 
+        service("Twofish", 256)
     };
 
      /*
@@ -112,14 +142,14 @@
     private static final String[] ASYMMETRIC_GENERIC =
     {
         // Android-changed: Unsupported algorithms
-        // "X509", "IES", "COMPOSITE"
+        // "X509", "IES", "COMPOSITE", "EXTERNAL"
         "X509"
     };
 
     private static final String[] ASYMMETRIC_CIPHERS =
     {
         // Android-changed: Unsupported algorithms
-        // "DSA", "DH", "EC", "RSA", "GOST", "ECGOST", "ElGamal", "DSTU4145", "GM", "EdEC"
+        // "DSA", "DH", "EC", "RSA", "GOST", "ECGOST", "ElGamal", "DSTU4145", "GM", "EdEC", "LMS", "SPHINCSPlus", "Dilithium", "Falcon", "NTRU"
         "DSA", "DH", "EC", "RSA",
     };
 
@@ -155,6 +185,8 @@
     //     "DRBG"
     // };
 
+    private Map<String, Service> serviceMap = new ConcurrentHashMap<String, Service>();
+
     /**
      * Construct a new provider.  This should only be required when
      * using runtime registration of the provider using the
@@ -162,7 +194,7 @@
      */
     public BouncyCastleProvider()
     {
-        super(PROVIDER_NAME, 1.68, info);
+        super(PROVIDER_NAME, 1.77, info);
 
         AccessController.doPrivileged(new PrivilegedAction()
         {
@@ -228,13 +260,9 @@
         put("Cipher.OLDPBEWITHSHAANDTWOFISH-CBC", "org.bouncycastle.jce.provider.BrokenJCEBlockCipher$OldPBEWithSHAAndTwofish");
 
         // Certification Path API
-        put("CertPathValidator.RFC3281", "org.bouncycastle.jce.provider.PKIXAttrCertPathValidatorSpi");
-        put("CertPathBuilder.RFC3281", "org.bouncycastle.jce.provider.PKIXAttrCertPathBuilderSpi");
-        put("CertPathValidator.RFC3280", "org.bouncycastle.jce.provider.PKIXCertPathValidatorSpi");
-        put("CertPathBuilder.RFC3280", "org.bouncycastle.jce.provider.PKIXCertPathBuilderSpi");
-
         if (revChkClass != null)
         {
+            put("CertPathValidator.RFC3281", "org.bouncycastle.jce.provider.PKIXAttrCertPathValidatorSpi");
             put("CertPathBuilder.RFC3281", "org.bouncycastle.jce.provider.PKIXAttrCertPathBuilderSpi");
             put("CertPathValidator.RFC3280", "org.bouncycastle.jce.provider.PKIXCertPathValidatorSpi_8");
             put("CertPathBuilder.RFC3280", "org.bouncycastle.jce.provider.PKIXCertPathBuilderSpi_8");
@@ -261,33 +289,149 @@
         // put("CertStore.Multi", "org.bouncycastle.jce.provider.MultiCertStoreSpi");
         // put("Alg.Alias.CertStore.X509LDAP", "LDAP");
         // END Android-removed: Unsupported algorithms
+
+        getService("SecureRandom", "DEFAULT");  // prime for new SecureRandom() on 1.8 JVMs.
+    }
+
+    public final Service getService(final String type, final String algorithm)
+    {
+        String upperCaseAlgName = Strings.toUpperCase(algorithm);
+        final String key = type + "." + upperCaseAlgName;
+
+        Service service = serviceMap.get(key);
+
+        if (service == null)
+        {
+            synchronized (this)
+            {
+                if (!serviceMap.containsKey(key))
+                {
+                    service = AccessController.doPrivileged(new PrivilegedAction<Service>()
+                    {
+                        @Override
+                        public Service run()
+                        {
+                            Service service = BouncyCastleProvider.super.getService(type, algorithm);
+                            if (service == null)
+                            {
+                                return null;
+                            }
+                            serviceMap.put(key, service);
+                            // remove legacy entry and swap to service entry
+                            BouncyCastleProvider.super.remove(service.getType() + "." + service.getAlgorithm());
+                            BouncyCastleProvider.super.putService(service);
+
+                            return service;
+                        }
+                    });
+                }
+                else
+                {
+                    service = serviceMap.get(key);
+                }
+            }
+        }
+
+        return service;
     }
 
     private void loadAlgorithms(String packageName, String[] names)
     {
         for (int i = 0; i != names.length; i++)
         {
-            Class clazz = ClassUtil.loadClass(BouncyCastleProvider.class, packageName + names[i] + "$Mappings");
+            loadServiceClass(packageName, names[i]);
+        }
+    }
 
-            if (clazz != null)
+    private void loadAlgorithms(String packageName, CryptoServiceProperties[] services)
+    {
+        for (int i = 0; i != services.length; i++)
+        {
+            CryptoServiceProperties service = services[i];
+            try
             {
-                try
+                CryptoServicesRegistrar.checkConstraints(service);
+
+                loadServiceClass(packageName, service.getServiceName());
+            }
+            catch (CryptoServiceConstraintsException e)
+            {
+                if (LOG.isLoggable(Level.FINE))
                 {
-                    ((AlgorithmProvider)clazz.newInstance()).configure(this);
-                }
-                catch (Exception e)
-                {   // this should never ever happen!!
-                    throw new InternalError("cannot create instance of "
-                        + packageName + names[i] + "$Mappings : " + e);
+                    LOG.fine("service for " + service.getServiceName() + " ignored due to constraints");
                 }
             }
         }
     }
 
+    private void loadServiceClass(String packageName, String serviceName)
+    {
+        Class clazz = ClassUtil.loadClass(BouncyCastleProvider.class, packageName + serviceName + "$Mappings");
+
+        if (clazz != null)
+        {
+            try
+            {
+                ((AlgorithmProvider)clazz.newInstance()).configure(this);
+            }
+            catch (Exception e)
+            {   // this should never ever happen!!
+                throw new InternalError("cannot create instance of "
+                    + packageName + serviceName + "$Mappings : " + e);
+            }
+        }
+    }
     // BEGIN Android-removed: Unsupported algorithms
     /*
+
     private void loadPQCKeys()
     {
+        addKeyInfoConverter(BCObjectIdentifiers.sphincsPlus, new SPHINCSPlusKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.sphincsPlus_sha2_128s_r3, new SPHINCSPlusKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.sphincsPlus_sha2_128f_r3, new SPHINCSPlusKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.sphincsPlus_shake_128s_r3, new SPHINCSPlusKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.sphincsPlus_shake_128f_r3, new SPHINCSPlusKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.sphincsPlus_haraka_128s_r3, new SPHINCSPlusKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.sphincsPlus_haraka_128f_r3, new SPHINCSPlusKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.sphincsPlus_sha2_192s_r3, new SPHINCSPlusKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.sphincsPlus_sha2_192f_r3, new SPHINCSPlusKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.sphincsPlus_shake_192s_r3, new SPHINCSPlusKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.sphincsPlus_shake_192f_r3, new SPHINCSPlusKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.sphincsPlus_haraka_192s_r3, new SPHINCSPlusKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.sphincsPlus_haraka_192f_r3, new SPHINCSPlusKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.sphincsPlus_sha2_256s_r3, new SPHINCSPlusKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.sphincsPlus_sha2_256f_r3, new SPHINCSPlusKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.sphincsPlus_shake_256s_r3, new SPHINCSPlusKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.sphincsPlus_shake_256f_r3, new SPHINCSPlusKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.sphincsPlus_haraka_256s_r3, new SPHINCSPlusKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.sphincsPlus_haraka_256f_r3, new SPHINCSPlusKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.sphincsPlus_sha2_128s_r3_simple, new SPHINCSPlusKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.sphincsPlus_sha2_128f_r3_simple, new SPHINCSPlusKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.sphincsPlus_shake_128s_r3_simple, new SPHINCSPlusKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.sphincsPlus_shake_128f_r3_simple, new SPHINCSPlusKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.sphincsPlus_haraka_128s_r3_simple, new SPHINCSPlusKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.sphincsPlus_haraka_128f_r3_simple, new SPHINCSPlusKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.sphincsPlus_sha2_192s_r3_simple, new SPHINCSPlusKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.sphincsPlus_sha2_192f_r3_simple, new SPHINCSPlusKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.sphincsPlus_shake_192s_r3_simple, new SPHINCSPlusKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.sphincsPlus_shake_192f_r3_simple, new SPHINCSPlusKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.sphincsPlus_haraka_192s_r3_simple, new SPHINCSPlusKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.sphincsPlus_haraka_192f_r3_simple, new SPHINCSPlusKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.sphincsPlus_sha2_256s_r3_simple, new SPHINCSPlusKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.sphincsPlus_sha2_256f_r3_simple, new SPHINCSPlusKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.sphincsPlus_shake_256s_r3_simple, new SPHINCSPlusKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.sphincsPlus_shake_256f_r3_simple, new SPHINCSPlusKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.sphincsPlus_haraka_256s_r3_simple, new SPHINCSPlusKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.sphincsPlus_haraka_256f_r3_simple, new SPHINCSPlusKeyFactorySpi());
+
+        addKeyInfoConverter(BCObjectIdentifiers.sphincsPlus_sha2_128s, new SPHINCSPlusKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.sphincsPlus_sha2_192s, new SPHINCSPlusKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.sphincsPlus_sha2_256s, new SPHINCSPlusKeyFactorySpi());
+        addKeyInfoConverter(new ASN1ObjectIdentifier("1.3.9999.6.4.10"), new SPHINCSPlusKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.sphincsPlus_shake_128f, new SPHINCSPlusKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.sphincsPlus_shake_192f, new SPHINCSPlusKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.sphincsPlus_shake_256f, new SPHINCSPlusKeyFactorySpi());
+
         addKeyInfoConverter(PQCObjectIdentifiers.sphincs256, new Sphincs256KeyFactorySpi());
         addKeyInfoConverter(PQCObjectIdentifiers.newHope, new NHKeyFactorySpi());
         addKeyInfoConverter(PQCObjectIdentifiers.xmss, new XMSSKeyFactorySpi());
@@ -300,6 +444,37 @@
         addKeyInfoConverter(PQCObjectIdentifiers.qTESLA_p_I, new QTESLAKeyFactorySpi());
         addKeyInfoConverter(PQCObjectIdentifiers.qTESLA_p_III, new QTESLAKeyFactorySpi());
         addKeyInfoConverter(PKCSObjectIdentifiers.id_alg_hss_lms_hashsig, new LMSKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.picnic_key, new PicnicKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.falcon_512, new FalconKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.falcon_1024, new FalconKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.dilithium2, new DilithiumKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.dilithium3, new DilithiumKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.dilithium5, new DilithiumKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.dilithium2_aes, new DilithiumKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.dilithium3_aes, new DilithiumKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.dilithium5_aes, new DilithiumKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.kyber512, new KyberKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.kyber768, new KyberKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.kyber1024, new KyberKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.mceliece348864_r3, new CMCEKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.mceliece460896_r3, new CMCEKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.mceliece6688128_r3, new CMCEKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.mceliece6960119_r3, new CMCEKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.mceliece8192128_r3, new CMCEKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.bike128, new BIKEKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.bike192, new BIKEKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.bike256, new BIKEKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.hqc128, new HQCKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.hqc192, new HQCKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.hqc256, new HQCKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.kyber1024, new KyberKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.kyber512_aes, new KyberKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.kyber768_aes, new KyberKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.kyber1024_aes, new KyberKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.ntruhps2048509, new NTRUKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.ntruhps2048677, new NTRUKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.ntruhps4096821, new NTRUKeyFactorySpi());
+        addKeyInfoConverter(BCObjectIdentifiers.ntruhrss701, new NTRUKeyFactorySpi());
     }
     */
     // END Android-removed: Unsupported algorithms
@@ -327,12 +502,25 @@
         put(key, value);
     }
 
+    public void addAlgorithm(String key, String value, Map<String, String> attributes)
+    {
+        addAlgorithm(key, value);
+        addAttributes(key, attributes);
+    }
+
     public void addAlgorithm(String type, ASN1ObjectIdentifier oid, String className)
     {
         addAlgorithm(type + "." + oid, className);
         addAlgorithm(type + ".OID." + oid, className);
     }
 
+    public void addAlgorithm(String type, ASN1ObjectIdentifier oid, String className, Map<String, String> attributes)
+    {
+        addAlgorithm(type, oid, className);
+        addAttributes(type + "." + oid, attributes);
+        addAttributes(type + ".OID." + oid, attributes);
+    }
+    
     public void addKeyInfoConverter(ASN1ObjectIdentifier oid, AsymmetricKeyInfoConverter keyInfoConverter)
     {
         synchronized (keyInfoConverters)
@@ -348,6 +536,8 @@
 
     public void addAttributes(String key, Map<String, String> attributeMap)
     {
+        put(key + " ImplementedIn", "Software");
+
         for (Iterator it = attributeMap.keySet().iterator(); it.hasNext();)
         {
             String attributeName = (String)it.next();
@@ -388,6 +578,11 @@
         }
         // Android-removed: see above
         /*
+        if (publicKeyInfo.getAlgorithm().getAlgorithm().on(BCObjectIdentifiers.picnic_key))
+        {
+            return new PicnicKeyFactorySpi().generatePublic(publicKeyInfo);
+        }
+
         AsymmetricKeyInfoConverter converter = getAsymmetricKeyInfoConverter(publicKeyInfo.getAlgorithm().getAlgorithm());
 
         if (converter == null)
@@ -461,4 +656,42 @@
         return privateProvider;
     }
     // END Android-added: Allow algorithms to be provided privately for BC internals.
+    private static CryptoServiceProperties service(String name, int bitsOfSecurity)
+    {
+        return new JcaCryptoService(name, bitsOfSecurity);
+    }
+
+    private static class JcaCryptoService
+        implements CryptoServiceProperties
+    {
+
+        private final String name;
+        private final int bitsOfSecurity;
+
+        JcaCryptoService(String name, int bitsOfSecurity)
+        {
+            this.name = name;
+            this.bitsOfSecurity = bitsOfSecurity;
+        }
+
+        public int bitsOfSecurity()
+        {
+            return bitsOfSecurity;
+        }
+
+        public String getServiceName()
+        {
+            return name;
+        }
+
+        public CryptoServicePurpose getPurpose()
+        {
+            return CryptoServicePurpose.ANY;
+        }
+
+        public Object getParams()
+        {
+            return null;
+        }
+    }
 }
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jce/provider/CertPathValidatorUtilities.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jce/provider/CertPathValidatorUtilities.java
index e6aa307..931487f 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jce/provider/CertPathValidatorUtilities.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jce/provider/CertPathValidatorUtilities.java
@@ -45,7 +45,6 @@
 import com.android.internal.org.bouncycastle.asn1.ASN1Encodable;
 import com.android.internal.org.bouncycastle.asn1.ASN1Enumerated;
 import com.android.internal.org.bouncycastle.asn1.ASN1GeneralizedTime;
-import com.android.internal.org.bouncycastle.asn1.ASN1InputStream;
 import com.android.internal.org.bouncycastle.asn1.ASN1Integer;
 import com.android.internal.org.bouncycastle.asn1.ASN1ObjectIdentifier;
 import com.android.internal.org.bouncycastle.asn1.ASN1OctetString;
@@ -55,7 +54,6 @@
 import com.android.internal.org.bouncycastle.asn1.ASN1String;
 import com.android.internal.org.bouncycastle.asn1.DEROctetString;
 import com.android.internal.org.bouncycastle.asn1.DERSequence;
-import com.android.internal.org.bouncycastle.asn1.isismtt.ISISMTTObjectIdentifiers;
 import com.android.internal.org.bouncycastle.asn1.x500.X500Name;
 import com.android.internal.org.bouncycastle.asn1.x500.style.RFC4519Style;
 import com.android.internal.org.bouncycastle.asn1.x509.AlgorithmIdentifier;
@@ -69,6 +67,7 @@
 import com.android.internal.org.bouncycastle.asn1.x509.GeneralNames;
 import com.android.internal.org.bouncycastle.asn1.x509.PolicyInformation;
 import com.android.internal.org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import com.android.internal.org.bouncycastle.internal.asn1.isismtt.ISISMTTObjectIdentifiers;
 import com.android.internal.org.bouncycastle.jcajce.PKIXCRLStore;
 import com.android.internal.org.bouncycastle.jcajce.PKIXCRLStoreSelector;
 import com.android.internal.org.bouncycastle.jcajce.PKIXCertRevocationCheckerParameters;
@@ -84,7 +83,6 @@
 import com.android.internal.org.bouncycastle.util.Store;
 import com.android.internal.org.bouncycastle.util.StoreException;
 import com.android.internal.org.bouncycastle.x509.X509AttributeCertificate;
-import com.android.internal.org.bouncycastle.x509.extension.X509ExtensionUtil;
 
 class CertPathValidatorUtilities
 {
@@ -777,7 +775,7 @@
 
                     for (int j = 0; j < genNames.length; j++)
                     {
-                        GeneralName name = genNames[i];
+                        GeneralName name = genNames[j];
                         if (name.getTagNo() == GeneralName.uniformResourceIdentifier)
                         {
                             try
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jce/provider/JCEDHPrivateKey.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jce/provider/JCEDHPrivateKey.java
index 18c6e32..04f6f90 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jce/provider/JCEDHPrivateKey.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jce/provider/JCEDHPrivateKey.java
@@ -20,7 +20,7 @@
 import com.android.internal.org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
 import com.android.internal.org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
 import com.android.internal.org.bouncycastle.asn1.x509.AlgorithmIdentifier;
-import com.android.internal.org.bouncycastle.asn1.x9.DHDomainParameters;
+import com.android.internal.org.bouncycastle.asn1.x9.DomainParameters;
 import com.android.internal.org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
 import com.android.internal.org.bouncycastle.crypto.params.DHPrivateKeyParameters;
 import com.android.internal.org.bouncycastle.jcajce.provider.asymmetric.util.PKCS12BagAttributeCarrierImpl;
@@ -85,9 +85,9 @@
         }
         else if (id.equals(X9ObjectIdentifiers.dhpublicnumber))
         {
-            DHDomainParameters params = DHDomainParameters.getInstance(seq);
+            DomainParameters params = DomainParameters.getInstance(seq);
 
-            this.dhSpec = new DHParameterSpec(params.getP().getValue(), params.getG().getValue());
+            this.dhSpec = new DHParameterSpec(params.getP(), params.getG());
         }
         else
         {
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jce/provider/JCEDHPublicKey.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jce/provider/JCEDHPublicKey.java
index f15f091..2859a58 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jce/provider/JCEDHPublicKey.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jce/provider/JCEDHPublicKey.java
@@ -17,7 +17,7 @@
 import com.android.internal.org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
 import com.android.internal.org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 import com.android.internal.org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
-import com.android.internal.org.bouncycastle.asn1.x9.DHDomainParameters;
+import com.android.internal.org.bouncycastle.asn1.x9.DomainParameters;
 import com.android.internal.org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
 import com.android.internal.org.bouncycastle.crypto.params.DHPublicKeyParameters;
 import com.android.internal.org.bouncycastle.jcajce.provider.asymmetric.util.KeyUtil;
@@ -80,8 +80,8 @@
 
         this.y = derY.getValue();
 
-        ASN1Sequence seq = ASN1Sequence.getInstance(info.getAlgorithmId().getParameters());
-        ASN1ObjectIdentifier id = info.getAlgorithmId().getAlgorithm();
+        ASN1Sequence seq = ASN1Sequence.getInstance(info.getAlgorithm().getParameters());
+        ASN1ObjectIdentifier id = info.getAlgorithm().getAlgorithm();
 
         // we need the PKCS check to handle older keys marked with the X9 oid.
         if (id.equals(PKCSObjectIdentifiers.dhKeyAgreement) || isPKCSParam(seq))
@@ -99,9 +99,9 @@
         }
         else if (id.equals(X9ObjectIdentifiers.dhpublicnumber))
         {
-            DHDomainParameters params = DHDomainParameters.getInstance(seq);
+            DomainParameters params = DomainParameters.getInstance(seq);
 
-            this.dhSpec = new DHParameterSpec(params.getP().getValue(), params.getG().getValue());
+            this.dhSpec = new DHParameterSpec(params.getP(), params.getG());
         }
         else
         {
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jce/provider/JCEECPrivateKey.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jce/provider/JCEECPrivateKey.java
index fd3fd5c..abbe4a9 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jce/provider/JCEECPrivateKey.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jce/provider/JCEECPrivateKey.java
@@ -7,24 +7,21 @@
 import java.math.BigInteger;
 import java.security.interfaces.ECPrivateKey;
 import java.security.spec.ECParameterSpec;
-import java.security.spec.ECPoint;
 import java.security.spec.ECPrivateKeySpec;
 import java.security.spec.EllipticCurve;
 import java.util.Enumeration;
 
+import com.android.internal.org.bouncycastle.asn1.ASN1BitString;
 import com.android.internal.org.bouncycastle.asn1.ASN1Encodable;
 import com.android.internal.org.bouncycastle.asn1.ASN1Encoding;
 import com.android.internal.org.bouncycastle.asn1.ASN1Integer;
 import com.android.internal.org.bouncycastle.asn1.ASN1ObjectIdentifier;
 import com.android.internal.org.bouncycastle.asn1.ASN1Primitive;
-import com.android.internal.org.bouncycastle.asn1.ASN1Sequence;
-import com.android.internal.org.bouncycastle.asn1.DERBitString;
 import com.android.internal.org.bouncycastle.asn1.DERNull;
 // Android-removed: Unsupported algorithms
 // import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers;
 // import org.bouncycastle.asn1.cryptopro.ECGOST3410NamedCurves;
 import com.android.internal.org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
-import com.android.internal.org.bouncycastle.asn1.sec.ECPrivateKeyStructure;
 import com.android.internal.org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 import com.android.internal.org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
 import com.android.internal.org.bouncycastle.asn1.x9.X962Parameters;
@@ -53,7 +50,7 @@
     private ECParameterSpec ecSpec;
     private boolean         withCompression;
 
-    private DERBitString publicKey;
+    private ASN1BitString publicKey;
 
     private PKCS12BagAttributeCarrierImpl attrCarrier = new PKCS12BagAttributeCarrierImpl();
 
@@ -218,6 +215,7 @@
             else
             */
             // END Android-removed: Unsupported algorithms
+            if (ecP != null)
             {
                 EllipticCurve ellipticCurve = EC5Util.convertCurve(ecP.getCurve(), ecP.getSeed());
 
@@ -254,7 +252,7 @@
         }
         else
         {
-            ECPrivateKeyStructure ec = new ECPrivateKeyStructure((ASN1Sequence)privKey);
+            com.android.internal.org.bouncycastle.asn1.sec.ECPrivateKey ec = com.android.internal.org.bouncycastle.asn1.sec.ECPrivateKey.getInstance(privKey);
 
             this.d = ec.getKey();
             this.publicKey = ec.getPublicKey();
@@ -314,15 +312,25 @@
         }
         
         PrivateKeyInfo          info;
-        ECPrivateKeyStructure keyStructure;
+        com.android.internal.org.bouncycastle.asn1.sec.ECPrivateKey keyStructure;
 
-        if (publicKey != null)
+        int orderBitLength;
+        if (ecSpec == null)
         {
-            keyStructure = new ECPrivateKeyStructure(this.getS(), publicKey, params);
+            orderBitLength = ECUtil.getOrderBitLength(null, null, this.getS());
         }
         else
         {
-            keyStructure = new ECPrivateKeyStructure(this.getS(), params);
+            orderBitLength = ECUtil.getOrderBitLength(null, ecSpec.getOrder(), this.getS());
+        }
+
+        if (publicKey != null)
+        {
+            keyStructure = new com.android.internal.org.bouncycastle.asn1.sec.ECPrivateKey(orderBitLength, this.getS(), publicKey, params);
+        }
+        else
+        {
+            keyStructure = new com.android.internal.org.bouncycastle.asn1.sec.ECPrivateKey(orderBitLength, this.getS(), params);
         }
 
         try
@@ -434,7 +442,7 @@
 
     }
 
-    private DERBitString getPublicKeyDetails(JCEECPublicKey   pub)
+    private ASN1BitString getPublicKeyDetails(JCEECPublicKey   pub)
     {
         try
         {
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jce/provider/JCEECPublicKey.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jce/provider/JCEECPublicKey.java
index 46cd235..6ef9017 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jce/provider/JCEECPublicKey.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jce/provider/JCEECPublicKey.java
@@ -11,11 +11,11 @@
 import java.security.spec.ECPublicKeySpec;
 import java.security.spec.EllipticCurve;
 
+import com.android.internal.org.bouncycastle.asn1.ASN1BitString;
 import com.android.internal.org.bouncycastle.asn1.ASN1Encodable;
 import com.android.internal.org.bouncycastle.asn1.ASN1ObjectIdentifier;
 import com.android.internal.org.bouncycastle.asn1.ASN1OctetString;
 import com.android.internal.org.bouncycastle.asn1.ASN1Primitive;
-import com.android.internal.org.bouncycastle.asn1.DERBitString;
 import com.android.internal.org.bouncycastle.asn1.DERNull;
 import com.android.internal.org.bouncycastle.asn1.DEROctetString;
 // Android-removed: Unsupported algorithms
@@ -192,7 +192,7 @@
 
         if (algID.getAlgorithm().equals(CryptoProObjectIdentifiers.gostR3410_2001))
         {
-            DERBitString bits = info.getPublicKeyData();
+            ASN1BitString bits = info.getPublicKeyData();
             ASN1OctetString key;
             this.algorithm = "ECGOST3410";
 
@@ -273,7 +273,7 @@
                     ecP.getH().intValue());
             }
 
-            DERBitString    bits = info.getPublicKeyData();
+            ASN1BitString   bits = info.getPublicKeyData();
             byte[]          data = bits.getBytes();
             ASN1OctetString key = new DEROctetString(data);
 
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jce/provider/PKIXCRLUtil.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jce/provider/PKIXCRLUtil.java
index c5fec2b..15bd316 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jce/provider/PKIXCRLUtil.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jce/provider/PKIXCRLUtil.java
@@ -41,8 +41,9 @@
         for (Iterator it = initialSet.iterator(); it.hasNext();)
         {
             X509CRL crl = (X509CRL)it.next();
-
-            if (crl.getNextUpdate().after(validityDate))
+            
+            Date nextUpdate = crl.getNextUpdate();
+            if (nextUpdate == null || nextUpdate.after(validityDate))
             {
                 X509Certificate cert = crlselect.getCertificateChecking();
 
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jce/provider/RFC3280CertPathUtilities.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jce/provider/RFC3280CertPathUtilities.java
index 64da571..69bf752 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jce/provider/RFC3280CertPathUtilities.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jce/provider/RFC3280CertPathUtilities.java
@@ -2,7 +2,6 @@
 package com.android.internal.org.bouncycastle.jce.provider;
 
 import java.io.IOException;
-import java.math.BigInteger;
 import java.security.GeneralSecurityException;
 import java.security.PublicKey;
 import java.security.cert.CertPath;
@@ -2099,18 +2098,12 @@
             throw new ExtCertPathValidatorException("Basic constraints extension cannot be decoded.", e, certPath,
                 index);
         }
-        if (bc != null)
+        if (bc != null && bc.isCA())  // if there is a path len constraint and we're not a CA, ignore it! (yes, it happens).
         {
-            BigInteger _pathLengthConstraint = bc.getPathLenConstraint();
-
-            if (_pathLengthConstraint != null)
+            ASN1Integer pathLenConstraint = bc.getPathLenConstraintInteger();
+            if (pathLenConstraint != null)
             {
-                int _plc = _pathLengthConstraint.intValue();
-
-                if (_plc < maxPathLength)
-                {
-                    return _plc;
-                }
+                maxPathLength = Math.min(maxPathLength, pathLenConstraint.intPositiveValueExact());
             }
         }
         return maxPathLength;
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jce/provider/X509CRLEntryObject.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jce/provider/X509CRLEntryObject.java
index 0efb820..23c975d 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jce/provider/X509CRLEntryObject.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jce/provider/X509CRLEntryObject.java
@@ -24,7 +24,6 @@
 import com.android.internal.org.bouncycastle.asn1.x509.GeneralName;
 import com.android.internal.org.bouncycastle.asn1.x509.GeneralNames;
 import com.android.internal.org.bouncycastle.asn1.x509.TBSCertList;
-import com.android.internal.org.bouncycastle.asn1.x509.X509Extension;
 import com.android.internal.org.bouncycastle.util.Strings;
 
 /**
@@ -288,11 +287,11 @@
                         buf.append("                       critical(").append(ext.isCritical()).append(") ");
                         try
                         {
-                            if (oid.equals(X509Extension.reasonCode))
+                            if (oid.equals(Extension.reasonCode))
                             {
                                 buf.append(CRLReason.getInstance(ASN1Enumerated.getInstance(dIn.readObject()))).append(nl);
                             }
-                            else if (oid.equals(X509Extension.certificateIssuer))
+                            else if (oid.equals(Extension.certificateIssuer))
                             {
                                 buf.append("Certificate issuer: ").append(GeneralNames.getInstance(dIn.readObject())).append(nl);
                             }
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jce/provider/X509CertificateObject.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jce/provider/X509CertificateObject.java
index 10c90b7..d5a7949 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jce/provider/X509CertificateObject.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jce/provider/X509CertificateObject.java
@@ -35,13 +35,13 @@
 import com.android.internal.org.bouncycastle.asn1.ASN1BitString;
 import com.android.internal.org.bouncycastle.asn1.ASN1Encodable;
 import com.android.internal.org.bouncycastle.asn1.ASN1Encoding;
+import com.android.internal.org.bouncycastle.asn1.ASN1IA5String;
 import com.android.internal.org.bouncycastle.asn1.ASN1InputStream;
+import com.android.internal.org.bouncycastle.asn1.ASN1Integer;
 import com.android.internal.org.bouncycastle.asn1.ASN1ObjectIdentifier;
 import com.android.internal.org.bouncycastle.asn1.ASN1Primitive;
 import com.android.internal.org.bouncycastle.asn1.ASN1Sequence;
 import com.android.internal.org.bouncycastle.asn1.ASN1String;
-import com.android.internal.org.bouncycastle.asn1.DERBitString;
-import com.android.internal.org.bouncycastle.asn1.DERIA5String;
 import com.android.internal.org.bouncycastle.asn1.DERNull;
 import com.android.internal.org.bouncycastle.asn1.DEROctetString;
 import com.android.internal.org.bouncycastle.asn1.misc.MiscObjectIdentifiers;
@@ -109,7 +109,7 @@
             byte[] bytes = this.getExtensionBytes("2.5.29.15");
             if (bytes != null)
             {
-                ASN1BitString bits = DERBitString.getInstance(ASN1Primitive.fromByteArray(bytes));
+                ASN1BitString bits = ASN1BitString.getInstance(ASN1Primitive.fromByteArray(bytes));
 
                 bytes = bits.getBytes();
                 int length = (bytes.length * 8) - bits.getPadBits();
@@ -292,7 +292,7 @@
 
     public boolean[] getIssuerUniqueID()
     {
-        DERBitString    id = c.getTBSCertificate().getIssuerUniqueId();
+        ASN1BitString    id = c.getTBSCertificate().getIssuerUniqueId();
 
         if (id != null)
         {
@@ -312,7 +312,7 @@
 
     public boolean[] getSubjectUniqueID()
     {
-        DERBitString    id = c.getTBSCertificate().getSubjectUniqueId();
+        ASN1BitString    id = c.getTBSCertificate().getSubjectUniqueId();
 
         if (id != null)
         {
@@ -366,26 +366,18 @@
     
     public int getBasicConstraints()
     {
-        if (basicConstraints != null)
+        if (basicConstraints == null || !basicConstraints.isCA())
         {
-            if (basicConstraints.isCA())
-            {
-                if (basicConstraints.getPathLenConstraint() == null)
-                {
-                    return Integer.MAX_VALUE;
-                }
-                else
-                {
-                    return basicConstraints.getPathLenConstraint().intValue();
-                }
-            }
-            else
-            {
-                return -1;
-            }
+            return -1;
         }
 
-        return -1;
+        ASN1Integer pathLenConstraint = basicConstraints.getPathLenConstraintInteger();
+        if (pathLenConstraint == null)
+        {
+            return Integer.MAX_VALUE;
+        }
+
+        return pathLenConstraint.intPositiveValueExact();
     }
 
     public Collection getSubjectAlternativeNames()
@@ -711,15 +703,15 @@
                         }
                         else if (oid.equals(MiscObjectIdentifiers.netscapeCertType))
                         {
-                            buf.append(new NetscapeCertType((DERBitString)dIn.readObject())).append(nl);
+                            buf.append(new NetscapeCertType((ASN1BitString)dIn.readObject())).append(nl);
                         }
                         else if (oid.equals(MiscObjectIdentifiers.netscapeRevocationURL))
                         {
-                            buf.append(new NetscapeRevocationURL((DERIA5String)dIn.readObject())).append(nl);
+                            buf.append(new NetscapeRevocationURL((ASN1IA5String)dIn.readObject())).append(nl);
                         }
                         else if (oid.equals(MiscObjectIdentifiers.verisignCzagExtension))
                         {
-                            buf.append(new VerisignCzagExtension((DERIA5String)dIn.readObject())).append(nl);
+                            buf.append(new VerisignCzagExtension((ASN1IA5String)dIn.readObject())).append(nl);
                         }
                         else 
                         {
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jce/spec/ECNamedCurveSpec.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jce/spec/ECNamedCurveSpec.java
index 401ef67..432f5e6 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jce/spec/ECNamedCurveSpec.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jce/spec/ECNamedCurveSpec.java
@@ -45,7 +45,7 @@
         {
             Polynomial poly = ((PolynomialExtensionField)field).getMinimalPolynomial();
             int[] exponents = poly.getExponentsPresent();
-            int[] ks = Arrays.reverse(Arrays.copyOfRange(exponents, 1, exponents.length - 1));
+            int[] ks = Arrays.reverseInPlace(Arrays.copyOfRange(exponents, 1, exponents.length - 1));
             return new ECFieldF2m(poly.getDegree(), ks);
         }
     }
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/math/ec/ECCurve.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/math/ec/ECCurve.java
index d936d10..8a702bb 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/math/ec/ECCurve.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/math/ec/ECCurve.java
@@ -3,9 +3,14 @@
 
 import java.math.BigInteger;
 import java.security.SecureRandom;
+import java.util.Collections;
+import java.util.HashSet;
 import java.util.Hashtable;
 import java.util.Random;
+import java.util.Set;
 
+import com.android.internal.org.bouncycastle.crypto.CryptoServicesRegistrar;
+import com.android.internal.org.bouncycastle.math.Primes;
 import com.android.internal.org.bouncycastle.math.ec.endo.ECEndomorphism;
 import com.android.internal.org.bouncycastle.math.ec.endo.GLVEndomorphism;
 import com.android.internal.org.bouncycastle.math.field.FiniteField;
@@ -13,6 +18,7 @@
 import com.android.internal.org.bouncycastle.math.raw.Nat;
 import com.android.internal.org.bouncycastle.util.BigIntegers;
 import com.android.internal.org.bouncycastle.util.Integers;
+import com.android.internal.org.bouncycastle.util.Properties;
 
 /**
  * base class for an elliptic curve
@@ -677,6 +683,8 @@
     public static class Fp extends AbstractFp
     {
         private static final int FP_DEFAULT_COORDS = ECCurve.COORD_JACOBIAN_MODIFIED;
+        private static final Set<BigInteger> knownQs = Collections.synchronizedSet(new HashSet<BigInteger>());
+        private static final BigIntegers.Cache validatedQs = new BigIntegers.Cache();
 
         BigInteger q, r;
         ECPoint.Fp infinity;
@@ -691,9 +699,44 @@
 
         public Fp(BigInteger q, BigInteger a, BigInteger b, BigInteger order, BigInteger cofactor)
         {
+            this(q, a, b, order, cofactor, false);
+        }
+
+        public Fp(BigInteger q, BigInteger a, BigInteger b, BigInteger order, BigInteger cofactor, boolean isInternal)
+        {
             super(q);
 
-            this.q = q;
+            if (isInternal)
+            {
+                this.q = q;
+                knownQs.add(q);
+            }
+            else if (knownQs.contains(q) || validatedQs.contains(q))
+            {
+                this.q = q;
+            }
+            else
+            {
+                int maxBitLength = Properties.asInteger("com.android.internal.org.bouncycastle.ec.fp_max_size", 1042); // 2 * 521
+                int certainty = Properties.asInteger("com.android.internal.org.bouncycastle.ec.fp_certainty", 100);
+
+                int qBitLength = q.bitLength();
+                if (maxBitLength < qBitLength)
+                {
+                    throw new IllegalArgumentException("Fp q value out of range");
+                }
+
+                if (Primes.hasAnySmallFactors(q) || !Primes.isMRProbablePrime(
+                    q, CryptoServicesRegistrar.getSecureRandom(), getNumberOfIterations(qBitLength, certainty)))
+                {
+                    throw new IllegalArgumentException("Fp q value not prime");
+                }
+
+                validatedQs.add(q);
+
+                this.q = q;
+            }
+
             this.r = ECFieldElement.Fp.calculateResidue(q);
             this.infinity = new ECPoint.Fp(this, null, null);
 
@@ -750,6 +793,11 @@
 
         public ECFieldElement fromBigInteger(BigInteger x)
         {
+            if (x == null || x.signum() < 0 || x.compareTo(q) >= 0)
+            {
+                throw new IllegalArgumentException("x value invalid for Fp field element");
+            }
+
             return new ECFieldElement.Fp(this.q, this.r, x);
         }
 
@@ -809,32 +857,11 @@
 
         private static FiniteField buildField(int m, int k1, int k2, int k3)
         {
-            if (k1 == 0)
-            {
-                throw new IllegalArgumentException("k1 must be > 0");
-            }
+            int[] exponents = (k2 | k3) == 0
+                ? new int[]{ 0, k1, m }
+                : new int[]{ 0, k1, k2, k3, m };
 
-            if (k2 == 0)
-            {
-                if (k3 != 0)
-                {
-                    throw new IllegalArgumentException("k3 must be 0 if k2 == 0");
-                }
-
-                return FiniteFields.getBinaryExtensionField(new int[]{ 0, k1, m });
-            }
-
-            if (k2 <= k1)
-            {
-                throw new IllegalArgumentException("k2 must be > k1");
-            }
-
-            if (k3 <= k2)
-            {
-                throw new IllegalArgumentException("k3 must be > k2");
-            }
-
-            return FiniteFields.getBinaryExtensionField(new int[]{ 0, k1, k2, k3, m });
+            return FiniteFields.getBinaryExtensionField(exponents);
         }
 
         protected AbstractF2m(int m, int k1, int k2, int k3)
@@ -927,7 +954,7 @@
                 y = this.getB().sqrt();
             }
             else
-            {
+            { 
                 ECFieldElement beta = x.square().invert().multiply(this.getB()).add(this.getA()).add(x);
                 ECFieldElement z = solveQuadraticEquation(beta);
                 if (z != null)
@@ -1288,7 +1315,16 @@
 
         public ECFieldElement fromBigInteger(BigInteger x)
         {
-            return new ECFieldElement.F2m(this.m, this.k1, this.k2, this.k3, x);
+            if (x == null || x.signum() < 0 || x.bitLength() > m)
+            {
+                throw new IllegalArgumentException("x value invalid in F2m field element");
+            }
+
+            int[] ks = (k2 | k3) == 0
+                ? new int[]{ k1 }
+                : new int[]{ k1, k2, k3 };
+
+            return new ECFieldElement.F2m(m, ks, new LongArray(x));
         }
 
         protected ECPoint createRawPoint(ECFieldElement x, ECFieldElement y)
@@ -1403,4 +1439,36 @@
             };
         }
     }
+
+    private static int getNumberOfIterations(int bits, int certainty)
+    {
+        /*
+         * NOTE: We enforce a minimum 'certainty' of 100 for bits >= 1024 (else 80). Where the
+         * certainty is higher than the FIPS 186-4 tables (C.2/C.3) cater to, extra iterations
+         * are added at the "worst case rate" for the excess.
+         */
+        if (bits >= 1536)
+        {
+            return  certainty <= 100 ? 3
+                :   certainty <= 128 ? 4
+                :   4 + (certainty - 128 + 1) / 2;
+        }
+        else if (bits >= 1024)
+        {
+            return  certainty <= 100 ? 4
+                :   certainty <= 112 ? 5
+                :   5 + (certainty - 112 + 1) / 2;
+        }
+        else if (bits >= 512)
+        {
+            return  certainty <= 80  ? 5
+                :   certainty <= 100 ? 7
+                :   7 + (certainty - 100 + 1) / 2;
+        }
+        else
+        {
+            return  certainty <= 80  ? 40
+                :   40 + (certainty - 80 + 1) / 2;
+        }
+    }
 }
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/math/ec/ECFieldElement.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/math/ec/ECFieldElement.java
index 7e612ec..50080f8 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/math/ec/ECFieldElement.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/math/ec/ECFieldElement.java
@@ -120,21 +120,8 @@
             return null;
         }
 
-        /**
-         * @deprecated Use ECCurve.fromBigInteger to construct field elements
-         */
-        public Fp(BigInteger q, BigInteger x)
-        {
-            this(q, calculateResidue(q), x);
-        }
-
         Fp(BigInteger q, BigInteger r, BigInteger x)
         {
-            if (x == null || x.signum() < 0 || x.compareTo(q) >= 0)
-            {
-                throw new IllegalArgumentException("x value invalid in Fp field element");
-            }
-
             this.q = q;
             this.r = r;
             this.x = x;
@@ -626,59 +613,6 @@
          */
         LongArray x;
 
-        /**
-         * Constructor for PPB.
-         * @param m  The exponent <code>m</code> of
-         * <code>F<sub>2<sup>m</sup></sub></code>.
-         * @param k1 The integer <code>k1</code> where <code>x<sup>m</sup> +
-         * x<sup>k3</sup> + x<sup>k2</sup> + x<sup>k1</sup> + 1</code>
-         * represents the reduction polynomial <code>f(z)</code>.
-         * @param k2 The integer <code>k2</code> where <code>x<sup>m</sup> +
-         * x<sup>k3</sup> + x<sup>k2</sup> + x<sup>k1</sup> + 1</code>
-         * represents the reduction polynomial <code>f(z)</code>.
-         * @param k3 The integer <code>k3</code> where <code>x<sup>m</sup> +
-         * x<sup>k3</sup> + x<sup>k2</sup> + x<sup>k1</sup> + 1</code>
-         * represents the reduction polynomial <code>f(z)</code>.
-         * @param x The BigInteger representing the value of the field element.
-         * @deprecated Use ECCurve.fromBigInteger to construct field elements
-         */
-        public F2m(
-            int m, 
-            int k1, 
-            int k2, 
-            int k3,
-            BigInteger x)
-        {
-            if (x == null || x.signum() < 0 || x.bitLength() > m)
-            {
-                throw new IllegalArgumentException("x value invalid in F2m field element");
-            }
-
-            if ((k2 == 0) && (k3 == 0))
-            {
-                this.representation = TPB;
-                this.ks = new int[]{ k1 }; 
-            }
-            else
-            {
-                if (k2 >= k3)
-                {
-                    throw new IllegalArgumentException(
-                            "k2 must be smaller than k3");
-                }
-                if (k2 <= 0)
-                {
-                    throw new IllegalArgumentException(
-                            "k2 must be larger than 0");
-                }
-                this.representation = PPB;
-                this.ks = new int[]{ k1, k2, k3 }; 
-            }
-
-            this.m = m;
-            this.x = new LongArray(x);
-        }
-
         F2m(int m, int[] ks, LongArray x)
         {
             this.m = m;
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/math/ec/Tnaf.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/math/ec/Tnaf.java
index f326608..95a342b 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/math/ec/Tnaf.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/math/ec/Tnaf.java
@@ -3,6 +3,8 @@
 
 import java.math.BigInteger;
 
+import com.android.internal.org.bouncycastle.util.BigIntegers;
+
 /**
  * Class holding methods for point multiplication based on the window
  * &tau;-adic nonadjacent form (WTNAF). The algorithms are based on the
@@ -29,20 +31,19 @@
     public static final byte WIDTH = 4;
 
     /**
-     * 2<sup>4</sup>
-     */
-    public static final byte POW_2_WIDTH = 16;
-
-    /**
      * The <code>&alpha;<sub>u</sub></code>'s for <code>a=0</code> as an array
      * of <code>ZTauElement</code>s.
      */
-    public static final ZTauElement[] alpha0 = {
-        null,
-        new ZTauElement(ECConstants.ONE, ECConstants.ZERO), null,
-        new ZTauElement(MINUS_THREE, MINUS_ONE), null,
-        new ZTauElement(MINUS_ONE, MINUS_ONE), null,
-        new ZTauElement(ECConstants.ONE, MINUS_ONE), null
+    public static final ZTauElement[] alpha0 =
+    {
+        null, new ZTauElement(ECConstants.ONE, ECConstants.ZERO),
+        null, new ZTauElement(MINUS_THREE, MINUS_ONE),
+        null, new ZTauElement(MINUS_ONE, MINUS_ONE),
+        null, new ZTauElement(ECConstants.ONE, MINUS_ONE),
+        null, new ZTauElement(MINUS_ONE, ECConstants.ONE),
+        null, new ZTauElement(ECConstants.ONE, ECConstants.ONE),
+        null, new ZTauElement(ECConstants.THREE, ECConstants.ONE),
+        null, new ZTauElement(MINUS_ONE, ECConstants.ZERO),
     };
 
     /**
@@ -57,11 +58,16 @@
      * The <code>&alpha;<sub>u</sub></code>'s for <code>a=1</code> as an array
      * of <code>ZTauElement</code>s.
      */
-    public static final ZTauElement[] alpha1 = {null,
-        new ZTauElement(ECConstants.ONE, ECConstants.ZERO), null,
-        new ZTauElement(MINUS_THREE, ECConstants.ONE), null,
-        new ZTauElement(MINUS_ONE, ECConstants.ONE), null,
-        new ZTauElement(ECConstants.ONE, ECConstants.ONE), null
+    public static final ZTauElement[] alpha1 =
+    {
+        null, new ZTauElement(ECConstants.ONE, ECConstants.ZERO),
+        null, new ZTauElement(MINUS_THREE, ECConstants.ONE),
+        null, new ZTauElement(MINUS_ONE, ECConstants.ONE),
+        null, new ZTauElement(ECConstants.ONE, ECConstants.ONE),
+        null, new ZTauElement(MINUS_ONE, MINUS_ONE),
+        null, new ZTauElement(ECConstants.ONE, MINUS_ONE),
+        null, new ZTauElement(ECConstants.THREE, MINUS_ONE),
+        null, new ZTauElement(MINUS_ONE, ECConstants.ZERO),
     };
 
     /**
@@ -82,31 +88,29 @@
      */
     public static BigInteger norm(final byte mu, ZTauElement lambda)
     {
-        BigInteger norm;
-
         // s1 = u^2
         BigInteger s1 = lambda.u.multiply(lambda.u);
 
         // s2 = u * v
-        BigInteger s2 = lambda.u.multiply(lambda.v);
+//        BigInteger s2 = lambda.u.multiply(lambda.v);
 
         // s3 = 2 * v^2
-        BigInteger s3 = lambda.v.multiply(lambda.v).shiftLeft(1);
+//        BigInteger s3 = lambda.v.multiply(lambda.v).shiftLeft(1);
 
         if (mu == 1)
         {
-            norm = s1.add(s2).add(s3);
+//            return s1.add(s2).add(s3);
+            return lambda.v.shiftLeft(1).add(lambda.u).multiply(lambda.v).add(s1);
         }
         else if (mu == -1)
         {
-            norm = s1.subtract(s2).add(s3);
+//            return s1.subtract(s2).add(s3);
+            return lambda.v.shiftLeft(1).subtract(lambda.u).multiply(lambda.v).add(s1);
         }
         else
         {
             throw new IllegalArgumentException("mu must be 1 or -1");
         }
-
-        return norm;
     }
 
     /**
@@ -452,10 +456,7 @@
             throw new IllegalArgumentException("mu must be 1 or -1");
         }
 
-        BigInteger u0;
-        BigInteger u1;
-        BigInteger u2;
-
+        BigInteger u0, u1, u2;
         if (doV)
         {
             u0 = ECConstants.TWO;
@@ -470,26 +471,18 @@
         for (int i = 1; i < k; i++)
         {
             // u2 = mu*u1 - 2*u0;
-            BigInteger s = null;
-            if (mu == 1)
+            BigInteger s = u1;
+            if (mu < 0)
             {
-                s = u1;
+                s = s.negate();
             }
-            else
-            {
-                // mu == -1
-                s = u1.negate();
-            }
-            
+
             u2 = s.subtract(u0.shiftLeft(1));
             u0 = u1;
             u1 = u2;
-//            System.out.println(i + ": " + u2);
-//            System.out.println();
         }
 
-        BigInteger[] retVal = {u0, u1};
-        return retVal;
+        return new BigInteger[]{ u0, u1 };
     }
 
     /**
@@ -520,11 +513,7 @@
             BigInteger[] us = getLucas(mu, w, false);
             BigInteger twoToW = ECConstants.ZERO.setBit(w);
             BigInteger u1invert = us[1].modInverse(twoToW);
-            BigInteger tw;
-            tw = ECConstants.TWO.multiply(us[0]).multiply(u1invert).mod(twoToW);
-//            System.out.println("mu = " + mu);
-//            System.out.println("tw = " + tw);
-            return tw;
+            return us[0].shiftLeft(1).multiply(u1invert).mod(twoToW);
         }
     }
 
@@ -543,22 +532,7 @@
             throw new IllegalArgumentException("si is defined for Koblitz curves only");
         }
 
-        int m = curve.getFieldSize();
-        int a = curve.getA().toBigInteger().intValue();
-        byte mu = getMu(a);
-        int shifts = getShiftsForCofactor(curve.getCofactor());
-        int index = m + 3 - a;
-        BigInteger[] ui = getLucas(mu, index, false);
-        if (mu == 1)
-        {
-            ui[0] = ui[0].negate();
-            ui[1] = ui[1].negate();
-        }
-
-        BigInteger dividend0 = ECConstants.ONE.add(ui[1]).shiftRight(shifts);
-        BigInteger dividend1 = ECConstants.ONE.add(ui[0]).shiftRight(shifts).negate();
-
-        return new BigInteger[] { dividend0, dividend1 };
+        return getSi(curve.getFieldSize(), curve.getA().toBigInteger().intValue(), curve.getCofactor());
     }
 
     public static BigInteger[] getSi(int fieldSize, int curveA, BigInteger cofactor)
@@ -609,9 +583,11 @@
      * modular reduction.
      * @return <code>&rho; := k partmod (&tau;<sup>m</sup> - 1)/(&tau; - 1)</code>
      */
-    public static ZTauElement partModReduction(BigInteger k, int m, byte a,
-            BigInteger[] s, byte mu, byte c)
+    public static ZTauElement partModReduction(ECCurve.AbstractF2m curve, BigInteger k, byte a, byte mu, byte c)
     {
+        int m = curve.getFieldSize();
+        BigInteger[] s = curve.getSi();
+
         // d0 = s[0] + mu*s[1]; mu is either 1 or -1
         BigInteger d0;
         if (mu == 1)
@@ -623,20 +599,29 @@
             d0 = s[0].subtract(s[1]);
         }
 
-        BigInteger[] v = getLucas(mu, m, true);
-        BigInteger vm = v[1];
+        BigInteger vm;
+        if (curve.isKoblitz())
+        {
+            /*
+             * Jerome A. Solinas, "Improved Algorithms for Arithmetic on Anomalous Binary Curves", (21).
+             */
+            vm = ECConstants.ONE.shiftLeft(m).add(ECConstants.ONE).subtract(
+                curve.getOrder().multiply(curve.getCofactor()));
+        }
+        else
+        {
+            BigInteger[] v = getLucas(mu, m, true);
+            vm = v[1];
+        }
 
-        SimpleBigDecimal lambda0 = approximateDivisionByN(
-                k, s[0], vm, a, m, c);
-        
-        SimpleBigDecimal lambda1 = approximateDivisionByN(
-                k, s[1], vm, a, m, c);
+        SimpleBigDecimal lambda0 = approximateDivisionByN(k, s[0], vm, a, m, c);
+        SimpleBigDecimal lambda1 = approximateDivisionByN(k, s[1], vm, a, m, c);
 
         ZTauElement q = round(lambda0, lambda1, mu);
 
         // r0 = n - d0*q0 - 2*s1*q1
         BigInteger r0 = k.subtract(d0.multiply(q.u)).subtract(
-                BigInteger.valueOf(2).multiply(s[1]).multiply(q.v));
+            s[1].multiply(q.v).shiftLeft(1));
 
         // r1 = s1*q0 - s0*q1
         BigInteger r1 = s[1].multiply(q.u).subtract(s[0].multiply(q.v));
@@ -655,11 +640,10 @@
     public static ECPoint.AbstractF2m multiplyRTnaf(ECPoint.AbstractF2m p, BigInteger k)
     {
         ECCurve.AbstractF2m curve = (ECCurve.AbstractF2m) p.getCurve();
-        int m = curve.getFieldSize();
         int a = curve.getA().toBigInteger().intValue();
         byte mu = getMu(a);
-        BigInteger[] s = curve.getSi();
-        ZTauElement rho = partModReduction(k, m, (byte)a, s, mu, (byte)10);
+
+        ZTauElement rho = partModReduction(curve, k, (byte)a, mu, (byte)10);
 
         return multiplyTnaf(p, rho);
     }
@@ -676,12 +660,11 @@
     public static ECPoint.AbstractF2m multiplyTnaf(ECPoint.AbstractF2m p, ZTauElement lambda)
     {
         ECCurve.AbstractF2m curve = (ECCurve.AbstractF2m)p.getCurve();
+        ECPoint.AbstractF2m pNeg = (ECPoint.AbstractF2m)p.negate();
         byte mu = getMu(curve.getA());
         byte[] u = tauAdicNaf(mu, lambda);
 
-        ECPoint.AbstractF2m q = multiplyFromTnaf(p, u);
-
-        return q;
+        return multiplyFromTnaf(p, pNeg, u);
     }
 
     /**
@@ -693,11 +676,10 @@
     * @param u The the TNAF of <code>&lambda;</code>..
     * @return <code>&lambda; * p</code>
     */
-    public static ECPoint.AbstractF2m multiplyFromTnaf(ECPoint.AbstractF2m p, byte[] u)
+    public static ECPoint.AbstractF2m multiplyFromTnaf(ECPoint.AbstractF2m p, ECPoint.AbstractF2m pNeg, byte[] u)
     {
         ECCurve curve = p.getCurve();
         ECPoint.AbstractF2m q = (ECPoint.AbstractF2m)curve.getInfinity();
-        ECPoint.AbstractF2m pNeg = (ECPoint.AbstractF2m)p.negate();
         int tauCount = 0;
         for (int i = u.length - 1; i >= 0; i--)
         {
@@ -733,10 +715,9 @@
      * @return The <code>[&tau;]</code>-adic window NAF of
      * <code>&lambda;</code>.
      */
-    public static byte[] tauAdicWNaf(byte mu, ZTauElement lambda,
-            byte width, BigInteger pow2w, BigInteger tw, ZTauElement[] alpha)
+    public static byte[] tauAdicWNaf(byte mu, ZTauElement lambda, int width, int tw, ZTauElement[] alpha)
     {
-        if (!((mu == 1) || (mu == -1)))
+        if (!(mu == 1 || mu == -1))
         {
             throw new IllegalArgumentException("mu must be 1 or -1");
         }
@@ -752,75 +733,72 @@
         // The array holding the TNAF
         byte[] u = new byte[maxLength];
 
-        // 2^(width - 1)
-        BigInteger pow2wMin1 = pow2w.shiftRight(1);
+        int pow2Width = 1 << width;
+        int pow2Mask = pow2Width - 1;
+        int s = 32 - width;
 
         // Split lambda into two BigIntegers to simplify calculations
-        BigInteger r0 = lambda.u;
-        BigInteger r1 = lambda.v;
-        int i = 0;
+        BigInteger R0 = lambda.u;
+        BigInteger R1 = lambda.v;
+        int uPos = 0;
 
         // while lambda <> (0, 0)
-        while (!((r0.equals(ECConstants.ZERO))&&(r1.equals(ECConstants.ZERO))))
+        while (R0.bitLength() > 62 || R1.bitLength() > 62)
         {
-            // if r0 is odd
-            if (r0.testBit(0))
+            if (R0.testBit(0))
             {
-                // uUnMod = r0 + r1*tw mod 2^width
-                BigInteger uUnMod
-                    = r0.add(r1.multiply(tw)).mod(pow2w);
-                
-                byte uLocal;
-                // if uUnMod >= 2^(width - 1)
-                if (uUnMod.compareTo(pow2wMin1) >= 0)
-                {
-                    uLocal = (byte) uUnMod.subtract(pow2w).intValue();
-                }
-                else
-                {
-                    uLocal = (byte) uUnMod.intValue();
-                }
-                // uLocal is now in [-2^(width-1), 2^(width-1)-1]
+                int uVal = R0.intValue() + (R1.intValue() * tw);
+                int alphaPos = uVal & pow2Mask;
 
-                u[i] = uLocal;
-                boolean s = true;
-                if (uLocal < 0)
-                {
-                    s = false;
-                    uLocal = (byte)-uLocal;
-                }
-                // uLocal is now >= 0
-
-                if (s)
-                {
-                    r0 = r0.subtract(alpha[uLocal].u);
-                    r1 = r1.subtract(alpha[uLocal].v);
-                }
-                else
-                {
-                    r0 = r0.add(alpha[uLocal].u);
-                    r1 = r1.add(alpha[uLocal].v);
-                }
-            }
-            else
-            {
-                u[i] = 0;
+                u[uPos] = (byte)((uVal << s) >> s);
+                R0 = R0.subtract(alpha[alphaPos].u);
+                R1 = R1.subtract(alpha[alphaPos].v);
             }
 
-            BigInteger t = r0;
+            ++uPos;
 
+            BigInteger t = R0.shiftRight(1);
             if (mu == 1)
             {
-                r0 = r1.add(r0.shiftRight(1));
+                R0 = R1.add(t);
             }
-            else
+            else // mu == -1
             {
-                // mu == -1
-                r0 = r1.subtract(r0.shiftRight(1));
+                R0 = R1.subtract(t);
             }
-            r1 = t.shiftRight(1).negate();
-            i++;
+            R1 = t.negate();
         }
+
+        long r0_64 = BigIntegers.longValueExact(R0);
+        long r1_64 = BigIntegers.longValueExact(R1);
+
+        // while lambda <> (0, 0)
+        while ((r0_64 | r1_64) != 0L)
+        {
+            if ((r0_64 & 1L) != 0L)
+            {
+                int uVal = (int)r0_64 + ((int)r1_64 * tw);
+                int alphaPos = uVal & pow2Mask;
+
+                u[uPos] = (byte)((uVal << s) >> s);
+                r0_64 -= alpha[alphaPos].u.intValue();
+                r1_64 -= alpha[alphaPos].v.intValue();
+            }
+
+            ++uPos;
+
+            long t_64 = r0_64 >> 1;
+            if (mu == 1)
+            {
+                r0_64 = r1_64 + t_64;
+            }
+            else // mu == -1
+            {
+                r0_64 = r1_64 - t_64;
+            }
+            r1_64 = -t_64;
+        }
+        
         return u;
     }
 
@@ -832,6 +810,7 @@
      */
     public static ECPoint.AbstractF2m[] getPreComp(ECPoint.AbstractF2m p, byte a)
     {
+        ECPoint.AbstractF2m pNeg = (ECPoint.AbstractF2m)p.negate();
         byte[][] alphaTnaf = (a == 0) ? Tnaf.alpha0Tnaf : Tnaf.alpha1Tnaf;
 
         ECPoint.AbstractF2m[] pu = new ECPoint.AbstractF2m[(alphaTnaf.length + 1) >>> 1];
@@ -840,7 +819,7 @@
         int precompLen = alphaTnaf.length;
         for (int i = 3; i < precompLen; i += 2)
         {
-            pu[i >>> 1] = Tnaf.multiplyFromTnaf(p, alphaTnaf[i]);
+            pu[i >>> 1] = Tnaf.multiplyFromTnaf(p, pNeg, alphaTnaf[i]);
         }
 
         p.getCurve().normalizeAll(pu);
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/math/ec/WTauNafMultiplier.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/math/ec/WTauNafMultiplier.java
index 43308b1..0494130 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/math/ec/WTauNafMultiplier.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/math/ec/WTauNafMultiplier.java
@@ -31,12 +31,10 @@
 
         ECPoint.AbstractF2m p = (ECPoint.AbstractF2m)point;
         ECCurve.AbstractF2m curve = (ECCurve.AbstractF2m)p.getCurve();
-        int m = curve.getFieldSize();
         byte a = curve.getA().toBigInteger().byteValue();
         byte mu = Tnaf.getMu(a);
-        BigInteger[] s = curve.getSi();
 
-        ZTauElement rho = Tnaf.partModReduction(k, m, a, s, mu, (byte)10);
+        ZTauElement rho = Tnaf.partModReduction(curve, k, a, mu, (byte)10);
 
         return multiplyWTnaf(p, rho, a, mu);
     }
@@ -57,8 +55,7 @@
 
         BigInteger tw = Tnaf.getTw(mu, Tnaf.WIDTH);
 
-        byte[]u = Tnaf.tauAdicWNaf(mu, lambda, Tnaf.WIDTH,
-            BigInteger.valueOf(Tnaf.POW_2_WIDTH), tw, alpha);
+        byte[] u = Tnaf.tauAdicWNaf(mu, lambda, Tnaf.WIDTH, tw.intValue(), alpha);
 
         return multiplyFromWTnaf(p, u);
     }
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/math/ec/custom/sec/SecP256K1Field.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/math/ec/custom/sec/SecP256K1Field.java
index fd47d21..a013a24 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/math/ec/custom/sec/SecP256K1Field.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/math/ec/custom/sec/SecP256K1Field.java
@@ -102,6 +102,12 @@
         reduce(tt, z);
     }
 
+    public static void multiply(int[] x, int[] y, int[] z, int[] tt)
+    {
+        Nat256.mul(x, y, tt);
+        reduce(tt, z);
+    }
+
     public static void multiplyAddToExt(int[] x, int[] y, int[] zz)
     {
         int c = Nat256.mulAddTo(x, y, zz);
@@ -175,6 +181,12 @@
         reduce(tt, z);
     }
 
+    public static void square(int[] x, int[] z, int[] tt)
+    {
+        Nat256.square(x, tt);
+        reduce(tt, z);
+    }
+
     public static void squareN(int[] x, int n, int[] z)
     {
 //        assert n > 0;
@@ -190,6 +202,20 @@
         }
     }
 
+    public static void squareN(int[] x, int n, int[] z, int[] tt)
+    {
+//        assert n > 0;
+
+        Nat256.square(x, tt);
+        reduce(tt, z);
+
+        while (--n > 0)
+        {
+            Nat256.square(z, tt);
+            reduce(tt, z);
+        }
+    }
+
     public static void subtract(int[] x, int[] y, int[] z)
     {
         int c = Nat256.sub(x, y, z);
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/math/ec/custom/sec/SecP256K1FieldElement.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/math/ec/custom/sec/SecP256K1FieldElement.java
index 0821894..8be290d 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/math/ec/custom/sec/SecP256K1FieldElement.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/math/ec/custom/sec/SecP256K1FieldElement.java
@@ -150,49 +150,51 @@
             return this;
         }
 
+        int[] tt0 = Nat256.createExt();
+
         int[] x2 = Nat256.create();
-        SecP256K1Field.square(x1, x2);
-        SecP256K1Field.multiply(x2, x1, x2);
+        SecP256K1Field.square(x1, x2, tt0);
+        SecP256K1Field.multiply(x2, x1, x2, tt0);
         int[] x3 = Nat256.create();
-        SecP256K1Field.square(x2, x3);
-        SecP256K1Field.multiply(x3, x1, x3);
+        SecP256K1Field.square(x2, x3, tt0);
+        SecP256K1Field.multiply(x3, x1, x3, tt0);
         int[] x6 = Nat256.create();
-        SecP256K1Field.squareN(x3, 3, x6);
-        SecP256K1Field.multiply(x6, x3, x6);
+        SecP256K1Field.squareN(x3, 3, x6, tt0);
+        SecP256K1Field.multiply(x6, x3, x6, tt0);
         int[] x9 = x6;
-        SecP256K1Field.squareN(x6, 3, x9);
-        SecP256K1Field.multiply(x9, x3, x9);
+        SecP256K1Field.squareN(x6, 3, x9, tt0);
+        SecP256K1Field.multiply(x9, x3, x9, tt0);
         int[] x11 = x9;
-        SecP256K1Field.squareN(x9, 2, x11);
-        SecP256K1Field.multiply(x11, x2, x11);
+        SecP256K1Field.squareN(x9, 2, x11, tt0);
+        SecP256K1Field.multiply(x11, x2, x11, tt0);
         int[] x22 = Nat256.create();
-        SecP256K1Field.squareN(x11, 11, x22);
-        SecP256K1Field.multiply(x22, x11, x22);
+        SecP256K1Field.squareN(x11, 11, x22, tt0);
+        SecP256K1Field.multiply(x22, x11, x22, tt0);
         int[] x44 = x11;
-        SecP256K1Field.squareN(x22, 22, x44);
-        SecP256K1Field.multiply(x44, x22, x44);
+        SecP256K1Field.squareN(x22, 22, x44, tt0);
+        SecP256K1Field.multiply(x44, x22, x44, tt0);
         int[] x88 = Nat256.create();
-        SecP256K1Field.squareN(x44, 44, x88);
-        SecP256K1Field.multiply(x88, x44, x88);
+        SecP256K1Field.squareN(x44, 44, x88, tt0);
+        SecP256K1Field.multiply(x88, x44, x88, tt0);
         int[] x176 = Nat256.create();
-        SecP256K1Field.squareN(x88, 88, x176);
-        SecP256K1Field.multiply(x176, x88, x176);
+        SecP256K1Field.squareN(x88, 88, x176, tt0);
+        SecP256K1Field.multiply(x176, x88, x176, tt0);
         int[] x220 = x88;
-        SecP256K1Field.squareN(x176, 44, x220);
-        SecP256K1Field.multiply(x220, x44, x220);
+        SecP256K1Field.squareN(x176, 44, x220, tt0);
+        SecP256K1Field.multiply(x220, x44, x220, tt0);
         int[] x223 = x44;
-        SecP256K1Field.squareN(x220, 3, x223);
-        SecP256K1Field.multiply(x223, x3, x223);
+        SecP256K1Field.squareN(x220, 3, x223, tt0);
+        SecP256K1Field.multiply(x223, x3, x223, tt0);
 
         int[] t1 = x223;
-        SecP256K1Field.squareN(t1, 23, t1);
-        SecP256K1Field.multiply(t1, x22, t1);
-        SecP256K1Field.squareN(t1, 6, t1);
-        SecP256K1Field.multiply(t1, x2, t1);
-        SecP256K1Field.squareN(t1, 2, t1);
+        SecP256K1Field.squareN(t1, 23, t1, tt0);
+        SecP256K1Field.multiply(t1, x22, t1, tt0);
+        SecP256K1Field.squareN(t1, 6, t1, tt0);
+        SecP256K1Field.multiply(t1, x2, t1, tt0);
+        SecP256K1Field.squareN(t1, 2, t1, tt0);
 
         int[] t2 = x2;
-        SecP256K1Field.square(t1, t2);
+        SecP256K1Field.square(t1, t2, tt0);
 
         return Nat256.eq(x1, t2) ? new SecP256K1FieldElement(t1) : null;
     }
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/math/ec/custom/sec/SecP256K1Point.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/math/ec/custom/sec/SecP256K1Point.java
index 283554e..f5140d1 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/math/ec/custom/sec/SecP256K1Point.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/math/ec/custom/sec/SecP256K1Point.java
@@ -52,6 +52,7 @@
         SecP256K1FieldElement Z2 = (SecP256K1FieldElement)b.getZCoord(0);
 
         int c;
+        int[] tt0 = Nat256.createExt();
         int[] tt1 = Nat256.createExt();
         int[] t2 = Nat256.create();
         int[] t3 = Nat256.create();
@@ -67,13 +68,13 @@
         else
         {
             S2 = t3;
-            SecP256K1Field.square(Z1.x, S2);
+            SecP256K1Field.square(Z1.x, S2, tt0);
 
             U2 = t2;
-            SecP256K1Field.multiply(S2, X2.x, U2);
+            SecP256K1Field.multiply(S2, X2.x, U2, tt0);
 
-            SecP256K1Field.multiply(S2, Z1.x, S2);
-            SecP256K1Field.multiply(S2, Y2.x, S2);
+            SecP256K1Field.multiply(S2, Z1.x, S2, tt0);
+            SecP256K1Field.multiply(S2, Y2.x, S2, tt0);
         }
 
         boolean Z2IsOne = Z2.isOne();
@@ -86,13 +87,13 @@
         else
         {
             S1 = t4;
-            SecP256K1Field.square(Z2.x, S1);
+            SecP256K1Field.square(Z2.x, S1, tt0);
 
             U1 = tt1;
-            SecP256K1Field.multiply(S1, X1.x, U1);
+            SecP256K1Field.multiply(S1, X1.x, U1, tt0);
 
-            SecP256K1Field.multiply(S1, Z2.x, S1);
-            SecP256K1Field.multiply(S1, Y1.x, S1);
+            SecP256K1Field.multiply(S1, Z2.x, S1, tt0);
+            SecP256K1Field.multiply(S1, Y1.x, S1, tt0);
         }
 
         int[] H = Nat256.create();
@@ -115,13 +116,13 @@
         }
 
         int[] HSquared = t3;
-        SecP256K1Field.square(H, HSquared);
+        SecP256K1Field.square(H, HSquared, tt0);
 
         int[] G = Nat256.create();
-        SecP256K1Field.multiply(HSquared, H, G);
+        SecP256K1Field.multiply(HSquared, H, G, tt0);
 
         int[] V = t3;
-        SecP256K1Field.multiply(HSquared, U1, V);
+        SecP256K1Field.multiply(HSquared, U1, V, tt0);
 
         SecP256K1Field.negate(G, G);
         Nat256.mul(S1, G, tt1);
@@ -130,7 +131,7 @@
         SecP256K1Field.reduce32(c, G);
 
         SecP256K1FieldElement X3 = new SecP256K1FieldElement(t4);
-        SecP256K1Field.square(R, X3.x);
+        SecP256K1Field.square(R, X3.x, tt0);
         SecP256K1Field.subtract(X3.x, G, X3.x);
 
         SecP256K1FieldElement Y3 = new SecP256K1FieldElement(G);
@@ -141,11 +142,11 @@
         SecP256K1FieldElement Z3 = new SecP256K1FieldElement(H);
         if (!Z1IsOne)
         {
-            SecP256K1Field.multiply(Z3.x, Z1.x, Z3.x);
+            SecP256K1Field.multiply(Z3.x, Z1.x, Z3.x, tt0);
         }
         if (!Z2IsOne)
         {
-            SecP256K1Field.multiply(Z3.x, Z2.x, Z3.x);
+            SecP256K1Field.multiply(Z3.x, Z2.x, Z3.x, tt0);
         }
 
         ECFieldElement[] zs = new ECFieldElement[] { Z3 };
@@ -172,20 +173,21 @@
         SecP256K1FieldElement X1 = (SecP256K1FieldElement)this.x, Z1 = (SecP256K1FieldElement)this.zs[0];
 
         int c;
+        int[] tt0 = Nat256.createExt();
 
         int[] Y1Squared = Nat256.create();
-        SecP256K1Field.square(Y1.x, Y1Squared);
+        SecP256K1Field.square(Y1.x, Y1Squared, tt0);
 
         int[] T = Nat256.create();
-        SecP256K1Field.square(Y1Squared, T);
+        SecP256K1Field.square(Y1Squared, T, tt0);
 
         int[] M = Nat256.create();
-        SecP256K1Field.square(X1.x, M);
+        SecP256K1Field.square(X1.x, M, tt0);
         c = Nat256.addBothTo(M, M, M);
         SecP256K1Field.reduce32(c, M);
 
         int[] S = Y1Squared;
-        SecP256K1Field.multiply(Y1Squared, X1.x, S);
+        SecP256K1Field.multiply(Y1Squared, X1.x, S, tt0);
         c = Nat.shiftUpBits(8, S, 2, 0);
         SecP256K1Field.reduce32(c, S);
 
@@ -194,20 +196,20 @@
         SecP256K1Field.reduce32(c, t1);
 
         SecP256K1FieldElement X3 = new SecP256K1FieldElement(T);
-        SecP256K1Field.square(M, X3.x);
+        SecP256K1Field.square(M, X3.x, tt0);
         SecP256K1Field.subtract(X3.x, S, X3.x);
         SecP256K1Field.subtract(X3.x, S, X3.x);
 
         SecP256K1FieldElement Y3 = new SecP256K1FieldElement(S);
         SecP256K1Field.subtract(S, X3.x, Y3.x);
-        SecP256K1Field.multiply(Y3.x, M, Y3.x);
+        SecP256K1Field.multiply(Y3.x, M, Y3.x, tt0);
         SecP256K1Field.subtract(Y3.x, t1, Y3.x);
 
         SecP256K1FieldElement Z3 = new SecP256K1FieldElement(M);
         SecP256K1Field.twice(Y1.x, Z3.x);
         if (!Z1.isOne())
         {
-            SecP256K1Field.multiply(Z3.x, Z1.x, Z3.x);
+            SecP256K1Field.multiply(Z3.x, Z1.x, Z3.x, tt0);
         }
 
         return new SecP256K1Point(curve, X3, Y3, new ECFieldElement[] { Z3 });
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/math/ec/custom/sec/SecP256R1Field.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/math/ec/custom/sec/SecP256R1Field.java
index b9cea7a..290420d 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/math/ec/custom/sec/SecP256R1Field.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/math/ec/custom/sec/SecP256R1Field.java
@@ -98,6 +98,12 @@
         reduce(tt, z);
     }
 
+    public static void multiply(int[] x, int[] y, int[] z, int[] tt)
+    {
+        Nat256.mul(x, y, tt);
+        reduce(tt, z);
+    }
+
     public static void multiplyAddToExt(int[] x, int[] y, int[] zz)
     {
         int c = Nat256.mulAddTo(x, y, zz);
@@ -244,6 +250,12 @@
         reduce(tt, z);
     }
 
+    public static void square(int[] x, int[] z, int[] tt)
+    {
+        Nat256.square(x, tt);
+        reduce(tt, z);
+    }
+
     public static void squareN(int[] x, int n, int[] z)
     {
 //        assert n > 0;
@@ -259,6 +271,20 @@
         }
     }
 
+    public static void squareN(int[] x, int n, int[] z, int[] tt)
+    {
+//        assert n > 0;
+
+        Nat256.square(x, tt);
+        reduce(tt, z);
+
+        while (--n > 0)
+        {
+            Nat256.square(z, tt);
+            reduce(tt, z);
+        }
+    }
+
     public static void subtract(int[] x, int[] y, int[] z)
     {
         int c = Nat256.sub(x, y, z);
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/math/ec/custom/sec/SecP256R1FieldElement.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/math/ec/custom/sec/SecP256R1FieldElement.java
index 3cfbea6..990a86a 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/math/ec/custom/sec/SecP256R1FieldElement.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/math/ec/custom/sec/SecP256R1FieldElement.java
@@ -141,32 +141,33 @@
             return this;
         }
 
+        int[] tt0 = Nat256.createExt();
         int[] t1 = Nat256.create();
         int[] t2 = Nat256.create();
 
-        SecP256R1Field.square(x1, t1);
-        SecP256R1Field.multiply(t1, x1, t1);
+        SecP256R1Field.square(x1, t1, tt0);
+        SecP256R1Field.multiply(t1, x1, t1, tt0);
 
-        SecP256R1Field.squareN(t1, 2, t2);
-        SecP256R1Field.multiply(t2, t1, t2);
+        SecP256R1Field.squareN(t1, 2, t2, tt0);
+        SecP256R1Field.multiply(t2, t1, t2, tt0);
 
-        SecP256R1Field.squareN(t2, 4, t1);
-        SecP256R1Field.multiply(t1, t2, t1);
+        SecP256R1Field.squareN(t2, 4, t1, tt0);
+        SecP256R1Field.multiply(t1, t2, t1, tt0);
 
-        SecP256R1Field.squareN(t1, 8, t2);
-        SecP256R1Field.multiply(t2, t1, t2);
+        SecP256R1Field.squareN(t1, 8, t2, tt0);
+        SecP256R1Field.multiply(t2, t1, t2, tt0);
 
-        SecP256R1Field.squareN(t2, 16, t1);
-        SecP256R1Field.multiply(t1, t2, t1);
+        SecP256R1Field.squareN(t2, 16, t1, tt0);
+        SecP256R1Field.multiply(t1, t2, t1, tt0);
 
-        SecP256R1Field.squareN(t1, 32, t1);
-        SecP256R1Field.multiply(t1, x1, t1);
+        SecP256R1Field.squareN(t1, 32, t1, tt0);
+        SecP256R1Field.multiply(t1, x1, t1, tt0);
 
-        SecP256R1Field.squareN(t1, 96, t1);
-        SecP256R1Field.multiply(t1, x1, t1);
+        SecP256R1Field.squareN(t1, 96, t1, tt0);
+        SecP256R1Field.multiply(t1, x1, t1, tt0);
 
-        SecP256R1Field.squareN(t1, 94, t1);
-        SecP256R1Field.square(t1, t2);
+        SecP256R1Field.squareN(t1, 94, t1, tt0);
+        SecP256R1Field.square(t1, t2, tt0);
 
         return Nat256.eq(x1, t2) ? new SecP256R1FieldElement(t1) : null;
     }
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/math/ec/custom/sec/SecP256R1Point.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/math/ec/custom/sec/SecP256R1Point.java
index 475624a..990a0ba 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/math/ec/custom/sec/SecP256R1Point.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/math/ec/custom/sec/SecP256R1Point.java
@@ -51,6 +51,7 @@
         SecP256R1FieldElement Z2 = (SecP256R1FieldElement)b.getZCoord(0);
 
         int c;
+        int[] tt0 = Nat256.createExt();
         int[] tt1 = Nat256.createExt();
         int[] t2 = Nat256.create();
         int[] t3 = Nat256.create();
@@ -66,13 +67,13 @@
         else
         {
             S2 = t3;
-            SecP256R1Field.square(Z1.x, S2);
+            SecP256R1Field.square(Z1.x, S2, tt0);
 
             U2 = t2;
-            SecP256R1Field.multiply(S2, X2.x, U2);
+            SecP256R1Field.multiply(S2, X2.x, U2, tt0);
 
-            SecP256R1Field.multiply(S2, Z1.x, S2);
-            SecP256R1Field.multiply(S2, Y2.x, S2);
+            SecP256R1Field.multiply(S2, Z1.x, S2, tt0);
+            SecP256R1Field.multiply(S2, Y2.x, S2, tt0);
         }
 
         boolean Z2IsOne = Z2.isOne();
@@ -85,13 +86,13 @@
         else
         {
             S1 = t4;
-            SecP256R1Field.square(Z2.x, S1);
+            SecP256R1Field.square(Z2.x, S1, tt0);
 
             U1 = tt1;
-            SecP256R1Field.multiply(S1, X1.x, U1);
+            SecP256R1Field.multiply(S1, X1.x, U1, tt0);
 
-            SecP256R1Field.multiply(S1, Z2.x, S1);
-            SecP256R1Field.multiply(S1, Y1.x, S1);
+            SecP256R1Field.multiply(S1, Z2.x, S1, tt0);
+            SecP256R1Field.multiply(S1, Y1.x, S1, tt0);
         }
 
         int[] H = Nat256.create();
@@ -114,13 +115,13 @@
         }
 
         int[] HSquared = t3;
-        SecP256R1Field.square(H, HSquared);
+        SecP256R1Field.square(H, HSquared, tt0);
 
         int[] G = Nat256.create();
-        SecP256R1Field.multiply(HSquared, H, G);
+        SecP256R1Field.multiply(HSquared, H, G, tt0);
 
         int[] V = t3;
-        SecP256R1Field.multiply(HSquared, U1, V);
+        SecP256R1Field.multiply(HSquared, U1, V, tt0);
 
         SecP256R1Field.negate(G, G);
         Nat256.mul(S1, G, tt1);
@@ -129,7 +130,7 @@
         SecP256R1Field.reduce32(c, G);
 
         SecP256R1FieldElement X3 = new SecP256R1FieldElement(t4);
-        SecP256R1Field.square(R, X3.x);
+        SecP256R1Field.square(R, X3.x, tt0);
         SecP256R1Field.subtract(X3.x, G, X3.x);
 
         SecP256R1FieldElement Y3 = new SecP256R1FieldElement(G);
@@ -140,11 +141,11 @@
         SecP256R1FieldElement Z3 = new SecP256R1FieldElement(H);
         if (!Z1IsOne)
         {
-            SecP256R1Field.multiply(Z3.x, Z1.x, Z3.x);
+            SecP256R1Field.multiply(Z3.x, Z1.x, Z3.x, tt0);
         }
         if (!Z2IsOne)
         {
-            SecP256R1Field.multiply(Z3.x, Z2.x, Z3.x);
+            SecP256R1Field.multiply(Z3.x, Z2.x, Z3.x, tt0);
         }
 
         ECFieldElement[] zs = new ECFieldElement[]{ Z3 };
@@ -170,14 +171,15 @@
         SecP256R1FieldElement X1 = (SecP256R1FieldElement)this.x, Z1 = (SecP256R1FieldElement)this.zs[0];
 
         int c;
+        int[] tt0 = Nat256.createExt();
         int[] t1 = Nat256.create();
         int[] t2 = Nat256.create();
 
         int[] Y1Squared = Nat256.create();
-        SecP256R1Field.square(Y1.x, Y1Squared);
+        SecP256R1Field.square(Y1.x, Y1Squared, tt0);
 
         int[] T = Nat256.create();
-        SecP256R1Field.square(Y1Squared, T);
+        SecP256R1Field.square(Y1Squared, T, tt0);
 
         boolean Z1IsOne = Z1.isOne();
 
@@ -185,19 +187,19 @@
         if (!Z1IsOne)
         {
             Z1Squared = t2;
-            SecP256R1Field.square(Z1.x, Z1Squared);
+            SecP256R1Field.square(Z1.x, Z1Squared, tt0);
         }
 
         SecP256R1Field.subtract(X1.x, Z1Squared, t1);
 
         int[] M = t2;
         SecP256R1Field.add(X1.x, Z1Squared, M);
-        SecP256R1Field.multiply(M, t1, M);
+        SecP256R1Field.multiply(M, t1, M, tt0);
         c = Nat256.addBothTo(M, M, M);
         SecP256R1Field.reduce32(c, M);
 
         int[] S = Y1Squared;
-        SecP256R1Field.multiply(Y1Squared, X1.x, S);
+        SecP256R1Field.multiply(Y1Squared, X1.x, S, tt0);
         c = Nat.shiftUpBits(8, S, 2, 0);
         SecP256R1Field.reduce32(c, S);
 
@@ -205,20 +207,20 @@
         SecP256R1Field.reduce32(c, t1);
 
         SecP256R1FieldElement X3 = new SecP256R1FieldElement(T);
-        SecP256R1Field.square(M, X3.x);
+        SecP256R1Field.square(M, X3.x, tt0);
         SecP256R1Field.subtract(X3.x, S, X3.x);
         SecP256R1Field.subtract(X3.x, S, X3.x);
 
         SecP256R1FieldElement Y3 = new SecP256R1FieldElement(S);
         SecP256R1Field.subtract(S, X3.x, Y3.x);
-        SecP256R1Field.multiply(Y3.x, M, Y3.x);
+        SecP256R1Field.multiply(Y3.x, M, Y3.x, tt0);
         SecP256R1Field.subtract(Y3.x, t1, Y3.x);
 
         SecP256R1FieldElement Z3 = new SecP256R1FieldElement(M);
         SecP256R1Field.twice(Y1.x, Z3.x);
         if (!Z1IsOne)
         {
-            SecP256R1Field.multiply(Z3.x, Z1.x, Z3.x);
+            SecP256R1Field.multiply(Z3.x, Z1.x, Z3.x, tt0);
         }
 
         return new SecP256R1Point(curve, X3, Y3, new ECFieldElement[]{ Z3 });
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/math/ec/custom/sec/SecP384R1Field.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/math/ec/custom/sec/SecP384R1Field.java
index c95f759..25e40ab 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/math/ec/custom/sec/SecP384R1Field.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/math/ec/custom/sec/SecP384R1Field.java
@@ -104,6 +104,12 @@
         reduce(tt, z);
     }
 
+    public static void multiply(int[] x, int[] y, int[] z, int[] tt)
+    {
+        Nat384.mul(x, y, tt);
+        reduce(tt, z);
+    }
+
     public static void negate(int[] x, int[] z)
     {
         if (0 != isZero(x))
@@ -240,6 +246,12 @@
         reduce(tt, z);
     }
 
+    public static void square(int[] x, int[] z, int[] tt)
+    {
+        Nat384.square(x, tt);
+        reduce(tt, z);
+    }
+
     public static void squareN(int[] x, int n, int[] z)
     {
 //        assert n > 0;
@@ -255,6 +267,20 @@
         }
     }
 
+    public static void squareN(int[] x, int n, int[] z, int[] tt)
+    {
+//        assert n > 0;
+
+        Nat384.square(x, tt);
+        reduce(tt, z);
+
+        while (--n > 0)
+        {
+            Nat384.square(z, tt);
+            reduce(tt, z);
+        }
+    }
+
     public static void subtract(int[] x, int[] y, int[] z)
     {
         int c = Nat.sub(12, x, y, z);
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/math/ec/custom/sec/SecP384R1FieldElement.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/math/ec/custom/sec/SecP384R1FieldElement.java
index a2791d4..5520ffc 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/math/ec/custom/sec/SecP384R1FieldElement.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/math/ec/custom/sec/SecP384R1FieldElement.java
@@ -141,54 +141,55 @@
             return this;
         }
 
+        int[] tt0 = Nat.create(24);
         int[] t1 = Nat.create(12);
         int[] t2 = Nat.create(12);
         int[] t3 = Nat.create(12);
         int[] t4 = Nat.create(12);
 
-        SecP384R1Field.square(x1, t1);
-        SecP384R1Field.multiply(t1, x1, t1);
+        SecP384R1Field.square(x1, t1, tt0);
+        SecP384R1Field.multiply(t1, x1, t1, tt0);
 
-        SecP384R1Field.squareN(t1, 2, t2);
-        SecP384R1Field.multiply(t2, t1, t2);
+        SecP384R1Field.squareN(t1, 2, t2, tt0);
+        SecP384R1Field.multiply(t2, t1, t2, tt0);
 
-        SecP384R1Field.square(t2, t2);
-        SecP384R1Field.multiply(t2, x1, t2);
+        SecP384R1Field.square(t2, t2, tt0);
+        SecP384R1Field.multiply(t2, x1, t2, tt0);
 
-        SecP384R1Field.squareN(t2, 5, t3);
-        SecP384R1Field.multiply(t3, t2, t3);
+        SecP384R1Field.squareN(t2, 5, t3, tt0);
+        SecP384R1Field.multiply(t3, t2, t3, tt0);
 
-        SecP384R1Field.squareN(t3, 5, t4);
-        SecP384R1Field.multiply(t4, t2, t4);
+        SecP384R1Field.squareN(t3, 5, t4, tt0);
+        SecP384R1Field.multiply(t4, t2, t4, tt0);
 
-        SecP384R1Field.squareN(t4, 15, t2);
-        SecP384R1Field.multiply(t2, t4, t2);
+        SecP384R1Field.squareN(t4, 15, t2, tt0);
+        SecP384R1Field.multiply(t2, t4, t2, tt0);
 
-        SecP384R1Field.squareN(t2, 2, t3);
-        SecP384R1Field.multiply(t1, t3, t1);
+        SecP384R1Field.squareN(t2, 2, t3, tt0);
+        SecP384R1Field.multiply(t1, t3, t1, tt0);
 
-        SecP384R1Field.squareN(t3, 28, t3);
-        SecP384R1Field.multiply(t2, t3, t2);
+        SecP384R1Field.squareN(t3, 28, t3, tt0);
+        SecP384R1Field.multiply(t2, t3, t2, tt0);
 
-        SecP384R1Field.squareN(t2, 60, t3);
-        SecP384R1Field.multiply(t3, t2, t3);
+        SecP384R1Field.squareN(t2, 60, t3, tt0);
+        SecP384R1Field.multiply(t3, t2, t3, tt0);
 
         int[] r = t2;
 
-        SecP384R1Field.squareN(t3, 120, r);
-        SecP384R1Field.multiply(r, t3, r);
+        SecP384R1Field.squareN(t3, 120, r, tt0);
+        SecP384R1Field.multiply(r, t3, r, tt0);
 
-        SecP384R1Field.squareN(r, 15, r);
-        SecP384R1Field.multiply(r, t4, r);
+        SecP384R1Field.squareN(r, 15, r, tt0);
+        SecP384R1Field.multiply(r, t4, r, tt0);
 
-        SecP384R1Field.squareN(r, 33, r);
-        SecP384R1Field.multiply(r, t1, r);
+        SecP384R1Field.squareN(r, 33, r, tt0);
+        SecP384R1Field.multiply(r, t1, r, tt0);
 
-        SecP384R1Field.squareN(r, 64, r);
-        SecP384R1Field.multiply(r, x1, r);
+        SecP384R1Field.squareN(r, 64, r, tt0);
+        SecP384R1Field.multiply(r, x1, r, tt0);
 
-        SecP384R1Field.squareN(r, 30, t1);
-        SecP384R1Field.square(t1, t2);
+        SecP384R1Field.squareN(r, 30, t1, tt0);
+        SecP384R1Field.square(t1, t2, tt0);
 
         return Nat.eq(12, x1, t2) ? new SecP384R1FieldElement(t1) : null;
     }
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/math/ec/custom/sec/SecP384R1Point.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/math/ec/custom/sec/SecP384R1Point.java
index 27b6aa5..5787a5f 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/math/ec/custom/sec/SecP384R1Point.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/math/ec/custom/sec/SecP384R1Point.java
@@ -51,6 +51,7 @@
         SecP384R1FieldElement Z2 = (SecP384R1FieldElement)b.getZCoord(0);
 
         int c;
+        int[] tt0 = Nat.create(24);
         int[] tt1 = Nat.create(24);
         int[] tt2 = Nat.create(24);
         int[] t3 = Nat.create(12);
@@ -66,13 +67,13 @@
         else
         {
             S2 = t3;
-            SecP384R1Field.square(Z1.x, S2);
+            SecP384R1Field.square(Z1.x, S2, tt0);
 
             U2 = tt2;
-            SecP384R1Field.multiply(S2, X2.x, U2);
+            SecP384R1Field.multiply(S2, X2.x, U2, tt0);
 
-            SecP384R1Field.multiply(S2, Z1.x, S2);
-            SecP384R1Field.multiply(S2, Y2.x, S2);
+            SecP384R1Field.multiply(S2, Z1.x, S2, tt0);
+            SecP384R1Field.multiply(S2, Y2.x, S2, tt0);
         }
 
         boolean Z2IsOne = Z2.isOne();
@@ -85,13 +86,13 @@
         else
         {
             S1 = t4;
-            SecP384R1Field.square(Z2.x, S1);
+            SecP384R1Field.square(Z2.x, S1, tt0);
 
             U1 = tt1;
-            SecP384R1Field.multiply(S1, X1.x, U1);
+            SecP384R1Field.multiply(S1, X1.x, U1, tt0);
 
-            SecP384R1Field.multiply(S1, Z2.x, S1);
-            SecP384R1Field.multiply(S1, Y1.x, S1);
+            SecP384R1Field.multiply(S1, Z2.x, S1, tt0);
+            SecP384R1Field.multiply(S1, Y1.x, S1, tt0);
         }
 
         int[] H = Nat.create(12);
@@ -114,13 +115,13 @@
         }
 
         int[] HSquared = t3;
-        SecP384R1Field.square(H, HSquared);
+        SecP384R1Field.square(H, HSquared, tt0);
 
         int[] G = Nat.create(12);
-        SecP384R1Field.multiply(HSquared, H, G);
+        SecP384R1Field.multiply(HSquared, H, G, tt0);
 
         int[] V = t3;
-        SecP384R1Field.multiply(HSquared, U1, V);
+        SecP384R1Field.multiply(HSquared, U1, V, tt0);
 
         SecP384R1Field.negate(G, G);
         Nat384.mul(S1, G, tt1);
@@ -129,7 +130,7 @@
         SecP384R1Field.reduce32(c, G);
 
         SecP384R1FieldElement X3 = new SecP384R1FieldElement(t4);
-        SecP384R1Field.square(R, X3.x);
+        SecP384R1Field.square(R, X3.x, tt0);
         SecP384R1Field.subtract(X3.x, G, X3.x);
 
         SecP384R1FieldElement Y3 = new SecP384R1FieldElement(G);
@@ -141,11 +142,11 @@
         SecP384R1FieldElement Z3 = new SecP384R1FieldElement(H);
         if (!Z1IsOne)
         {
-            SecP384R1Field.multiply(Z3.x, Z1.x, Z3.x);
+            SecP384R1Field.multiply(Z3.x, Z1.x, Z3.x, tt0);
         }
         if (!Z2IsOne)
         {
-            SecP384R1Field.multiply(Z3.x, Z2.x, Z3.x);
+            SecP384R1Field.multiply(Z3.x, Z2.x, Z3.x, tt0);
         }
 
         ECFieldElement[] zs = new ECFieldElement[]{ Z3 };
@@ -171,14 +172,15 @@
         SecP384R1FieldElement X1 = (SecP384R1FieldElement)this.x, Z1 = (SecP384R1FieldElement)this.zs[0];
 
         int c;
+        int[] tt0 = Nat.create(24);
         int[] t1 = Nat.create(12);
         int[] t2 = Nat.create(12);
 
         int[] Y1Squared = Nat.create(12);
-        SecP384R1Field.square(Y1.x, Y1Squared);
+        SecP384R1Field.square(Y1.x, Y1Squared, tt0);
 
         int[] T = Nat.create(12);
-        SecP384R1Field.square(Y1Squared, T);
+        SecP384R1Field.square(Y1Squared, T, tt0);
 
         boolean Z1IsOne = Z1.isOne();
 
@@ -186,19 +188,19 @@
         if (!Z1IsOne)
         {
             Z1Squared = t2;
-            SecP384R1Field.square(Z1.x, Z1Squared);
+            SecP384R1Field.square(Z1.x, Z1Squared, tt0);
         }
 
         SecP384R1Field.subtract(X1.x, Z1Squared, t1);
 
         int[] M = t2;
         SecP384R1Field.add(X1.x, Z1Squared, M);
-        SecP384R1Field.multiply(M, t1, M);
+        SecP384R1Field.multiply(M, t1, M, tt0);
         c = Nat.addBothTo(12, M, M, M);
         SecP384R1Field.reduce32(c, M);
 
         int[] S = Y1Squared;
-        SecP384R1Field.multiply(Y1Squared, X1.x, S);
+        SecP384R1Field.multiply(Y1Squared, X1.x, S, tt0);
         c = Nat.shiftUpBits(12, S, 2, 0);
         SecP384R1Field.reduce32(c, S);
 
@@ -206,20 +208,20 @@
         SecP384R1Field.reduce32(c, t1);
 
         SecP384R1FieldElement X3 = new SecP384R1FieldElement(T);
-        SecP384R1Field.square(M, X3.x);
+        SecP384R1Field.square(M, X3.x, tt0);
         SecP384R1Field.subtract(X3.x, S, X3.x);
         SecP384R1Field.subtract(X3.x, S, X3.x);
 
         SecP384R1FieldElement Y3 = new SecP384R1FieldElement(S);
         SecP384R1Field.subtract(S, X3.x, Y3.x);
-        SecP384R1Field.multiply(Y3.x, M, Y3.x);
+        SecP384R1Field.multiply(Y3.x, M, Y3.x, tt0);
         SecP384R1Field.subtract(Y3.x, t1, Y3.x);
 
         SecP384R1FieldElement Z3 = new SecP384R1FieldElement(M);
         SecP384R1Field.twice(Y1.x, Z3.x);
         if (!Z1IsOne)
         {
-            SecP384R1Field.multiply(Z3.x, Z1.x, Z3.x);
+            SecP384R1Field.multiply(Z3.x, Z1.x, Z3.x, tt0);
         }
 
         return new SecP384R1Point(curve, X3, Y3, new ECFieldElement[]{ Z3 });
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/math/ec/custom/sec/SecP521R1Field.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/math/ec/custom/sec/SecP521R1Field.java
index d4780f3..1f3ca22 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/math/ec/custom/sec/SecP521R1Field.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/math/ec/custom/sec/SecP521R1Field.java
@@ -81,6 +81,12 @@
         reduce(tt, z);
     }
 
+    public static void multiply(int[] x, int[] y, int[] z, int[] tt)
+    {
+        implMultiply(x, y, tt);
+        reduce(tt, z);
+    }
+
     public static void negate(int[] x, int[] z)
     {
         if (0 != isZero(x))
@@ -149,6 +155,12 @@
         reduce(tt, z);
     }
 
+    public static void square(int[] x, int[] z, int[] tt)
+    {
+        implSquare(x, tt);
+        reduce(tt, z);
+    }
+
     public static void squareN(int[] x, int n, int[] z)
     {
 //        assert n > 0;
@@ -164,6 +176,20 @@
         }
     }
 
+    public static void squareN(int[] x, int n, int[] z, int[] tt)
+    {
+//        assert n > 0;
+
+        implSquare(x, tt);
+        reduce(tt, z);
+
+        while (--n > 0)
+        {
+            implSquare(z, tt);
+            reduce(tt, z);
+        }
+    }
+
     public static void subtract(int[] x, int[] y, int[] z)
     {
         int c = Nat.sub(16, x, y, z) + x[16] - y[16];
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/math/ec/custom/sec/SecP521R1FieldElement.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/math/ec/custom/sec/SecP521R1FieldElement.java
index 4e52472..c7bd309 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/math/ec/custom/sec/SecP521R1FieldElement.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/math/ec/custom/sec/SecP521R1FieldElement.java
@@ -142,11 +142,12 @@
             return this;
         }
 
+        int[] tt0 = Nat.create(33);
         int[] t1 = Nat.create(17);
         int[] t2 = Nat.create(17);
 
-        SecP521R1Field.squareN(x1, 519, t1);
-        SecP521R1Field.square(t1, t2);
+        SecP521R1Field.squareN(x1, 519, t1, tt0);
+        SecP521R1Field.square(t1, t2, tt0);
 
         return Nat.eq(17, x1, t2) ? new SecP521R1FieldElement(t1) : null;
     }
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/math/ec/custom/sec/SecP521R1Point.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/math/ec/custom/sec/SecP521R1Point.java
index 3bb4f69..e9e31ad 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/math/ec/custom/sec/SecP521R1Point.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/math/ec/custom/sec/SecP521R1Point.java
@@ -49,6 +49,7 @@
         SecP521R1FieldElement Z1 = (SecP521R1FieldElement)this.zs[0];
         SecP521R1FieldElement Z2 = (SecP521R1FieldElement)b.getZCoord(0);
 
+        int[] tt0 = Nat.create(33);
         int[] t1 = Nat.create(17);
         int[] t2 = Nat.create(17);
         int[] t3 = Nat.create(17);
@@ -64,13 +65,13 @@
         else
         {
             S2 = t3;
-            SecP521R1Field.square(Z1.x, S2);
+            SecP521R1Field.square(Z1.x, S2, tt0);
 
             U2 = t2;
-            SecP521R1Field.multiply(S2, X2.x, U2);
+            SecP521R1Field.multiply(S2, X2.x, U2, tt0);
 
-            SecP521R1Field.multiply(S2, Z1.x, S2);
-            SecP521R1Field.multiply(S2, Y2.x, S2);
+            SecP521R1Field.multiply(S2, Z1.x, S2, tt0);
+            SecP521R1Field.multiply(S2, Y2.x, S2, tt0);
         }
 
         boolean Z2IsOne = Z2.isOne();
@@ -83,13 +84,13 @@
         else
         {
             S1 = t4;
-            SecP521R1Field.square(Z2.x, S1);
+            SecP521R1Field.square(Z2.x, S1, tt0);
 
             U1 = t1;
-            SecP521R1Field.multiply(S1, X1.x, U1);
+            SecP521R1Field.multiply(S1, X1.x, U1, tt0);
 
-            SecP521R1Field.multiply(S1, Z2.x, S1);
-            SecP521R1Field.multiply(S1, Y1.x, S1);
+            SecP521R1Field.multiply(S1, Z2.x, S1, tt0);
+            SecP521R1Field.multiply(S1, Y1.x, S1, tt0);
         }
 
         int[] H = Nat.create(17);
@@ -112,35 +113,35 @@
         }
 
         int[] HSquared = t3;
-        SecP521R1Field.square(H, HSquared);
+        SecP521R1Field.square(H, HSquared, tt0);
 
         int[] G = Nat.create(17);
-        SecP521R1Field.multiply(HSquared, H, G);
+        SecP521R1Field.multiply(HSquared, H, G, tt0);
 
         int[] V = t3;
-        SecP521R1Field.multiply(HSquared, U1, V);
+        SecP521R1Field.multiply(HSquared, U1, V, tt0);
 
-        SecP521R1Field.multiply(S1, G, t1);
+        SecP521R1Field.multiply(S1, G, t1, tt0);
 
         SecP521R1FieldElement X3 = new SecP521R1FieldElement(t4);
-        SecP521R1Field.square(R, X3.x);
+        SecP521R1Field.square(R, X3.x, tt0);
         SecP521R1Field.add(X3.x, G, X3.x);
         SecP521R1Field.subtract(X3.x, V, X3.x);
         SecP521R1Field.subtract(X3.x, V, X3.x);
 
         SecP521R1FieldElement Y3 = new SecP521R1FieldElement(G);
         SecP521R1Field.subtract(V, X3.x, Y3.x);
-        SecP521R1Field.multiply(Y3.x, R, t2);
+        SecP521R1Field.multiply(Y3.x, R, t2, tt0);
         SecP521R1Field.subtract(t2, t1, Y3.x);
 
         SecP521R1FieldElement Z3 = new SecP521R1FieldElement(H);
         if (!Z1IsOne)
         {
-            SecP521R1Field.multiply(Z3.x, Z1.x, Z3.x);
+            SecP521R1Field.multiply(Z3.x, Z1.x, Z3.x, tt0);
         }
         if (!Z2IsOne)
         {
-            SecP521R1Field.multiply(Z3.x, Z2.x, Z3.x);
+            SecP521R1Field.multiply(Z3.x, Z2.x, Z3.x, tt0);
         }
 
         ECFieldElement[] zs = new ECFieldElement[]{ Z3 };
@@ -165,14 +166,15 @@
 
         SecP521R1FieldElement X1 = (SecP521R1FieldElement)this.x, Z1 = (SecP521R1FieldElement)this.zs[0];
 
+        int[] tt0 = Nat.create(33);
         int[] t1 = Nat.create(17);
         int[] t2 = Nat.create(17);
 
         int[] Y1Squared = Nat.create(17);
-        SecP521R1Field.square(Y1.x, Y1Squared);
+        SecP521R1Field.square(Y1.x, Y1Squared, tt0);
 
         int[] T = Nat.create(17);
-        SecP521R1Field.square(Y1Squared, T);
+        SecP521R1Field.square(Y1Squared, T, tt0);
 
         boolean Z1IsOne = Z1.isOne();
 
@@ -180,19 +182,19 @@
         if (!Z1IsOne)
         {
             Z1Squared = t2;
-            SecP521R1Field.square(Z1.x, Z1Squared);
+            SecP521R1Field.square(Z1.x, Z1Squared, tt0);
         }
 
         SecP521R1Field.subtract(X1.x, Z1Squared, t1);
 
         int[] M = t2;
         SecP521R1Field.add(X1.x, Z1Squared, M);
-        SecP521R1Field.multiply(M, t1, M);
+        SecP521R1Field.multiply(M, t1, M, tt0);
         Nat.addBothTo(17, M, M, M);
         SecP521R1Field.reduce23(M);
 
         int[] S = Y1Squared;
-        SecP521R1Field.multiply(Y1Squared, X1.x, S);
+        SecP521R1Field.multiply(Y1Squared, X1.x, S, tt0);
         Nat.shiftUpBits(17, S, 2, 0);
         SecP521R1Field.reduce23(S);
 
@@ -200,20 +202,20 @@
         SecP521R1Field.reduce23(t1);
 
         SecP521R1FieldElement X3 = new SecP521R1FieldElement(T);
-        SecP521R1Field.square(M, X3.x);
+        SecP521R1Field.square(M, X3.x, tt0);
         SecP521R1Field.subtract(X3.x, S, X3.x);
         SecP521R1Field.subtract(X3.x, S, X3.x);
 
         SecP521R1FieldElement Y3 = new SecP521R1FieldElement(S);
         SecP521R1Field.subtract(S, X3.x, Y3.x);
-        SecP521R1Field.multiply(Y3.x, M, Y3.x);
+        SecP521R1Field.multiply(Y3.x, M, Y3.x, tt0);
         SecP521R1Field.subtract(Y3.x, t1, Y3.x);
 
         SecP521R1FieldElement Z3 = new SecP521R1FieldElement(M);
         SecP521R1Field.twice(Y1.x, Z3.x);
         if (!Z1IsOne)
         {
-            SecP521R1Field.multiply(Z3.x, Z1.x, Z3.x);
+            SecP521R1Field.multiply(Z3.x, Z1.x, Z3.x, tt0);
         }
 
         return new SecP521R1Point(curve, X3, Y3, new ECFieldElement[]{ Z3 });
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/math/ec/endo/GLVTypeBParameters.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/math/ec/endo/GLVTypeBParameters.java
index 94dd7fa..f6272a4 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/math/ec/endo/GLVTypeBParameters.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/math/ec/endo/GLVTypeBParameters.java
@@ -11,17 +11,6 @@
     protected final BigInteger beta, lambda;
     protected final ScalarSplitParameters splitParams;
 
-    /**
-     * @deprecated Use constructor taking a {@link ScalarSplitParameters} instead.
-     */
-    public GLVTypeBParameters(BigInteger beta, BigInteger lambda, BigInteger[] v1, BigInteger[] v2, BigInteger g1,
-        BigInteger g2, int bits)
-    {
-        this.beta = beta;
-        this.lambda = lambda;
-        this.splitParams = new ScalarSplitParameters(v1, v2, g1, g2, bits);
-    }
-
     public GLVTypeBParameters(BigInteger beta, BigInteger lambda, ScalarSplitParameters splitParams)
     {
         this.beta = beta;
@@ -43,60 +32,4 @@
     {
         return splitParams;
     }
-
-    /**
-     * @deprecated Access via {@link #getSplitParams()} instead.
-     */
-    public BigInteger getV1A()
-    {
-        return getSplitParams().getV1A();
-    }
-
-    /**
-     * @deprecated Access via {@link #getSplitParams()} instead.
-     */
-    public BigInteger getV1B()
-    {
-        return getSplitParams().getV1B();
-    }
-
-    /**
-     * @deprecated Access via {@link #getSplitParams()} instead.
-     */
-    public BigInteger getV2A()
-    {
-        return getSplitParams().getV2A();
-    }
-
-    /**
-     * @deprecated Access via {@link #getSplitParams()} instead.
-     */
-    public BigInteger getV2B()
-    {
-        return getSplitParams().getV2B();
-    }
-
-    /**
-     * @deprecated Access via {@link #getSplitParams()} instead.
-     */
-    public BigInteger getG1()
-    {
-        return getSplitParams().getG1();
-    }
-
-    /**
-     * @deprecated Access via {@link #getSplitParams()} instead.
-     */
-    public BigInteger getG2()
-    {
-        return getSplitParams().getG2();
-    }
-    
-    /**
-     * @deprecated Access via {@link #getSplitParams()} instead.
-     */
-    public int getBits()
-    {
-        return getSplitParams().getBits();
-    }
 }
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/math/field/FiniteFields.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/math/field/FiniteFields.java
index 45cc70f..d736859 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/math/field/FiniteFields.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/math/field/FiniteFields.java
@@ -3,6 +3,8 @@
 
 import java.math.BigInteger;
 
+import com.android.internal.org.bouncycastle.util.BigIntegers;
+
 /**
  * @hide This class is not part of the Android public SDK API
  */
@@ -43,7 +45,7 @@
 
         if (bitLength < 3)
         {
-            switch (characteristic.intValue())
+            switch (BigIntegers.intValueExact(characteristic))
             {
             case 2:
                 return GF_2;
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/math/raw/Mod.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/math/raw/Mod.java
index c6d6bec..88ac856 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/math/raw/Mod.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/math/raw/Mod.java
@@ -18,17 +18,6 @@
     private static final int M30 = 0x3FFFFFFF;
     private static final long M32L = 0xFFFFFFFFL;
 
-    /** @deprecated Will be removed. */
-    public static void add(int[] p, int[] x, int[] y, int[] z)
-    {
-        int len = p.length;
-        int c = Nat.add(len, x, y, z);
-        if (c != 0)
-        {
-            Nat.subFrom(len, p, z);
-        }
-    }
-
     public static void checkedModOddInverse(int[] m, int[] x, int[] z)
     {
         if (0 == modOddInverse(m, x, z))
@@ -58,12 +47,6 @@
         return  x;
     }
 
-    /** @deprecated Use {@link #checkedModOddInverseVar(int[], int[], int[])} instead. */
-    public static void invert(int[] m, int[] x, int[] z)
-    {
-        checkedModOddInverseVar(m,  x,  z);
-    }
-
     public static int modOddInverse(int[] m, int[] x, int[] z)
     {
         int len32 = m.length;
@@ -86,13 +69,13 @@
         encode30(bits, m, 0, M, 0);
         System.arraycopy(M, 0, F, 0, len30);
 
-        int eta = -1;
+        int delta = 0;
         int m0Inv32 = inverse32(M[0]);
         int maxDivsteps = getMaximumDivsteps(bits);
 
         for (int divSteps = 0; divSteps < maxDivsteps; divSteps += 30)
         {
-            eta = divsteps30(eta, F[0], G[0], t);
+            delta = divsteps30(delta, F[0], G[0], t);
             updateDE30(len30, D, E, t, m0Inv32, M);
             updateFG30(len30, F, G, t);
         }
@@ -232,17 +215,6 @@
         return s;
     }
 
-    /** @deprecated Will be removed. */
-    public static void subtract(int[] p, int[] x, int[] y, int[] z)
-    {
-        int len = p.length;
-        int c = Nat.sub(len, x, y, z);
-        if (c != 0)
-        {
-            Nat.addTo(len, p, z);
-        }
-    }
-
     private static int add30(int len30, int[] D, int[] M)
     {
 //        assert len30 > 0;
@@ -280,7 +252,6 @@
 //        assert len30 > 0;
 //        assert D.length >= len30;
 //        assert M.length >= len30;
-
         int last = len30 - 1;
 
         {
@@ -335,38 +306,38 @@
         }
     }
 
-    private static int divsteps30(int eta, int f0, int g0, int[] t)
+    private static int divsteps30(int delta, int f0, int g0, int[] t)
     {
-        int u = 1, v = 0, q = 0, r = 1;
+        int u = 1 << 30, v = 0, q = 0, r = 1 << 30;
         int f = f0, g = g0;
 
         for (int i = 0; i < 30; ++i)
         {
 //            assert (f & 1) == 1;
-//            assert (u * f0 + v * g0) == f << i;
-//            assert (q * f0 + r * g0) == g << i;
+//            assert ((u >> (30 - i)) * f0 + (v >> (30 - i)) * g0) == f << i;
+//            assert ((q >> (30 - i)) * f0 + (r >> (30 - i)) * g0) == g << i;
 
-            int c1 = eta >> 31;
+            int c1 = delta >> 31;
             int c2 = -(g & 1);
 
-            int x = (f ^ c1) - c1;
-            int y = (u ^ c1) - c1;
-            int z = (v ^ c1) - c1;
+            int x = f ^ c1;
+            int y = u ^ c1;
+            int z = v ^ c1;
 
-            g += x & c2;
-            q += y & c2;
-            r += z & c2;
+            g -= x & c2;
+            q -= y & c2;
+            r -= z & c2;
 
-            c1 &= c2;
-            eta = (eta ^ c1) - (c1 + 1);
+            c2 &= ~c1;
+            delta = (delta ^ c2) - (c2 - 1);
 
-            f += g & c1;
-            u += q & c1;
-            v += r & c1;
+            f += g & c2;
+            u += q & c2;
+            v += r & c2;
 
             g >>= 1;
-            u <<= 1;
-            v <<= 1;
+            q >>= 1;
+            r >>= 1;
         }
 
         t[0] = u;
@@ -374,7 +345,7 @@
         t[2] = q;
         t[3] = r;
 
-        return eta;
+        return delta;
     }
 
     private static int divsteps30Var(int eta, int f0, int g0, int[] t)
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/math/raw/Nat.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/math/raw/Nat.java
index 515ac47..ff53899 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/math/raw/Nat.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/math/raw/Nat.java
@@ -236,6 +236,19 @@
         return (int)c;
     }
 
+    public static int caddTo(int len, int mask, int[] x, int[] z)
+    {
+        long MASK = -(mask & 1) & M;
+        long c = 0;
+        for (int i = 0; i < len; ++i)
+        {
+            c += (z[i] & M) + (x[i] & MASK);
+            z[i] = (int)c;
+            c >>>= 32;
+        }
+        return (int)c;
+    }
+
     public static void cmov(int len, int mask, int[] x, int xOff, int[] z, int zOff)
     {
         mask = -(mask & 1);
@@ -1129,41 +1142,6 @@
         shiftUpBit(extLen, zz, zzOff, x[xOff] << 31);
     }
 
-    /**
-     * @deprecated Use {@link #squareWordAddTo(int[], int, int[])} instead.
-     */
-    public static int squareWordAdd(int[] x, int xPos, int[] z)
-    {
-        long c = 0, xVal = x[xPos] & M;
-        int i = 0;
-        do
-        {
-            c += xVal * (x[i] & M) + (z[xPos + i] & M);
-            z[xPos + i] = (int)c;
-            c >>>= 32;
-        }
-        while (++i < xPos);
-        return (int)c;
-    }
-
-    /**
-     * @deprecated Use {@link #squareWordAddTo(int[], int, int, int[], int)} instead.
-     */
-    public static int squareWordAdd(int[] x, int xOff, int xPos, int[] z, int zOff)
-    {
-        long c = 0, xVal = x[xOff + xPos] & M;
-        int i = 0;
-        do
-        {
-            c += xVal * (x[xOff + i] & M) + (z[xPos + zOff] & M);
-            z[xPos + zOff] = (int)c;
-            c >>>= 32;
-            ++zOff;
-        }
-        while (++i < xPos);
-        return (int)c;
-    }
-
     public static int squareWordAddTo(int[] x, int xPos, int[] z)
     {
         long c = 0, xVal = x[xPos] & M;
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/math/raw/Nat224.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/math/raw/Nat224.java
index 8f8423d..114971d 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/math/raw/Nat224.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/math/raw/Nat224.java
@@ -147,6 +147,33 @@
         return (int)c;
     }
 
+    public static int addTo(int[] x, int[] z, int cIn)
+    {
+        long c = cIn & M;
+        c += (x[0] & M) + (z[0] & M);
+        z[0] = (int)c;
+        c >>>= 32;
+        c += (x[1] & M) + (z[1] & M);
+        z[1] = (int)c;
+        c >>>= 32;
+        c += (x[2] & M) + (z[2] & M);
+        z[2] = (int)c;
+        c >>>= 32;
+        c += (x[3] & M) + (z[3] & M);
+        z[3] = (int)c;
+        c >>>= 32;
+        c += (x[4] & M) + (z[4] & M);
+        z[4] = (int)c;
+        c >>>= 32;
+        c += (x[5] & M) + (z[5] & M);
+        z[5] = (int)c;
+        c >>>= 32;
+        c += (x[6] & M) + (z[6] & M);
+        z[6] = (int)c;
+        c >>>= 32;
+        return (int)c;
+    }
+
     public static int addTo(int[] x, int xOff, int[] z, int zOff, int cIn)
     {
         long c = cIn & M;
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/math/raw/Nat256.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/math/raw/Nat256.java
index 0771409..70beb5c 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/math/raw/Nat256.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/math/raw/Nat256.java
@@ -162,6 +162,36 @@
         return (int)c;
     }
 
+    public static int addTo(int[] x, int[] z, int cIn)
+    {
+        long c = cIn & M;
+        c += (x[0] & M) + (z[0] & M);
+        z[0] = (int)c;
+        c >>>= 32;
+        c += (x[1] & M) + (z[1] & M);
+        z[1] = (int)c;
+        c >>>= 32;
+        c += (x[2] & M) + (z[2] & M);
+        z[2] = (int)c;
+        c >>>= 32;
+        c += (x[3] & M) + (z[3] & M);
+        z[3] = (int)c;
+        c >>>= 32;
+        c += (x[4] & M) + (z[4] & M);
+        z[4] = (int)c;
+        c >>>= 32;
+        c += (x[5] & M) + (z[5] & M);
+        z[5] = (int)c;
+        c >>>= 32;
+        c += (x[6] & M) + (z[6] & M);
+        z[6] = (int)c;
+        c >>>= 32;
+        c += (x[7] & M) + (z[7] & M);
+        z[7] = (int)c;
+        c >>>= 32;
+        return (int)c;
+    }
+
     public static int addTo(int[] x, int xOff, int[] z, int zOff, int cIn)
     {
         long c = cIn & M;
@@ -606,6 +636,77 @@
         }
     }
 
+    public static void mul128(int[] x, int[] y128, int[] zz)
+    {
+        long x_0 = x[0] & M;
+        long x_1 = x[1] & M;
+        long x_2 = x[2] & M;
+        long x_3 = x[3] & M;
+        long x_4 = x[4] & M;
+        long x_5 = x[5] & M;
+        long x_6 = x[6] & M;
+        long x_7 = x[7] & M;
+
+        {
+            long c = 0, y_0 = y128[0] & M;
+            c += y_0 * x_0;
+            zz[0] = (int)c;
+            c >>>= 32;
+            c += y_0 * x_1;
+            zz[1] = (int)c;
+            c >>>= 32;
+            c += y_0 * x_2;
+            zz[2] = (int)c;
+            c >>>= 32;
+            c += y_0 * x_3;
+            zz[3] = (int)c;
+            c >>>= 32;
+            c += y_0 * x_4;
+            zz[4] = (int)c;
+            c >>>= 32;
+            c += y_0 * x_5;
+            zz[5] = (int)c;
+            c >>>= 32;
+            c += y_0 * x_6;
+            zz[6] = (int)c;
+            c >>>= 32;
+            c += y_0 * x_7;
+            zz[7] = (int)c;
+            c >>>= 32;
+            zz[8] = (int)c;
+        }
+
+        for (int i = 1; i < 4; ++i)
+        {
+            long c = 0, y_i = y128[i] & M;
+            c += y_i * x_0 + (zz[i + 0] & M);
+            zz[i + 0] = (int)c;
+            c >>>= 32;
+            c += y_i * x_1 + (zz[i + 1] & M);
+            zz[i + 1] = (int)c;
+            c >>>= 32;
+            c += y_i * x_2 + (zz[i + 2] & M);
+            zz[i + 2] = (int)c;
+            c >>>= 32;
+            c += y_i * x_3 + (zz[i + 3] & M);
+            zz[i + 3] = (int)c;
+            c >>>= 32;
+            c += y_i * x_4 + (zz[i + 4] & M);
+            zz[i + 4] = (int)c;
+            c >>>= 32;
+            c += y_i * x_5 + (zz[i + 5] & M);
+            zz[i + 5] = (int)c;
+            c >>>= 32;
+            c += y_i * x_6 + (zz[i + 6] & M);
+            zz[i + 6] = (int)c;
+            c >>>= 32;
+            c += y_i * x_7 + (zz[i + 7] & M);
+            zz[i + 7] = (int)c;
+            c >>>= 32;
+            zz[i + 8] = (int)c;
+        }
+    }
+
     public static int mulAddTo(int[] x, int[] y, int[] zz)
     {
         long y_0 = y[0] & M;
@@ -1351,6 +1452,36 @@
         return (int)c;
     }
 
+    public static int subFrom(int[] x, int[] z, int cIn)
+    {
+        long c = cIn & M;
+        c += (z[0] & M) - (x[0] & M);
+        z[0] = (int)c;
+        c >>= 32;
+        c += (z[1] & M) - (x[1] & M);
+        z[1] = (int)c;
+        c >>= 32;
+        c += (z[2] & M) - (x[2] & M);
+        z[2] = (int)c;
+        c >>= 32;
+        c += (z[3] & M) - (x[3] & M);
+        z[3] = (int)c;
+        c >>= 32;
+        c += (z[4] & M) - (x[4] & M);
+        z[4] = (int)c;
+        c >>= 32;
+        c += (z[5] & M) - (x[5] & M);
+        z[5] = (int)c;
+        c >>= 32;
+        c += (z[6] & M) - (x[6] & M);
+        z[6] = (int)c;
+        c >>= 32;
+        c += (z[7] & M) - (x[7] & M);
+        z[7] = (int)c;
+        c >>= 32;
+        return (int)c;
+    }
+
     public static int subFrom(int[] x, int xOff, int[] z, int zOff)
     {
         long c = 0;
@@ -1381,6 +1512,36 @@
         return (int)c;
     }
 
+    public static int subFrom(int[] x, int xOff, int[] z, int zOff, int cIn)
+    {
+        long c = cIn & M;
+        c += (z[zOff + 0] & M) - (x[xOff + 0] & M);
+        z[zOff + 0] = (int)c;
+        c >>= 32;
+        c += (z[zOff + 1] & M) - (x[xOff + 1] & M);
+        z[zOff + 1] = (int)c;
+        c >>= 32;
+        c += (z[zOff + 2] & M) - (x[xOff + 2] & M);
+        z[zOff + 2] = (int)c;
+        c >>= 32;
+        c += (z[zOff + 3] & M) - (x[xOff + 3] & M);
+        z[zOff + 3] = (int)c;
+        c >>= 32;
+        c += (z[zOff + 4] & M) - (x[xOff + 4] & M);
+        z[zOff + 4] = (int)c;
+        c >>= 32;
+        c += (z[zOff + 5] & M) - (x[xOff + 5] & M);
+        z[zOff + 5] = (int)c;
+        c >>= 32;
+        c += (z[zOff + 6] & M) - (x[xOff + 6] & M);
+        z[zOff + 6] = (int)c;
+        c >>= 32;
+        c += (z[zOff + 7] & M) - (x[xOff + 7] & M);
+        z[zOff + 7] = (int)c;
+        c >>= 32;
+        return (int)c;
+    }
+
     public static BigInteger toBigInteger(int[] x)
     {
         byte[] bs = new byte[32];
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/util/Arrays.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/util/Arrays.java
index 3ce0b11..fdb5d27 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/util/Arrays.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/util/Arrays.java
@@ -151,6 +151,47 @@
         return 0 == d;
     }
 
+    /**
+     * A constant time equals comparison - does not terminate early if
+     * comparison fails. For best results always pass the expected value
+     * as the first parameter.
+     *
+     * @param expected first array
+     * @param supplied second array
+     * @return true if arrays equal, false otherwise.
+     */
+    public static boolean constantTimeAreEqual(
+        char[] expected,
+        char[] supplied)
+    {
+        if (expected == null || supplied == null)
+        {
+            return false;
+        }
+
+        if (expected == supplied)
+        {
+            return true;
+        }
+
+        int len = Math.min(expected.length, supplied.length);
+
+        int nonEqual = expected.length ^ supplied.length;
+
+        // do the char-wise comparison
+        for (int i = 0; i != len; i++)
+        {
+            nonEqual |= (expected[i] ^ supplied[i]);
+        }
+        // If supplied is longer than expected, iterate over rest of supplied with NOPs
+        for (int i = len; i < supplied.length; i++)
+        {
+            nonEqual |= ((byte)supplied[i] ^ (byte)~supplied[i]);
+        }
+
+        return nonEqual == 0;
+    }
+
     public static int compareUnsigned(byte[] a, byte[] b)
     {
         if (a == b)
@@ -276,14 +317,6 @@
         java.util.Arrays.fill(a, val);
     }
 
-    /**
-     * @deprecated Use {@link #fill(byte[], int, int, byte)} instead.
-     */
-    public static void fill(byte[] a, int fromIndex, byte val)
-    {
-        fill(a, fromIndex, a.length, val);
-    }
-
     public static void fill(byte[] a, int fromIndex, int toIndex, byte val)
     {
         java.util.Arrays.fill(a, fromIndex, toIndex, val);
@@ -304,14 +337,6 @@
         java.util.Arrays.fill(a, val);
     }
 
-    /**
-     * @deprecated Use {@link #fill(int[], int, int, int)} instead.
-     */
-    public static void fill(int[] a, int fromIndex, int val)
-    {
-        java.util.Arrays.fill(a, fromIndex, a.length, val);
-    }
-
     public static void fill(int[] a, int fromIndex, int toIndex, int val)
     {
         java.util.Arrays.fill(a, fromIndex, toIndex, val);
@@ -322,14 +347,6 @@
         java.util.Arrays.fill(a, val);
     }
 
-    /**
-     * @deprecated Use {@link #fill(long[], int, int, long)} instead.
-     */
-    public static void fill(long[] a, int fromIndex, long val)
-    {
-        java.util.Arrays.fill(a, fromIndex, a.length, val);
-    }
-
     public static void fill(long[] a, int fromIndex, int toIndex, long val)
     {
         java.util.Arrays.fill(a, fromIndex, toIndex, val);
@@ -350,14 +367,6 @@
         java.util.Arrays.fill(a, val);
     }
 
-    /**
-     * @deprecated Use {@link #fill(short[], int, int, short)} instead.
-     */
-    public static void fill(short[] a, int fromIndex, short val)
-    {
-        java.util.Arrays.fill(a, fromIndex, a.length, val);
-    }
-
     public static void fill(short[] a, int fromIndex, int toIndex, short val)
     {
         java.util.Arrays.fill(a, fromIndex, toIndex, val);
@@ -796,9 +805,7 @@
         int newLength = to - from;
         if (newLength < 0)
         {
-            StringBuffer sb = new StringBuffer(from);
-            sb.append(" > ").append(to);
-            throw new IllegalArgumentException(sb.toString());
+            throw new IllegalArgumentException(from + " > " + to);
         }
         return newLength;
     }
@@ -1065,6 +1072,80 @@
         return result;
     }
 
+    public static void reverse(byte[] input, byte[] output)
+    {
+        int last = input.length - 1;
+        for (int i = 0; i <= last; ++i)
+        {
+            output[i] = input[last - i];
+        }
+    }
+
+    public static byte[] reverseInPlace(byte[] a)
+    {
+        if (null == a)
+        {
+            return null;
+        }
+
+        int p1 = 0, p2 = a.length - 1;
+        while (p1 < p2)
+        {
+            byte t1 = a[p1], t2 = a[p2];
+            a[p1++] = t2;
+            a[p2--] = t1;
+        }
+
+        return a;
+    }
+
+    public static void reverseInPlace(byte[] a, int aOff, int aLen)
+    {
+        int p1 = aOff, p2 = aOff + aLen - 1;
+        while (p1 < p2)
+        {
+            byte t1 = a[p1], t2 = a[p2];
+            a[p1++] = t2;
+            a[p2--] = t1;
+        }
+    }
+
+    public static short[] reverseInPlace(short[] a)
+    {
+        if (null == a)
+        {
+            return null;
+        }
+
+        int p1 = 0, p2 = a.length - 1;
+        while (p1 < p2)
+        {
+            short t1 = a[p1], t2 = a[p2];
+            a[p1++] = t2;
+            a[p2--] = t1;
+        }
+
+        return a;
+    }
+
+    public static int[] reverseInPlace(int[] a)
+    {
+        if (null == a)
+        {
+            return null;
+        }
+
+        int p1 = 0, p2 = a.length - 1;
+        while (p1 < p2)
+        {
+            int t1 = a[p1], t2 = a[p2];
+            a[p1++] = t2;
+            a[p2--] = t1;
+        }
+
+        return a;
+    }
+
     /**
      * Iterator backed by a specific array.
      * @hide This class is not part of the Android public SDK API
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/util/BigIntegers.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/util/BigIntegers.java
index 62a5367..9d6749b 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/util/BigIntegers.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/util/BigIntegers.java
@@ -3,6 +3,11 @@
 
 import java.math.BigInteger;
 import java.security.SecureRandom;
+import java.util.Map;
+import java.util.WeakHashMap;
+
+import com.android.internal.org.bouncycastle.math.raw.Mod;
+import com.android.internal.org.bouncycastle.math.raw.Nat;
 
 import com.android.internal.org.bouncycastle.math.raw.Mod;
 import com.android.internal.org.bouncycastle.math.raw.Nat;
@@ -23,7 +28,7 @@
 
     /**
      * Return the passed in value as an unsigned byte array.
-     * 
+     *
      * @param value the value to be converted.
      * @return a byte array without a leading zero byte if present in the signed encoding.
      */
@@ -31,16 +36,15 @@
         BigInteger value)
     {
         byte[] bytes = value.toByteArray();
-        
         if (bytes[0] == 0 && bytes.length != 1)
         {
             byte[] tmp = new byte[bytes.length - 1];
-            
+
             System.arraycopy(bytes, 1, tmp, 0, tmp.length);
-            
+
             return tmp;
         }
-        
+
         return bytes;
     }
 
@@ -48,10 +52,8 @@
      * Return the passed in value as an unsigned byte array of the specified length, padded with
      * leading zeros as necessary..
      *
-     * @param length
-     *            the fixed length of the result
-     * @param value
-     *            the value to be converted.
+     * @param length the fixed length of the result
+     * @param value  the value to be converted.
      * @return a byte array padded to a fixed length with leading zeros.
      */
     public static byte[] asUnsignedByteArray(int length, BigInteger value)
@@ -79,14 +81,10 @@
      * Write the passed in value as unsigned bytes to the specified buffer range, padded with
      * leading zeros as necessary.
      *
-     * @param value
-     *            the value to be converted.
-     * @param buf
-     *            the buffer to which the value is written.
-     * @param off
-     *            the start offset in array <code>buf</code> at which the data is written.
-     * @param len
-     *            the fixed length of data written (possibly padded with leading zeros).
+     * @param value the value to be converted.
+     * @param buf   the buffer to which the value is written.
+     * @param off   the start offset in array <code>buf</code> at which the data is written.
+     * @param len   the fixed length of data written (possibly padded with leading zeros).
      */
     public static void asUnsignedByteArray(BigInteger value, byte[] buf, int off, int len)
     {
@@ -106,22 +104,23 @@
         }
 
         int padLen = len - count;
-        Arrays.fill(buf,  off, off + padLen, (byte)0x00);
+        Arrays.fill(buf, off, off + padLen, (byte)0x00);
         System.arraycopy(bytes, start, buf, off + padLen, count);
     }
 
+
     /**
      * Return a random BigInteger not less than 'min' and not greater than 'max'
-     * 
-     * @param min the least value that may be generated
-     * @param max the greatest value that may be generated
+     *
+     * @param min    the least value that may be generated
+     * @param max    the greatest value that may be generated
      * @param random the source of randomness
      * @return a random BigInteger value in the range [min,max]
      */
     public static BigInteger createRandomInRange(
-        BigInteger      min,
-        BigInteger      max,
-        SecureRandom    random)
+        BigInteger min,
+        BigInteger max,
+        SecureRandom random)
     {
         int cmp = min.compareTo(max);
         if (cmp >= 0)
@@ -152,6 +151,7 @@
         return createRandomBigInteger(max.subtract(min).bitLength() - 1, random).add(min);
     }
 
+
     public static BigInteger fromUnsignedByteArray(byte[] buf)
     {
         return new BigInteger(1, buf);
@@ -168,6 +168,28 @@
         return new BigInteger(1, mag);
     }
 
+    public static byte byteValueExact(BigInteger x)
+    {
+        // Since Java 1.8 could use BigInteger.byteValueExact instead
+        if (x.bitLength() > 7)
+        {
+            throw new ArithmeticException("BigInteger out of int range");
+        }
+
+        return x.byteValue();
+    }
+
+    public static short shortValueExact(BigInteger x)
+    {
+        // Since Java 1.8 could use BigInteger.shortValueExact instead
+        if (x.bitLength() > 15)
+        {
+            throw new ArithmeticException("BigInteger out of int range");
+        }
+
+        return x.shortValue();
+    }
+
     public static int intValueExact(BigInteger x)
     {
         // Since Java 1.8 could use BigInteger.intValueExact instead
@@ -176,7 +198,7 @@
             throw new ArithmeticException("BigInteger out of int range");
         }
 
-        return x.intValue(); 
+        return x.intValue();
     }
 
     public static long longValueExact(BigInteger x)
@@ -187,7 +209,7 @@
             throw new ArithmeticException("BigInteger out of long range");
         }
 
-        return x.longValue(); 
+        return x.longValue();
     }
 
     public static BigInteger modOddInverse(BigInteger M, BigInteger X)
@@ -266,7 +288,7 @@
      * Return a positive BigInteger in the range of 0 to 2**bitLength - 1.
      *
      * @param bitLength maximum bit length for the generated BigInteger.
-     * @param random a source of randomness.
+     * @param random    a source of randomness.
      * @return a positive BigInteger
      */
     public static BigInteger createRandomBigInteger(int bitLength, SecureRandom random)
@@ -276,7 +298,7 @@
 
     // Hexadecimal value of the product of the 131 smallest odd primes from 3 to 743
     private static final BigInteger SMALL_PRIMES_PRODUCT = new BigInteger(
-              "8138e8a0fcf3a4e84a771d40fd305d7f4aa59306d7251de54d98af8fe95729a1f"
+        "8138e8a0fcf3a4e84a771d40fd305d7f4aa59306d7251de54d98af8fe95729a1f"
             + "73d893fa424cd2edc8636a6c3285e022b0e3866a565ae8108eed8591cd4fe8d2"
             + "ce86165a978d719ebf647f362d33fca29cd179fb42401cbaf3df0c614056f9c8"
             + "f3cfd51e474afb6bc6974f78db8aba8e9e517fded658591ab7502bd41849462f",
@@ -287,7 +309,7 @@
      * Return a prime number candidate of the specified bit length.
      *
      * @param bitLength bit length for the generated BigInteger.
-     * @param random a source of randomness.
+     * @param random    a source of randomness.
      * @return a positive BigInteger of numBits length
      */
     public static BigInteger createRandomPrime(int bitLength, int certainty, SecureRandom random)
@@ -349,4 +371,41 @@
 
         return rv;
     }
+
+    /**
+     * @hide This class is not part of the Android public SDK API
+     */
+    public static class Cache
+    {
+        private final Map<BigInteger, Boolean> values = new WeakHashMap<BigInteger, Boolean>();
+        private final BigInteger[] preserve = new BigInteger[8];
+
+        private int preserveCounter = 0;
+
+        public synchronized void add(BigInteger value)
+        {
+            values.put(value, Boolean.TRUE);
+            preserve[preserveCounter] = value;
+            preserveCounter = (preserveCounter + 1) % preserve.length;
+        }
+
+        public synchronized boolean contains(BigInteger value)
+        {
+            return values.containsKey(value);
+        }
+
+        public synchronized int size()
+        {
+            return values.size();
+        }
+
+        public synchronized void clear()
+        {
+            values.clear();
+            for (int i = 0; i != preserve.length; i++)
+            {
+                preserve[i] = null;
+            }
+        }
+    }
 }
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/util/Bytes.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/util/Bytes.java
new file mode 100644
index 0000000..35860bb
--- /dev/null
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/util/Bytes.java
@@ -0,0 +1,44 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.util;
+
+/**
+ * Utility methods and constants for bytes.
+ * @hide This class is not part of the Android public SDK API
+ */
+public class Bytes
+{
+    public static final int BYTES = 1;
+    public static final int SIZE = Byte.SIZE;
+
+    public static void xor(int len, byte[] x, byte[] y, byte[] z)
+    {
+        for (int i = 0; i < len; ++i)
+        {
+            z[i] = (byte)(x[i] ^ y[i]);
+        }
+    }
+
+    public static void xor(int len, byte[] x, int xOff, byte[] y, int yOff, byte[] z, int zOff)
+    {
+        for (int i = 0; i < len; ++i)
+        {
+            z[zOff + i] = (byte)(x[xOff + i] ^ y[yOff + i]);
+        }
+    }
+
+    public static void xorTo(int len, byte[] x, byte[] z)
+    {
+        for (int i = 0; i < len; ++i)
+        {
+            z[i] ^= x[i];
+        }
+    }
+
+    public static void xorTo(int len, byte[] x, int xOff, byte[] z, int zOff)
+    {
+        for (int i = 0; i < len; ++i)
+        {
+            z[zOff + i] ^= x[xOff + i];
+        }
+    }
+}
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/util/Characters.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/util/Characters.java
new file mode 100644
index 0000000..c5e2df8
--- /dev/null
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/util/Characters.java
@@ -0,0 +1,13 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.util;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+public class Characters
+{
+    public static Character valueOf(char c)
+    {
+        return Character.valueOf(c);
+    }
+}
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/util/Exceptions.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/util/Exceptions.java
new file mode 100644
index 0000000..3d2f233
--- /dev/null
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/util/Exceptions.java
@@ -0,0 +1,26 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.util;
+
+import java.io.IOException;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+public class Exceptions
+{
+    public static IllegalArgumentException illegalArgumentException(String message, Throwable cause)
+    {
+        return new IllegalArgumentException(message, cause);
+    }
+
+    public static IllegalStateException illegalStateException(String message, Throwable cause)
+    {
+        return new IllegalStateException(message, cause);
+    }
+
+    public static IOException ioException(String message, Throwable cause)
+    {
+        return new IOException(message, cause);
+    }
+
+}
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/util/IPAddress.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/util/IPAddress.java
index 47c159a..29d9f75 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/util/IPAddress.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/util/IPAddress.java
@@ -14,8 +14,7 @@
      *
      * @return true if a valid address, false otherwise
      */
-    public static boolean isValid(
-        String address)
+    public static boolean isValid(String address)
     {
         return isValidIPv4(address) || isValidIPv6(address);
     }
@@ -27,8 +26,7 @@
      *
      * @return true if a valid address with netmask, false otherwise
      */
-    public static boolean isValidWithNetMask(
-        String address)
+    public static boolean isValidWithNetMask(String address)
     {
         return isValidIPv4WithNetmask(address) || isValidIPv6WithNetmask(address);
     }
@@ -40,79 +38,42 @@
      *
      * @return true if a valid IPv4 address, false otherwise
      */
-    public static boolean isValidIPv4(
-        String address)
+    public static boolean isValidIPv4(String address)
     {
-        if (address.length() == 0)
+        int length = address.length();
+        if (length < 7 || length > 15)
         {
             return false;
         }
 
-        int octet;
-        int octets = 0;
-        
-        String temp = address+".";
-
-        int pos;
-        int start = 0;
-        while (start < temp.length()
-            && (pos = temp.indexOf('.', start)) > start)
+        int pos = 0;
+        for (int octetIndex = 0; octetIndex < 3; ++octetIndex)
         {
-            if (octets == 4)
+            int end = address.indexOf('.', pos);
+
+            if (!isParseableIPv4Octet(address, pos, end))
             {
                 return false;
             }
-            try
-            {
-                octet = Integer.parseInt(temp.substring(start, pos));
-            }
-            catch (NumberFormatException ex)
-            {
-                return false;
-            }
-            if (octet < 0 || octet > 255)
-            {
-                return false;
-            }
-            start = pos + 1;
-            octets++;
+
+            pos = end + 1;
         }
 
-        return octets == 4;
+        return isParseableIPv4Octet(address, pos, length);
     }
 
-    public static boolean isValidIPv4WithNetmask(
-        String address)
+    public static boolean isValidIPv4WithNetmask(String address)
     {
         int index = address.indexOf("/");
-        String mask = address.substring(index + 1);
-
-        return (index > 0) && isValidIPv4(address.substring(0, index))
-                           && (isValidIPv4(mask) || isMaskValue(mask, 32));
-    }
-
-    public static boolean isValidIPv6WithNetmask(
-        String address)
-    {
-        int index = address.indexOf("/");
-        String mask = address.substring(index + 1);
-
-        return (index > 0) && (isValidIPv6(address.substring(0, index))
-                           && (isValidIPv6(mask) || isMaskValue(mask, 128)));
-    }
-
-    private static boolean isMaskValue(String component, int size)
-    {
-        try
-        {
-            int value = Integer.parseInt(component);
-
-            return value >= 0 && value <= size;
-        }
-        catch (NumberFormatException e)
+        if (index < 1)
         {
             return false;
         }
+
+        String before = address.substring(0, index);
+        String after = address.substring(index + 1);
+
+        return isValidIPv4(before) && (isValidIPv4(after) || isParseableIPv4Mask(after));
     }
 
     /**
@@ -122,72 +83,131 @@
      *
      * @return true if a valid IPv6 address, false otherwise
      */
-    public static boolean isValidIPv6(
-        String address)
+    public static boolean isValidIPv6(String address)
     {
         if (address.length() == 0)
         {
             return false;
         }
 
-        int octet;
-        int octets = 0;
+        char firstChar = address.charAt(0);
+        if (firstChar != ':' && Character.digit(firstChar, 16) < 0)
+        {
+            return false;
+        }        
 
+        int segmentCount = 0;
         String temp = address + ":";
         boolean doubleColonFound = false;
-        int pos;
-        int start = 0;
-        while (start < temp.length()
-            && (pos = temp.indexOf(':', start)) >= start)
+
+        int pos = 0, end;
+        while (pos < temp.length() && (end = temp.indexOf(':', pos)) >= pos)
         {
-            if (octets == 8)
+            if (segmentCount == 8)
             {
                 return false;
             }
 
-            if (start != pos)
+            if (pos != end)
             {
-                String value = temp.substring(start, pos);
+                String value = temp.substring(pos, end);
 
-                if (pos == (temp.length() - 1) && value.indexOf('.') > 0)
+                if (end == temp.length() - 1 && value.indexOf('.') > 0)
                 {
+                    // add an extra one as address covers 2 words.
+                    if (++segmentCount == 8)
+                    {
+                        return false;
+                    }
                     if (!isValidIPv4(value))
                     {
                         return false;
                     }
-
-                    octets++; // add an extra one as address covers 2 words.
                 }
-                else
+                else if (!isParseableIPv6Segment(temp, pos, end))
                 {
-                    try
-                    {
-                        octet = Integer.parseInt(temp.substring(start, pos), 16);
-                    }
-                    catch (NumberFormatException ex)
-                    {
-                        return false;
-                    }
-                    if (octet < 0 || octet > 0xffff)
-                    {
-                        return false;
-                    }
+                    return false;
                 }
             }
             else
             {
-                if (pos != 1 && pos != temp.length() - 1 && doubleColonFound)
+                if (end != 1 && end != temp.length() - 1 && doubleColonFound)
                 {
                     return false;
                 }
                 doubleColonFound = true;
             }
-            start = pos + 1;
-            octets++;
+
+            pos = end + 1;
+            ++segmentCount;
         }
 
-        return octets == 8 || doubleColonFound;
+        return segmentCount == 8 || doubleColonFound;
+    }
+
+    public static boolean isValidIPv6WithNetmask(String address)
+    {
+        int index = address.indexOf("/");
+        if (index < 1)
+        {
+            return false;
+        }
+
+        String before = address.substring(0, index);
+        String after = address.substring(index + 1);
+
+        return isValidIPv6(before) && (isValidIPv6(after) || isParseableIPv6Mask(after));
+    }
+
+    private static boolean isParseableIPv4Mask(String s)
+    {
+        return isParseable(s, 0, s.length(), 10, 2, false, 0, 32);
+    }
+
+    private static boolean isParseableIPv4Octet(String s, int pos, int end)
+    {
+        return isParseable(s, pos, end, 10, 3, true, 0, 255);
+    }
+
+    private static boolean isParseableIPv6Mask(String s)
+    {
+        return isParseable(s, 0, s.length(), 10, 3, false, 1, 128);
+    }
+
+    private static boolean isParseableIPv6Segment(String s, int pos, int end)
+    {
+        return isParseable(s, pos, end, 16, 4, true, 0x0000, 0xFFFF);
+    }
+
+    private static boolean isParseable(String s, int pos, int end, int radix, int maxLength, boolean allowLeadingZero,
+        int minValue, int maxValue)
+    {
+        int length = end - pos;
+        if (length < 1 | length > maxLength)
+        {
+            return false;
+        }
+
+        boolean checkLeadingZero = length > 1 & !allowLeadingZero; 
+        if (checkLeadingZero && Character.digit(s.charAt(pos), radix) <= 0)
+        {
+            return false;
+        }
+
+        int value = 0;
+        while (pos < end)
+        {
+            char c = s.charAt(pos++);
+            int d = Character.digit(c, radix);
+            if (d < 0)
+            {
+                return false;
+            }
+
+            value *= radix;
+            value += d;
+        }
+
+        return value >= minValue & value <= maxValue;
     }
 }
-
-
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/util/Integers.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/util/Integers.java
index 09db121..0886aa3 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/util/Integers.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/util/Integers.java
@@ -2,11 +2,29 @@
 package com.android.internal.org.bouncycastle.util;
 
 /**
- * Utility methods for ints.
+ * Utility methods and constants for ints.
  * @hide This class is not part of the Android public SDK API
  */
 public class Integers
 {
+    public static final int BYTES = 4;
+    public static final int SIZE = Integer.SIZE;
+
+    public static int bitCount(int i)
+    {
+        return Integer.bitCount(i);
+    }
+
+    public static int highestOneBit(int i)
+    {
+        return Integer.highestOneBit(i);
+    }
+
+    public static int lowestOneBit(int i)
+    {
+        return Integer.lowestOneBit(i);
+    }
+
     public static int numberOfLeadingZeros(int i)
     {
         return Integer.numberOfLeadingZeros(i);
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/util/Longs.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/util/Longs.java
index f41ee5f..6260246 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/util/Longs.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/util/Longs.java
@@ -2,10 +2,34 @@
 package com.android.internal.org.bouncycastle.util;
 
 /**
+ * Utility methods and constants for longs.
  * @hide This class is not part of the Android public SDK API
  */
 public class Longs
 {
+    public static final int BYTES = 8;
+    public static final int SIZE = Long.SIZE;
+
+    public static long highestOneBit(long i)
+    {
+        return Long.highestOneBit(i);
+    }
+
+    public static long lowestOneBit(long i)
+    {
+        return Long.lowestOneBit(i);
+    }
+
+    public static int numberOfLeadingZeros(long i)
+    {
+        return Long.numberOfLeadingZeros(i);
+    }
+
+    public static int numberOfTrailingZeros(long i)
+    {
+        return Long.numberOfTrailingZeros(i);
+    }
+
     public static long reverse(long i)
     {
         return Long.reverse(i);
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/util/Memoable.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/util/Memoable.java
index 75ce09a..7f84a44 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/util/Memoable.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/util/Memoable.java
@@ -3,7 +3,7 @@
 
 /**
  * Interface for Memoable objects. Memoable objects allow the taking of a snapshot of their internal state
- * via the copy() method and then reseting the object back to that state later using the reset() method.
+ * via the copy() method and then resetting the object back to that state later using the reset() method.
  * @hide This class is not part of the Android public SDK API
  */
 public interface Memoable
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/util/Pack.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/util/Pack.java
index ef00137..5ce877a 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/util/Pack.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/util/Pack.java
@@ -149,7 +149,6 @@
      * @param bs    The target.
      * @param off   Position in target to start.
      * @param bytes number of bytes to write.
-     * 
      * @deprecated Will be removed
      */
     public static void longToBigEndian(long value, byte[] bs, int off, int bytes)
@@ -177,6 +176,25 @@
         return n;
     }
 
+    public static int littleEndianToInt_High(byte[] bs, int off, int len)
+    {
+        return littleEndianToInt_Low(bs, off, len) << ((4 - len) << 3);
+    }
+
+    public static int littleEndianToInt_Low(byte[] bs, int off, int len)
+    {
+//        assert 1 <= len && len <= 4;
+
+        int result = bs[off] & 0xff;
+        int pos = 0;
+        for (int i = 1; i < len; ++i)
+        {
+            pos += 8;
+            result |= (bs[off + i] & 0xff) << pos;
+        }
+        return result;
+    }
+
     public static void littleEndianToInt(byte[] bs, int off, int[] ns)
     {
         for (int i = 0; i < ns.length; ++i)
@@ -299,6 +317,40 @@
         }
     }
 
+    public static void longToLittleEndian_High(long n, byte[] bs, int off, int len)
+    {
+        //Debug.Assert(1 <= len && len <= 8);
+        int pos = 56;
+        bs[off] = (byte)(n >>> pos);
+        for (int i = 1; i < len; ++i)
+        {
+            pos -= 8;
+            bs[off + i] = (byte)(n >>> pos);
+        }
+    }
+
+//    public static void longToLittleEndian_Low(long n, byte[] bs, int off, int len)
+//    {
+//        longToLittleEndian_High(n << ((8 - len) << 3), bs, off, len);
+//    }
+
+    public static long littleEndianToLong_High(byte[] bs, int off, int len)
+    {
+        return littleEndianToLong_Low(bs, off, len) << ((8 - len) << 3);
+    }
+
+    public static long littleEndianToLong_Low(byte[] bs, int off, int len)
+    {
+        //Debug.Assert(1 <= len && len <= 8);
+        long result = bs[off] & 0xFF;
+        for (int i = 1; i < len; ++i)
+        {
+            result <<= 8;
+            result |= bs[off + i] & 0xFF;
+        }
+        return result;
+    }
+
     public static byte[] longToLittleEndian(long n)
     {
         byte[] bs = new byte[8];
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/util/Properties.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/util/Properties.java
index 4817548..2935389 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/util/Properties.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/util/Properties.java
@@ -14,11 +14,18 @@
 import java.util.StringTokenizer;
 
 /**
- * Utility method for accessing system properties.
+ * Utility method for accessing properties values - properties can be set in java.security,
+ * thread local, and system properties. They are checked for in the same order with
+ * checking stopped as soon as a value is found.
  * @hide This class is not part of the Android public SDK API
  */
 public class Properties
 {
+    /**
+     * If set the provider will attempt, where possible, to behave the same way as the oracle one.
+     */
+    public static final String EMULATE_ORACLE = "com.android.internal.org.bouncycastle.emulate.oracle";
+
     private Properties()
     {
     }
@@ -117,6 +124,31 @@
         return false;
     }
 
+    /**
+     * Return propertyName as an integer, defaultValue used if not defined.
+     *
+     * @param propertyName name of property.
+     * @param defaultValue integer to return if property not defined.
+     * @return value of property, or default if not found, as an int.
+     */
+    public static int asInteger(String propertyName, int defaultValue)
+    {
+        String p = getPropertyValue(propertyName);
+
+        if (p != null)
+        {
+            return Integer.parseInt(p);
+        }
+
+        return defaultValue;
+    }
+
+    /**
+     * Return propertyName as a BigInteger.
+     *
+     * @param propertyName name of property.
+     * @return value of property as a BigInteger, null if not defined.
+     */
     public static BigInteger asBigInteger(String propertyName)
     {
         String p = getPropertyValue(propertyName);
@@ -147,6 +179,13 @@
         return Collections.unmodifiableSet(set);
     }
 
+    /**
+     * Return the String value of the property propertyName. Property valuation
+     * starts with java.security, then thread local, then system properties.
+     *
+     * @param propertyName name of property.
+     * @return value of property as a String, null if not defined.
+     */
     public static String getPropertyValue(final String propertyName)
     {
         String val = (String)AccessController.doPrivileged(new PrivilegedAction()
@@ -180,6 +219,18 @@
         });
     }
 
+    public static String getPropertyValue(final String propertyName,  String defValue)
+    {
+        String rv = getPropertyValue(propertyName);
+
+        if (rv == null)
+        {
+            return defValue;
+        }
+
+        return rv;
+    }
+
     private static boolean isSetFalse(String p)
     {
         if (p == null || p.length() != 5)
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/util/Strings.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/util/Strings.java
index dfbc7f9..35a0f43 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/util/Strings.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/util/Strings.java
@@ -58,6 +58,17 @@
         return new String(chars, 0, len);
     }
 
+    public static String fromUTF8ByteArray(byte[] bytes, int off, int length)
+    {
+        char[] chars = new char[length];
+        int len = UTF8.transcodeToUTF16(bytes, off, length, chars);
+        if (len < 0)
+        {
+            throw new IllegalArgumentException("Invalid UTF-8 input");
+        }
+        return new String(chars, 0, len);
+    }
+
     public static byte[] toUTF8ByteArray(String string)
     {
         return toUTF8ByteArray(string.toCharArray());
@@ -230,6 +241,37 @@
     }
 
     /**
+     * Constant time string comparison.
+     *
+     * @param a a string.
+     * @param b another string to compare to a.
+     *
+     * @return true if a and b represent the same string, false otherwise.
+     */
+    public static boolean constantTimeAreEqual(String a, String b)
+    {
+        boolean isEqual = a.length() == b.length();
+        int     len = a.length();
+
+        if (isEqual)
+        {
+            for (int i = 0; i != len; i++)
+            {
+                isEqual &= (a.charAt(i) == b.charAt(i));
+            }
+        }
+        else
+        {
+            for (int i = 0; i != len; i++)
+            {
+                isEqual &= (a.charAt(i) == ' ');
+            }
+        }
+
+        return isEqual;
+    }
+
+    /**
      * Convert an array of 8 bit characters into a string.
      *
      * @param bytes 8 bit characters.
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/util/encoders/Base32.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/util/encoders/Base32.java
new file mode 100644
index 0000000..61fa7f2
--- /dev/null
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/util/encoders/Base32.java
@@ -0,0 +1,176 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.util.encoders;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+import com.android.internal.org.bouncycastle.util.Strings;
+
+/**
+ * Utility class for converting Base32 data to bytes and back again.
+ * @hide This class is not part of the Android public SDK API
+ */
+public class Base32
+{
+    private static final Encoder encoder = new Base32Encoder();
+
+    public static String toBase32String(
+        byte[] data)
+    {
+        return toBase32String(data, 0, data.length);
+    }
+
+    public static String toBase32String(
+        byte[] data,
+        int off,
+        int length)
+    {
+        byte[] encoded = encode(data, off, length);
+        return Strings.fromByteArray(encoded);
+    }
+
+    /**
+     * encode the input data producing a base 32 encoded byte array.
+     *
+     * @return a byte array containing the base 32 encoded data.
+     */
+    public static byte[] encode(
+        byte[] data)
+    {
+        return encode(data, 0, data.length);
+    }
+
+    /**
+     * encode the input data producing a base 32 encoded byte array.
+     *
+     * @return a byte array containing the base 32 encoded data.
+     */
+    public static byte[] encode(
+        byte[] data,
+        int off,
+        int length)
+    {
+        int len = encoder.getEncodedLength(length);
+        ByteArrayOutputStream bOut = new ByteArrayOutputStream(len);
+
+        try
+        {
+            encoder.encode(data, off, length, bOut);
+        }
+        catch (Exception e)
+        {
+            throw new EncoderException("exception encoding base32 string: " + e.getMessage(), e);
+        }
+
+        return bOut.toByteArray();
+    }
+
+    /**
+     * Encode the byte data to base 32 writing it to the given output stream.
+     *
+     * @return the number of bytes produced.
+     */
+    public static int encode(
+        byte[] data,
+        OutputStream out)
+        throws IOException
+    {
+        return encoder.encode(data, 0, data.length, out);
+    }
+
+    /**
+     * Encode the byte data to base 32 writing it to the given output stream.
+     *
+     * @return the number of bytes produced.
+     */
+    public static int encode(
+        byte[] data,
+        int off,
+        int length,
+        OutputStream out)
+        throws IOException
+    {
+        return encoder.encode(data, off, length, out);
+    }
+
+    /**
+     * decode the base 32 encoded input data. It is assumed the input data is valid.
+     *
+     * @return a byte array representing the decoded data.
+     */
+    public static byte[] decode(
+        byte[] data)
+    {
+        int len = data.length / 8 * 5;
+        ByteArrayOutputStream bOut = new ByteArrayOutputStream(len);
+
+        try
+        {
+            encoder.decode(data, 0, data.length, bOut);
+        }
+        catch (Exception e)
+        {
+            throw new DecoderException("unable to decode base32 data: " + e.getMessage(), e);
+        }
+
+        return bOut.toByteArray();
+    }
+
+    /**
+     * decode the base 32 encoded String data - whitespace will be ignored.
+     *
+     * @return a byte array representing the decoded data.
+     */
+    public static byte[] decode(
+        String data)
+    {
+        int len = data.length() / 8 * 5;
+        ByteArrayOutputStream bOut = new ByteArrayOutputStream(len);
+
+        try
+        {
+            encoder.decode(data, bOut);
+        }
+        catch (Exception e)
+        {
+            throw new DecoderException("unable to decode base32 string: " + e.getMessage(), e);
+        }
+
+        return bOut.toByteArray();
+    }
+
+    /**
+     * decode the base 32 encoded String data writing it to the given output stream,
+     * whitespace characters will be ignored.
+     *
+     * @return the number of bytes produced.
+     */
+    public static int decode(
+        String data,
+        OutputStream out)
+        throws IOException
+    {
+        return encoder.decode(data, out);
+    }
+
+    /**
+     * Decode to an output stream;
+     *
+     * @param base32Data       The source data.
+     * @param start            Start position.
+     * @param length           the length.
+     * @param out The output stream to write to.
+     */
+    public static int decode(byte[] base32Data, int start, int length, OutputStream out)
+    {
+        try
+        {
+           return encoder.decode(base32Data, start, length, out);
+        }
+        catch (Exception e)
+        {
+            throw new DecoderException("unable to decode base32 data: " + e.getMessage(), e);
+        }
+    }
+}
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/util/encoders/Base32Encoder.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/util/encoders/Base32Encoder.java
new file mode 100644
index 0000000..2711d7d
--- /dev/null
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/util/encoders/Base32Encoder.java
@@ -0,0 +1,455 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.internal.org.bouncycastle.util.encoders;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+import com.android.internal.org.bouncycastle.util.Arrays;
+import com.android.internal.org.bouncycastle.util.Strings;
+
+/**
+ * A streaming Base32 encoder.
+ * @hide This class is not part of the Android public SDK API
+ */
+public class Base32Encoder
+    implements Encoder
+{
+    private static final byte[] DEAULT_ENCODING_TABLE =
+    {
+        (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F', (byte)'G',
+        (byte)'H', (byte)'I', (byte)'J', (byte)'K', (byte)'L', (byte)'M', (byte)'N',
+        (byte)'O', (byte)'P', (byte)'Q', (byte)'R', (byte)'S', (byte)'T', (byte)'U',
+        (byte)'V', (byte)'W', (byte)'X', (byte)'Y', (byte)'Z',
+        (byte)'2', (byte)'3', (byte)'4', (byte)'5', (byte)'6', (byte)'7'
+    };
+
+    private static final byte DEFAULT_PADDING = (byte)'=';
+
+    /*
+     * set up the decoding table.
+     */
+    private final byte[] encodingTable;
+    private final byte   padding;
+    private final byte[] decodingTable = new byte[128];
+
+    protected void initialiseDecodingTable()
+    {
+        for (int i = 0; i < decodingTable.length; i++)
+        {
+            decodingTable[i] = (byte)0xff;
+        }
+
+        for (int i = 0; i < encodingTable.length; i++)
+        {
+            decodingTable[encodingTable[i]] = (byte)i;
+        }
+    }
+
+    /**
+     * Base constructor for RFC 4648, Section 6.
+     */
+    public Base32Encoder()
+    {
+        this.encodingTable = DEAULT_ENCODING_TABLE;
+        this.padding = DEFAULT_PADDING;
+
+        initialiseDecodingTable();
+    }
+
+    /**
+     * Constructor allowing the setting of an alternative alphabet.
+     *
+     * @param encodingTable a 32 entry encoding table to do the mapping.
+     * @param padding the padding value to use.
+     */
+    public Base32Encoder(byte[] encodingTable, byte padding)
+    {
+        if (encodingTable.length != 32)
+        {
+            throw new IllegalArgumentException("encoding table needs to be length 32");
+        }
+
+        this.encodingTable = Arrays.clone(encodingTable);
+        this.padding = padding;
+        
+        initialiseDecodingTable();
+    }
+
+    public int encode(byte[] inBuf, int inOff, int inLen, byte[] outBuf, int outOff) throws IOException
+    {
+        int inPos = inOff;
+        int inEnd = inOff + inLen - 4;
+        int outPos = outOff;
+
+        while (inPos < inEnd)
+        {
+             encodeBlock(inBuf, inPos, outBuf, outPos);
+             inPos += 5;
+             outPos += 8;
+        }
+
+        int extra = inLen - (inPos - inOff);
+        if (extra > 0)
+        {
+            byte[] in = new byte[5];
+            System.arraycopy(inBuf, inPos, in, 0, extra);
+            encodeBlock(in, 0, outBuf, outPos);
+            switch (extra)
+            {
+            case 1:
+                outBuf[outPos + 2] = padding;
+                outBuf[outPos + 3] = padding;
+                outBuf[outPos + 4] = padding;
+                outBuf[outPos + 5] = padding;
+                outBuf[outPos + 6] = padding;
+                outBuf[outPos + 7] = padding;
+                break;
+            case 2:
+                outBuf[outPos + 4] = padding;
+                outBuf[outPos + 5] = padding;
+                outBuf[outPos + 6] = padding;
+                outBuf[outPos + 7] = padding;
+                break;
+            case 3:
+                outBuf[outPos + 5] = padding;
+                outBuf[outPos + 6] = padding;
+                outBuf[outPos + 7] = padding;
+                break;
+            case 4:
+                outBuf[outPos + 7] = padding;
+                break;
+            }
+
+            outPos += 8;
+        }
+
+        return outPos - outOff;
+    }
+
+    private void encodeBlock(byte[] inBuf, int inPos, byte[] outBuf, int outPos)
+    {
+        int a1 = inBuf[inPos++];
+        int a2 = inBuf[inPos++] & 0xFF;
+        int a3 = inBuf[inPos++] & 0xFF;
+        int a4 = inBuf[inPos++] & 0xFF;
+        int a5 = inBuf[inPos] & 0xFF;
+
+        outBuf[outPos++] = encodingTable[(a1 >>> 3) & 0x1F];
+        outBuf[outPos++] = encodingTable[((a1 << 2) | (a2 >>> 6)) & 0x1F];
+        outBuf[outPos++] = encodingTable[(a2 >>> 1) & 0x1F];
+        outBuf[outPos++] = encodingTable[((a2 << 4) | (a3 >>> 4)) & 0x1F];
+        outBuf[outPos++] = encodingTable[((a3 << 1) | (a4 >>> 7)) & 0x1F];
+        outBuf[outPos++] = encodingTable[(a4 >>> 2) & 0x1F];
+        outBuf[outPos++] = encodingTable[((a4 << 3) | (a5 >>> 5)) & 0x1F];
+        outBuf[outPos] = encodingTable[a5 & 0x1F];
+    }
+
+    public int getEncodedLength(int inputLength)
+    {
+        return (inputLength + 4) / 5 * 8;
+    }
+
+    public int getMaxDecodedLength(int inputLength)
+    {
+        return inputLength / 8 * 5;
+    }
+
+    /**
+     * encode the input data producing a base 32 output stream.
+     *
+     * @return the number of bytes produced.
+     */
+    public int encode(byte[] buf, int off, int len, OutputStream out) 
+        throws IOException
+    {
+        if (len < 0)
+        {
+            return 0;
+        }
+
+        byte[] tmp = new byte[72];
+        int remaining = len;
+        while (remaining > 0)
+        {
+            int inLen = Math.min(45, remaining);
+            int outLen = encode(buf, off, inLen, tmp, 0);
+            out.write(tmp, 0, outLen);
+            off += inLen;
+            remaining -= inLen;
+        }
+        return (len + 2) / 3 * 4;
+    }
+
+    private boolean ignore(
+        char    c)
+    {
+        return (c == '\n' || c =='\r' || c == '\t' || c == ' ');
+    }
+    
+    /**
+     * decode the base 32 encoded byte data writing it to the given output stream,
+     * whitespace characters will be ignored.
+     *
+     * @return the number of bytes produced.
+     */
+    public int decode(
+        byte[]          data,
+        int             off,
+        int             length,
+        OutputStream    out)
+        throws IOException
+    {
+        byte    b1, b2, b3, b4, b5, b6, b7, b8;
+        byte[]  outBuffer = new byte[55];
+        int     bufOff = 0;
+        int     outLen = 0;
+        
+        int     end = off + length;
+        
+        while (end > off)
+        {
+            if (!ignore((char)data[end - 1]))
+            {
+                break;
+            }
+            
+            end--;
+        }
+
+        // empty data!
+        if (end == 0)
+        {
+            return 0;
+        }
+        
+        int  i = 0;
+        int  finish = end;
+
+        while (finish > off && i != 8)
+        {
+            if (!ignore((char)data[finish - 1]))
+            {
+                i++;
+            }
+
+            finish--;
+        }
+
+        i = nextI(data, off, finish);
+
+        while (i < finish)
+        {
+            b1 = decodingTable[data[i++]];
+            
+            i = nextI(data, i, finish);
+            
+            b2 = decodingTable[data[i++]];
+            
+            i = nextI(data, i, finish);
+            
+            b3 = decodingTable[data[i++]];
+            
+            i = nextI(data, i, finish);
+            
+            b4 = decodingTable[data[i++]];
+
+            i = nextI(data, i, finish);
+
+            b5 = decodingTable[data[i++]];
+
+            i = nextI(data, i, finish);
+
+            b6 = decodingTable[data[i++]];
+
+            i = nextI(data, i, finish);
+
+            b7 = decodingTable[data[i++]];
+
+            i = nextI(data, i, finish);
+
+            b8 = decodingTable[data[i++]];
+
+            if ((b1 | b2 | b3 | b4 | b5 | b6 | b7 | b8) < 0)
+            {
+                throw new IOException("invalid characters encountered in base32 data");
+            }
+
+            outBuffer[bufOff++] = (byte)((b1 << 3) | (b2 >> 2));
+            outBuffer[bufOff++] = (byte)((b2 << 6) | (b3 << 1) | (b4 >> 4));
+            outBuffer[bufOff++] = (byte)((b4 << 4) | (b5 >> 1));
+            outBuffer[bufOff++] = (byte)((b5 << 7) | (b6 << 2) | (b7 >> 3));
+            outBuffer[bufOff++] = (byte)((b7 << 5) | b8);
+
+            if (bufOff == outBuffer.length)
+            {
+                out.write(outBuffer);
+                bufOff = 0;
+            }
+            
+            outLen += 5;
+            
+            i = nextI(data, i, finish);
+        }
+
+        if (bufOff > 0)
+        {
+            out.write(outBuffer, 0, bufOff);
+        }
+
+        int e0 = nextI(data, i, end);
+        int e1 = nextI(data, e0 + 1, end);
+        int e2 = nextI(data, e1 + 1, end);
+        int e3 = nextI(data, e2 + 1, end);
+        int e4 = nextI(data, e3 + 1, end);
+        int e5 = nextI(data, e4 + 1, end);
+        int e6 = nextI(data, e5 + 1, end);
+        int e7 = nextI(data, e6 + 1, end);
+
+        outLen += decodeLastBlock(out,
+            (char)data[e0], (char)data[e1], (char)data[e2], (char)data[e3],
+            (char)data[e4], (char)data[e5], (char)data[e6], (char)data[e7]);
+
+        return outLen;
+    }
+
+    private int nextI(byte[] data, int i, int finish)
+    {
+        while ((i < finish) && ignore((char)data[i]))
+        {
+            i++;
+        }
+        return i;
+    }
+    
+    /**
+     * decode the base 32 encoded String data writing it to the given output stream,
+     * whitespace characters will be ignored.
+     *
+     * @return the number of bytes produced.
+     */
+    public int decode(
+        String          data,
+        OutputStream    out)
+        throws IOException
+    {
+        byte[] bytes = Strings.toByteArray(data);
+        return decode(bytes, 0, bytes.length, out);
+    }
+
+    private int decodeLastBlock(OutputStream out,
+                                char c1, char c2, char c3, char c4,
+                                char c5, char c6, char c7, char c8)
+        throws IOException
+    {
+        byte    b1, b2, b3, b4, b5, b6, b7, b8;
+        
+        if (c8 == padding)
+        {
+            if (c7 != padding)
+            {
+                b1 = decodingTable[c1];
+                b2 = decodingTable[c2];
+                b3 = decodingTable[c3];
+                b4 = decodingTable[c4];
+                b5 = decodingTable[c5];
+                b6 = decodingTable[c6];
+                b7 = decodingTable[c7];
+
+                if ((b1 | b2 | b3 | b4 | b5 | b6 | b7) < 0)
+                {
+                    throw new IOException("invalid characters encountered at end of base32 data");
+                }
+
+                out.write((b1 << 3) | (b2 >> 2));
+                out.write((b2 << 6) | (b3 << 1) | (b4 >> 4));
+                out.write((b4 << 4) | (b5 >> 1));
+                out.write((b5 << 7) | (b6 << 2) | (b7 >> 3));
+
+                return 4;
+            }
+            if (c6 != padding)
+            {
+                throw new IOException("invalid characters encountered at end of base32 data");
+            }
+
+            if (c5 != padding)
+            {
+                b1 = decodingTable[c1];
+                b2 = decodingTable[c2];
+                b3 = decodingTable[c3];
+                b4 = decodingTable[c4];
+                b5 = decodingTable[c5];
+
+                if ((b1 | b2 | b3 | b4 | b5) < 0)
+                {
+                    throw new IOException("invalid characters encountered at end of base32 data");
+                }
+
+                out.write((b1 << 3) | (b2 >> 2));
+                out.write((b2 << 6) | (b3 << 1) | (b4 >> 4));
+                out.write((b4 << 4) | (b5 >> 1));
+
+                return 3;
+            }
+
+            if (c4 != padding)
+            {
+                b1 = decodingTable[c1];
+                b2 = decodingTable[c2];
+                b3 = decodingTable[c3];
+                b4 = decodingTable[c4];
+
+                if ((b1 | b2 | b3 | b4) < 0)
+                {
+                    throw new IOException("invalid characters encountered at end of base32 data");
+                }
+
+                out.write((b1 << 3) | (b2 >> 2));
+                out.write((b2 << 6) | (b3 << 1) | (b4 >> 4));
+
+                return 2;
+            }
+            
+            if (c3 != padding)
+            {
+                throw new IOException("invalid characters encountered at end of base32 data");
+            }
+
+            b1 = decodingTable[c1];
+            b2 = decodingTable[c2];
+
+            if ((b1 | b2) < 0)
+            {
+                throw new IOException("invalid characters encountered at end of base32 data");
+            }
+
+            out.write((b1 << 3) | (b2 >> 2));
+            
+            return 1;
+        }
+        else
+        {
+            b1 = decodingTable[c1];
+            b2 = decodingTable[c2];
+            b3 = decodingTable[c3];
+            b4 = decodingTable[c4];
+            b5 = decodingTable[c5];
+            b6 = decodingTable[c6];
+            b7 = decodingTable[c7];
+            b8 = decodingTable[c8];
+
+            if ((b1 | b2 | b3 | b4 | b5 | b6 | b7 | b8) < 0)
+            {
+                throw new IOException("invalid characters encountered at end of base32 data");
+            }
+            
+            out.write((b1 << 3) | (b2 >> 2));
+            out.write((b2 << 6) | (b3 << 1) | (b4 >> 4));
+            out.write((b4 << 4) | (b5 >> 1));
+            out.write((b5 << 7) | (b6 << 2) | (b7 >> 3));
+            out.write((b7 << 5) | b8);
+            
+            return 5;
+        } 
+    }
+}
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/util/encoders/Base64.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/util/encoders/Base64.java
index fa5bebf..7bc0560 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/util/encoders/Base64.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/util/encoders/Base64.java
@@ -51,7 +51,7 @@
         int off,
         int length)
     {
-        int len = (length + 2) / 3 * 4;
+        int len = encoder.getEncodedLength(length);
         ByteArrayOutputStream bOut = new ByteArrayOutputStream(len);
 
         try
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/util/encoders/Base64Encoder.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/util/encoders/Base64Encoder.java
index cda38c1..52c4b8c 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/util/encoders/Base64Encoder.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/util/encoders/Base64Encoder.java
@@ -98,24 +98,40 @@
         return outPos - outOff;
     }
 
+    public int getEncodedLength(int inputLength)
+    {
+        return (inputLength + 2) / 3 * 4;
+    }
+
+    public int getMaxDecodedLength(int inputLength)
+    {
+        return inputLength / 4 * 3;
+    }
+
     /**
      * encode the input data producing a base 64 output stream.
      *
      * @return the number of bytes produced.
      */
-    public int encode(byte[] buf, int off, int len, OutputStream out) 
+    public int encode(byte[] buf, int off, int len, OutputStream out)
         throws IOException
     {
-        byte[] tmp = new byte[72];
-        while (len > 0)
+        if (len < 0)
         {
-            int inLen = Math.min(54, len);
+            return 0;
+        }
+
+        byte[] tmp = new byte[72];
+        int remaining = len;
+        while (remaining > 0)
+        {
+            int inLen = Math.min(54, remaining);
             int outLen = encode(buf, off, inLen, tmp, 0);
             out.write(tmp, 0, outLen);
             off += inLen;
-            len -= inLen;
+            remaining -= inLen;
         }
-        return ((len + 2) / 3) * 4;
+        return (len + 2) / 3 * 4;
     }
 
     private boolean ignore(
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/util/encoders/Encoder.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/util/encoders/Encoder.java
index 59a5114..5f425f4 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/util/encoders/Encoder.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/util/encoders/Encoder.java
@@ -11,6 +11,23 @@
  */
 public interface Encoder
 {
+    /**
+     * Return the expected output length of the encoding.
+     *
+     * @param inputLength the input length of the data.
+     * @return the output length of an encoding.
+     */
+    int getEncodedLength(int inputLength);
+
+    /**
+     * Return the maximum expected output length of a decoding. If padding
+     * is present the value returned will be greater than the decoded data length.
+     *
+     * @param inputLength the input length of the encoded data.
+     * @return the upper bound of the output length of a decoding.
+     */
+    int getMaxDecodedLength(int inputLength);
+
     int encode(byte[] data, int off, int length, OutputStream out) throws IOException;
     
     int decode(byte[] data, int off, int length, OutputStream out) throws IOException;
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/util/encoders/HexEncoder.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/util/encoders/HexEncoder.java
index 93ec068..14a0081 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/util/encoders/HexEncoder.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/util/encoders/HexEncoder.java
@@ -64,6 +64,16 @@
         return outPos - outOff;
     }
 
+    public int getEncodedLength(int inputLength)
+    {
+        return inputLength * 2;
+    }
+
+    public int getMaxDecodedLength(int inputLength)
+    {
+        return inputLength / 2;
+    }
+
     /**
      * encode the input data producing a Hex output stream.
      *
@@ -72,14 +82,20 @@
     public int encode(byte[] buf, int off, int len, OutputStream out) 
         throws IOException
     {
-        byte[] tmp = new byte[72];
-        while (len > 0)
+        if (len < 0)
         {
-            int inLen = Math.min(36, len);
+            return 0;
+        }
+
+        byte[] tmp = new byte[72];
+        int remaining = len;
+        while (remaining > 0)
+        {
+            int inLen = Math.min(36, remaining);
             int outLen = encode(buf, off, inLen, tmp, 0);
             out.write(tmp, 0, outLen);
             off += inLen;
-            len -= inLen;
+            remaining -= inLen;
         }
         return len * 2;
     }
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/util/encoders/UTF8.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/util/encoders/UTF8.java
index c6f49f6..1a541a1 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/util/encoders/UTF8.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/util/encoders/UTF8.java
@@ -3,7 +3,7 @@
 
 /**
  * Utilities for working with UTF-8 encodings.
- * 
+ * <p>
  * Decoding of UTF-8 is based on a presentation by Bob Steagall at CppCon2018 (see
  * https://github.com/BobSteagall/CppCon2018). It uses a Deterministic Finite Automaton (DFA) to
  * recognize and decode multi-byte code points.
@@ -73,8 +73,8 @@
         fill(transitionTable, S_P4A + 0x9, S_P4A + 0xB, S_CS2);
         fill(transitionTable, S_P4B + 0x8, S_P4B + 0x8, S_CS2);
 
-        byte[] firstUnitMasks = { 0x00, 0x00, 0x00, 0x00, 0x1F, 0x0F, 0x0F, 0x0F, 0x07, 0x07, 0x07 };
-        byte[] firstUnitTransitions = { S_ERR, S_ERR, S_ERR, S_ERR, S_CS1, S_P3A, S_CS2, S_P3B, S_P4A, S_CS3, S_P4B };
+        byte[] firstUnitMasks = {0x00, 0x00, 0x00, 0x00, 0x1F, 0x0F, 0x0F, 0x0F, 0x07, 0x07, 0x07};
+        byte[] firstUnitTransitions = {S_ERR, S_ERR, S_ERR, S_ERR, S_CS1, S_P3A, S_CS2, S_P3B, S_P4A, S_CS3, S_P4B};
 
         for (int i = 0x00; i < 0x80; ++i)
         {
@@ -96,26 +96,52 @@
      * "overlong" encodings, and unmappable code points. In particular, no unmatched surrogates will
      * be produced. An error will also result if {@code utf16} is found to be too small to store the
      * complete output.
-     * 
-     * @param utf8
-     *            A non-null array containing a well-formed UTF-8 encoding.
-     * @param utf16
-     *            A non-null array, at least as long as the {@code utf8} array in order to ensure
-     *            the output will fit.
+     *
+     * @param utf8  A non-null array containing a well-formed UTF-8 encoding.
+     * @param utf16 A non-null array, at least as long as the {@code utf8} array in order to ensure
+     *              the output will fit.
      * @return The number of UTF-16 code units written to {@code utf16} (beginning from index 0), or
-     *         else -1 if the input was either malformed or encoded any unmappable characters, or if
-     *         the {@code utf16} is too small.
+     * else -1 if the input was either malformed or encoded any unmappable characters, or if
+     * the {@code utf16} is too small.
      */
     public static int transcodeToUTF16(byte[] utf8, char[] utf16)
     {
-        int i = 0, j = 0;
+        return transcodeToUTF16(utf8, 0, utf8.length, utf16);
+    }
 
-        while (i < utf8.length)
+    /**
+     * Transcode a UTF-8 encoding into a UTF-16 representation. In the general case the output
+     * {@code utf16} array should be at least as long as the input length from {@code utf8} to handle
+     * arbitrary inputs. The number of output UTF-16 code units is returned, or -1 if any errors are
+     * encountered (in which case an arbitrary amount of data may have been written into the output
+     * array). Errors that will be detected are malformed UTF-8, including incomplete, truncated or
+     * "overlong" encodings, and unmappable code points. In particular, no unmatched surrogates will
+     * be produced. An error will also result if {@code utf16} is found to be too small to store the
+     * complete output.
+     *
+     * @param utf8  A non-null array containing a well-formed UTF-8 encoding.
+     * @param utf8Off start position in the array for the well-formed encoding.
+     * @param utf8Length length in bytes of the well-formed encoding.
+     * @param utf16 A non-null array, at least as long as the {@code utf8} array in order to ensure
+     *              the output will fit.
+     * @return The number of UTF-16 code units written to {@code utf16} (beginning from index 0), or
+     * else -1 if the input was either malformed or encoded any unmappable characters, or if
+     * the {@code utf16} is too small.
+     */
+    public static int transcodeToUTF16(byte[] utf8, int utf8Off, int utf8Length, char[] utf16)
+    {
+        int i = utf8Off, j = 0;
+        int maxI = utf8Off + utf8Length;
+
+        while (i < maxI)
         {
             byte codeUnit = utf8[i++];
             if (codeUnit >= 0)
             {
-                if (j >= utf16.length) { return -1; }
+                if (j >= utf16.length)
+                {
+                    return -1;
+                }
 
                 utf16[j++] = (char)codeUnit;
                 continue;
@@ -127,25 +153,37 @@
 
             while (state >= 0)
             {
-                if (i >= utf8.length) { return -1; }
+                if (i >= maxI)
+                {
+                    return -1;
+                }
 
                 codeUnit = utf8[i++];
                 codePoint = (codePoint << 6) | (codeUnit & 0x3F);
                 state = transitionTable[state + ((codeUnit & 0xFF) >>> 4)];
             }
 
-            if (state == S_ERR) { return -1; }
+            if (state == S_ERR)
+            {
+                return -1;
+            }
 
             if (codePoint <= 0xFFFF)
             {
-                if (j >= utf16.length) { return -1; }
+                if (j >= utf16.length)
+                {
+                    return -1;
+                }
 
                 // Code points from U+D800 to U+DFFF are caught by the DFA
                 utf16[j++] = (char)codePoint;
             }
             else
             {
-                if (j >= utf16.length - 1) { return -1; }
+                if (j >= utf16.length - 1)
+                {
+                    return -1;
+                }
 
                 // Code points above U+10FFFF are caught by the DFA
                 utf16[j++] = (char)(0xD7C0 + (codePoint >>> 10));
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/util/io/Streams.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/util/io/Streams.java
index 2c0a2ad..74a2cbf 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/util/io/Streams.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/util/io/Streams.java
@@ -30,6 +30,64 @@
     }
 
     /**
+     * Write the full contents of inStr to the destination stream outStr.
+     *
+     * @param inStr source input stream.
+     * @param outStr destination output stream.
+     * @throws IOException in case of underlying IOException.
+     */
+    public static void pipeAll(InputStream inStr, OutputStream outStr)
+        throws IOException
+    {
+        pipeAll(inStr, outStr, BUFFER_SIZE);
+    }
+
+    /**
+     * Write the full contents of inStr to the destination stream outStr.
+     *
+     * @param inStr source input stream.
+     * @param outStr destination output stream.
+     * @param bufferSize the size of temporary buffer to use.
+     * @throws IOException in case of underlying IOException.
+     */
+    public static void pipeAll(InputStream inStr, OutputStream outStr, int bufferSize)
+        throws IOException
+    {
+        byte[] bs = new byte[bufferSize];
+        int numRead;
+        while ((numRead = inStr.read(bs, 0, bs.length)) >= 0)
+        {
+            outStr.write(bs, 0, numRead);
+        }
+    }
+
+    /**
+     * Write up to limit bytes of data from inStr to the destination stream outStr.
+     *
+     * @param inStr source input stream.
+     * @param limit the maximum number of bytes allowed to be read.
+     * @param outStr destination output stream.
+     * @throws IOException in case of underlying IOException, or if limit is reached on inStr still has data in it.
+     */
+    public static long pipeAllLimited(InputStream inStr, long limit, OutputStream outStr)
+        throws IOException
+    {
+        long total = 0;
+        byte[] bs = new byte[BUFFER_SIZE];
+        int numRead;
+        while ((numRead = inStr.read(bs, 0, bs.length)) >= 0)
+        {
+            if ((limit - total) < numRead)
+            {
+                throw new StreamOverflowException("Data Overflow");
+            }
+            total += numRead;
+            outStr.write(bs, 0, numRead);
+        }
+        return total;
+    }
+
+    /**
      * Read stream fully, returning contents in a byte array.
      *
      * @param inStr stream to be read.
@@ -98,51 +156,22 @@
             }
             totalRead += numRead;
         }
+        
         return totalRead;
     }
 
-    /**
-     * Write the full contents of inStr to the destination stream outStr.
-     *
-     * @param inStr source input stream.
-     * @param outStr destination output stream.
-     * @throws IOException in case of underlying IOException.
-     */
-    public static void pipeAll(InputStream inStr, OutputStream outStr)
-        throws IOException
+    public static void validateBufferArguments(byte[] buf, int off, int len)
     {
-        byte[] bs = new byte[BUFFER_SIZE];
-        int numRead;
-        while ((numRead = inStr.read(bs, 0, bs.length)) >= 0)
+        if (buf == null)
         {
-            outStr.write(bs, 0, numRead);
+            throw new NullPointerException();
         }
-    }
-
-    /**
-     * Write up to limit bytes of data from inStr to the destination stream outStr.
-     *
-     * @param inStr source input stream.
-     * @param limit the maximum number of bytes allowed to be read.
-     * @param outStr destination output stream.
-     * @throws IOException in case of underlying IOException, or if limit is reached on inStr still has data in it.
-     */
-    public static long pipeAllLimited(InputStream inStr, long limit, OutputStream outStr)
-        throws IOException
-    {
-        long total = 0;
-        byte[] bs = new byte[BUFFER_SIZE];
-        int numRead;
-        while ((numRead = inStr.read(bs, 0, bs.length)) >= 0)
+        int available = buf.length - off;
+        int remaining = available - len;
+        if ((off | len | available | remaining) < 0)
         {
-            if ((limit - total) < numRead)
-            {
-                throw new StreamOverflowException("Data Overflow");
-            }
-            total += numRead;
-            outStr.write(bs, 0, numRead);
+            throw new IndexOutOfBoundsException();
         }
-        return total;
     }
 
     public static void writeBufTo(ByteArrayOutputStream buf, OutputStream output)
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/util/io/pem/PemReader.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/util/io/pem/PemReader.java
index a192b36..9d3f1af 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/util/io/pem/PemReader.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/util/io/pem/PemReader.java
@@ -66,9 +66,9 @@
 
         while ((line = readLine()) != null)
         {
-            if (line.indexOf(":") >= 0)
+            int index = line.indexOf(':');
+            if (index >= 0)
             {
-                int index = line.indexOf(':');
                 String hdr = line.substring(0, index);
                 String value = line.substring(index + 1).trim();
 
diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/x509/X509V2AttributeCertificate.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/x509/X509V2AttributeCertificate.java
index 6ae4aee..7a93330 100644
--- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/x509/X509V2AttributeCertificate.java
+++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/x509/X509V2AttributeCertificate.java
@@ -22,12 +22,12 @@
 import java.util.List;
 import java.util.Set;
 
+import com.android.internal.org.bouncycastle.asn1.ASN1BitString;
 import com.android.internal.org.bouncycastle.asn1.ASN1Encodable;
 import com.android.internal.org.bouncycastle.asn1.ASN1Encoding;
 import com.android.internal.org.bouncycastle.asn1.ASN1InputStream;
 import com.android.internal.org.bouncycastle.asn1.ASN1ObjectIdentifier;
 import com.android.internal.org.bouncycastle.asn1.ASN1Sequence;
-import com.android.internal.org.bouncycastle.asn1.DERBitString;
 import com.android.internal.org.bouncycastle.asn1.x509.AttributeCertificate;
 import com.android.internal.org.bouncycastle.asn1.x509.Extension;
 import com.android.internal.org.bouncycastle.asn1.x509.Extensions;
@@ -125,7 +125,7 @@
     
     public boolean[] getIssuerUniqueID()
     {
-        DERBitString    id = cert.getAcinfo().getIssuerUniqueID();
+        ASN1BitString id = cert.getAcinfo().getIssuerUniqueID();
 
         if (id != null)
         {