release-request-d9dc98f7-19b2-484c-b4d1-f35dc43e9c05-for-git_oc-mr1-release-4152006 snap-temp-L91700000079405440

Change-Id: I879cf076f5ca1d38b28bce2a0ab17f64cf769fd8
diff --git a/bcpkix/src/main/java/org/bouncycastle/cert/CertUtils.java b/bcpkix/src/main/java/org/bouncycastle/cert/CertUtils.java
index 9e2e488..87cddd9 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cert/CertUtils.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cert/CertUtils.java
@@ -15,6 +15,7 @@
 import org.bouncycastle.asn1.ASN1EncodableVector;
 import org.bouncycastle.asn1.ASN1GeneralizedTime;
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1Primitive;
 import org.bouncycastle.asn1.DERBitString;
 import org.bouncycastle.asn1.DERNull;
 import org.bouncycastle.asn1.DEROutputStream;
@@ -35,6 +36,18 @@
     private static Set EMPTY_SET = Collections.unmodifiableSet(new HashSet());
     private static List EMPTY_LIST = Collections.unmodifiableList(new ArrayList());
 
+    static ASN1Primitive parseNonEmptyASN1(byte[] encoding)
+        throws IOException
+    {
+        ASN1Primitive p = ASN1Primitive.fromByteArray(encoding);
+
+        if (p == null)
+        {
+            throw new IOException("no content found");
+        }
+        return p;
+    }
+
     static X509CertificateHolder generateFullCert(ContentSigner signer, TBSCertificate tbsCert)
     {
         try
diff --git a/bcpkix/src/main/java/org/bouncycastle/cert/X509AttributeCertificateHolder.java b/bcpkix/src/main/java/org/bouncycastle/cert/X509AttributeCertificateHolder.java
index e6b0d7e..fac9b44 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cert/X509AttributeCertificateHolder.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cert/X509AttributeCertificateHolder.java
@@ -9,7 +9,6 @@
 import java.util.Set;
 
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
-import org.bouncycastle.asn1.ASN1Primitive;
 import org.bouncycastle.asn1.ASN1Sequence;
 import org.bouncycastle.asn1.DEROutputStream;
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
@@ -39,7 +38,7 @@
     {
         try
         {
-            return AttributeCertificate.getInstance(ASN1Primitive.fromByteArray(certEncoding));
+            return AttributeCertificate.getInstance(CertUtils.parseNonEmptyASN1(certEncoding));
         }
         catch (ClassCastException e)
         {
diff --git a/bcpkix/src/main/java/org/bouncycastle/cert/X509CRLHolder.java b/bcpkix/src/main/java/org/bouncycastle/cert/X509CRLHolder.java
index 4b773e2..67abd31 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cert/X509CRLHolder.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cert/X509CRLHolder.java
@@ -13,6 +13,7 @@
 
 import org.bouncycastle.asn1.ASN1InputStream;
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1Primitive;
 import org.bouncycastle.asn1.DEROutputStream;
 import org.bouncycastle.asn1.x500.X500Name;
 import org.bouncycastle.asn1.x509.CertificateList;
@@ -42,7 +43,12 @@
     {
         try
         {
-            return CertificateList.getInstance(new ASN1InputStream(stream, true).readObject());
+            ASN1Primitive obj = new ASN1InputStream(stream, true).readObject();
+            if (obj == null)
+            {
+                throw new IOException("no content found");
+            }
+            return CertificateList.getInstance(obj);
         }
         catch (ClassCastException e)
         {
diff --git a/bcpkix/src/main/java/org/bouncycastle/cert/X509CertificateHolder.java b/bcpkix/src/main/java/org/bouncycastle/cert/X509CertificateHolder.java
index b5ad578..2396aac 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cert/X509CertificateHolder.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cert/X509CertificateHolder.java
@@ -8,7 +8,6 @@
 import java.util.Set;
 
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
-import org.bouncycastle.asn1.ASN1Primitive;
 import org.bouncycastle.asn1.DEROutputStream;
 import org.bouncycastle.asn1.x500.X500Name;
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
@@ -35,7 +34,7 @@
     {
         try
         {
-            return Certificate.getInstance(ASN1Primitive.fromByteArray(certEncoding));
+            return Certificate.getInstance(CertUtils.parseNonEmptyASN1(certEncoding));
         }
         catch (ClassCastException e)
         {
diff --git a/bcpkix/src/main/java/org/bouncycastle/cmc/CMCException.java b/bcpkix/src/main/java/org/bouncycastle/cmc/CMCException.java
new file mode 100644
index 0000000..eaa1294
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/cmc/CMCException.java
@@ -0,0 +1,23 @@
+package org.bouncycastle.cmc;
+
+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/bcpkix/src/main/java/org/bouncycastle/cmc/SimplePKIResponse.java b/bcpkix/src/main/java/org/bouncycastle/cmc/SimplePKIResponse.java
new file mode 100644
index 0000000..23f70d6
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/cmc/SimplePKIResponse.java
@@ -0,0 +1,106 @@
+package org.bouncycastle.cmc;
+
+import java.io.IOException;
+
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.cms.ContentInfo;
+import org.bouncycastle.cert.X509CRLHolder;
+import org.bouncycastle.cert.X509CertificateHolder;
+import org.bouncycastle.cms.CMSException;
+import org.bouncycastle.cms.CMSSignedData;
+import org.bouncycastle.util.Encodable;
+import 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>
+ */
+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 IOException 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/bcpkix/src/main/java/org/bouncycastle/cms/CMSSignedData.java b/bcpkix/src/main/java/org/bouncycastle/cms/CMSSignedData.java
index 7f6d4a2..3119715 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cms/CMSSignedData.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/CMSSignedData.java
@@ -26,6 +26,9 @@
 import org.bouncycastle.asn1.cms.SignedData;
 import org.bouncycastle.asn1.cms.SignerInfo;
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.cert.X509AttributeCertificateHolder;
+import org.bouncycastle.cert.X509CRLHolder;
+import org.bouncycastle.cert.X509CertificateHolder;
 import org.bouncycastle.operator.OperatorCreationException;
 import org.bouncycastle.util.Encodable;
 import org.bouncycastle.util.Store;
@@ -292,7 +295,7 @@
      *
      * @return a Store of X509CertificateHolder objects.
      */
-    public Store getCertificates()
+    public Store<X509CertificateHolder> getCertificates()
     {
         return HELPER.getCertificates(signedData.getCertificates());
     }
@@ -302,7 +305,7 @@
      *
      * @return a Store of X509CRLHolder objects.
      */
-    public Store getCRLs()
+    public Store<X509CRLHolder> getCRLs()
     {
         return HELPER.getCRLs(signedData.getCRLs());
     }
@@ -312,7 +315,7 @@
      *
      * @return a Store of X509AttributeCertificateHolder objects.
      */
-    public Store getAttributeCertificates()
+    public Store<X509AttributeCertificateHolder> getAttributeCertificates()
     {
         return HELPER.getAttributeCertificates(signedData.getCertificates());
     }
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/CMSUtils.java b/bcpkix/src/main/java/org/bouncycastle/cms/CMSUtils.java
index 7dd16e0..0cd1f5f 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cms/CMSUtils.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/CMSUtils.java
@@ -262,7 +262,13 @@
     {
         try
         {
-            return ContentInfo.getInstance(in.readObject());
+            ContentInfo info = ContentInfo.getInstance(in.readObject());
+            if (info == null)
+            {
+                throw new CMSException("No content found.");
+            }
+
+            return info;
         }
         catch (IOException e)
         {
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/SignerInformationVerifier.java b/bcpkix/src/main/java/org/bouncycastle/cms/SignerInformationVerifier.java
index ada4d0e..06d2c1b 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cms/SignerInformationVerifier.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/SignerInformationVerifier.java
@@ -37,9 +37,10 @@
     public ContentVerifier getContentVerifier(AlgorithmIdentifier signingAlgorithm, AlgorithmIdentifier digestAlgorithm)
         throws OperatorCreationException
     {
-        String          signatureName = sigNameGenerator.getSignatureName(digestAlgorithm, signingAlgorithm);
+        String              signatureName = sigNameGenerator.getSignatureName(digestAlgorithm, signingAlgorithm);
+        AlgorithmIdentifier baseAlgID = sigAlgorithmFinder.find(signatureName);
 
-        return verifierProvider.get(sigAlgorithmFinder.find(signatureName));
+        return verifierProvider.get(new AlgorithmIdentifier(baseAlgID.getAlgorithm(), signingAlgorithm.getParameters()));
     }
 
     public DigestCalculator getDigestCalculator(AlgorithmIdentifier algorithmIdentifier)
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/bc/package.html b/bcpkix/src/main/java/org/bouncycastle/cms/bc/package.html
new file mode 100644
index 0000000..7182ae3
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/bc/package.html
@@ -0,0 +1,5 @@
+<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/package.html b/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/package.html
new file mode 100644
index 0000000..5221afe
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/package.html
@@ -0,0 +1,5 @@
+<html>
+<body bgcolor="#ffffff">
+CMS operator implementations for doing message encryption, signing, digesting, and MACing operations using the JCA and the JCE.
+</body>
+</html>
diff --git a/bcpkix/src/main/java/org/bouncycastle/operator/DefaultDigestAlgorithmIdentifierFinder.java b/bcpkix/src/main/java/org/bouncycastle/operator/DefaultDigestAlgorithmIdentifierFinder.java
index 5792018..e369185 100644
--- a/bcpkix/src/main/java/org/bouncycastle/operator/DefaultDigestAlgorithmIdentifierFinder.java
+++ b/bcpkix/src/main/java/org/bouncycastle/operator/DefaultDigestAlgorithmIdentifierFinder.java
@@ -103,13 +103,17 @@
         digestNameToOids.put("SHA-256", NISTObjectIdentifiers.id_sha256);
         digestNameToOids.put("SHA-384", NISTObjectIdentifiers.id_sha384);
         digestNameToOids.put("SHA-512", NISTObjectIdentifiers.id_sha512);
-
         // BEGIN android-removed
+        // digestNameToOids.put("SHA-512-224", NISTObjectIdentifiers.id_sha512_224);
+        // digestNameToOids.put("SHA-512-256", NISTObjectIdentifiers.id_sha512_256);
+        //
         // digestNameToOids.put("SHA1", OIWObjectIdentifiers.idSHA1);
         // digestNameToOids.put("SHA224", NISTObjectIdentifiers.id_sha224);
         // digestNameToOids.put("SHA256", NISTObjectIdentifiers.id_sha256);
         // digestNameToOids.put("SHA384", NISTObjectIdentifiers.id_sha384);
         // digestNameToOids.put("SHA512", NISTObjectIdentifiers.id_sha512);
+        // digestNameToOids.put("SHA512-224", NISTObjectIdentifiers.id_sha512_224);
+        // digestNameToOids.put("SHA512-256", NISTObjectIdentifiers.id_sha512_256);
 
         // digestNameToOids.put("SHA3-224", NISTObjectIdentifiers.id_sha3_224);
         // digestNameToOids.put("SHA3-256", NISTObjectIdentifiers.id_sha3_256);
diff --git a/bcpkix/src/main/java/org/bouncycastle/operator/DefaultSignatureAlgorithmIdentifierFinder.java b/bcpkix/src/main/java/org/bouncycastle/operator/DefaultSignatureAlgorithmIdentifierFinder.java
index 77f358a..d1976af 100644
--- a/bcpkix/src/main/java/org/bouncycastle/operator/DefaultSignatureAlgorithmIdentifierFinder.java
+++ b/bcpkix/src/main/java/org/bouncycastle/operator/DefaultSignatureAlgorithmIdentifierFinder.java
@@ -14,6 +14,7 @@
 // import org.bouncycastle.asn1.bsi.BSIObjectIdentifiers;
 // import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers;
 // import org.bouncycastle.asn1.eac.EACObjectIdentifiers;
+// import org.bouncycastle.asn1.gm.GMObjectIdentifiers;
 // END android-removed
 import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
 import org.bouncycastle.asn1.oiw.OIWObjectIdentifiers;
@@ -105,6 +106,7 @@
         // algorithms.put("SHA512WITHCVC-ECDSA", EACObjectIdentifiers.id_TA_ECDSA_SHA_512);
         // algorithms.put("SHA3-512WITHSPHINCS256", BCObjectIdentifiers.sphincs256_with_SHA3_512);
         // algorithms.put("SHA512WITHSPHINCS256", BCObjectIdentifiers.sphincs256_with_SHA512);
+        // algorithms.put("SM3WITHSM2", GMObjectIdentifiers.sm2sign_with_sm3);
         // END android-removed
 
         //
@@ -122,20 +124,22 @@
         noParams.add(NISTObjectIdentifiers.dsa_with_sha384);
         noParams.add(NISTObjectIdentifiers.dsa_with_sha512);
 
+        // BEGIN Android-removed
         //
         // RFC 4491
         //
-        // BEGIN android-removed
         // noParams.add(CryptoProObjectIdentifiers.gostR3411_94_with_gostR3410_94);
         // noParams.add(CryptoProObjectIdentifiers.gostR3411_94_with_gostR3410_2001);
-        // END android-removed
-
-        // BEGIN android-removed
         //
         // SPHINCS-256
         //
         // noParams.add(BCObjectIdentifiers.sphincs256_with_SHA512);
         // noParams.add(BCObjectIdentifiers.sphincs256_with_SHA3_512);
+
+        //
+        // SM2
+        //
+        // noParams.add(GMObjectIdentifiers.sm2sign_with_sm3);
         // END android-removed
 
         //
diff --git a/bcpkix/src/main/java/org/bouncycastle/operator/bc/package.html b/bcpkix/src/main/java/org/bouncycastle/operator/bc/package.html
new file mode 100644
index 0000000..6d7ebba
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/operator/bc/package.html
@@ -0,0 +1,5 @@
+<html>
+<body bgcolor="#ffffff">
+Basic operator implementations for doing encryption, signing, and digest operations using the BC lightweight API.
+</body>
+</html>
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 532d3b5..8ac72ea 100644
--- a/bcpkix/src/main/java/org/bouncycastle/operator/jcajce/OperatorHelper.java
+++ b/bcpkix/src/main/java/org/bouncycastle/operator/jcajce/OperatorHelper.java
@@ -23,6 +23,7 @@
 
 import org.bouncycastle.asn1.ASN1Encodable;
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1Sequence;
 import org.bouncycastle.asn1.DERNull;
 // BEGIN android-removed
 // import org.bouncycastle.asn1.bsi.BSIObjectIdentifiers;
@@ -319,6 +320,27 @@
             }
         }
 
+        if (sigAlgId.getAlgorithm().equals(PKCSObjectIdentifiers.id_RSASSA_PSS))
+        {
+            ASN1Sequence seq = ASN1Sequence.getInstance(sigAlgId.getParameters());
+          
+            if (notDefaultPSSParams(seq))
+            {
+                try
+                {
+                    AlgorithmParameters algParams = helper.createAlgorithmParameters("PSS");
+
+                    algParams.init(seq.getEncoded());
+
+                    sig.setParameter(algParams.getParameterSpec(PSSParameterSpec.class));
+                }
+                catch (IOException e)
+                {
+                    throw new GeneralSecurityException("unable to process PSS parameters: " + e.getMessage());
+                }
+            }
+        }
+
         return sig;
     }
 
@@ -468,4 +490,31 @@
 
         return oid.getId();
     }
+
+    // for our purposes default includes varient digest with salt the same size as digest
+    private boolean notDefaultPSSParams(ASN1Sequence seq)
+        throws GeneralSecurityException
+    {
+        if (seq == null || seq.size() == 0)
+        {
+            return false;
+        }
+
+        RSASSAPSSparams pssParams = RSASSAPSSparams.getInstance(seq);
+
+        if (!pssParams.getMaskGenAlgorithm().getAlgorithm().equals(PKCSObjectIdentifiers.id_mgf1))
+        {
+            return true;
+        }
+
+        // same digest for sig and MGF1
+        if (!pssParams.getHashAlgorithm().equals(AlgorithmIdentifier.getInstance(pssParams.getMaskGenAlgorithm().getParameters())))
+        {
+            return true;
+        }
+
+        MessageDigest digest = createDigest(pssParams.getHashAlgorithm());
+
+        return pssParams.getSaltLength().intValue() != digest.getDigestLength();
+    }
 }
diff --git a/bcpkix/src/main/java/org/bouncycastle/operator/jcajce/package.html b/bcpkix/src/main/java/org/bouncycastle/operator/jcajce/package.html
new file mode 100644
index 0000000..5a7fa1c
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/operator/jcajce/package.html
@@ -0,0 +1,5 @@
+<html>
+<body bgcolor="#ffffff">
+Basic operator implementations for doing encryption, signing, and digest operations using the JCA and the JCE.
+</body>
+</html>
diff --git a/bcpkix/src/main/java/org/bouncycastle/pkcs/bc/package.html b/bcpkix/src/main/java/org/bouncycastle/pkcs/bc/package.html
new file mode 100644
index 0000000..bb47e1a
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/pkcs/bc/package.html
@@ -0,0 +1,7 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+        "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<body bgcolor="#ffffff">
+BC lightweight API extensions and operators for the PKCS#10 certification request package.
+</body>
+</html>
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/gm/GMObjectIdentifiers.java b/bcprov/src/main/java/org/bouncycastle/asn1/gm/GMObjectIdentifiers.java
new file mode 100644
index 0000000..ade6782
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/gm/GMObjectIdentifiers.java
@@ -0,0 +1,84 @@
+package org.bouncycastle.asn1.gm;
+
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+
+public interface GMObjectIdentifiers
+{
+    ASN1ObjectIdentifier sm_scheme = new ASN1ObjectIdentifier("1.2.156.10197.1");
+
+    ASN1ObjectIdentifier sm6_ecb = sm_scheme.branch("101.1");
+    ASN1ObjectIdentifier sm6_cbc = sm_scheme.branch("101.2");
+    ASN1ObjectIdentifier sm6_ofb128 = sm_scheme.branch("101.3");
+    ASN1ObjectIdentifier sm6_cfb128 = sm_scheme.branch("101.4");
+
+    ASN1ObjectIdentifier sm1_ecb = sm_scheme.branch("102.1");
+    ASN1ObjectIdentifier sm1_cbc = sm_scheme.branch("102.2");
+    ASN1ObjectIdentifier sm1_ofb128 = sm_scheme.branch("102.3");
+    ASN1ObjectIdentifier sm1_cfb128 = sm_scheme.branch("102.4");
+    ASN1ObjectIdentifier sm1_cfb1 = sm_scheme.branch("102.5");
+    ASN1ObjectIdentifier sm1_cfb8 = sm_scheme.branch("102.6");
+
+    ASN1ObjectIdentifier ssf33_ecb = sm_scheme.branch("103.1");
+    ASN1ObjectIdentifier ssf33_cbc = sm_scheme.branch("103.2");
+    ASN1ObjectIdentifier ssf33_ofb128 = sm_scheme.branch("103.3");
+    ASN1ObjectIdentifier ssf33_cfb128 = sm_scheme.branch("103.4");
+    ASN1ObjectIdentifier ssf33_cfb1 = sm_scheme.branch("103.5");
+    ASN1ObjectIdentifier ssf33_cfb8 = sm_scheme.branch("103.6");
+
+    ASN1ObjectIdentifier sms4_ecb = sm_scheme.branch("104.1");
+    ASN1ObjectIdentifier sms4_cbc = sm_scheme.branch("104.2");
+    ASN1ObjectIdentifier sms4_ofb128 = sm_scheme.branch("104.3");
+    ASN1ObjectIdentifier sms4_cfb128 = sm_scheme.branch("104.4");
+    ASN1ObjectIdentifier sms4_cfb1 = sm_scheme.branch("104.5");
+    ASN1ObjectIdentifier sms4_cfb8 = sm_scheme.branch("104.6");
+    ASN1ObjectIdentifier sms4_ctr = sm_scheme.branch("104.7");
+    ASN1ObjectIdentifier sms4_gcm = sm_scheme.branch("104.8");
+    ASN1ObjectIdentifier sms4_ccm = sm_scheme.branch("104.9");
+    ASN1ObjectIdentifier sms4_xts = sm_scheme.branch("104.10");
+    ASN1ObjectIdentifier sms4_wrap = sm_scheme.branch("104.11");
+    ASN1ObjectIdentifier sms4_wrap_pad = sm_scheme.branch("104.12");
+    ASN1ObjectIdentifier sms4_ocb = sm_scheme.branch("104.100");
+
+    ASN1ObjectIdentifier sm5 = sm_scheme.branch("201");
+
+    ASN1ObjectIdentifier sm2p256v1 = sm_scheme.branch("301");
+    ASN1ObjectIdentifier sm2sign = sm_scheme.branch("301.1");
+    ASN1ObjectIdentifier sm2exchange = sm_scheme.branch("301.2");
+    ASN1ObjectIdentifier sm2encrypt = sm_scheme.branch("301.3");
+
+    ASN1ObjectIdentifier wapip192v1 = sm_scheme.branch("301.101");
+
+    ASN1ObjectIdentifier sm2encrypt_recommendedParameters = sm2encrypt.branch("1");
+    ASN1ObjectIdentifier sm2encrypt_specifiedParameters = sm2encrypt.branch("2");
+    ASN1ObjectIdentifier sm2encrypt_with_sm3 = sm2encrypt.branch("2.1");
+    ASN1ObjectIdentifier sm2encrypt_with_sha1 = sm2encrypt.branch("2.2");
+    ASN1ObjectIdentifier sm2encrypt_with_sha224 = sm2encrypt.branch("2.3");
+    ASN1ObjectIdentifier sm2encrypt_with_sha256 = sm2encrypt.branch("2.4");
+    ASN1ObjectIdentifier sm2encrypt_with_sha384 = sm2encrypt.branch("2.5");
+    ASN1ObjectIdentifier sm2encrypt_with_sha512 = sm2encrypt.branch("2.6");
+    ASN1ObjectIdentifier sm2encrypt_with_rmd160 =  sm2encrypt.branch("2.7");
+    ASN1ObjectIdentifier sm2encrypt_with_whirlpool =sm2encrypt.branch("2.8");
+    ASN1ObjectIdentifier sm2encrypt_with_blake2b512 = sm2encrypt.branch("2.9");
+    ASN1ObjectIdentifier sm2encrypt_with_blake2s256 = sm2encrypt.branch("2.10");
+    ASN1ObjectIdentifier sm2encrypt_with_md5 = sm2encrypt.branch("2.11");
+
+    ASN1ObjectIdentifier id_sm9PublicKey = sm_scheme.branch("302");
+    ASN1ObjectIdentifier sm9sign = sm_scheme.branch("302.1");
+    ASN1ObjectIdentifier sm9keyagreement = sm_scheme.branch("302.2");
+    ASN1ObjectIdentifier sm9encrypt = sm_scheme.branch("302.3");
+
+    ASN1ObjectIdentifier sm3 = sm_scheme.branch("401");
+
+    ASN1ObjectIdentifier hmac_sm3 = sm3.branch("2");
+
+    ASN1ObjectIdentifier sm2sign_with_sm3 = sm_scheme.branch("501");
+    ASN1ObjectIdentifier sm2sign_with_sha1 = sm_scheme.branch("502");
+    ASN1ObjectIdentifier sm2sign_with_sha256 = sm_scheme.branch("503");
+    ASN1ObjectIdentifier sm2sign_with_sha512 = sm_scheme.branch("504");
+    ASN1ObjectIdentifier sm2sign_with_sha224 = sm_scheme.branch("505");
+    ASN1ObjectIdentifier sm2sign_with_sha384 = sm_scheme.branch("506");
+    ASN1ObjectIdentifier sm2sign_with_rmd160 = sm_scheme.branch("507");
+    ASN1ObjectIdentifier sm2sign_with_whirlpool = sm_scheme.branch("520");
+    ASN1ObjectIdentifier sm2sign_with_blake2b512 = sm_scheme.branch("521");
+    ASN1ObjectIdentifier sm2sign_with_blake2s256 = sm_scheme.branch("522");
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/nsri/NSRIObjectIdentifiers.java b/bcprov/src/main/java/org/bouncycastle/asn1/nsri/NSRIObjectIdentifiers.java
new file mode 100644
index 0000000..4ef8148
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/nsri/NSRIObjectIdentifiers.java
@@ -0,0 +1,58 @@
+package org.bouncycastle.asn1.nsri;
+
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+
+public interface NSRIObjectIdentifiers
+{
+    static final ASN1ObjectIdentifier   nsri                = new ASN1ObjectIdentifier("1.2.410.200046");
+
+    static final ASN1ObjectIdentifier   id_algorithm        = nsri.branch("1");
+
+    static final ASN1ObjectIdentifier   id_sea              = id_algorithm.branch("1");
+    static final ASN1ObjectIdentifier   id_pad              = id_algorithm.branch("2");
+
+    static final ASN1ObjectIdentifier   id_pad_null         = id_algorithm.branch("0");
+    static final ASN1ObjectIdentifier   id_pad_1            = id_algorithm.branch("1");
+
+    static final ASN1ObjectIdentifier   id_aria128_ecb      = id_sea.branch("1");
+    static final ASN1ObjectIdentifier   id_aria128_cbc      = id_sea.branch("2");
+    static final ASN1ObjectIdentifier   id_aria128_cfb      = id_sea.branch("3");
+    static final ASN1ObjectIdentifier   id_aria128_ofb      = id_sea.branch("4");
+    static final ASN1ObjectIdentifier   id_aria128_ctr      = id_sea.branch("5");
+
+    static final ASN1ObjectIdentifier   id_aria192_ecb      = id_sea.branch("6");
+    static final ASN1ObjectIdentifier   id_aria192_cbc      = id_sea.branch("7");
+    static final ASN1ObjectIdentifier   id_aria192_cfb      = id_sea.branch("8");
+    static final ASN1ObjectIdentifier   id_aria192_ofb      = id_sea.branch("9");
+    static final ASN1ObjectIdentifier   id_aria192_ctr      = id_sea.branch("10");
+
+    static final ASN1ObjectIdentifier   id_aria256_ecb      = id_sea.branch("11");
+    static final ASN1ObjectIdentifier   id_aria256_cbc      = id_sea.branch("12");
+    static final ASN1ObjectIdentifier   id_aria256_cfb      = id_sea.branch("13");
+    static final ASN1ObjectIdentifier   id_aria256_ofb      = id_sea.branch("14");
+    static final ASN1ObjectIdentifier   id_aria256_ctr      = id_sea.branch("15");
+
+    static final ASN1ObjectIdentifier   id_aria128_cmac     = id_sea.branch("21");
+    static final ASN1ObjectIdentifier   id_aria192_cmac     = id_sea.branch("22");
+    static final ASN1ObjectIdentifier   id_aria256_cmac     = id_sea.branch("23");
+
+    static final ASN1ObjectIdentifier   id_aria128_ocb2     = id_sea.branch("31");
+    static final ASN1ObjectIdentifier   id_aria192_ocb2     = id_sea.branch("32");
+    static final ASN1ObjectIdentifier   id_aria256_ocb2     = id_sea.branch("33");
+
+    static final ASN1ObjectIdentifier   id_aria128_gcm      = id_sea.branch("34");
+    static final ASN1ObjectIdentifier   id_aria192_gcm      = id_sea.branch("35");
+    static final ASN1ObjectIdentifier   id_aria256_gcm      = id_sea.branch("36");
+
+    static final ASN1ObjectIdentifier   id_aria128_ccm      = id_sea.branch("37");
+    static final ASN1ObjectIdentifier   id_aria192_ccm      = id_sea.branch("38");
+    static final ASN1ObjectIdentifier   id_aria256_ccm      = id_sea.branch("39");
+
+    static final ASN1ObjectIdentifier   id_aria128_kw       = id_sea.branch("40");
+    static final ASN1ObjectIdentifier   id_aria192_kw       = id_sea.branch("41");
+    static final ASN1ObjectIdentifier   id_aria256_kw       = id_sea.branch("42");
+
+    static final ASN1ObjectIdentifier   id_aria128_kwp      = id_sea.branch("43");
+    static final ASN1ObjectIdentifier   id_aria192_kwp      = id_sea.branch("44");
+    static final ASN1ObjectIdentifier   id_aria256_kwp      = id_sea.branch("45");
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/Attribute.java b/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/Attribute.java
new file mode 100644
index 0000000..6374c98
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/Attribute.java
@@ -0,0 +1,88 @@
+package org.bouncycastle.asn1.pkcs;
+
+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.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1Set;
+import org.bouncycastle.asn1.DERSequence;
+
+public class Attribute
+    extends ASN1Object
+{
+    private ASN1ObjectIdentifier attrType;
+    private ASN1Set              attrValues;
+
+    /**
+     * return an Attribute object from the given object.
+     *
+     * @param o the object we want converted.
+     * @exception IllegalArgumentException if the object cannot be converted.
+     */
+    public static Attribute getInstance(
+        Object o)
+    {
+        if (o == null || o instanceof Attribute)
+        {
+            return (Attribute)o;
+        }
+        
+        if (o instanceof ASN1Sequence)
+        {
+            return new Attribute((ASN1Sequence)o);
+        }
+
+        throw new IllegalArgumentException("unknown object in factory: " + o.getClass().getName());
+    }
+    
+    public Attribute(
+        ASN1Sequence seq)
+    {
+        attrType = (ASN1ObjectIdentifier)seq.getObjectAt(0);
+        attrValues = (ASN1Set)seq.getObjectAt(1);
+    }
+
+    public Attribute(
+        ASN1ObjectIdentifier attrType,
+        ASN1Set             attrValues)
+    {
+        this.attrType = attrType;
+        this.attrValues = attrValues;
+    }
+
+    public ASN1ObjectIdentifier getAttrType()
+    {
+        return attrType;
+    }
+    
+    public ASN1Set getAttrValues()
+    {
+        return attrValues;
+    }
+
+    public ASN1Encodable[] getAttributeValues()
+    {
+        return attrValues.toArray();
+    }
+
+    /** 
+     * Produce an object suitable for an ASN1OutputStream.
+     * <pre>
+     * Attribute ::= SEQUENCE {
+     *     attrType OBJECT IDENTIFIER,
+     *     attrValues SET OF AttributeValue
+     * }
+     * </pre>
+     */
+    public ASN1Primitive toASN1Primitive()
+    {
+        ASN1EncodableVector v = new ASN1EncodableVector();
+
+        v.add(attrType);
+        v.add(attrValues);
+
+        return new DERSequence(v);
+    }
+}
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 987d4eb..e089cbc 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/CertificationRequest.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/CertificationRequest.java
@@ -54,6 +54,9 @@
         this.sigBits = signature;
     }
 
+    /**
+     * @deprecated use getInstance()
+     */
     public CertificationRequest(
         ASN1Sequence seq)
     {
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 dca0719..25e6286 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/CertificationRequestInfo.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/CertificationRequestInfo.java
@@ -1,5 +1,7 @@
 package org.bouncycastle.asn1.pkcs;
 
+import java.util.Enumeration;
+
 import org.bouncycastle.asn1.ASN1EncodableVector;
 import org.bouncycastle.asn1.ASN1Integer;
 import org.bouncycastle.asn1.ASN1Object;
@@ -77,6 +79,8 @@
             throw new IllegalArgumentException("Not all mandatory fields set in CertificationRequestInfo generator.");
         }
 
+        validateAttributes(attributes);
+
         this.subject = subject;
         this.subjectPKInfo = pkInfo;
         this.attributes = attributes;
@@ -90,14 +94,7 @@
         SubjectPublicKeyInfo    pkInfo,
         ASN1Set                 attributes)
     {
-        if ((subject == null) || (pkInfo == null))
-        {
-            throw new IllegalArgumentException("Not all mandatory fields set in CertificationRequestInfo generator.");
-        }
-        
-        this.subject = X500Name.getInstance(subject.toASN1Primitive());
-        this.subjectPKInfo = pkInfo;
-        this.attributes = attributes;
+        this(X500Name.getInstance(subject.toASN1Primitive()), pkInfo, attributes);
     }
 
     /**
@@ -121,6 +118,8 @@
             attributes = ASN1Set.getInstance(tagobj, false);
         }
 
+        validateAttributes(attributes);
+
         if ((subject == null) || (version == null) || (subjectPKInfo == null))
         {
             throw new IllegalArgumentException("Not all mandatory fields set in CertificationRequestInfo generator.");
@@ -162,4 +161,24 @@
 
         return new DERSequence(v);
     }
+
+    private static void validateAttributes(ASN1Set attributes)
+    {
+        if (attributes == null)
+        {
+            return;
+        }
+
+        for (Enumeration en = attributes.getObjects(); en.hasMoreElements();)
+        {
+            Attribute attr = Attribute.getInstance(en.nextElement());
+            if (attr.getAttrType().equals(PKCSObjectIdentifiers.pkcs_9_at_challengePassword))
+            {
+                if (attr.getAttrValues().size() != 1)
+                {
+                    throw new IllegalArgumentException("challengePassword attribute must have one value");
+                }
+            }
+        }
+    }
 }
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 6e50f55..75df2aa 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/PKCSObjectIdentifiers.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/PKCSObjectIdentifiers.java
@@ -343,6 +343,20 @@
     /** PKCS#9: 1.2.840.113549.1.9.16.6.2.27 - <a href="http://tools.ietf.org/html/rfc3126">RFC 3126</a> */
     static final ASN1ObjectIdentifier id_aa_ets_archiveTimestamp = id_aa.branch("27");
 
+    /** PKCS#9: 1.2.840.113549.1.9.16.6.2.37 - <a href="https://tools.ietf.org/html/rfc4108#section-2.2.5">RFC 4108</a> */
+    static final ASN1ObjectIdentifier id_aa_decryptKeyID = id_aa.branch("37");
+
+    /** PKCS#9: 1.2.840.113549.1.9.16.6.2.38 - <a href="https://tools.ietf.org/html/rfc4108#section-2.2.6">RFC 4108</a> */
+    static final ASN1ObjectIdentifier id_aa_implCryptoAlgs = id_aa.branch("38");
+
+    /** PKCS#9: 1.2.840.113549.1.9.16.2.54 <a href="https://tools.ietf.org/html/rfc7030">RFC7030</a>*/
+    static final ASN1ObjectIdentifier id_aa_asymmDecryptKeyID = id_aa.branch("54");
+
+    /** PKCS#9: 1.2.840.113549.1.9.16.2.43   <a href="https://tools.ietf.org/html/rfc7030">RFC7030</a>*/
+    static final ASN1ObjectIdentifier id_aa_implCompressAlgs = id_aa.branch("43");
+    /** PKCS#9: 1.2.840.113549.1.9.16.2.40   <a href="https://tools.ietf.org/html/rfc7030">RFC7030</a>*/
+    static final ASN1ObjectIdentifier id_aa_communityIdentifiers = id_aa.branch("40");
+
     /** @deprecated use id_aa_ets_sigPolicyId instead */
     static final ASN1ObjectIdentifier id_aa_sigPolicyId    = id_aa_ets_sigPolicyId;
     /** @deprecated use id_aa_ets_commitmentType instead */
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 01c9aa2..f545d0b 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/x509/KeyPurposeId.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/x509/KeyPurposeId.java
@@ -9,7 +9,7 @@
  * <pre>
  *     KeyPurposeId ::= OBJECT IDENTIFIER
  *
- *     id-kp ::= OBJECT IDENTIFIER { iso(1) identified-organization(3) 
+ *     id-kp ::= OBJECT IDENTIFIER { iso(1) identified-organization(3)
  *          dod(6) internet(1) security(5) mechanisms(5) pkix(7) 3}
  *
  * </pre>
@@ -115,6 +115,24 @@
      */
     public static final KeyPurposeId id_kp_smartcardlogon = new KeyPurposeId(new ASN1ObjectIdentifier("1.3.6.1.4.1.311.20.2.2"));
 
+
+    /**
+     *
+     */
+    public static final KeyPurposeId id_kp_macAddress = new KeyPurposeId(new ASN1ObjectIdentifier("1.3.6.1.1.1.1.22"));
+
+
+    /**
+     * Microsoft Server Gated Crypto (msSGC) see http://www.alvestrand.no/objectid/1.3.6.1.4.1.311.10.3.3.html
+     */
+    public static final KeyPurposeId id_kp_msSGC = new KeyPurposeId(new ASN1ObjectIdentifier("1.3.6.1.4.1.311.10.3.3"));
+
+    /**
+     * Netscape Server Gated Crypto (nsSGC) see http://www.alvestrand.no/objectid/2.16.840.1.113730.4.1.html
+     */
+    public static final KeyPurposeId id_kp_nsSGC = new KeyPurposeId(new ASN1ObjectIdentifier("2.16.840.1.113730.4.1"));
+
+
     private ASN1ObjectIdentifier id;
 
     private KeyPurposeId(ASN1ObjectIdentifier id)
@@ -123,8 +141,8 @@
     }
 
     /**
-     * @deprecated use getInstance and an OID or one of the constants above.
      * @param id string representation of an OID.
+     * @deprecated use getInstance and an OID or one of the constants above.
      */
     public KeyPurposeId(String id)
     {
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x509/PolicyConstraints.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/PolicyConstraints.java
index aeb53f0..e2d22a7 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/x509/PolicyConstraints.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/x509/PolicyConstraints.java
@@ -93,12 +93,12 @@
 
         if (requireExplicitPolicyMapping != null)
         {
-            v.add(new DERTaggedObject(0, new ASN1Integer(requireExplicitPolicyMapping)));
+            v.add(new DERTaggedObject(false,0, new ASN1Integer(requireExplicitPolicyMapping)));
         }
 
         if (inhibitPolicyMapping != null)
         {
-            v.add(new DERTaggedObject(1, new ASN1Integer(inhibitPolicyMapping)));
+            v.add(new DERTaggedObject(false, 1, new ASN1Integer(inhibitPolicyMapping)));
         }
 
         return new DERSequence(v);
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 54e54ce..1ba5ebb 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/modes/GCMBlockCipher.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/modes/GCMBlockCipher.java
@@ -37,6 +37,7 @@
 
     // These fields are set by init and not modified by processing
     private boolean             forEncryption;
+    private boolean             initialised;
     private int                 macSize;
     private byte[]              lastKey;
     private byte[]              nonce;
@@ -99,6 +100,7 @@
     {
         this.forEncryption = forEncryption;
         this.macBlock = null;
+        this.initialised = true;
 
         KeyParameter keyParam;
         byte[] newNonce = null;
@@ -260,6 +262,7 @@
 
     public void processAADByte(byte in)
     {
+        checkStatus();
         // BEGIN android-added
         if (getTotalInputSizeAfterNewInput(1) > MAX_INPUT_SIZE) {
             throw new DataLengthException("Input exceeded " + MAX_INPUT_SIZE + " bytes");
@@ -319,11 +322,13 @@
     public int processByte(byte in, byte[] out, int outOff)
         throws DataLengthException
     {
+        checkStatus();
         // BEGIN android-added
         if (getTotalInputSizeAfterNewInput(1) > MAX_INPUT_SIZE) {
             throw new DataLengthException("Input exceeded " + MAX_INPUT_SIZE + " bytes");
         }
         // END android-added
+
         bufBlock[bufOff] = in;
         if (++bufOff == bufBlock.length)
         {
@@ -336,11 +341,13 @@
     public int processBytes(byte[] in, int inOff, int len, byte[] out, int outOff)
         throws DataLengthException
     {
+        checkStatus();
         // BEGIN android-added
         if (getTotalInputSizeAfterNewInput(len) > MAX_INPUT_SIZE) {
             throw new DataLengthException("Input exceeded " + MAX_INPUT_SIZE + " bytes");
         }
         // END android-added
+
         if (in.length < (inOff + len))
         {
             throw new DataLengthException("Input buffer too short");
@@ -385,6 +392,8 @@
     public int doFinal(byte[] out, int outOff)
         throws IllegalStateException, InvalidCipherTextException
     {
+        checkStatus();
+
         if (totalLength == 0)
         {
             initCipher();
@@ -534,9 +543,16 @@
             macBlock = null;
         }
 
-        if (initialAssociatedText != null)
+        if (forEncryption)
         {
-            processAADBytes(initialAssociatedText, 0, initialAssociatedText.length);
+            initialised = false;
+        }
+        else
+        {
+            if (initialAssociatedText != null)
+            {
+                processAADBytes(initialAssociatedText, 0, initialAssociatedText.length);
+            }
         }
     }
 
@@ -604,4 +620,16 @@
         cipher.processBlock(counter, 0, tmp, 0);
         return tmp;
     }
+
+    private void checkStatus()
+    {
+        if (!initialised)
+        {
+            if (forEncryption)
+            {
+                throw new IllegalStateException("GCM cipher cannot be reused for encryption");
+            }
+            throw new IllegalStateException("GCM cipher needs to be initialised");
+        }
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/params/ParametersWithID.java b/bcprov/src/main/java/org/bouncycastle/crypto/params/ParametersWithID.java
new file mode 100644
index 0000000..e942a5a
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/params/ParametersWithID.java
@@ -0,0 +1,28 @@
+package org.bouncycastle.crypto.params;
+
+import org.bouncycastle.crypto.CipherParameters;
+
+public class ParametersWithID
+    implements CipherParameters
+{
+    private CipherParameters  parameters;
+    private byte[] id;
+
+    public ParametersWithID(
+        CipherParameters parameters,
+        byte[] id)
+    {
+        this.parameters = parameters;
+        this.id = id;
+    }
+
+    public byte[] getID()
+    {
+        return id;
+    }
+
+    public CipherParameters getParameters()
+    {
+        return parameters;
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsNoCloseNotifyException.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsNoCloseNotifyException.java
new file mode 100644
index 0000000..a5c583b
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsNoCloseNotifyException.java
@@ -0,0 +1,15 @@
+package org.bouncycastle.crypto.tls;
+
+import java.io.EOFException;
+
+/**
+ * This exception will be thrown (only) when the connection is closed by the peer without sending a
+ * {@link AlertDescription#close_notify close_notify} warning alert. If this happens, the TLS
+ * protocol cannot rule out truncation of the connection data (potentially malicious). It may be
+ * possible to check for truncation via some property of a higher level protocol built upon TLS,
+ * e.g. the Content-Length header for HTTPS.
+ */
+public class TlsNoCloseNotifyException
+    extends EOFException
+{
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/DH.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/DH.java
index da326f4..579b973 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/DH.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/DH.java
@@ -1,5 +1,8 @@
 package org.bouncycastle.jcajce.provider.asymmetric;
 
+import java.util.HashMap;
+import java.util.Map;
+
 import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
 import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
 import org.bouncycastle.jcajce.provider.asymmetric.dh.KeyFactorySpi;
@@ -10,6 +13,14 @@
 {
     private static final String PREFIX = "org.bouncycastle.jcajce.provider.asymmetric" + ".dh.";
 
+    private static final Map<String, String> generalDhAttributes = new HashMap<String, String>();
+
+    static
+    {
+        generalDhAttributes.put("SupportedKeyClasses", "javax.crypto.interfaces.DHPublicKey|javax.crypto.interfaces.DHPrivateKey");
+        generalDhAttributes.put("SupportedKeyFormats", "PKCS#8|X.509");
+    }
+
     public static class Mappings
         extends AsymmetricAlgorithmProvider
     {
@@ -22,6 +33,7 @@
             provider.addAlgorithm("KeyPairGenerator.DH", PREFIX + "KeyPairGeneratorSpi");
             provider.addAlgorithm("Alg.Alias.KeyPairGenerator.DIFFIEHELLMAN", "DH");
 
+            provider.addAttributes("KeyAgreement.DH", generalDhAttributes);
             provider.addAlgorithm("KeyAgreement.DH", PREFIX + "KeyAgreementSpi");
             provider.addAlgorithm("Alg.Alias.KeyAgreement.DIFFIEHELLMAN", "DH");
             // BEGIN android-removed
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 92581f4..70ae120 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
@@ -97,7 +97,7 @@
                 // END android-changed
 
                 registerOid(provider, DSAUtil.dsaOids[i], "DSA", keyFact);
-                registerOidAlgorithmParameters(provider, DSAUtil.dsaOids[i], "DSA");
+                registerOidAlgorithmParameterGenerator(provider, DSAUtil.dsaOids[i], "DSA");
             }
         }
     }
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 7d0baa9..1c49da9 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
@@ -1,5 +1,8 @@
 package org.bouncycastle.jcajce.provider.asymmetric;
 
+import java.util.HashMap;
+import java.util.Map;
+
 // BEGIN android-removed
 // import org.bouncycastle.asn1.bsi.BSIObjectIdentifiers;
 // import org.bouncycastle.asn1.eac.EACObjectIdentifiers;
@@ -19,6 +22,14 @@
 {
     private static final String PREFIX = "org.bouncycastle.jcajce.provider.asymmetric" + ".ec.";
 
+    private static final Map<String, String> generalEcAttributes = new HashMap<String, String>();
+
+    static
+    {
+        generalEcAttributes.put("SupportedKeyClasses", "java.security.interfaces.ECPublicKey|java.security.interfaces.ECPrivateKey");
+        generalEcAttributes.put("SupportedKeyFormats", "PKCS#8|X.509");
+    }
+
     public static class Mappings
         extends AsymmetricAlgorithmProvider
     {
@@ -30,9 +41,12 @@
         {
             provider.addAlgorithm("AlgorithmParameters.EC", PREFIX + "AlgorithmParametersSpi");
 
+            provider.addAttributes("KeyAgreement.ECDH", generalEcAttributes);
             provider.addAlgorithm("KeyAgreement.ECDH", PREFIX + "KeyAgreementSpi$DH");
             // BEGIN android-removed
+            // 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." + X9ObjectIdentifiers.dhSinglePass_stdDH_sha1kdf_scheme, PREFIX + "KeyAgreementSpi$DHwithSHA1KDFAndSharedInfo");
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 20899c9..86026cc 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
@@ -1,5 +1,8 @@
 package org.bouncycastle.jcajce.provider.asymmetric;
 
+import java.util.HashMap;
+import java.util.Map;
+
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
 import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
 import org.bouncycastle.asn1.oiw.OIWObjectIdentifiers;
@@ -17,6 +20,14 @@
 {
     private static final String PREFIX = "org.bouncycastle.jcajce.provider.asymmetric" + ".rsa.";
 
+    private static final Map<String, String> generalRsaAttributes = new HashMap<String, String>();
+
+    static
+    {
+        generalRsaAttributes.put("SupportedKeyClasses", "javax.crypto.interfaces.RSAPublicKey|javax.crypto.interfaces.RSAPrivateKey");
+        generalRsaAttributes.put("SupportedKeyFormats", "PKCS#8|X.509");
+    }
+
     public static class Mappings
         extends AsymmetricAlgorithmProvider
     {
@@ -48,6 +59,7 @@
             // provider.addAlgorithm("Alg.Alias.AlgorithmParameters.NONEWITHRSAANDMGF1", "PSS");
             // END android-removed
 
+            provider.addAttributes("Cipher.RSA", generalRsaAttributes);
             provider.addAlgorithm("Cipher.RSA", PREFIX + "CipherSpi$NoPadding");
             // BEGIN android-changed
             provider.addAlgorithm("Alg.Alias.Cipher.RSA/RAW", "RSA");
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/dh/AlgorithmParameterGeneratorSpi.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/dh/AlgorithmParameterGeneratorSpi.java
index e4c8172..bf6bfe7 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/dh/AlgorithmParameterGeneratorSpi.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/dh/AlgorithmParameterGeneratorSpi.java
@@ -11,12 +11,13 @@
 import org.bouncycastle.crypto.generators.DHParametersGenerator;
 import org.bouncycastle.crypto.params.DHParameters;
 import org.bouncycastle.jcajce.provider.asymmetric.util.BaseAlgorithmParameterGeneratorSpi;
+import org.bouncycastle.jcajce.provider.asymmetric.util.PrimeCertaintyCalculator;
 
 public class AlgorithmParameterGeneratorSpi
     extends BaseAlgorithmParameterGeneratorSpi
 {
     protected SecureRandom random;
-    protected int strength = 1024;
+    protected int strength = 2048;
 
     private int l = 0;
 
@@ -48,13 +49,15 @@
     {
         DHParametersGenerator pGen = new DHParametersGenerator();
 
+        int certainty = PrimeCertaintyCalculator.getDefaultCertainty(strength);
+
         if (random != null)
         {
-            pGen.init(strength, 20, random);
+            pGen.init(strength, certainty, random);
         }
         else
         {
-            pGen.init(strength, 20, new SecureRandom());
+            pGen.init(strength, certainty, new SecureRandom());
         }
 
         DHParameters p = pGen.generateParameters();
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/dh/KeyPairGeneratorSpi.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/dh/KeyPairGeneratorSpi.java
index 793f729..864bf56 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/dh/KeyPairGeneratorSpi.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/dh/KeyPairGeneratorSpi.java
@@ -15,6 +15,7 @@
 import org.bouncycastle.crypto.params.DHParameters;
 import org.bouncycastle.crypto.params.DHPrivateKeyParameters;
 import org.bouncycastle.crypto.params.DHPublicKeyParameters;
+import org.bouncycastle.jcajce.provider.asymmetric.util.PrimeCertaintyCalculator;
 import org.bouncycastle.jce.provider.BouncyCastleProvider;
 import org.bouncycastle.util.Integers;
 
@@ -26,8 +27,7 @@
 
     DHKeyGenerationParameters param;
     DHBasicKeyPairGenerator engine = new DHBasicKeyPairGenerator();
-    int strength = 1024;
-    int certainty = 20;
+    int strength = 2048;
     SecureRandom random = new SecureRandom();
     boolean initialised = false;
 
@@ -95,7 +95,7 @@
 
                             DHParametersGenerator pGen = new DHParametersGenerator();
 
-                            pGen.init(strength, certainty, random);
+                            pGen.init(strength, PrimeCertaintyCalculator.getDefaultCertainty(strength), random);
 
                             param = new DHKeyGenerationParameters(random, pGen.generateParameters());
 
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 2d7c4c5..6a0dffa 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
@@ -12,11 +12,16 @@
 import org.bouncycastle.crypto.params.DSAParameterGenerationParameters;
 import org.bouncycastle.crypto.params.DSAParameters;
 import org.bouncycastle.jcajce.provider.asymmetric.util.BaseAlgorithmParameterGeneratorSpi;
+import org.bouncycastle.jcajce.provider.asymmetric.util.PrimeCertaintyCalculator;
 
 public class AlgorithmParameterGeneratorSpi
     extends BaseAlgorithmParameterGeneratorSpi
 {
     protected SecureRandom random;
+    // Android-changed: Change default strength to 1024
+    // In 1.57, the default strength was changed to 2048.  We keep it at 1024 for app
+    // compatibility, particularly because the default digest (SHA-1) doesn't have
+    // a sufficiently long digest to work with 2048-bit keys.
     protected int strength = 1024;
     protected DSAParameterGenerationParameters params;
 
@@ -69,19 +74,21 @@
             random = new SecureRandom();
         }
 
+        int certainty = PrimeCertaintyCalculator.getDefaultCertainty(strength);
+
         if (strength == 1024)
         {
-            params = new DSAParameterGenerationParameters(1024, 160, 80, random);
+            params = new DSAParameterGenerationParameters(1024, 160, certainty, random);
             pGen.init(params);
         }
         else if (strength > 1024)
         {
-            params = new DSAParameterGenerationParameters(strength, 256, 80, random);
+            params = new DSAParameterGenerationParameters(strength, 256, certainty, random);
             pGen.init(params);
         }
         else
         {
-            pGen.init(strength, 20, random);
+            pGen.init(strength, certainty, random);
         }
 
         DSAParameters p = pGen.generateParameters();
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 bacbb6c..366e6dc 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
@@ -17,6 +17,7 @@
 import org.bouncycastle.crypto.params.DSAParameters;
 import org.bouncycastle.crypto.params.DSAPrivateKeyParameters;
 import org.bouncycastle.crypto.params.DSAPublicKeyParameters;
+import org.bouncycastle.jcajce.provider.asymmetric.util.PrimeCertaintyCalculator;
 import org.bouncycastle.util.Integers;
 import org.bouncycastle.util.Properties;
 
@@ -28,8 +29,12 @@
 
     DSAKeyGenerationParameters param;
     DSAKeyPairGenerator engine = new DSAKeyPairGenerator();
+    // Android-changed: Change default strength to 1024
+    // In 1.57, the default strength was changed to 2048.  We keep it at 1024 for app
+    // compatibility, particularly because the default digest (SHA-1) doesn't have
+    // a sufficiently long digest to work with 2048-bit keys.
     int strength = 1024;
-    int certainty = 20;
+
     SecureRandom random = new SecureRandom();
     boolean initialised = false;
 
@@ -94,6 +99,8 @@
                         DSAParametersGenerator pGen;
                         DSAParameterGenerationParameters dsaParams;
 
+                        int certainty = PrimeCertaintyCalculator.getDefaultCertainty(strength);
+
                         // Typical combination of keysize and size of q.
                         //     keysize = 1024, q's size = 160
                         //     keysize = 2048, q's size = 224
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/rsa/KeyPairGeneratorSpi.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/rsa/KeyPairGeneratorSpi.java
index adb130e..4c3089a 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/rsa/KeyPairGeneratorSpi.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/rsa/KeyPairGeneratorSpi.java
@@ -12,6 +12,7 @@
 import org.bouncycastle.crypto.params.RSAKeyGenerationParameters;
 import org.bouncycastle.crypto.params.RSAKeyParameters;
 import org.bouncycastle.crypto.params.RSAPrivateCrtKeyParameters;
+import org.bouncycastle.jcajce.provider.asymmetric.util.PrimeCertaintyCalculator;
 
 public class KeyPairGeneratorSpi
     extends java.security.KeyPairGenerator
@@ -23,7 +24,6 @@
     }
 
     final static BigInteger defaultPublicExponent = BigInteger.valueOf(0x10001);
-    final static int defaultTests = 112;
 
     RSAKeyGenerationParameters param;
     RSAKeyPairGenerator engine;
@@ -34,7 +34,7 @@
 
         engine = new RSAKeyPairGenerator();
         param = new RSAKeyGenerationParameters(defaultPublicExponent,
-            new SecureRandom(), 2048, defaultTests);
+            new SecureRandom(), 2048, PrimeCertaintyCalculator.getDefaultCertainty(2048));
         engine.init(param);
     }
 
@@ -44,8 +44,8 @@
     {
         param = new RSAKeyGenerationParameters(defaultPublicExponent,
             // BEGIN android-changed
-            // Was: random, strength, defaultTests);
-            (random != null) ? random : new SecureRandom(), strength, defaultTests);
+            // Was: random, strength, PrimeCertaintyCalculator.getDefaultCertainty(strength));
+            (random != null) ? random : new SecureRandom(), strength, PrimeCertaintyCalculator.getDefaultCertainty(strength));
             // END android-changed
 
         engine.init(param);
@@ -65,8 +65,8 @@
         param = new RSAKeyGenerationParameters(
             rsaParams.getPublicExponent(),
             // BEGIN android-changed
-            // Was: random, rsaParams.getKeysize(), defaultTests);
-            (random != null) ? random : new SecureRandom(), rsaParams.getKeysize(), defaultTests);
+            // Was: random, rsaParams.getKeysize(), PrimeCertaintyCalculator.getDefaultCertainty(2048));
+            (random != null) ? random : new SecureRandom(), rsaParams.getKeysize(), PrimeCertaintyCalculator.getDefaultCertainty(2048));
             // END android-changed
 
         engine.init(param);
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 6fd4a0d..cba154a 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
@@ -11,6 +11,7 @@
 // BEGIN android-removed
 // import org.bouncycastle.asn1.anssi.ANSSINamedCurves;
 // import org.bouncycastle.asn1.cryptopro.ECGOST3410NamedCurves;
+// import org.bouncycastle.asn1.gm.GMNamedCurves;
 // END android-removed
 import org.bouncycastle.asn1.nist.NISTNamedCurves;
 import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
@@ -350,6 +351,10 @@
             // {
             //     oid = ANSSINamedCurves.getOID(name);
             // }
+            // if (oid == null)
+            // {
+            //     oid = GMNamedCurves.getOID(name);
+            // }
             // END android-removed
         }
 
@@ -398,6 +403,10 @@
             // {
             //     params = TeleTrusTNamedCurves.getByOID(oid);
             // }
+            // if (params == null)
+            // {
+            //     params = GMNamedCurves.getByOID(oid);
+            // }
             // END android-removed
         }
 
@@ -425,6 +434,10 @@
             // {
             //     params = TeleTrusTNamedCurves.getByName(curveName);
             // }
+            // if (params == null)
+            // {
+            //     params = GMNamedCurves.getByName(curveName);
+            // }
             // END android-removed
         }
 
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/util/PrimeCertaintyCalculator.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/util/PrimeCertaintyCalculator.java
new file mode 100644
index 0000000..92431ef
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/util/PrimeCertaintyCalculator.java
@@ -0,0 +1,21 @@
+package org.bouncycastle.jcajce.provider.asymmetric.util;
+
+public class PrimeCertaintyCalculator
+{
+    private PrimeCertaintyCalculator()
+    {
+
+    }
+
+    /**
+     * Return the current wisdom on prime certainty requirements.
+     *
+     * @param keySizeInBits size of the key being generated.
+     * @return a certainty value.
+     */
+    public static int getDefaultCertainty(int keySizeInBits)
+    {
+        // Based on FIPS 186-4 Table C.1
+        return keySizeInBits <= 1024 ? 80 : (96 + 16 * ((keySizeInBits - 1) / 1024));
+    }
+}
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 f00ee7b..58310c7 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
@@ -4,7 +4,9 @@
 import java.io.ByteArrayInputStream;
 import java.io.IOException;
 import java.io.InputStream;
+// BEGIN Android-added
 import java.io.PushbackInputStream;
+// END Android-added
 import java.security.cert.CRL;
 import java.security.cert.CRLException;
 import java.security.cert.CertPath;
@@ -44,6 +46,7 @@
 
     private static final PEMUtil PEM_CERT_PARSER = new PEMUtil("CERTIFICATE");
     private static final PEMUtil PEM_CRL_PARSER = new PEMUtil("CRL");
+    private static final PEMUtil PEM_PKCS7_PARSER = new PEMUtil("PKCS7");
 
     private ASN1Set sData = null;
     private int                sDataObjectCount = 0;
@@ -57,8 +60,24 @@
         ASN1InputStream dIn)
         throws IOException, CertificateParsingException
     {
-        ASN1Sequence seq = (ASN1Sequence)dIn.readObject();
+        return getCertificate(ASN1Sequence.getInstance(dIn.readObject()));
+    }
 
+    private java.security.cert.Certificate readPEMCertificate(
+        InputStream in)
+        throws IOException, CertificateParsingException
+    {
+        return getCertificate(PEM_CERT_PARSER.readPEMObject(in));
+    }
+
+    private java.security.cert.Certificate getCertificate(ASN1Sequence seq)
+        throws CertificateParsingException
+    {
+        if (seq == null)
+        {
+            return null;
+        }
+        
         if (seq.size() > 1
                 && seq.getObjectAt(0) instanceof ASN1ObjectIdentifier)
         {
@@ -95,23 +114,9 @@
         return null;
     }
 
-    private java.security.cert.Certificate readPEMCertificate(
-        InputStream in)
-        throws IOException, CertificateParsingException
-    {
-        ASN1Sequence seq = PEM_CERT_PARSER.readPEMObject(in);
-
-        if (seq != null)
-        {
-            return new X509CertificateObject(bcHelper,
-                            Certificate.getInstance(seq));
-        }
-
-        return null;
-    }
 
     protected CRL createCRL(CertificateList c)
-    throws CRLException
+        throws CRLException
     {
         return new X509CRLObject(bcHelper, c);
     }
@@ -120,23 +125,24 @@
         InputStream in)
         throws IOException, CRLException
     {
-        ASN1Sequence seq = PEM_CRL_PARSER.readPEMObject(in);
-
-        if (seq != null)
-        {
-            return createCRL(
-                            CertificateList.getInstance(seq));
-        }
-
-        return null;
+        return getCRL(PEM_CRL_PARSER.readPEMObject(in));
     }
 
     private CRL readDERCRL(
         ASN1InputStream aIn)
         throws IOException, CRLException
     {
-        ASN1Sequence seq = (ASN1Sequence)aIn.readObject();
+        return getCRL(ASN1Sequence.getInstance(aIn.readObject()));
+    }
 
+    private CRL getCRL(ASN1Sequence seq)
+        throws CRLException
+    {
+        if (seq == null)
+        {
+            return null;
+        }
+        
         if (seq.size() > 1
                 && seq.getObjectAt(0) instanceof ASN1ObjectIdentifier)
         {
@@ -144,7 +150,7 @@
             {
                 sCrlData = SignedData.getInstance(ASN1Sequence.getInstance(
                     (ASN1TaggedObject)seq.getObjectAt(1), true)).getCRLs();
-    
+
                 return getCRL();
             }
         }
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 3efd2d6..7badbdc 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
@@ -10,16 +10,20 @@
 {
     private final String _header1;
     private final String _header2;
+    private final String _header3;
     private final String _footer1;
     private final String _footer2;
+    private final String _footer3;
 
     PEMUtil(
         String type)
     {
         _header1 = "-----BEGIN " + type + "-----";
         _header2 = "-----BEGIN X509 " + type + "-----";
+        _header3 = "-----BEGIN PKCS7-----";
         _footer1 = "-----END " + type + "-----";
         _footer2 = "-----END X509 " + type + "-----";
+        _footer3 = "-----END PKCS7-----";
     }
 
     private String readLine(
@@ -71,7 +75,7 @@
 
         while ((line = readLine(in)) != null)
         {
-            if (line.startsWith(_header1) || line.startsWith(_header2))
+            if (line.startsWith(_header1) || line.startsWith(_header2) || line.startsWith(_header3))
             {
                 break;
             }
@@ -79,7 +83,7 @@
 
         while ((line = readLine(in)) != null)
         {
-            if (line.startsWith(_footer1) || line.startsWith(_footer2))
+            if (line.startsWith(_footer1) || line.startsWith(_footer2) || line.startsWith(_footer3))
             {
                 break;
             }
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 0865b57..768df66 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
@@ -1,5 +1,7 @@
 package org.bouncycastle.jcajce.provider.config;
 
+import java.util.Map;
+
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
 import org.bouncycastle.jcajce.provider.util.AsymmetricKeyInfoConverter;
 
@@ -48,4 +50,6 @@
     boolean hasAlgorithm(String type, String name);
 
     void addKeyInfoConverter(ASN1ObjectIdentifier oid, AsymmetricKeyInfoConverter keyInfoConverter);
+
+    void addAttributes(String key, Map<String, String> attributeMap);
 }
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 ed7342d..f118e5c 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
@@ -11,6 +11,8 @@
 import java.security.SecureRandom;
 import java.security.spec.AlgorithmParameterSpec;
 import java.security.spec.InvalidParameterSpecException;
+import java.util.HashMap;
+import java.util.Map;
 
 import javax.crypto.spec.IvParameterSpec;
 
@@ -33,6 +35,7 @@
 import org.bouncycastle.crypto.engines.AESEngine;
 import org.bouncycastle.crypto.engines.AESWrapEngine;
 // BEGIN android-removed
+// import org.bouncycastle.crypto.engines.AESWrapPadEngine;
 // import org.bouncycastle.crypto.engines.RFC3211WrapEngine;
 // import org.bouncycastle.crypto.engines.RFC5649WrapEngine;
 // import org.bouncycastle.crypto.generators.Poly1305KeyGenerator;
@@ -67,6 +70,14 @@
 {
     private static final Class gcmSpecClass = lookup("javax.crypto.spec.GCMParameterSpec");
 
+    private static final Map<String, String> generalAesAttributes = new HashMap<String, String>();
+
+    static
+    {
+        generalAesAttributes.put("SupportedKeyClasses", "javax.crypto.SecretKey");
+        generalAesAttributes.put("SupportedKeyFormats", "RAW");
+    }
+
     private AES()
     {
     }
@@ -262,6 +273,15 @@
     }
 
     // BEGIN android-removed
+    // public static class WrapPad
+    //         extends BaseWrapCipher
+    // {
+    //     public WrapPad()
+    //     {
+    //         super(new AESWrapPadEngine());
+    //     }
+    // }
+
     // public static class RFC3211Wrap
     //     extends BaseWrapCipher
     // {
@@ -852,6 +872,7 @@
             // provider.addAlgorithm("Alg.Alias.AlgorithmParameterGenerator." + NISTObjectIdentifiers.id_aes256_CBC, "AES");
             // END android-removed
 
+            provider.addAttributes("Cipher.AES", generalAesAttributes);
             provider.addAlgorithm("Cipher.AES", PREFIX + "$ECB");
             provider.addAlgorithm("Alg.Alias.Cipher." + wrongAES128, "AES");
             provider.addAlgorithm("Alg.Alias.Cipher." + wrongAES192, "AES");
@@ -870,6 +891,8 @@
             // provider.addAlgorithm("Cipher", NISTObjectIdentifiers.id_aes192_CFB, PREFIX + "$CFB");
             // provider.addAlgorithm("Cipher", NISTObjectIdentifiers.id_aes256_CFB, PREFIX + "$CFB");
             // END android-removed
+
+            provider.addAttributes("Cipher.AESWRAP", generalAesAttributes);
             provider.addAlgorithm("Cipher.AESWRAP", PREFIX + "$Wrap");
             provider.addAlgorithm("Alg.Alias.Cipher", NISTObjectIdentifiers.id_aes128_wrap, "AESWRAP");
             provider.addAlgorithm("Alg.Alias.Cipher", NISTObjectIdentifiers.id_aes192_wrap, "AESWRAP");
@@ -877,6 +900,13 @@
             provider.addAlgorithm("Alg.Alias.Cipher.AESKW", "AESWRAP");
 
             // BEGIN android-removed
+            // provider.addAttributes("Cipher.AESWRAPPAD", generalAesAttributes);
+            // provider.addAlgorithm("Cipher.AESWRAPPAD", PREFIX + "$WrapPad");
+            // provider.addAlgorithm("Alg.Alias.Cipher", NISTObjectIdentifiers.id_aes128_wrap_pad, "AESWRAPPAD");
+            // provider.addAlgorithm("Alg.Alias.Cipher", NISTObjectIdentifiers.id_aes192_wrap_pad, "AESWRAPPAD");
+            // provider.addAlgorithm("Alg.Alias.Cipher", NISTObjectIdentifiers.id_aes256_wrap_pad, "AESWRAPPAD");
+            // provider.addAlgorithm("Alg.Alias.Cipher.AESKWP", "AESWRAPPAD");
+            //
             // provider.addAlgorithm("Cipher.AESRFC3211WRAP", PREFIX + "$RFC3211Wrap");
             // provider.addAlgorithm("Cipher.AESRFC5649WRAP", PREFIX + "$RFC5649Wrap");
             //
@@ -885,6 +915,7 @@
             // provider.addAlgorithm("Alg.Alias.AlgorithmParameterGenerator." + NISTObjectIdentifiers.id_aes192_CCM, "CCM");
             // provider.addAlgorithm("Alg.Alias.AlgorithmParameterGenerator." + NISTObjectIdentifiers.id_aes256_CCM, "CCM");
             //
+            // provider.addAttributes("Cipher.CCM", generalAesAttributes);
             // provider.addAlgorithm("Cipher.CCM", PREFIX + "$CCM");
             // provider.addAlgorithm("Alg.Alias.Cipher", NISTObjectIdentifiers.id_aes128_CCM, "CCM");
             // provider.addAlgorithm("Alg.Alias.Cipher", NISTObjectIdentifiers.id_aes192_CCM, "CCM");
@@ -897,6 +928,7 @@
             // END android-removed
 
             // BEGIN android-changed
+            provider.addAttributes("Cipher.AES/GCM/NOPADDING", generalAesAttributes);
             provider.addAlgorithm("Cipher.AES/GCM/NOPADDING", PREFIX + "$GCM");
             provider.addAlgorithm("Alg.Alias.Cipher." + NISTObjectIdentifiers.id_aes128_GCM, "AES/GCM/NOPADDING");
             provider.addAlgorithm("Alg.Alias.Cipher." + NISTObjectIdentifiers.id_aes192_GCM, "AES/GCM/NOPADDING");
@@ -930,6 +962,10 @@
             // provider.addAlgorithm("KeyGenerator", NISTObjectIdentifiers.id_aes128_CCM, PREFIX + "$KeyGen128");
             // provider.addAlgorithm("KeyGenerator", NISTObjectIdentifiers.id_aes192_CCM, PREFIX + "$KeyGen192");
             // provider.addAlgorithm("KeyGenerator", NISTObjectIdentifiers.id_aes256_CCM, PREFIX + "$KeyGen256");
+            // provider.addAlgorithm("KeyGenerator.AESWRAPPAD", PREFIX + "$KeyGen");
+            // provider.addAlgorithm("KeyGenerator", NISTObjectIdentifiers.id_aes128_wrap_pad, PREFIX + "$KeyGen128");
+            // provider.addAlgorithm("KeyGenerator", NISTObjectIdentifiers.id_aes192_wrap_pad, PREFIX + "$KeyGen192");
+            // provider.addAlgorithm("KeyGenerator", NISTObjectIdentifiers.id_aes256_wrap_pad, PREFIX + "$KeyGen256");
 
             // provider.addAlgorithm("Mac.AESCMAC", PREFIX + "$AESCMAC");
 
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 51c29d9..7102db5 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
@@ -18,6 +18,7 @@
 
 import org.bouncycastle.crypto.CipherParameters;
 import org.bouncycastle.crypto.Mac;
+import org.bouncycastle.crypto.macs.HMac;
 import org.bouncycastle.crypto.params.AEADParameters;
 import org.bouncycastle.crypto.params.KeyParameter;
 import org.bouncycastle.crypto.params.ParametersWithIV;
@@ -39,8 +40,8 @@
     private Mac macEngine;
 
     private int scheme = PKCS12;
-    private int                     pbeHash = SHA1;
-    private int                     keySize = 160;
+    private int pbeHash = SHA1;
+    private int keySize = 160;
 
     protected BaseMac(
         Mac macEngine)
@@ -109,12 +110,44 @@
             //      keySize = 256;
             // }
             // BEGIN android-changed
-            // Was: else if (macEngine.getAlgorithmName().startsWith("SHA256"))
-            if (macEngine.getAlgorithmName().startsWith("SHA256"))
+            // Was: else if (macEngine instanceof HMac)
+            if (macEngine instanceof HMac)
             // END android-changed
             {
-                digest = SHA256;
-                keySize = 256;
+                if (!macEngine.getAlgorithmName().startsWith("SHA-1"))
+                {
+                    if (macEngine.getAlgorithmName().startsWith("SHA-224"))
+                    {
+                        digest = SHA224;
+                        keySize = 224;
+                    }
+                    else if (macEngine.getAlgorithmName().startsWith("SHA-256"))
+                    {
+                        digest = SHA256;
+                        keySize = 256;
+                    }
+                    else if (macEngine.getAlgorithmName().startsWith("SHA-384"))
+                    {
+                        digest = SHA384;
+                        keySize = 384;
+                    }
+                    else if (macEngine.getAlgorithmName().startsWith("SHA-512"))
+                    {
+                        digest = SHA512;
+                        keySize = 512;
+                    }
+                    // BEGIN android-removed
+                    // else if (macEngine.getAlgorithmName().startsWith("RIPEMD160"))
+                    // {
+                    //     digest = RIPEMD160;
+                    //     keySize = 160;
+                    // }
+                    // END android-removed
+                    else
+                    {
+                        throw new InvalidAlgorithmParameterException("no PKCS12 mapping for HMAC: " + macEngine.getAlgorithmName());
+                    }
+                }
             }
             // TODO: add correct handling for other digests
             param = PBE.Util.makePBEMacParameters(k, PKCS12, digest, keySize, pbeSpec);
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/util/BaseStreamCipher.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/util/BaseStreamCipher.java
index ea3ac5b..f289308 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/util/BaseStreamCipher.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/util/BaseStreamCipher.java
@@ -5,6 +5,7 @@
 import java.security.InvalidKeyException;
 import java.security.InvalidParameterException;
 import java.security.Key;
+import java.security.NoSuchAlgorithmException;
 import java.security.SecureRandom;
 import java.security.spec.AlgorithmParameterSpec;
 
@@ -123,10 +124,11 @@
      */
     protected void engineSetMode(
         String  mode)
+        throws NoSuchAlgorithmException
     {
         if (!mode.equalsIgnoreCase("ECB"))
         {
-            throw new IllegalArgumentException("can't support mode " + mode);
+            throw new NoSuchAlgorithmException("can't support mode " + mode);
         }
     }
 
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 c401084..448c352 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
@@ -36,6 +36,11 @@
 
     protected void registerOidAlgorithmParameters(ConfigurableProvider provider, ASN1ObjectIdentifier oid, String name)
     {
+        provider.addAlgorithm("Alg.Alias.AlgorithmParameters." + oid, name);
+    }
+
+    protected void registerOidAlgorithmParameterGenerator(ConfigurableProvider provider, ASN1ObjectIdentifier oid, String name)
+    {
         provider.addAlgorithm("Alg.Alias.AlgorithmParameterGenerator." + oid, name);
         provider.addAlgorithm("Alg.Alias.AlgorithmParameters." + oid, name);
     }
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 6a7c9e6..5cb5d52 100644
--- a/bcprov/src/main/java/org/bouncycastle/jce/provider/BouncyCastleProvider.java
+++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/BouncyCastleProvider.java
@@ -7,6 +7,7 @@
 import java.security.Provider;
 import java.security.PublicKey;
 import java.util.HashMap;
+import java.util.Iterator;
 import java.util.Map;
 
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
@@ -44,7 +45,7 @@
 public final class BouncyCastleProvider extends Provider
     implements ConfigurableProvider
 {
-    private static String info = "BouncyCastle Security Provider v1.56";
+    private static String info = "BouncyCastle Security Provider v1.57";
 
     public static final String PROVIDER_NAME = "BC";
 
@@ -74,7 +75,7 @@
     private static final String[] SYMMETRIC_CIPHERS =
     {
         // BEGIN android-removed
-        // "AES", "ARC4", "Blowfish", "Camellia", "CAST5", "CAST6", "ChaCha", "DES", "DESede",
+        // "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"
@@ -104,7 +105,7 @@
     private static final String[] ASYMMETRIC_CIPHERS =
     {
         // BEGIN android-removed
-        // "DSA", "DH", "EC", "RSA", "GOST", "ECGOST", "ElGamal", "DSTU4145"
+        // "DSA", "DH", "EC", "RSA", "GOST", "ECGOST", "ElGamal", "DSTU4145", "GM"
         // END android-removed
         // BEGIN android-added
         "DSA", "DH", "EC", "RSA",
@@ -152,7 +153,7 @@
      */
     public BouncyCastleProvider()
     {
-        super(PROVIDER_NAME, 1.56, info);
+        super(PROVIDER_NAME, 1.57, info);
 
         AccessController.doPrivileged(new PrivilegedAction()
         {
@@ -305,6 +306,21 @@
         }
     }
 
+    public void addAttributes(String key, Map<String, String> attributeMap)
+    {
+        for (Iterator it = attributeMap.keySet().iterator(); it.hasNext();)
+        {
+            String attributeName = (String)it.next();
+            String attributeKey = key + " " + attributeName;
+            if (containsKey(attributeKey))
+            {
+                throw new IllegalStateException("duplicate provider attribute key (" + attributeKey + ") found");
+            }
+
+            put(attributeKey, attributeMap.get(attributeName));
+        }
+    }
+
     private static AsymmetricKeyInfoConverter getAsymmetricKeyInfoConverter(ASN1ObjectIdentifier algorithm)
     {
         synchronized (keyInfoConverters)
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/FixedPointCombMultiplier.java b/bcprov/src/main/java/org/bouncycastle/math/ec/FixedPointCombMultiplier.java
index 9fe00b9..c91de7b 100644
--- a/bcprov/src/main/java/org/bouncycastle/math/ec/FixedPointCombMultiplier.java
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/FixedPointCombMultiplier.java
@@ -47,7 +47,7 @@
             R = R.twicePlus(lookupTable[index]);
         }
 
-        return R;
+        return R.add(info.getOffset());
     }
 
     protected int getWidthForCombSize(int combSize)
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/FixedPointPreCompInfo.java b/bcprov/src/main/java/org/bouncycastle/math/ec/FixedPointPreCompInfo.java
index b7569aa..31f5d10 100644
--- a/bcprov/src/main/java/org/bouncycastle/math/ec/FixedPointPreCompInfo.java
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/FixedPointPreCompInfo.java
@@ -5,6 +5,8 @@
  */
 public class FixedPointPreCompInfo implements PreCompInfo
 {
+    protected ECPoint offset = null;
+
     /**
      * Array holding the precomputed <code>ECPoint</code>s used for a fixed
      * point multiplication.
@@ -18,6 +20,16 @@
      */
     protected int width = -1;
 
+    public ECPoint getOffset()
+    {
+        return offset;
+    }
+
+    public void setOffset(ECPoint offset)
+    {
+        this.offset = offset;
+    }
+
     public ECPoint[] getPreComp()
     {
         return preComp;
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/FixedPointUtil.java b/bcprov/src/main/java/org/bouncycastle/math/ec/FixedPointUtil.java
index e4fbb8d..93b435c 100644
--- a/bcprov/src/main/java/org/bouncycastle/math/ec/FixedPointUtil.java
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/FixedPointUtil.java
@@ -35,18 +35,21 @@
             int bits = getCombSize(c);
             int d = (bits + minWidth - 1) / minWidth;
 
-            ECPoint[] pow2Table = new ECPoint[minWidth];
+            ECPoint[] pow2Table = new ECPoint[minWidth + 1];
             pow2Table[0] = p;
             for (int i = 1; i < minWidth; ++i)
             {
                 pow2Table[i] = pow2Table[i - 1].timesPow2(d);
             }
-    
+
+            // This will be the 'offset' value 
+            pow2Table[minWidth] = pow2Table[0].subtract(pow2Table[1]);
+
             c.normalizeAll(pow2Table);
-    
+
             lookupTable = new ECPoint[n];
-            lookupTable[0] = c.getInfinity();
-    
+            lookupTable[0] = pow2Table[0];
+
             for (int bit = minWidth - 1; bit >= 0; --bit)
             {
                 ECPoint pow2 = pow2Table[bit];
@@ -60,6 +63,7 @@
 
             c.normalizeAll(lookupTable);
 
+            info.setOffset(pow2Table[minWidth]);
             info.setPreComp(lookupTable);
             info.setWidth(minWidth);
 
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 2057c8b..6b77f8f 100644
--- a/bcprov/src/main/java/org/bouncycastle/math/raw/Nat.java
+++ b/bcprov/src/main/java/org/bouncycastle/math/raw/Nat.java
@@ -1075,4 +1075,12 @@
             z[i] = 0;
         }
     }
+
+    public static void zero64(int len, long[] z)
+    {
+        for (int i = 0; i < len; ++i)
+        {
+            z[i] = 0L;
+        }
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/util/Arrays.java b/bcprov/src/main/java/org/bouncycastle/util/Arrays.java
index ae60928..6873117 100644
--- a/bcprov/src/main/java/org/bouncycastle/util/Arrays.java
+++ b/bcprov/src/main/java/org/bouncycastle/util/Arrays.java
@@ -944,6 +944,22 @@
         return result;
     }
 
+    public static String[] append(String[] a, String b)
+    {
+        if (a == null)
+        {
+            return new String[]{ b };
+        }
+
+        int length = a.length;
+        String[] result = new String[length + 1];
+        System.arraycopy(a, 0, result, 0, length);
+        result[length] = b;
+        return result;
+    }
+
+
+
     public static byte[] concatenate(byte[] a, byte[] b)
     {
         if (a != null && b != null)
diff --git a/bcprov/src/main/java/org/bouncycastle/util/Properties.java b/bcprov/src/main/java/org/bouncycastle/util/Properties.java
index 96cef35..e533b58 100644
--- a/bcprov/src/main/java/org/bouncycastle/util/Properties.java
+++ b/bcprov/src/main/java/org/bouncycastle/util/Properties.java
@@ -3,6 +3,10 @@
 import java.security.AccessControlException;
 import java.security.AccessController;
 import java.security.PrivilegedAction;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.StringTokenizer;
 
 /**
  * Utility method for accessing system properties.
@@ -33,4 +37,19 @@
             return false;
         }
     }
+
+    public static Set<String> asKeySet(final String propertyName)
+    {
+        Set<String> set = new HashSet<String>();
+        String p = System.getProperty(propertyName);
+        if (p != null)
+        {
+            StringTokenizer sTok = new StringTokenizer(p, ",");
+            while (sTok.hasMoreElements())
+            {
+                set.add(Strings.toLowerCase(sTok.nextToken()).trim());
+            }
+        }
+        return Collections.unmodifiableSet(set);
+    }
 }
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 c04a8cc..e3129f0 100644
--- a/bcprov/src/main/java/org/bouncycastle/util/encoders/Base64.java
+++ b/bcprov/src/main/java/org/bouncycastle/util/encoders/Base64.java
@@ -12,7 +12,7 @@
 public class Base64
 {
     private static final Encoder encoder = new Base64Encoder();
-    
+
     public static String toBase64String(
         byte[] data)
     {
@@ -21,8 +21,8 @@
 
     public static String toBase64String(
         byte[] data,
-        int    off,
-        int    length)
+        int off,
+        int length)
     {
         byte[] encoded = encode(data, off, length);
         return Strings.fromByteArray(encoded);
@@ -34,7 +34,7 @@
      * @return a byte array containing the base 64 encoded data.
      */
     public static byte[] encode(
-        byte[]    data)
+        byte[] data)
     {
         return encode(data, 0, data.length);
     }
@@ -46,8 +46,8 @@
      */
     public static byte[] encode(
         byte[] data,
-        int    off,
-        int    length)
+        int off,
+        int length)
     {
         int len = (length + 2) / 3 * 4;
         ByteArrayOutputStream bOut = new ByteArrayOutputStream(len);
@@ -60,7 +60,7 @@
         {
             throw new EncoderException("exception encoding base64 string: " + e.getMessage(), e);
         }
-        
+
         return bOut.toByteArray();
     }
 
@@ -70,39 +70,39 @@
      * @return the number of bytes produced.
      */
     public static int encode(
-        byte[]                data,
-        OutputStream    out)
+        byte[] data,
+        OutputStream out)
         throws IOException
     {
         return encoder.encode(data, 0, data.length, out);
     }
-    
+
     /**
      * Encode the byte data to base 64 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)
+        byte[] data,
+        int off,
+        int length,
+        OutputStream out)
         throws IOException
     {
         return encoder.encode(data, off, length, out);
     }
-    
+
     /**
      * decode the base 64 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)
+        byte[] data)
     {
         int len = data.length / 4 * 3;
         ByteArrayOutputStream bOut = new ByteArrayOutputStream(len);
-        
+
         try
         {
             encoder.decode(data, 0, data.length, bOut);
@@ -111,21 +111,21 @@
         {
             throw new DecoderException("unable to decode base64 data: " + e.getMessage(), e);
         }
-        
+
         return bOut.toByteArray();
     }
-    
+
     /**
      * decode the base 64 encoded String data - whitespace will be ignored.
      *
      * @return a byte array representing the decoded data.
      */
     public static byte[] decode(
-        String    data)
+        String data)
     {
         int len = data.length() / 4 * 3;
         ByteArrayOutputStream bOut = new ByteArrayOutputStream(len);
-        
+
         try
         {
             encoder.decode(data, bOut);
@@ -134,10 +134,10 @@
         {
             throw new DecoderException("unable to decode base64 string: " + e.getMessage(), e);
         }
-        
+
         return bOut.toByteArray();
     }
-    
+
     /**
      * decode the base 64 encoded String data writing it to the given output stream,
      * whitespace characters will be ignored.
@@ -145,10 +145,31 @@
      * @return the number of bytes produced.
      */
     public static int decode(
-        String                data,
-        OutputStream    out)
+        String data,
+        OutputStream out)
         throws IOException
     {
         return encoder.decode(data, out);
     }
+
+    /**
+     * Decode to an output stream;
+     *
+     * @param base64Data       The source data.
+     * @param start            Start position.
+     * @param length           the length.
+     * @param out The output stream to write to.
+     */
+    public static int decode(byte[] base64Data, int start, int length, OutputStream out)
+    {
+        try
+        {
+           return encoder.decode(base64Data, start, length, out);
+        }
+        catch (Exception e)
+        {
+            throw new DecoderException("unable to decode base64 data: " + e.getMessage(), e);
+        }
+
+    }
 }
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 abad02c..4216674 100644
--- a/bcprov/src/main/java/org/bouncycastle/util/encoders/Base64Encoder.java
+++ b/bcprov/src/main/java/org/bouncycastle/util/encoders/Base64Encoder.java
@@ -272,6 +272,11 @@
         
         if (c3 == padding)
         {
+            if (c4 != padding)
+            {
+                throw new IOException("invalid characters encountered at end of base64 data");
+            }
+            
             b1 = decodingTable[c1];
             b2 = decodingTable[c2];
 
diff --git a/bcprov/src/main/java/org/bouncycastle/util/io/SimpleOutputStream.java b/bcprov/src/main/java/org/bouncycastle/util/io/SimpleOutputStream.java
new file mode 100644
index 0000000..af2f45f
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/util/io/SimpleOutputStream.java
@@ -0,0 +1,21 @@
+package org.bouncycastle.util.io;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+public abstract class SimpleOutputStream extends OutputStream
+{
+    public void close()
+    {
+    }
+
+    public void flush()
+    {
+    }
+
+    public void write(int b) throws IOException
+    {
+        byte[] buf = new byte[]{ (byte)b };
+        write(buf, 0, 1);
+    }
+}
diff --git a/bouncycastle.version b/bouncycastle.version
index c7e252d..e258c89 100644
--- a/bouncycastle.version
+++ b/bouncycastle.version
@@ -1,2 +1,2 @@
 BOUNCYCASTLE_JDK=15on
-BOUNCYCASTLE_VERSION=156
+BOUNCYCASTLE_VERSION=157