Make existing bouncycastle bcprov build on host and add host-only bcpkix build

- Move existing provider source to bcprov
- Added bcpkix host build to support built/tooks/signapk

sha1sum of sources:
- 10bfea344842fe8e065c80e399c93f8651dc87d8  bcprov-jdk15on-147.tar.gz
- 913828c7ae36e030508e97e07b3c213fb1db1e9c  bcpkix-jdk15on-147.tar.gz

Bug: 7056297
Change-Id: Id4f957f300a39aa34b4c3c679b2312631d3f1639
diff --git a/Android.mk b/Android.mk
index 9d162fb..71dd898 100644
--- a/Android.mk
+++ b/Android.mk
@@ -15,12 +15,21 @@
 #
 LOCAL_PATH := $(call my-dir)
 
-bouncycastle_src_files := $(call all-java-files-under,src/main/java)
+all_bcprov_src_files := $(call all-java-files-under,bcprov/src/main/java)
+
+android_bcprov_src_files := $(filter-out \
+ bcprov/src/main/java/org/bouncycastle/crypto/digests/AndroidDigestFactoryBouncyCastle.java, \
+ $(all_bcprov_src_files))
+
+ri_bcprov_src_files := $(filter-out \
+ bcprov/src/main/java/org/bouncycastle/crypto/digests/AndroidDigestFactoryOpenSSL.java \
+ bcprov/src/main/java/org/bouncycastle/crypto/digests/OpenSSLDigest.java, \
+ $(all_bcprov_src_files))
 
 include $(CLEAR_VARS)
 LOCAL_MODULE := bouncycastle
 LOCAL_MODULE_TAGS := optional
-LOCAL_SRC_FILES := $(bouncycastle_src_files)
+LOCAL_SRC_FILES := $(android_bcprov_src_files)
 LOCAL_JAVACFLAGS := -encoding UTF-8
 LOCAL_JAVA_LIBRARIES := core
 LOCAL_NO_STANDARD_LIBRARIES := true
@@ -71,7 +80,7 @@
     include $(CLEAR_VARS)
     LOCAL_MODULE := bouncycastle-hostdex
     LOCAL_MODULE_TAGS := optional
-    LOCAL_SRC_FILES := $(bouncycastle_src_files)
+    LOCAL_SRC_FILES := $(android_bcprov_src_files)
     LOCAL_JAVACFLAGS := -encoding UTF-8
     LOCAL_JAVA_LIBRARIES := core-hostdex
     LOCAL_NO_STANDARD_LIBRARIES := true
@@ -80,3 +89,20 @@
     LOCAL_JARJAR_RULES := $(LOCAL_PATH)/jarjar-rules.txt
     include $(BUILD_HOST_JAVA_LIBRARY)
 endif
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := bouncycastle-host
+LOCAL_MODULE_TAGS := optional
+LOCAL_SRC_FILES := $(ri_bcprov_src_files)
+LOCAL_JAVACFLAGS := -encoding UTF-8
+LOCAL_MODULE_TAGS := optional
+include $(BUILD_HOST_JAVA_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := bouncycastle-bcpkix-host
+LOCAL_MODULE_TAGS := optional
+LOCAL_SRC_FILES := $(call all-java-files-under,bcpkix/src/main/java)
+LOCAL_JAVACFLAGS := -encoding UTF-8
+LOCAL_MODULE_TAGS := optional
+LOCAL_JAVA_LIBRARIES := bouncycastle-host
+include $(BUILD_HOST_JAVA_LIBRARY)
diff --git a/README.android b/README.android
index 80f8ea1..ec227bb 100644
--- a/README.android
+++ b/README.android
@@ -11,20 +11,25 @@
 The following steps are recommended for porting new Bouncy Castle versions.
 
 1) Retrieve the appropriate version of the Bouncy Castle source from
-   www.bouncycastle.org/latest_releases.html (in bcprov-jdk*-*.tar.gz
-   file). Check the checksum (found at http://bouncycastle.org/checksums.html) with:
+   www.bouncycastle.org/latest_releases.html (both bcprov-jdk*-*.tar.gz
+   and bcpkix-jdk*-*.tar.gz files).
+
+   Check the checksum (found at http://bouncycastle.org/checksums.html) with:
 
      md5sum bcprov-jdk*-*.tar.gz
      sha1sum bcprov-jdk*-*.tar.gz
+     md5sum bcpkix-jdk*-*.tar.gz
+     sha1sum bcpkix-jdk*-*.tar.gz
 
 2) Update the variables in bouncycastle.config and bouncycastle.version as appropriate.
    At the very least you will need to update the bouncycastle.version.
-   Similarly update ThirdPartyProject.prop.
 
 3) Run:
 
      ./import_bouncycastle.sh import bcprov-jdk*-*.tar.gz
 
+   Note the script expects to find the bcpkix-jdk*-*.tar.gz alongside the bcprov file.
+
 4) If there are any errors, then modify bouncycastle.config, bouncycastle.version
    and patches in patches/ as appropriate.  You might want to use:
 
diff --git a/bcpkix/src/main/java/org/bouncycastle/cert/AttributeCertificateHolder.java b/bcpkix/src/main/java/org/bouncycastle/cert/AttributeCertificateHolder.java
new file mode 100644
index 0000000..f354bc7
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/cert/AttributeCertificateHolder.java
@@ -0,0 +1,357 @@
+package org.bouncycastle.cert;
+
+import java.io.OutputStream;
+import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.x500.X500Name;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.asn1.x509.GeneralName;
+import org.bouncycastle.asn1.x509.GeneralNames;
+import org.bouncycastle.asn1.x509.Holder;
+import org.bouncycastle.asn1.x509.IssuerSerial;
+import org.bouncycastle.asn1.x509.ObjectDigestInfo;
+import org.bouncycastle.operator.DigestCalculator;
+import org.bouncycastle.operator.DigestCalculatorProvider;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.Selector;
+
+/**
+ * The Holder object.
+ * 
+ * <pre>
+ *          Holder ::= SEQUENCE {
+ *                baseCertificateID   [0] IssuerSerial OPTIONAL,
+ *                         -- the issuer and serial number of
+ *                         -- the holder's Public Key Certificate
+ *                entityName          [1] GeneralNames OPTIONAL,
+ *                         -- the name of the claimant or role
+ *                objectDigestInfo    [2] ObjectDigestInfo OPTIONAL
+ *                         -- used to directly authenticate the holder,
+ *                         -- for example, an executable
+ *          }
+ * </pre>
+ * <p>
+ * <b>Note:</b> If objectDigestInfo comparisons are to be carried out the static
+ * method setDigestCalculatorProvider <b>must</b> be called once to configure the class
+ * to do the necessary calculations.
+ * </p>
+ */
+public class AttributeCertificateHolder
+    implements Selector
+{
+    private static DigestCalculatorProvider digestCalculatorProvider;
+
+    final Holder holder;
+
+    AttributeCertificateHolder(ASN1Sequence seq)
+    {
+        holder = Holder.getInstance(seq);
+    }
+
+    public AttributeCertificateHolder(X500Name issuerName,
+        BigInteger serialNumber)
+    {
+        holder = new Holder(new IssuerSerial(
+            new GeneralNames(new GeneralName(issuerName)),
+            new ASN1Integer(serialNumber)));
+    }
+
+    public AttributeCertificateHolder(X509CertificateHolder cert)
+    {
+        holder = new Holder(new IssuerSerial(generateGeneralNames(cert.getIssuer()),
+            new ASN1Integer(cert.getSerialNumber())));
+    }
+
+    public AttributeCertificateHolder(X500Name principal)
+    {
+        holder = new Holder(generateGeneralNames(principal));
+    }
+
+    /**
+     * Constructs a holder for v2 attribute certificates with a hash value for
+     * some type of object.
+     * <p>
+     * <code>digestedObjectType</code> can be one of the following:
+     * <ul>
+     * <li>0 - publicKey - A hash of the public key of the holder must be
+     * passed.
+     * <li>1 - publicKeyCert - A hash of the public key certificate of the
+     * holder must be passed.
+     * <li>2 - otherObjectDigest - A hash of some other object type must be
+     * passed. <code>otherObjectTypeID</code> must not be empty.
+     * </ul>
+     * <p>
+     * This cannot be used if a v1 attribute certificate is used.
+     * 
+     * @param digestedObjectType The digest object type.
+     * @param digestAlgorithm The algorithm identifier for the hash.
+     * @param otherObjectTypeID The object type ID if
+     *            <code>digestedObjectType</code> is
+     *            <code>otherObjectDigest</code>.
+     * @param objectDigest The hash value.
+     */
+    public AttributeCertificateHolder(int digestedObjectType,
+        ASN1ObjectIdentifier digestAlgorithm, ASN1ObjectIdentifier otherObjectTypeID, byte[] objectDigest)
+    {
+        holder = new Holder(new ObjectDigestInfo(digestedObjectType,
+            otherObjectTypeID, new AlgorithmIdentifier(digestAlgorithm), Arrays
+                .clone(objectDigest)));
+    }
+
+    /**
+     * Returns the digest object type if an object digest info is used.
+     * <p>
+     * <ul>
+     * <li>0 - publicKey - A hash of the public key of the holder must be
+     * passed.
+     * <li>1 - publicKeyCert - A hash of the public key certificate of the
+     * holder must be passed.
+     * <li>2 - otherObjectDigest - A hash of some other object type must be
+     * passed. <code>otherObjectTypeID</code> must not be empty.
+     * </ul>
+     * 
+     * @return The digest object type or -1 if no object digest info is set.
+     */
+    public int getDigestedObjectType()
+    {
+        if (holder.getObjectDigestInfo() != null)
+        {
+            return holder.getObjectDigestInfo().getDigestedObjectType()
+                .getValue().intValue();
+        }
+        return -1;
+    }
+
+    /**
+     * Returns algorithm identifier for the digest used if ObjectDigestInfo is present.
+     * 
+     * @return digest AlgorithmIdentifier or <code>null</code> if ObjectDigestInfo is absent.
+     */
+    public AlgorithmIdentifier getDigestAlgorithm()
+    {
+        if (holder.getObjectDigestInfo() != null)
+        {
+            return holder.getObjectDigestInfo().getDigestAlgorithm();
+        }
+        return null;
+    }
+
+    /**
+     * Returns the hash if an object digest info is used.
+     * 
+     * @return The hash or <code>null</code> if ObjectDigestInfo is absent.
+     */
+    public byte[] getObjectDigest()
+    {
+        if (holder.getObjectDigestInfo() != null)
+        {
+            return holder.getObjectDigestInfo().getObjectDigest().getBytes();
+        }
+        return null;
+    }
+
+    /**
+     * Returns the digest algorithm ID if an object digest info is used.
+     * 
+     * @return The digest algorithm ID or <code>null</code> if no object
+     *         digest info is set.
+     */
+    public ASN1ObjectIdentifier getOtherObjectTypeID()
+    {
+        if (holder.getObjectDigestInfo() != null)
+        {
+            new ASN1ObjectIdentifier(holder.getObjectDigestInfo().getOtherObjectTypeID().getId());
+        }
+        return null;
+    }
+
+    private GeneralNames generateGeneralNames(X500Name principal)
+    {
+        return new GeneralNames(new GeneralName(principal));
+    }
+
+    private boolean matchesDN(X500Name subject, GeneralNames targets)
+    {
+        GeneralName[] names = targets.getNames();
+
+        for (int i = 0; i != names.length; i++)
+        {
+            GeneralName gn = names[i];
+
+            if (gn.getTagNo() == GeneralName.directoryName)
+            {
+                if (X500Name.getInstance(gn.getName()).equals(subject))
+                {
+                    return true;
+                }
+            }
+        }
+
+        return false;
+    }
+
+    private X500Name[] getPrincipals(GeneralName[] names)
+    {
+        List l = new ArrayList(names.length);
+
+        for (int i = 0; i != names.length; i++)
+        {
+            if (names[i].getTagNo() == GeneralName.directoryName)
+            {
+                l.add(X500Name.getInstance(names[i].getName()));
+            }
+        }
+
+        return (X500Name[])l.toArray(new X500Name[l.size()]);
+    }
+
+    /**
+     * Return any principal objects inside the attribute certificate holder
+     * entity names field.
+     * 
+     * @return an array of Principal objects (usually X500Principal), null if no
+     *         entity names field is set.
+     */
+    public X500Name[] getEntityNames()
+    {
+        if (holder.getEntityName() != null)
+        {
+            return getPrincipals(holder.getEntityName().getNames());
+        }
+
+        return null;
+    }
+
+    /**
+     * Return the principals associated with the issuer attached to this holder
+     * 
+     * @return an array of principals, null if no BaseCertificateID is set.
+     */
+    public X500Name[] getIssuer()
+    {
+        if (holder.getBaseCertificateID() != null)
+        {
+            return getPrincipals(holder.getBaseCertificateID().getIssuer().getNames());
+        }
+
+        return null;
+    }
+
+    /**
+     * Return the serial number associated with the issuer attached to this
+     * holder.
+     * 
+     * @return the certificate serial number, null if no BaseCertificateID is
+     *         set.
+     */
+    public BigInteger getSerialNumber()
+    {
+        if (holder.getBaseCertificateID() != null)
+        {
+            return holder.getBaseCertificateID().getSerial().getValue();
+        }
+
+        return null;
+    }
+
+    public Object clone()
+    {
+        return new AttributeCertificateHolder((ASN1Sequence)holder.toASN1Object());
+    }
+
+    public boolean match(Object obj)
+    {
+        if (!(obj instanceof X509CertificateHolder))
+        {
+            return false;
+        }
+
+        X509CertificateHolder x509Cert = (X509CertificateHolder)obj;
+
+        if (holder.getBaseCertificateID() != null)
+        {
+            return holder.getBaseCertificateID().getSerial().getValue().equals(x509Cert.getSerialNumber())
+                && matchesDN(x509Cert.getIssuer(), holder.getBaseCertificateID().getIssuer());
+        }
+
+        if (holder.getEntityName() != null)
+        {
+            if (matchesDN(x509Cert.getSubject(),
+                holder.getEntityName()))
+            {
+                return true;
+            }
+        }
+
+        if (holder.getObjectDigestInfo() != null)
+        {
+            try
+            {
+                DigestCalculator digCalc = digestCalculatorProvider.get(holder.getObjectDigestInfo().getDigestAlgorithm());
+                OutputStream     digOut = digCalc.getOutputStream();
+
+                switch (getDigestedObjectType())
+                {
+                case ObjectDigestInfo.publicKey:
+                    // TODO: DSA Dss-parms
+                    digOut.write(x509Cert.getSubjectPublicKeyInfo().getEncoded());
+                    break;
+                case ObjectDigestInfo.publicKeyCert:
+                    digOut.write(x509Cert.getEncoded());
+                    break;
+                }
+
+                digOut.close();
+
+                if (!Arrays.areEqual(digCalc.getDigest(), getObjectDigest()))
+                {
+                    return false;
+                }
+            }
+            catch (Exception e)
+            {
+                return false;
+            }
+        }
+
+        return false;
+    }
+
+    public boolean equals(Object obj)
+    {
+        if (obj == this)
+        {
+            return true;
+        }
+
+        if (!(obj instanceof AttributeCertificateHolder))
+        {
+            return false;
+        }
+
+        AttributeCertificateHolder other = (AttributeCertificateHolder)obj;
+
+        return this.holder.equals(other.holder);
+    }
+
+    public int hashCode()
+    {
+        return this.holder.hashCode();
+    }
+
+    /**
+     * Set a digest calculator provider to be used if matches are attempted using
+     * ObjectDigestInfo,
+     *
+     * @param digCalcProvider a provider of digest calculators.
+     */
+    public static void setDigestCalculatorProvider(DigestCalculatorProvider digCalcProvider)
+    {
+        digestCalculatorProvider = digCalcProvider;
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/cert/AttributeCertificateIssuer.java b/bcpkix/src/main/java/org/bouncycastle/cert/AttributeCertificateIssuer.java
new file mode 100644
index 0000000..b5084c9
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/cert/AttributeCertificateIssuer.java
@@ -0,0 +1,147 @@
+package org.bouncycastle.cert;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.x500.X500Name;
+import org.bouncycastle.asn1.x509.AttCertIssuer;
+import org.bouncycastle.asn1.x509.GeneralName;
+import org.bouncycastle.asn1.x509.GeneralNames;
+import org.bouncycastle.asn1.x509.V2Form;
+import org.bouncycastle.util.Selector;
+
+/**
+ * Carrying class for an attribute certificate issuer.
+ */
+public class AttributeCertificateIssuer
+    implements Selector
+{
+    final ASN1Encodable form;
+
+    /**
+     * Set the issuer directly with the ASN.1 structure.
+     *
+     * @param issuer The issuer
+     */
+    public AttributeCertificateIssuer(AttCertIssuer issuer)
+    {
+        form = issuer.getIssuer();
+    }
+
+    public AttributeCertificateIssuer(X500Name principal)
+    {
+        form = new V2Form(new GeneralNames(new GeneralName(principal)));
+    }
+
+    public X500Name[] getNames()
+    {
+        GeneralNames name;
+
+        if (form instanceof V2Form)
+        {
+            name = ((V2Form)form).getIssuerName();
+        }
+        else
+        {
+            name = (GeneralNames)form;
+        }
+
+        GeneralName[] names = name.getNames();
+
+        List l = new ArrayList(names.length);
+
+        for (int i = 0; i != names.length; i++)
+        {
+            if (names[i].getTagNo() == GeneralName.directoryName)
+            {
+                l.add(X500Name.getInstance(names[i].getName()));
+            }
+        }
+
+        return (X500Name[])l.toArray(new X500Name[l.size()]);
+    }
+
+    private boolean matchesDN(X500Name subject, GeneralNames targets)
+    {
+        GeneralName[] names = targets.getNames();
+
+        for (int i = 0; i != names.length; i++)
+        {
+            GeneralName gn = names[i];
+
+            if (gn.getTagNo() == GeneralName.directoryName)
+            {
+                if (X500Name.getInstance(gn.getName()).equals(subject))
+                {
+                    return true;
+                }
+            }
+        }
+
+        return false;
+    }
+
+    public Object clone()
+    {
+        return new AttributeCertificateIssuer(AttCertIssuer.getInstance(form));
+    }
+
+    public boolean equals(Object obj)
+    {
+        if (obj == this)
+        {
+            return true;
+        }
+
+        if (!(obj instanceof AttributeCertificateIssuer))
+        {
+            return false;
+        }
+
+        AttributeCertificateIssuer other = (AttributeCertificateIssuer)obj;
+
+        return this.form.equals(other.form);
+    }
+
+    public int hashCode()
+    {
+        return this.form.hashCode();
+    }
+
+    public boolean match(Object obj)
+    {
+        if (!(obj instanceof X509CertificateHolder))
+        {
+            return false;
+        }
+
+        X509CertificateHolder x509Cert = (X509CertificateHolder)obj;
+
+        if (form instanceof V2Form)
+        {
+            V2Form issuer = (V2Form)form;
+            if (issuer.getBaseCertificateID() != null)
+            {
+                return issuer.getBaseCertificateID().getSerial().getValue().equals(x509Cert.getSerialNumber())
+                    && matchesDN(x509Cert.getIssuer(), issuer.getBaseCertificateID().getIssuer());
+            }
+
+            GeneralNames name = issuer.getIssuerName();
+            if (matchesDN(x509Cert.getSubject(), name))
+            {
+                return true;
+            }
+        }
+        else
+        {
+            GeneralNames name = (GeneralNames)form;
+            if (matchesDN(x509Cert.getSubject(), name))
+            {
+                return true;
+            }
+        }
+
+        return false;
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/cert/CertException.java b/bcpkix/src/main/java/org/bouncycastle/cert/CertException.java
new file mode 100644
index 0000000..eb67a5d
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/cert/CertException.java
@@ -0,0 +1,27 @@
+package org.bouncycastle.cert;
+
+/**
+ * General checked Exception thrown in the cert package and its sub-packages.
+ */
+public class CertException
+    extends Exception
+{
+    private Throwable cause;
+
+    public CertException(String msg, Throwable cause)
+    {
+        super(msg);
+
+        this.cause = cause;
+    }
+
+    public CertException(String msg)
+    {
+        super(msg);
+    }
+
+    public Throwable getCause()
+    {
+        return cause;
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/cert/CertIOException.java b/bcpkix/src/main/java/org/bouncycastle/cert/CertIOException.java
new file mode 100644
index 0000000..929d95e
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/cert/CertIOException.java
@@ -0,0 +1,29 @@
+package org.bouncycastle.cert;
+
+import java.io.IOException;
+
+/**
+ * General IOException thrown in the cert package and its sub-packages.
+ */
+public class CertIOException
+    extends IOException
+{
+    private Throwable cause;
+
+    public CertIOException(String msg, Throwable cause)
+    {
+        super(msg);
+
+        this.cause = cause;
+    }
+
+    public CertIOException(String msg)
+    {
+        super(msg);
+    }
+
+    public Throwable getCause()
+    {
+        return cause;
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/cert/CertUtils.java b/bcpkix/src/main/java/org/bouncycastle/cert/CertUtils.java
new file mode 100644
index 0000000..e3c2079
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/cert/CertUtils.java
@@ -0,0 +1,213 @@
+package org.bouncycastle.cert;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.text.ParseException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.DERBitString;
+import org.bouncycastle.asn1.DERGeneralizedTime;
+import org.bouncycastle.asn1.DEROutputStream;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.asn1.x509.AttributeCertificate;
+import org.bouncycastle.asn1.x509.AttributeCertificateInfo;
+import org.bouncycastle.asn1.x509.Certificate;
+import org.bouncycastle.asn1.x509.CertificateList;
+import org.bouncycastle.asn1.x509.Extensions;
+import org.bouncycastle.asn1.x509.ExtensionsGenerator;
+import org.bouncycastle.asn1.x509.TBSCertList;
+import org.bouncycastle.asn1.x509.TBSCertificate;
+import org.bouncycastle.operator.ContentSigner;
+
+class CertUtils
+{
+    private static Set EMPTY_SET = Collections.unmodifiableSet(new HashSet());
+    private static List EMPTY_LIST = Collections.unmodifiableList(new ArrayList());
+
+    static X509CertificateHolder generateFullCert(ContentSigner signer, TBSCertificate tbsCert)
+    {
+        try
+        {
+            return new X509CertificateHolder(generateStructure(tbsCert, signer.getAlgorithmIdentifier(), generateSig(signer, tbsCert)));
+        }
+        catch (IOException e)
+        {
+            throw new IllegalStateException("cannot produce certificate signature");
+        }
+    }
+
+    static X509AttributeCertificateHolder generateFullAttrCert(ContentSigner signer, AttributeCertificateInfo attrInfo)
+    {
+        try
+        {
+            return new X509AttributeCertificateHolder(generateAttrStructure(attrInfo, signer.getAlgorithmIdentifier(), generateSig(signer, attrInfo)));
+        }
+        catch (IOException e)
+        {
+            throw new IllegalStateException("cannot produce attribute certificate signature");
+        }
+    }
+
+    static X509CRLHolder generateFullCRL(ContentSigner signer, TBSCertList tbsCertList)
+    {
+        try
+        {
+            return new X509CRLHolder(generateCRLStructure(tbsCertList, signer.getAlgorithmIdentifier(), generateSig(signer, tbsCertList)));
+        }
+        catch (IOException e)
+        {
+            throw new IllegalStateException("cannot produce certificate signature");
+        }
+    }
+
+    private static byte[] generateSig(ContentSigner signer, ASN1Encodable tbsObj)
+        throws IOException
+    {
+        OutputStream sOut = signer.getOutputStream();
+        DEROutputStream dOut = new DEROutputStream(sOut);
+
+        dOut.writeObject(tbsObj);
+
+        sOut.close();
+
+        return signer.getSignature();
+    }
+
+    private static Certificate generateStructure(TBSCertificate tbsCert, AlgorithmIdentifier sigAlgId, byte[] signature)
+    {
+        ASN1EncodableVector v = new ASN1EncodableVector();
+
+        v.add(tbsCert);
+        v.add(sigAlgId);
+        v.add(new DERBitString(signature));
+
+        return Certificate.getInstance(new DERSequence(v));
+    }
+
+    private static AttributeCertificate generateAttrStructure(AttributeCertificateInfo attrInfo, AlgorithmIdentifier sigAlgId, byte[] signature)
+    {
+        ASN1EncodableVector v = new ASN1EncodableVector();
+
+        v.add(attrInfo);
+        v.add(sigAlgId);
+        v.add(new DERBitString(signature));
+
+        return AttributeCertificate.getInstance(new DERSequence(v));
+    }
+
+    private static CertificateList generateCRLStructure(TBSCertList tbsCertList, AlgorithmIdentifier sigAlgId, byte[] signature)
+    {
+        ASN1EncodableVector v = new ASN1EncodableVector();
+
+        v.add(tbsCertList);
+        v.add(sigAlgId);
+        v.add(new DERBitString(signature));
+
+        return CertificateList.getInstance(new DERSequence(v));
+    }
+
+    static Set getCriticalExtensionOIDs(Extensions extensions)
+    {
+        if (extensions == null)
+        {
+            return EMPTY_SET;
+        }
+
+        return Collections.unmodifiableSet(new HashSet(Arrays.asList(extensions.getCriticalExtensionOIDs())));
+    }
+
+    static Set getNonCriticalExtensionOIDs(Extensions extensions)
+    {
+        if (extensions == null)
+        {
+            return EMPTY_SET;
+        }
+
+        // TODO: should probably produce a set that imposes correct ordering
+        return Collections.unmodifiableSet(new HashSet(Arrays.asList(extensions.getNonCriticalExtensionOIDs())));
+    }
+
+    static List getExtensionOIDs(Extensions extensions)
+    {
+        if (extensions == null)
+        {
+            return EMPTY_LIST;
+        }
+
+        return Collections.unmodifiableList(Arrays.asList(extensions.getExtensionOIDs()));
+    }
+
+    static void addExtension(ExtensionsGenerator extGenerator, ASN1ObjectIdentifier oid, boolean isCritical, ASN1Encodable value)
+        throws CertIOException
+    {
+        try
+        {
+            extGenerator.addExtension(oid, isCritical, value);
+        }
+        catch (IOException e)
+        {
+            throw new CertIOException("cannot encode extension: " + e.getMessage(), e);
+        }
+    }
+
+    static DERBitString booleanToBitString(boolean[] id)
+    {
+        byte[] bytes = new byte[(id.length + 7) / 8];
+
+        for (int i = 0; i != id.length; i++)
+        {
+            bytes[i / 8] |= (id[i]) ? (1 << ((7 - (i % 8)))) : 0;
+        }
+
+        int pad = id.length % 8;
+
+        if (pad == 0)
+        {
+            return new DERBitString(bytes);
+        }
+        else
+        {
+            return new DERBitString(bytes, 8 - pad);
+        }
+    }
+
+    static boolean[] bitStringToBoolean(DERBitString bitString)
+    {
+        if (bitString != null)
+        {
+            byte[]          bytes = bitString.getBytes();
+            boolean[]       boolId = new boolean[bytes.length * 8 - bitString.getPadBits()];
+
+            for (int i = 0; i != boolId.length; i++)
+            {
+                boolId[i] = (bytes[i / 8] & (0x80 >>> (i % 8))) != 0;
+            }
+
+            return boolId;
+        }
+
+        return null;
+    }
+
+    static Date recoverDate(DERGeneralizedTime time)
+    {
+        try
+        {
+            return time.getDate();
+        }
+        catch (ParseException e)
+        {
+            throw new IllegalStateException("unable to recover date: " + e.getMessage());
+        }
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/cert/X509AttributeCertificateHolder.java b/bcpkix/src/main/java/org/bouncycastle/cert/X509AttributeCertificateHolder.java
new file mode 100644
index 0000000..e2ce015
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/cert/X509AttributeCertificateHolder.java
@@ -0,0 +1,356 @@
+package org.bouncycastle.cert;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+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;
+import org.bouncycastle.asn1.x509.AttCertValidityPeriod;
+import org.bouncycastle.asn1.x509.Attribute;
+import org.bouncycastle.asn1.x509.AttributeCertificate;
+import org.bouncycastle.asn1.x509.AttributeCertificateInfo;
+import org.bouncycastle.asn1.x509.Extension;
+import org.bouncycastle.asn1.x509.Extensions;
+import org.bouncycastle.operator.ContentVerifier;
+import org.bouncycastle.operator.ContentVerifierProvider;
+
+/**
+ * Holding class for an X.509 AttributeCertificate structure.
+ */
+public class X509AttributeCertificateHolder
+{
+    private static Attribute[] EMPTY_ARRAY = new Attribute[0];
+    
+    private AttributeCertificate attrCert;
+    private Extensions extensions;
+
+    private static AttributeCertificate parseBytes(byte[] certEncoding)
+        throws IOException
+    {
+        try
+        {
+            return AttributeCertificate.getInstance(ASN1Primitive.fromByteArray(certEncoding));
+        }
+        catch (ClassCastException e)
+        {
+            throw new CertIOException("malformed data: " + e.getMessage(), e);
+        }
+        catch (IllegalArgumentException e)
+        {
+            throw new CertIOException("malformed data: " + e.getMessage(), e);
+        }
+    }
+
+    /**
+     * Create a X509AttributeCertificateHolder from the passed in bytes.
+     *
+     * @param certEncoding BER/DER encoding of the certificate.
+     * @throws IOException in the event of corrupted data, or an incorrect structure.
+     */
+    public X509AttributeCertificateHolder(byte[] certEncoding)
+        throws IOException
+    {
+        this(parseBytes(certEncoding));
+    }
+
+    /**
+     * Create a X509AttributeCertificateHolder from the passed in ASN.1 structure.
+     *
+     * @param attrCert an ASN.1 AttributeCertificate structure.
+     */
+    public X509AttributeCertificateHolder(AttributeCertificate attrCert)
+    {
+        this.attrCert = attrCert;
+        this.extensions = attrCert.getAcinfo().getExtensions();
+    }
+
+    /**
+     * Return the ASN.1 encoding of this holder's attribute certificate.
+     *
+     * @return a DER encoded byte array.
+     * @throws IOException if an encoding cannot be generated.
+     */
+    public byte[] getEncoded()
+        throws IOException
+    {
+        return attrCert.getEncoded();
+    }
+
+    public int getVersion()
+    {
+        return attrCert.getAcinfo().getVersion().getValue().intValue() + 1;
+    }
+
+    /**
+     * Return the serial number of this attribute certificate.
+     *
+     * @return the serial number.
+     */
+    public BigInteger getSerialNumber()
+    {
+        return attrCert.getAcinfo().getSerialNumber().getValue();
+    }
+
+    /**
+     * Return the holder details for this attribute certificate.
+     *
+     * @return this attribute certificate's holder structure.
+     */
+    public AttributeCertificateHolder getHolder()
+    {
+        return new AttributeCertificateHolder((ASN1Sequence)attrCert.getAcinfo().getHolder().toASN1Primitive());
+    }
+
+    /**
+     * Return the issuer details for this attribute certificate.
+     *
+     * @return this attribute certificate's issuer structure,
+     */
+    public AttributeCertificateIssuer getIssuer()
+    {
+        return new AttributeCertificateIssuer(attrCert.getAcinfo().getIssuer());
+    }
+
+    /**
+     * Return the date before which this attribute certificate is not valid.
+     *
+     * @return the start date for the attribute certificate's validity period.
+     */
+    public Date getNotBefore()
+    {
+        return CertUtils.recoverDate(attrCert.getAcinfo().getAttrCertValidityPeriod().getNotBeforeTime());
+    }
+
+    /**
+     * Return the date after which this attribute certificate is not valid.
+     *
+     * @return the final date for the attribute certificate's validity period.
+     */
+    public Date getNotAfter()
+    {
+        return CertUtils.recoverDate(attrCert.getAcinfo().getAttrCertValidityPeriod().getNotAfterTime());
+    }
+
+    /**
+     * Return the attributes, if any associated with this request.
+     *
+     * @return an array of Attribute, zero length if none present.
+     */
+    public Attribute[] getAttributes()
+    {
+        ASN1Sequence seq = attrCert.getAcinfo().getAttributes();
+        Attribute[] attrs = new Attribute[seq.size()];
+
+        for (int i = 0; i != seq.size(); i++)
+        {
+            attrs[i] = Attribute.getInstance(seq.getObjectAt(i));
+        }
+
+        return attrs;
+    }
+
+    /**
+     * Return an  array of attributes matching the passed in type OID.
+     *
+     * @param type the type of the attribute being looked for.
+     * @return an array of Attribute of the requested type, zero length if none present.
+     */
+    public Attribute[] getAttributes(ASN1ObjectIdentifier type)
+    {
+        ASN1Sequence    seq = attrCert.getAcinfo().getAttributes();
+        List            list = new ArrayList();
+
+        for (int i = 0; i != seq.size(); i++)
+        {
+            Attribute attr = Attribute.getInstance(seq.getObjectAt(i));
+            if (attr.getAttrType().equals(type))
+            {
+                list.add(attr);
+            }
+        }
+
+        if (list.size() == 0)
+        {
+            return EMPTY_ARRAY;
+        }
+
+        return (Attribute[])list.toArray(new Attribute[list.size()]);
+    }
+
+    /**
+     * Return whether or not the holder's attribute certificate contains extensions.
+     *
+     * @return true if extension are present, false otherwise.
+     */
+    public boolean hasExtensions()
+    {
+        return extensions != null;
+    }
+
+    /**
+     * Look up the extension associated with the passed in OID.
+     *
+     * @param oid the OID of the extension of interest.
+     *
+     * @return the extension if present, null otherwise.
+     */
+    public Extension getExtension(ASN1ObjectIdentifier oid)
+    {
+        if (extensions != null)
+        {
+            return extensions.getExtension(oid);
+        }
+
+        return null;
+    }
+
+    /**
+     * Returns a list of ASN1ObjectIdentifier objects representing the OIDs of the
+     * extensions contained in this holder's attribute certificate.
+     *
+     * @return a list of extension OIDs.
+     */
+    public List getExtensionOIDs()
+    {
+        return CertUtils.getExtensionOIDs(extensions);
+    }
+
+    /**
+     * Returns a set of ASN1ObjectIdentifier objects representing the OIDs of the
+     * critical extensions contained in this holder's attribute certificate.
+     *
+     * @return a set of critical extension OIDs.
+     */
+    public Set getCriticalExtensionOIDs()
+    {
+        return CertUtils.getCriticalExtensionOIDs(extensions);
+    }
+
+    /**
+     * Returns a set of ASN1ObjectIdentifier objects representing the OIDs of the
+     * non-critical extensions contained in this holder's attribute certificate.
+     *
+     * @return a set of non-critical extension OIDs.
+     */
+    public Set getNonCriticalExtensionOIDs()
+    {
+        return CertUtils.getNonCriticalExtensionOIDs(extensions);
+    }
+
+    public boolean[] getIssuerUniqueID()
+    {
+        return CertUtils.bitStringToBoolean(attrCert.getAcinfo().getIssuerUniqueID());
+    }
+
+    /**
+     * Return the details of the signature algorithm used to create this attribute certificate.
+     *
+     * @return the AlgorithmIdentifier describing the signature algorithm used to create this attribute certificate.
+     */
+    public AlgorithmIdentifier getSignatureAlgorithm()
+    {
+        return attrCert.getSignatureAlgorithm();
+    }
+
+    /**
+     * Return the bytes making up the signature associated with this attribute certificate.
+     *
+     * @return the attribute certificate signature bytes.
+     */
+    public byte[] getSignature()
+    {
+        return attrCert.getSignatureValue().getBytes();
+    }
+
+    /**
+     * Return the underlying ASN.1 structure for the attribute certificate in this holder.
+     *
+     * @return a AttributeCertificate object.
+     */
+    public AttributeCertificate toASN1Structure()
+    {
+        return attrCert;
+    }
+
+    /**
+     * Return whether or not this attribute certificate is valid on a particular date.
+     *
+     * @param date the date of interest.
+     * @return true if the attribute certificate is valid, false otherwise.
+     */
+    public boolean isValidOn(Date date)
+    {
+        AttCertValidityPeriod certValidityPeriod = attrCert.getAcinfo().getAttrCertValidityPeriod();
+
+        return !date.before(CertUtils.recoverDate(certValidityPeriod.getNotBeforeTime())) && !date.after(CertUtils.recoverDate(certValidityPeriod.getNotAfterTime()));
+    }
+
+    /**
+     * Validate the signature on the attribute certificate in this holder.
+     *
+     * @param verifierProvider a ContentVerifierProvider that can generate a verifier for the signature.
+     * @return true if the signature is valid, false otherwise.
+     * @throws CertException if the signature cannot be processed or is inappropriate.
+     */
+    public boolean isSignatureValid(ContentVerifierProvider verifierProvider)
+        throws CertException
+    {
+        AttributeCertificateInfo acinfo = attrCert.getAcinfo();
+
+        if (!acinfo.getSignature().equals(attrCert.getSignatureAlgorithm()))
+        {
+            throw new CertException("signature invalid - algorithm identifier mismatch");
+        }
+
+        ContentVerifier verifier;
+
+        try
+        {
+            verifier = verifierProvider.get((acinfo.getSignature()));
+
+            OutputStream sOut = verifier.getOutputStream();
+            DEROutputStream dOut = new DEROutputStream(sOut);
+
+            dOut.writeObject(acinfo);
+
+            sOut.close();
+        }
+        catch (Exception e)
+        {
+            throw new CertException("unable to process signature: " + e.getMessage(), e);
+        }
+
+        return verifier.verify(attrCert.getSignatureValue().getBytes());
+    }
+
+    public boolean equals(
+        Object o)
+    {
+        if (o == this)
+        {
+            return true;
+        }
+
+        if (!(o instanceof X509AttributeCertificateHolder))
+        {
+            return false;
+        }
+
+        X509AttributeCertificateHolder other = (X509AttributeCertificateHolder)o;
+
+        return this.attrCert.equals(other.attrCert);
+    }
+
+    public int hashCode()
+    {
+        return this.attrCert.hashCode();
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/cert/X509CRLEntryHolder.java b/bcpkix/src/main/java/org/bouncycastle/cert/X509CRLEntryHolder.java
new file mode 100644
index 0000000..c6b4d3d
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/cert/X509CRLEntryHolder.java
@@ -0,0 +1,134 @@
+package org.bouncycastle.cert;
+
+import java.math.BigInteger;
+import java.util.Date;
+import java.util.List;
+import java.util.Set;
+
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.x509.Extension;
+import org.bouncycastle.asn1.x509.Extensions;
+import org.bouncycastle.asn1.x509.GeneralNames;
+import org.bouncycastle.asn1.x509.TBSCertList;
+
+/**
+ * Holding class for an X.509 CRL Entry structure.
+ */
+public class X509CRLEntryHolder
+{
+    private TBSCertList.CRLEntry entry;
+    private GeneralNames ca;
+
+    X509CRLEntryHolder(TBSCertList.CRLEntry entry, boolean isIndirect, GeneralNames previousCA)
+    {
+        this.entry = entry;
+        this.ca = previousCA;
+
+        if (isIndirect && entry.hasExtensions())
+        {
+            Extension currentCaName = entry.getExtensions().getExtension(Extension.certificateIssuer);
+
+            if (currentCaName != null)
+            {
+                ca = GeneralNames.getInstance(currentCaName.getParsedValue());
+            }
+        }
+    }
+
+    /**
+     * Return the serial number of the certificate associated with this CRLEntry.
+     *
+     * @return the revoked certificate's serial number.
+     */
+    public BigInteger getSerialNumber()
+    {
+        return entry.getUserCertificate().getValue();
+    }
+
+    /**
+     * Return the date on which the certificate associated with this CRLEntry was revoked.
+     *
+     * @return the revocation date for the revoked certificate.
+     */
+    public Date getRevocationDate()
+    {
+        return entry.getRevocationDate().getDate();
+    }
+
+    /**
+     * Return whether or not the holder's CRL entry contains extensions.
+     *
+     * @return true if extension are present, false otherwise.
+     */
+    public boolean hasExtensions()
+    {
+        return entry.hasExtensions();
+    }
+
+    /**
+     * Return the available names for the certificate issuer for the certificate referred to by this CRL entry.
+     * <p>
+     * Note: this will be the issuer of the CRL unless it has been specified that the CRL is indirect
+     * in the IssuingDistributionPoint extension and either a previous entry, or the current one,
+     * has specified a different CA via the certificateIssuer extension.
+     * </p>
+     *
+     * @return the revoked certificate's issuer.
+     */
+    public GeneralNames getCertificateIssuer()
+    {
+        return this.ca;
+    }
+
+    /**
+     * Look up the extension associated with the passed in OID.
+     *
+     * @param oid the OID of the extension of interest.
+     *
+     * @return the extension if present, null otherwise.
+     */
+    public Extension getExtension(ASN1ObjectIdentifier oid)
+    {
+        Extensions extensions = entry.getExtensions();
+
+        if (extensions != null)
+        {
+            return extensions.getExtension(oid);
+        }
+
+        return null;
+    }
+
+    /**
+     * Returns a list of ASN1ObjectIdentifier objects representing the OIDs of the
+     * extensions contained in this holder's CRL entry.
+     *
+     * @return a list of extension OIDs.
+     */
+    public List getExtensionOIDs()
+    {
+        return CertUtils.getExtensionOIDs(entry.getExtensions());
+    }
+
+    /**
+     * Returns a set of ASN1ObjectIdentifier objects representing the OIDs of the
+     * critical extensions contained in this holder's CRL entry.
+     *
+     * @return a set of critical extension OIDs.
+     */
+    public Set getCriticalExtensionOIDs()
+    {
+        return CertUtils.getCriticalExtensionOIDs(entry.getExtensions());
+    }
+
+    /**
+     * Returns a set of ASN1ObjectIdentifier objects representing the OIDs of the
+     * non-critical extensions contained in this holder's CRL entry.
+     *
+     * @return a set of non-critical extension OIDs.
+     */
+    public Set getNonCriticalExtensionOIDs()
+    {
+        return CertUtils.getNonCriticalExtensionOIDs(entry.getExtensions());
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/cert/X509CRLHolder.java b/bcpkix/src/main/java/org/bouncycastle/cert/X509CRLHolder.java
new file mode 100644
index 0000000..3bb2327
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/cert/X509CRLHolder.java
@@ -0,0 +1,307 @@
+package org.bouncycastle.cert;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.Set;
+
+import org.bouncycastle.asn1.ASN1InputStream;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.DEROutputStream;
+import org.bouncycastle.asn1.x500.X500Name;
+import org.bouncycastle.asn1.x509.CertificateList;
+import org.bouncycastle.asn1.x509.Extension;
+import org.bouncycastle.asn1.x509.Extensions;
+import org.bouncycastle.asn1.x509.GeneralName;
+import org.bouncycastle.asn1.x509.GeneralNames;
+import org.bouncycastle.asn1.x509.IssuingDistributionPoint;
+import org.bouncycastle.asn1.x509.TBSCertList;
+import org.bouncycastle.operator.ContentVerifier;
+import org.bouncycastle.operator.ContentVerifierProvider;
+
+/**
+ * Holding class for an X.509 CRL structure.
+ */
+public class X509CRLHolder
+{
+    private CertificateList x509CRL;
+    private boolean isIndirect;
+    private Extensions extensions;
+    private GeneralNames issuerName;
+
+    private static CertificateList parseStream(InputStream stream)
+        throws IOException
+    {
+        try
+        {
+            return CertificateList.getInstance(new ASN1InputStream(stream, true).readObject());
+        }
+        catch (ClassCastException e)
+        {
+            throw new CertIOException("malformed data: " + e.getMessage(), e);
+        }
+        catch (IllegalArgumentException e)
+        {
+            throw new CertIOException("malformed data: " + e.getMessage(), e);
+        }
+    }
+
+    private static boolean isIndirectCRL(Extensions extensions)
+    {
+        if (extensions == null)
+        {
+            return false;
+        }
+
+        Extension ext = extensions.getExtension(Extension.issuingDistributionPoint);
+
+        return ext != null && IssuingDistributionPoint.getInstance(ext.getParsedValue()).isIndirectCRL();
+    }
+
+    /**
+     * Create a X509CRLHolder from the passed in bytes.
+     *
+     * @param crlEncoding BER/DER encoding of the CRL
+     * @throws IOException in the event of corrupted data, or an incorrect structure.
+     */
+    public X509CRLHolder(byte[] crlEncoding)
+        throws IOException
+    {
+        this(parseStream(new ByteArrayInputStream(crlEncoding)));
+    }
+
+    /**
+     * Create a X509CRLHolder from the passed in InputStream.
+     *
+     * @param crlStream BER/DER encoded InputStream of the CRL
+     * @throws IOException in the event of corrupted data, or an incorrect structure.
+     */
+    public X509CRLHolder(InputStream crlStream)
+        throws IOException
+    {
+        this(parseStream(crlStream));
+    }
+
+    /**
+     * Create a X509CRLHolder from the passed in ASN.1 structure.
+     *
+     * @param x509CRL an ASN.1 CertificateList structure.
+     */
+    public X509CRLHolder(CertificateList x509CRL)
+    {
+        this.x509CRL = x509CRL;
+        this.extensions = x509CRL.getTBSCertList().getExtensions();
+        this.isIndirect = isIndirectCRL(extensions);
+        this.issuerName = new GeneralNames(new GeneralName(x509CRL.getIssuer()));
+    }
+
+    /**
+     * Return the ASN.1 encoding of this holder's CRL.
+     *
+     * @return a DER encoded byte array.
+     * @throws IOException if an encoding cannot be generated.
+     */
+    public byte[] getEncoded()
+        throws IOException
+    {
+        return x509CRL.getEncoded();
+    }
+
+    /**
+     * Return the issuer of this holder's CRL.
+     *
+     * @return the CRL issuer.
+     */
+    public X500Name getIssuer()
+    {
+        return X500Name.getInstance(x509CRL.getIssuer());
+    }
+
+    public X509CRLEntryHolder getRevokedCertificate(BigInteger serialNumber)
+    {
+        GeneralNames currentCA = issuerName;
+        for (Enumeration en = x509CRL.getRevokedCertificateEnumeration(); en.hasMoreElements();)
+        {
+            TBSCertList.CRLEntry entry = (TBSCertList.CRLEntry)en.nextElement();
+
+            if (entry.getUserCertificate().getValue().equals(serialNumber))
+            {
+                return new X509CRLEntryHolder(entry, isIndirect, currentCA);
+            }
+
+            if (isIndirect && entry.hasExtensions())
+            {
+                Extension currentCaName = entry.getExtensions().getExtension(Extension.certificateIssuer);
+
+                if (currentCaName != null)
+                {
+                    currentCA = GeneralNames.getInstance(currentCaName.getParsedValue());
+                }
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Return a collection of X509CRLEntryHolder objects, giving the details of the
+     * revoked certificates that appear on this CRL.
+     *
+     * @return the revoked certificates as a collection of X509CRLEntryHolder objects.
+     */
+    public Collection getRevokedCertificates()
+    {
+        TBSCertList.CRLEntry[] entries = x509CRL.getRevokedCertificates();
+        List l = new ArrayList(entries.length);
+        GeneralNames currentCA = issuerName;
+
+        for (Enumeration en = x509CRL.getRevokedCertificateEnumeration(); en.hasMoreElements();)
+        {
+            TBSCertList.CRLEntry entry = (TBSCertList.CRLEntry)en.nextElement();
+            X509CRLEntryHolder crlEntry = new X509CRLEntryHolder(entry, isIndirect, currentCA);
+
+            l.add(crlEntry);
+
+            currentCA = crlEntry.getCertificateIssuer();
+        }
+
+        return l;
+    }
+    
+    /**
+     * Return whether or not the holder's CRL contains extensions.
+     *
+     * @return true if extension are present, false otherwise.
+     */
+    public boolean hasExtensions()
+    {
+        return extensions != null;
+    }
+
+    /**
+     * Look up the extension associated with the passed in OID.
+     *
+     * @param oid the OID of the extension of interest.
+     *
+     * @return the extension if present, null otherwise.
+     */
+    public Extension getExtension(ASN1ObjectIdentifier oid)
+    {
+        if (extensions != null)
+        {
+            return extensions.getExtension(oid);
+        }
+
+        return null;
+    }
+
+    /**
+     * Returns a list of ASN1ObjectIdentifier objects representing the OIDs of the
+     * extensions contained in this holder's CRL.
+     *
+     * @return a list of extension OIDs.
+     */
+    public List getExtensionOIDs()
+    {
+        return CertUtils.getExtensionOIDs(extensions);
+    }
+
+    /**
+     * Returns a set of ASN1ObjectIdentifier objects representing the OIDs of the
+     * critical extensions contained in this holder's CRL.
+     *
+     * @return a set of critical extension OIDs.
+     */
+    public Set getCriticalExtensionOIDs()
+    {
+        return CertUtils.getCriticalExtensionOIDs(extensions);
+    }
+
+    /**
+     * Returns a set of ASN1ObjectIdentifier objects representing the OIDs of the
+     * non-critical extensions contained in this holder's CRL.
+     *
+     * @return a set of non-critical extension OIDs.
+     */
+    public Set getNonCriticalExtensionOIDs()
+    {
+        return CertUtils.getNonCriticalExtensionOIDs(extensions);
+    }
+
+    /**
+     * Return the underlying ASN.1 structure for the CRL in this holder.
+     *
+     * @return a CertificateList object.
+     */
+    public CertificateList toASN1Structure()
+    {
+        return x509CRL;
+    }
+
+    /**
+     * Validate the signature on the CRL.
+     *
+     * @param verifierProvider a ContentVerifierProvider that can generate a verifier for the signature.
+     * @return true if the signature is valid, false otherwise.
+     * @throws CertException if the signature cannot be processed or is inappropriate.
+     */
+    public boolean isSignatureValid(ContentVerifierProvider verifierProvider)
+        throws CertException
+    {
+        TBSCertList tbsCRL = x509CRL.getTBSCertList();
+
+        if (!tbsCRL.getSignature().equals(x509CRL.getSignatureAlgorithm()))
+        {
+            throw new CertException("signature invalid - algorithm identifier mismatch");
+        }
+
+        ContentVerifier verifier;
+
+        try
+        {
+            verifier = verifierProvider.get((tbsCRL.getSignature()));
+
+            OutputStream sOut = verifier.getOutputStream();
+            DEROutputStream dOut = new DEROutputStream(sOut);
+
+            dOut.writeObject(tbsCRL);
+
+            sOut.close();
+        }
+        catch (Exception e)
+        {
+            throw new CertException("unable to process signature: " + e.getMessage(), e);
+        }
+
+        return verifier.verify(x509CRL.getSignature().getBytes());
+    }
+
+    public boolean equals(
+        Object o)
+    {
+        if (o == this)
+        {
+            return true;
+        }
+
+        if (!(o instanceof X509CRLHolder))
+        {
+            return false;
+        }
+
+        X509CRLHolder other = (X509CRLHolder)o;
+
+        return this.x509CRL.equals(other.x509CRL);
+    }
+
+    public int hashCode()
+    {
+        return this.x509CRL.hashCode();
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/cert/X509CertificateHolder.java b/bcpkix/src/main/java/org/bouncycastle/cert/X509CertificateHolder.java
new file mode 100644
index 0000000..52d5bcf
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/cert/X509CertificateHolder.java
@@ -0,0 +1,317 @@
+package org.bouncycastle.cert;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.math.BigInteger;
+import java.util.Date;
+import java.util.List;
+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;
+import org.bouncycastle.asn1.x509.Certificate;
+import org.bouncycastle.asn1.x509.Extension;
+import org.bouncycastle.asn1.x509.Extensions;
+import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.bouncycastle.asn1.x509.TBSCertificate;
+import org.bouncycastle.operator.ContentVerifier;
+import org.bouncycastle.operator.ContentVerifierProvider;
+
+/**
+ * Holding class for an X.509 Certificate structure.
+ */
+public class X509CertificateHolder
+{
+    private Certificate x509Certificate;
+    private Extensions  extensions;
+
+    private static Certificate parseBytes(byte[] certEncoding)
+        throws IOException
+    {
+        try
+        {
+            return Certificate.getInstance(ASN1Primitive.fromByteArray(certEncoding));
+        }
+        catch (ClassCastException e)
+        {
+            throw new CertIOException("malformed data: " + e.getMessage(), e);
+        }
+        catch (IllegalArgumentException e)
+        {
+            throw new CertIOException("malformed data: " + e.getMessage(), e);
+        }
+    }
+
+    /**
+     * Create a X509CertificateHolder from the passed in bytes.
+     *
+     * @param certEncoding BER/DER encoding of the certificate.
+     * @throws IOException in the event of corrupted data, or an incorrect structure.
+     */
+    public X509CertificateHolder(byte[] certEncoding)
+        throws IOException
+    {
+        this(parseBytes(certEncoding));
+    }
+
+    /**
+     * Create a X509CertificateHolder from the passed in ASN.1 structure.
+     *
+     * @param x509Certificate an ASN.1 Certificate structure.
+     */
+    public X509CertificateHolder(Certificate x509Certificate)
+    {
+        this.x509Certificate = x509Certificate;
+        this.extensions = x509Certificate.getTBSCertificate().getExtensions();
+    }
+
+    public int getVersionNumber()
+    {
+        return x509Certificate.getVersionNumber();
+    }
+
+    /**
+     * @deprecated use getVersionNumber
+     */
+    public int getVersion()
+    {
+        return x509Certificate.getVersionNumber();
+    }
+
+    /**
+     * Return whether or not the holder's certificate contains extensions.
+     *
+     * @return true if extension are present, false otherwise.
+     */
+    public boolean hasExtensions()
+    {
+        return extensions != null;
+    }
+
+    /**
+     * Look up the extension associated with the passed in OID.
+     *
+     * @param oid the OID of the extension of interest.
+     *
+     * @return the extension if present, null otherwise.
+     */
+    public Extension getExtension(ASN1ObjectIdentifier oid)
+    {
+        if (extensions != null)
+        {
+            return extensions.getExtension(oid);
+        }
+
+        return null;
+    }
+
+    /**
+     * Returns a list of ASN1ObjectIdentifier objects representing the OIDs of the
+     * extensions contained in this holder's certificate.
+     *
+     * @return a list of extension OIDs.
+     */
+    public List getExtensionOIDs()
+    {
+        return CertUtils.getExtensionOIDs(extensions);
+    }
+
+    /**
+     * Returns a set of ASN1ObjectIdentifier objects representing the OIDs of the
+     * critical extensions contained in this holder's certificate.
+     *
+     * @return a set of critical extension OIDs.
+     */
+    public Set getCriticalExtensionOIDs()
+    {
+        return CertUtils.getCriticalExtensionOIDs(extensions);
+    }
+
+    /**
+     * Returns a set of ASN1ObjectIdentifier objects representing the OIDs of the
+     * non-critical extensions contained in this holder's certificate.
+     *
+     * @return a set of non-critical extension OIDs.
+     */
+    public Set getNonCriticalExtensionOIDs()
+    {
+        return CertUtils.getNonCriticalExtensionOIDs(extensions);
+    }
+
+    /**
+     * Return the serial number of this attribute certificate.
+     *
+     * @return the serial number.
+     */
+    public BigInteger getSerialNumber()
+    {
+        return x509Certificate.getSerialNumber().getValue();
+    }
+
+    /**
+     * Return the issuer of this certificate.
+     *
+     * @return the certificate issuer.
+     */
+    public X500Name getIssuer()
+    {
+        return X500Name.getInstance(x509Certificate.getIssuer());
+    }
+
+    /**
+     * Return the subject this certificate is for.
+     *
+     * @return the subject for the certificate.
+     */
+    public X500Name getSubject()
+    {
+        return X500Name.getInstance(x509Certificate.getSubject());
+    }
+
+    /**
+     * Return the date before which this certificate is not valid.
+     *
+     * @return the start time for the certificate's validity period.
+     */
+    public Date getNotBefore()
+    {
+        return x509Certificate.getStartDate().getDate();
+    }
+
+    /**
+     * Return the date after which this certificate is not valid.
+     *
+     * @return the final time for the certificate's validity period.
+     */
+    public Date getNotAfter()
+    {
+        return x509Certificate.getEndDate().getDate();
+    }
+
+    /**
+     * Return the SubjectPublicKeyInfo describing the public key this certificate is carrying.
+     *
+     * @return the public key ASN.1 structure contained in the certificate.
+     */
+    public SubjectPublicKeyInfo getSubjectPublicKeyInfo()
+    {
+        return x509Certificate.getSubjectPublicKeyInfo();
+    }
+
+    /**
+     * Return the underlying ASN.1 structure for the certificate in this holder.
+     *
+     * @return a X509CertificateStructure object.
+     */
+    public Certificate toASN1Structure()
+    {
+        return x509Certificate;
+    }
+
+    /**
+     * Return the details of the signature algorithm used to create this attribute certificate.
+     *
+     * @return the AlgorithmIdentifier describing the signature algorithm used to create this attribute certificate.
+     */
+    public AlgorithmIdentifier getSignatureAlgorithm()
+    {
+        return x509Certificate.getSignatureAlgorithm();
+    }
+
+    /**
+     * Return the bytes making up the signature associated with this attribute certificate.
+     *
+     * @return the attribute certificate signature bytes.
+     */
+    public byte[] getSignature()
+    {
+        return x509Certificate.getSignature().getBytes();
+    }
+
+    /**
+     * Return whether or not this certificate is valid on a particular date.
+     *
+     * @param date the date of interest.
+     * @return true if the certificate is valid, false otherwise.
+     */
+    public boolean isValidOn(Date date)
+    {
+        return !date.before(x509Certificate.getStartDate().getDate()) && !date.after(x509Certificate.getEndDate().getDate());
+    }
+
+    /**
+     * Validate the signature on the certificate in this holder.
+     *
+     * @param verifierProvider a ContentVerifierProvider that can generate a verifier for the signature.
+     * @return true if the signature is valid, false otherwise.
+     * @throws CertException if the signature cannot be processed or is inappropriate.
+     */
+    public boolean isSignatureValid(ContentVerifierProvider verifierProvider)
+        throws CertException
+    {
+        TBSCertificate tbsCert = x509Certificate.getTBSCertificate();
+
+        if (!tbsCert.getSignature().equals(x509Certificate.getSignatureAlgorithm()))
+        {
+            throw new CertException("signature invalid - algorithm identifier mismatch");
+        }
+
+        ContentVerifier verifier;
+
+        try
+        {
+            verifier = verifierProvider.get((tbsCert.getSignature()));
+
+            OutputStream sOut = verifier.getOutputStream();
+            DEROutputStream dOut = new DEROutputStream(sOut);
+
+            dOut.writeObject(tbsCert);
+
+            sOut.close();
+        }
+        catch (Exception e)
+        {
+            throw new CertException("unable to process signature: " + e.getMessage(), e);
+        }
+
+        return verifier.verify(x509Certificate.getSignature().getBytes());
+    }
+
+    public boolean equals(
+        Object o)
+    {
+        if (o == this)
+        {
+            return true;
+        }
+
+        if (!(o instanceof X509CertificateHolder))
+        {
+            return false;
+        }
+
+        X509CertificateHolder other = (X509CertificateHolder)o;
+
+        return this.x509Certificate.equals(other.x509Certificate);
+    }
+
+    public int hashCode()
+    {
+        return this.x509Certificate.hashCode();
+    }
+
+    /**
+     * Return the ASN.1 encoding of this holder's certificate.
+     *
+     * @return a DER encoded byte array.
+     * @throws IOException if an encoding cannot be generated.
+     */
+    public byte[] getEncoded()
+        throws IOException
+    {
+        return x509Certificate.getEncoded();
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/cert/jcajce/JcaCertStore.java b/bcpkix/src/main/java/org/bouncycastle/cert/jcajce/JcaCertStore.java
new file mode 100644
index 0000000..e743364
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/cert/jcajce/JcaCertStore.java
@@ -0,0 +1,64 @@
+package org.bouncycastle.cert.jcajce;
+
+import java.io.IOException;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+
+import org.bouncycastle.cert.X509CertificateHolder;
+import org.bouncycastle.util.CollectionStore;
+
+/**
+ * Class for storing Certificates for later lookup.
+ * <p>
+ * The class will convert X509Certificate objects into X509CertificateHolder objects.
+ * </p>
+ */
+public class JcaCertStore
+    extends CollectionStore
+{
+    /**
+     * Basic constructor.
+     *
+     * @param collection - initial contents for the store, this is copied.
+     */
+    public JcaCertStore(Collection collection)
+        throws CertificateEncodingException
+    {
+        super(convertCerts(collection));
+    }
+
+    private static Collection convertCerts(Collection collection)
+        throws CertificateEncodingException
+    {
+        List list = new ArrayList(collection.size());
+
+        for (Iterator it = collection.iterator(); it.hasNext();)
+        {
+            Object o = it.next();
+
+            if (o instanceof X509Certificate)
+            {
+                X509Certificate cert = (X509Certificate)o;
+
+                try
+                {
+                    list.add(new X509CertificateHolder(cert.getEncoded()));
+                }
+                catch (IOException e)
+                {
+                    throw new CertificateEncodingException("unable to read encoding: " + e.getMessage());
+                }
+            }
+            else
+            {
+                list.add((X509CertificateHolder)o);
+            }
+        }
+
+        return list;
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/cert/jcajce/JcaX509CertificateHolder.java b/bcpkix/src/main/java/org/bouncycastle/cert/jcajce/JcaX509CertificateHolder.java
new file mode 100644
index 0000000..d061184
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/cert/jcajce/JcaX509CertificateHolder.java
@@ -0,0 +1,26 @@
+package org.bouncycastle.cert.jcajce;
+
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.X509Certificate;
+
+import org.bouncycastle.asn1.x509.Certificate;
+import org.bouncycastle.cert.X509CertificateHolder;
+
+/**
+ * JCA helper class for converting an X509Certificate into a X509CertificateHolder object.
+ */
+public class JcaX509CertificateHolder
+    extends X509CertificateHolder
+{
+    /**
+     * Base constructor.
+     *
+     * @param cert certificate to be used a the source for the holder creation.
+     * @throws CertificateEncodingException if there is a problem extracting the certificate information.
+     */
+    public JcaX509CertificateHolder(X509Certificate cert)
+        throws CertificateEncodingException
+    {
+        super(Certificate.getInstance(cert.getEncoded()));
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/cert/selector/MSOutlookKeyIdCalculator.java b/bcpkix/src/main/java/org/bouncycastle/cert/selector/MSOutlookKeyIdCalculator.java
new file mode 100644
index 0000000..3f4e22c
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/cert/selector/MSOutlookKeyIdCalculator.java
@@ -0,0 +1,33 @@
+package org.bouncycastle.cert.selector;
+
+import java.io.IOException;
+
+import org.bouncycastle.asn1.ASN1Encoding;
+import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.digests.SHA1Digest;
+
+class MSOutlookKeyIdCalculator
+{
+    static byte[] calculateKeyId(SubjectPublicKeyInfo info)
+    {
+        Digest dig = new SHA1Digest();    // TODO: include definition of SHA-1 here
+        byte[] hash = new byte[dig.getDigestSize()];
+        byte[] spkiEnc = new byte[0];
+        try
+        {
+            spkiEnc = info.getEncoded(ASN1Encoding.DER);
+        }
+        catch (IOException e)
+        {
+            return new byte[0];
+        }
+
+        // try the outlook 2010 calculation
+        dig.update(spkiEnc, 0, spkiEnc.length);
+
+        dig.doFinal(hash, 0);
+
+        return hash;
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/cert/selector/X509CertificateHolderSelector.java b/bcpkix/src/main/java/org/bouncycastle/cert/selector/X509CertificateHolderSelector.java
new file mode 100644
index 0000000..5af5860
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/cert/selector/X509CertificateHolderSelector.java
@@ -0,0 +1,152 @@
+package org.bouncycastle.cert.selector;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.cms.IssuerAndSerialNumber;
+import org.bouncycastle.asn1.x500.X500Name;
+import org.bouncycastle.asn1.x509.Extension;
+import org.bouncycastle.cert.X509CertificateHolder;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.Selector;
+
+/**
+ * a basic index for a X509CertificateHolder class
+ */
+public class X509CertificateHolderSelector
+    implements Selector
+{
+    private byte[] subjectKeyId;
+
+    private X500Name issuer;
+    private BigInteger serialNumber;
+
+    /**
+     * Construct a selector with the value of a public key's subjectKeyId.
+     *
+     * @param subjectKeyId a subjectKeyId
+     */
+    public X509CertificateHolderSelector(byte[] subjectKeyId)
+    {
+        this(null, null, subjectKeyId);
+    }
+
+    /**
+     * Construct a signer ID based on the issuer and serial number of the signer's associated
+     * certificate.
+     *
+     * @param issuer the issuer of the signer's associated certificate.
+     * @param serialNumber the serial number of the signer's associated certificate.
+     */
+    public X509CertificateHolderSelector(X500Name issuer, BigInteger serialNumber)
+    {
+        this(issuer, serialNumber, null);
+    }
+
+    /**
+     * Construct a signer ID based on the issuer and serial number of the signer's associated
+     * certificate.
+     *
+     * @param issuer the issuer of the signer's associated certificate.
+     * @param serialNumber the serial number of the signer's associated certificate.
+     * @param subjectKeyId the subject key identifier to use to match the signers associated certificate.
+     */
+    public X509CertificateHolderSelector(X500Name issuer, BigInteger serialNumber, byte[] subjectKeyId)
+    {
+        this.issuer = issuer;
+        this.serialNumber = serialNumber;
+        this.subjectKeyId = subjectKeyId;
+    }
+
+    public X500Name getIssuer()
+    {
+        return issuer;
+    }
+
+    public BigInteger getSerialNumber()
+    {
+        return serialNumber;
+    }
+
+    public byte[] getSubjectKeyIdentifier()
+    {
+        return Arrays.clone(subjectKeyId);
+    }
+
+    public int hashCode()
+    {
+        int code = Arrays.hashCode(subjectKeyId);
+
+        if (this.serialNumber != null)
+        {
+            code ^= this.serialNumber.hashCode();
+        }
+
+        if (this.issuer != null)
+        {
+            code ^= this.issuer.hashCode();
+        }
+
+        return code;
+    }
+
+    public boolean equals(
+        Object  o)
+    {
+        if (!(o instanceof X509CertificateHolderSelector))
+        {
+            return false;
+        }
+
+        X509CertificateHolderSelector id = (X509CertificateHolderSelector)o;
+
+        return Arrays.areEqual(subjectKeyId, id.subjectKeyId)
+            && equalsObj(this.serialNumber, id.serialNumber)
+            && equalsObj(this.issuer, id.issuer);
+    }
+
+    private boolean equalsObj(Object a, Object b)
+    {
+        return (a != null) ? a.equals(b) : b == null;
+    }
+
+    public boolean match(Object obj)
+    {
+        if (obj instanceof X509CertificateHolder)
+        {
+            X509CertificateHolder certHldr = (X509CertificateHolder)obj;
+
+            if (this.getSerialNumber() != null)
+            {
+                IssuerAndSerialNumber iAndS = new IssuerAndSerialNumber(certHldr.toASN1Structure());
+
+                return iAndS.getName().equals(this.issuer)
+                    && iAndS.getSerialNumber().getValue().equals(this.serialNumber);
+            }
+            else if (subjectKeyId != null)
+            {
+                Extension ext = certHldr.getExtension(Extension.subjectKeyIdentifier);
+
+                if (ext == null)
+                {
+                    return Arrays.areEqual(subjectKeyId, MSOutlookKeyIdCalculator.calculateKeyId(certHldr.getSubjectPublicKeyInfo()));
+                }
+
+                byte[] subKeyID = ASN1OctetString.getInstance(ext.getParsedValue()).getOctets();
+
+                return Arrays.areEqual(subjectKeyId, subKeyID);
+            }
+        }
+        else if (obj instanceof byte[])
+        {
+            return Arrays.areEqual(subjectKeyId, (byte[])obj);
+        }
+
+        return false;
+    }
+
+    public Object clone()
+    {
+        return new X509CertificateHolderSelector(this.issuer, this.serialNumber, this.subjectKeyId);
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/CMSAbsentContent.java b/bcpkix/src/main/java/org/bouncycastle/cms/CMSAbsentContent.java
new file mode 100644
index 0000000..f256e2a
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/CMSAbsentContent.java
@@ -0,0 +1,49 @@
+package org.bouncycastle.cms;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.cms.CMSObjectIdentifiers;
+
+/**
+ * a class representing null or absent content.
+ */
+public class CMSAbsentContent
+    implements CMSTypedData, CMSReadable
+{
+    private final ASN1ObjectIdentifier type;
+
+    public CMSAbsentContent()
+    {
+        this(new ASN1ObjectIdentifier(CMSObjectIdentifiers.data.getId()));
+    }
+
+    public CMSAbsentContent(
+        ASN1ObjectIdentifier type)
+    {
+        this.type = type;
+    }
+
+    public InputStream getInputStream()
+    {
+        return null;
+    }
+
+    public void write(OutputStream zOut)
+        throws IOException, CMSException
+    {
+        // do nothing
+    }
+
+    public Object getContent()
+    {
+        return null;
+    }
+
+    public ASN1ObjectIdentifier getContentType()
+    {
+        return type;
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/CMSAttributeTableGenerationException.java b/bcpkix/src/main/java/org/bouncycastle/cms/CMSAttributeTableGenerationException.java
new file mode 100644
index 0000000..e3cab8a
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/CMSAttributeTableGenerationException.java
@@ -0,0 +1,32 @@
+package org.bouncycastle.cms;
+
+public class CMSAttributeTableGenerationException
+    extends CMSRuntimeException
+{
+    Exception   e;
+
+    public CMSAttributeTableGenerationException(
+        String name)
+    {
+        super(name);
+    }
+
+    public CMSAttributeTableGenerationException(
+        String name,
+        Exception e)
+    {
+        super(name);
+
+        this.e = e;
+    }
+
+    public Exception getUnderlyingException()
+    {
+        return e;
+    }
+    
+    public Throwable getCause()
+    {
+        return e;
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/CMSAttributeTableGenerator.java b/bcpkix/src/main/java/org/bouncycastle/cms/CMSAttributeTableGenerator.java
new file mode 100644
index 0000000..528c738
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/CMSAttributeTableGenerator.java
@@ -0,0 +1,19 @@
+package org.bouncycastle.cms;
+
+import org.bouncycastle.asn1.cms.AttributeTable;
+
+import java.util.Map;
+
+/**
+ * Note: The SIGNATURE parameter is only available when generating unsigned attributes.
+ */
+public interface CMSAttributeTableGenerator
+{
+    static final String CONTENT_TYPE = "contentType";
+    static final String DIGEST = "digest";
+    static final String SIGNATURE = "encryptedDigest";
+    static final String DIGEST_ALGORITHM_IDENTIFIER = "digestAlgID";
+
+    AttributeTable getAttributes(Map parameters)
+        throws CMSAttributeTableGenerationException;
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/CMSException.java b/bcpkix/src/main/java/org/bouncycastle/cms/CMSException.java
new file mode 100644
index 0000000..04bbd69
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/CMSException.java
@@ -0,0 +1,32 @@
+package org.bouncycastle.cms;
+
+public class CMSException
+    extends Exception
+{
+    Exception   e;
+
+    public CMSException(
+        String msg)
+    {
+        super(msg);
+    }
+
+    public CMSException(
+        String msg,
+        Exception e)
+    {
+        super(msg);
+
+        this.e = e;
+    }
+
+    public Exception getUnderlyingException()
+    {
+        return e;
+    }
+    
+    public Throwable getCause()
+    {
+        return e;
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/CMSProcessable.java b/bcpkix/src/main/java/org/bouncycastle/cms/CMSProcessable.java
new file mode 100644
index 0000000..9f34b9a
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/CMSProcessable.java
@@ -0,0 +1,21 @@
+package org.bouncycastle.cms;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * Use CMSTypedData instead of this. See CMSProcessableFile/ByteArray for defaults.
+ */
+public interface CMSProcessable
+{
+    /**
+     * generic routine to copy out the data we want processed - the OutputStream
+     * passed in will do the handling on it's own.
+     * <p>
+     * Note: this routine may be called multiple times.
+     */
+    public void write(OutputStream out)
+        throws IOException, CMSException;
+
+    public Object getContent();
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/CMSProcessableByteArray.java b/bcpkix/src/main/java/org/bouncycastle/cms/CMSProcessableByteArray.java
new file mode 100644
index 0000000..2b2c354
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/CMSProcessableByteArray.java
@@ -0,0 +1,54 @@
+package org.bouncycastle.cms;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.cms.CMSObjectIdentifiers;
+
+/**
+ * a holding class for a byte array of data to be processed.
+ */
+public class CMSProcessableByteArray
+    implements CMSTypedData, CMSReadable
+{
+    private final ASN1ObjectIdentifier type;
+    private final byte[]  bytes;
+
+    public CMSProcessableByteArray(
+        byte[]  bytes)
+    {
+        this(new ASN1ObjectIdentifier(CMSObjectIdentifiers.data.getId()), bytes);
+    }
+
+    public CMSProcessableByteArray(
+        ASN1ObjectIdentifier type,
+        byte[]  bytes)
+    {
+        this.type = type;
+        this.bytes = bytes;
+    }
+
+    public InputStream getInputStream()
+    {
+        return new ByteArrayInputStream(bytes);
+    }
+
+    public void write(OutputStream zOut)
+        throws IOException, CMSException
+    {
+        zOut.write(bytes);
+    }
+
+    public Object getContent()
+    {
+        return bytes.clone();
+    }
+
+    public ASN1ObjectIdentifier getContentType()
+    {
+        return type;
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/CMSReadable.java b/bcpkix/src/main/java/org/bouncycastle/cms/CMSReadable.java
new file mode 100644
index 0000000..ca86766
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/CMSReadable.java
@@ -0,0 +1,10 @@
+package org.bouncycastle.cms;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+interface CMSReadable
+{
+    public InputStream getInputStream()
+        throws IOException, CMSException;
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/CMSRuntimeException.java b/bcpkix/src/main/java/org/bouncycastle/cms/CMSRuntimeException.java
new file mode 100644
index 0000000..d9f8acc
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/CMSRuntimeException.java
@@ -0,0 +1,32 @@
+package org.bouncycastle.cms;
+
+public class CMSRuntimeException
+    extends RuntimeException
+{
+    Exception   e;
+
+    public CMSRuntimeException(
+        String name)
+    {
+        super(name);
+    }
+
+    public CMSRuntimeException(
+        String name,
+        Exception e)
+    {
+        super(name);
+
+        this.e = e;
+    }
+
+    public Exception getUnderlyingException()
+    {
+        return e;
+    }
+    
+    public Throwable getCause()
+    {
+        return e;
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/CMSSignatureAlgorithmNameGenerator.java b/bcpkix/src/main/java/org/bouncycastle/cms/CMSSignatureAlgorithmNameGenerator.java
new file mode 100644
index 0000000..59d6ce8
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/CMSSignatureAlgorithmNameGenerator.java
@@ -0,0 +1,15 @@
+package org.bouncycastle.cms;
+
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+
+public interface CMSSignatureAlgorithmNameGenerator
+{
+    /**
+     * Return the digest algorithm using one of the standard string
+     * representations rather than the algorithm object identifier (if possible).
+     *
+     * @param digestAlg the digest algorithm id.
+     * @param encryptionAlg the encryption, or signing, algorithm id.
+     */
+    String getSignatureName(AlgorithmIdentifier digestAlg, AlgorithmIdentifier encryptionAlg);
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/CMSSignatureEncryptionAlgorithmFinder.java b/bcpkix/src/main/java/org/bouncycastle/cms/CMSSignatureEncryptionAlgorithmFinder.java
new file mode 100644
index 0000000..b1cd91f
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/CMSSignatureEncryptionAlgorithmFinder.java
@@ -0,0 +1,17 @@
+package org.bouncycastle.cms;
+
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+
+/**
+ * Finder which is used to look up the algorithm identifiers representing the encryption algorithms that
+ * are associated with a particular signature algorithm.
+ */
+public interface CMSSignatureEncryptionAlgorithmFinder
+{
+    /**
+     * Return the encryption algorithm identifier associated with the passed in signatureAlgorithm
+     * @param signatureAlgorithm the algorithm identifier of the signature of interest
+     * @return  the algorithm identifier to be associated with the encryption algorithm used in signature creation.
+     */
+    AlgorithmIdentifier findEncryptionAlgorithm(AlgorithmIdentifier signatureAlgorithm);
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/CMSSignedData.java b/bcpkix/src/main/java/org/bouncycastle/cms/CMSSignedData.java
new file mode 100644
index 0000000..7a8adeb
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/CMSSignedData.java
@@ -0,0 +1,745 @@
+package org.bouncycastle.cms;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.Provider;
+import java.security.cert.CertStore;
+import java.security.cert.CertStoreException;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1InputStream;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1Set;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.BERSequence;
+import org.bouncycastle.asn1.DERSet;
+import org.bouncycastle.asn1.cms.ContentInfo;
+import org.bouncycastle.asn1.cms.SignedData;
+import org.bouncycastle.asn1.cms.SignerInfo;
+import org.bouncycastle.asn1.x509.AttributeCertificate;
+import org.bouncycastle.asn1.x509.Certificate;
+import org.bouncycastle.asn1.x509.CertificateList;
+import org.bouncycastle.cert.X509AttributeCertificateHolder;
+import org.bouncycastle.cert.X509CRLHolder;
+import org.bouncycastle.cert.X509CertificateHolder;
+import org.bouncycastle.operator.DefaultSignatureAlgorithmIdentifierFinder;
+import org.bouncycastle.operator.SignatureAlgorithmIdentifierFinder;
+import org.bouncycastle.util.CollectionStore;
+import org.bouncycastle.util.Store;
+import org.bouncycastle.x509.NoSuchStoreException;
+import org.bouncycastle.x509.X509Store;
+
+/**
+ * general class for handling a pkcs7-signature message.
+ *
+ * A simple example of usage - note, in the example below the validity of
+ * the certificate isn't verified, just the fact that one of the certs 
+ * matches the given signer...
+ *
+ * <pre>
+ *  Store                   certStore = s.getCertificates();
+ *  SignerInformationStore  signers = s.getSignerInfos();
+ *  Collection              c = signers.getSigners();
+ *  Iterator                it = c.iterator();
+ *  
+ *  while (it.hasNext())
+ *  {
+ *      SignerInformation   signer = (SignerInformation)it.next();
+ *      Collection          certCollection = certStore.getMatches(signer.getSID());
+ *
+ *      Iterator              certIt = certCollection.iterator();
+ *      X509CertificateHolder cert = (X509CertificateHolder)certIt.next();
+ *  
+ *      if (signer.verify(new JcaSimpleSignerInfoVerifierBuilder().setProvider("BC").build(cert)))
+ *      {
+ *          verified++;
+ *      }   
+ *  }
+ * </pre>
+ */
+public class CMSSignedData
+{
+    private static final CMSSignedHelper HELPER = CMSSignedHelper.INSTANCE;
+    
+    SignedData              signedData;
+    ContentInfo             contentInfo;
+    CMSProcessable          signedContent;
+    SignerInformationStore  signerInfoStore;
+    X509Store               attributeStore;
+    X509Store               certificateStore;
+    X509Store               crlStore;
+    private Map             hashes;
+
+    private CMSSignedData(
+        CMSSignedData   c)
+    {
+        this.signedData = c.signedData;
+        this.contentInfo = c.contentInfo;
+        this.signedContent = c.signedContent;
+        this.signerInfoStore = c.signerInfoStore;
+    }
+
+    public CMSSignedData(
+        byte[]      sigBlock)
+        throws CMSException
+    {
+        this(CMSUtils.readContentInfo(sigBlock));
+    }
+
+    public CMSSignedData(
+        CMSProcessable  signedContent,
+        byte[]          sigBlock)
+        throws CMSException
+    {
+        this(signedContent, CMSUtils.readContentInfo(sigBlock));
+    }
+
+    /**
+     * Content with detached signature, digests precomputed
+     *
+     * @param hashes a map of precomputed digests for content indexed by name of hash.
+     * @param sigBlock the signature object.
+     */
+    public CMSSignedData(
+        Map     hashes,
+        byte[]  sigBlock)
+        throws CMSException
+    {
+        this(hashes, CMSUtils.readContentInfo(sigBlock));
+    }
+
+    /**
+     * base constructor - content with detached signature.
+     *
+     * @param signedContent the content that was signed.
+     * @param sigData the signature object.
+     */
+    public CMSSignedData(
+        CMSProcessable  signedContent,
+        InputStream     sigData)
+        throws CMSException
+    {
+        this(signedContent, CMSUtils.readContentInfo(new ASN1InputStream(sigData)));
+    }
+
+    /**
+     * base constructor - with encapsulated content
+     */
+    public CMSSignedData(
+        InputStream sigData)
+        throws CMSException
+    {
+        this(CMSUtils.readContentInfo(sigData));
+    }
+
+    public CMSSignedData(
+        CMSProcessable  signedContent,
+        ContentInfo     sigData)
+        throws CMSException
+    {
+        this.signedContent = signedContent;
+        this.contentInfo = sigData;
+        this.signedData = getSignedData();
+    }
+
+    public CMSSignedData(
+        Map             hashes,
+        ContentInfo     sigData)
+        throws CMSException
+    {
+        this.hashes = hashes;
+        this.contentInfo = sigData;
+        this.signedData = getSignedData();
+    }
+
+    public CMSSignedData(
+        ContentInfo sigData)
+        throws CMSException
+    {
+        this.contentInfo = sigData;
+        this.signedData = getSignedData();
+
+        //
+        // this can happen if the signed message is sent simply to send a
+        // certificate chain.
+        //
+        if (signedData.getEncapContentInfo().getContent() != null)
+        {
+            this.signedContent = new CMSProcessableByteArray(
+                    ((ASN1OctetString)(signedData.getEncapContentInfo()
+                                                .getContent())).getOctets());
+        }
+        else
+        {
+            this.signedContent = null;
+        }
+    }
+
+    private SignedData getSignedData()
+        throws CMSException
+    {
+        try
+        {
+            return SignedData.getInstance(contentInfo.getContent());
+        }
+        catch (ClassCastException e)
+        {
+            throw new CMSException("Malformed content.", e);
+        }
+        catch (IllegalArgumentException e)
+        {
+            throw new CMSException("Malformed content.", e);
+        }
+    }
+
+    /**
+     * Return the version number for this object
+     */
+    public int getVersion()
+    {
+        return signedData.getVersion().getValue().intValue();
+    }
+
+    /**
+     * return the collection of signers that are associated with the
+     * signatures for the message.
+     */
+    public SignerInformationStore getSignerInfos()
+    {
+        if (signerInfoStore == null)
+        {
+            ASN1Set         s = signedData.getSignerInfos();
+            List            signerInfos = new ArrayList();
+            SignatureAlgorithmIdentifierFinder sigAlgFinder = new DefaultSignatureAlgorithmIdentifierFinder();
+
+            for (int i = 0; i != s.size(); i++)
+            {
+                SignerInfo info = SignerInfo.getInstance(s.getObjectAt(i));
+                ASN1ObjectIdentifier contentType = signedData.getEncapContentInfo().getContentType();
+
+                if (hashes == null)
+                {
+                    signerInfos.add(new SignerInformation(info, contentType, signedContent, null));
+                }
+                else
+                {
+                    Object obj = hashes.keySet().iterator().next();
+                    byte[] hash = (obj instanceof String) ? (byte[])hashes.get(info.getDigestAlgorithm().getAlgorithm().getId()) : (byte[])hashes.get(info.getDigestAlgorithm().getAlgorithm());
+
+                    signerInfos.add(new SignerInformation(info, contentType, null, hash));
+                }
+            }
+
+            signerInfoStore = new SignerInformationStore(signerInfos);
+        }
+
+        return signerInfoStore;
+    }
+
+    /**
+     * return a X509Store containing the attribute certificates, if any, contained
+     * in this message.
+     *
+     * @param type type of store to create
+     * @param provider name of provider to use
+     * @return a store of attribute certificates
+     * @exception NoSuchProviderException if the provider requested isn't available.
+     * @exception NoSuchStoreException if the store type isn't available.
+     * @exception CMSException if a general exception prevents creation of the X509Store
+     * @deprecated use base Store returning method
+     */
+    public X509Store getAttributeCertificates(
+        String type,
+        String provider)
+        throws NoSuchStoreException, NoSuchProviderException, CMSException
+    {
+        return getAttributeCertificates(type, CMSUtils.getProvider(provider));
+    }
+
+    /**
+     * return a X509Store containing the attribute certificates, if any, contained
+     * in this message.
+     *
+     * @param type type of store to create
+     * @param provider provider to use
+     * @return a store of attribute certificates
+     * @exception NoSuchStoreException if the store type isn't available.
+     * @exception CMSException if a general exception prevents creation of the X509Store
+     * @deprecated use base Store returning method
+     */
+    public X509Store getAttributeCertificates(
+        String type,
+        Provider provider)
+        throws NoSuchStoreException, CMSException
+    {
+        if (attributeStore == null)
+        {
+            attributeStore = HELPER.createAttributeStore(type, provider, signedData.getCertificates());
+        }
+
+        return attributeStore;
+    }
+
+    /**
+     * return a X509Store containing the public key certificates, if any, contained
+     * in this message.
+     *
+     * @param type type of store to create
+     * @param provider name of provider to use
+     * @return a store of public key certificates
+     * @exception NoSuchProviderException if the provider requested isn't available.
+     * @exception NoSuchStoreException if the store type isn't available.
+     * @exception CMSException if a general exception prevents creation of the X509Store
+     * @deprecated use base Store returning method
+     */
+    public X509Store getCertificates(
+        String type,
+        String provider)
+        throws NoSuchStoreException, NoSuchProviderException, CMSException
+    {
+        return getCertificates(type, CMSUtils.getProvider(provider));
+    }
+
+    /**
+     * return a X509Store containing the public key certificates, if any, contained
+     * in this message.
+     *
+     * @param type type of store to create
+     * @param provider provider to use
+     * @return a store of public key certificates
+     * @exception NoSuchStoreException if the store type isn't available.
+     * @exception CMSException if a general exception prevents creation of the X509Store
+     * @deprecated use base Store returning method
+     */
+    public X509Store getCertificates(
+        String type,
+        Provider provider)
+        throws NoSuchStoreException, CMSException
+    {
+        if (certificateStore == null)
+        {
+            certificateStore = HELPER.createCertificateStore(type, provider, signedData.getCertificates());
+        }
+
+        return certificateStore;
+    }
+
+    /**
+     * return a X509Store containing CRLs, if any, contained
+     * in this message.
+     *
+     * @param type type of store to create
+     * @param provider name of provider to use
+     * @return a store of CRLs
+     * @exception NoSuchProviderException if the provider requested isn't available.
+     * @exception NoSuchStoreException if the store type isn't available.
+     * @exception CMSException if a general exception prevents creation of the X509Store
+     * @deprecated use base Store returning method
+     */
+    public X509Store getCRLs(
+        String type,
+        String provider)
+        throws NoSuchStoreException, NoSuchProviderException, CMSException
+    {
+        return getCRLs(type, CMSUtils.getProvider(provider));
+    }
+
+    /**
+     * return a X509Store containing CRLs, if any, contained
+     * in this message.
+     *
+     * @param type type of store to create
+     * @param provider provider to use
+     * @return a store of CRLs
+     * @exception NoSuchStoreException if the store type isn't available.
+     * @exception CMSException if a general exception prevents creation of the X509Store
+     * @deprecated use base Store returning method
+     */
+    public X509Store getCRLs(
+        String type,
+        Provider provider)
+        throws NoSuchStoreException, CMSException
+    {
+        if (crlStore == null)
+        {
+            crlStore = HELPER.createCRLsStore(type, provider, signedData.getCRLs());
+        }
+
+        return crlStore;
+    }
+  
+    /**
+     * return a CertStore containing the certificates and CRLs associated with
+     * this message.
+     *
+     * @exception NoSuchProviderException if the provider requested isn't available.
+     * @exception NoSuchAlgorithmException if the cert store isn't available.
+     * @exception CMSException if a general exception prevents creation of the CertStore
+     * @deprecated use base Store returning method
+     */
+    public CertStore getCertificatesAndCRLs(
+        String  type,
+        String  provider)
+        throws NoSuchAlgorithmException, NoSuchProviderException, CMSException
+    {
+        return getCertificatesAndCRLs(type, CMSUtils.getProvider(provider));
+    }
+
+    /**
+     * return a CertStore containing the certificates and CRLs associated with
+     * this message.
+     *
+     * @exception NoSuchAlgorithmException if the cert store isn't available.
+     * @exception CMSException if a general exception prevents creation of the CertStore
+     * @deprecated use base Store returning method
+     */
+    public CertStore getCertificatesAndCRLs(
+        String  type,
+        Provider  provider)
+        throws NoSuchAlgorithmException, CMSException
+    {
+        ASN1Set certSet = signedData.getCertificates();
+        ASN1Set crlSet = signedData.getCRLs();
+
+        return HELPER.createCertStore(type, provider, certSet, crlSet);
+    }
+
+    public Store getCertificates()
+    {
+        ASN1Set certSet = signedData.getCertificates();
+
+        if (certSet != null)
+        {
+            List    certList = new ArrayList(certSet.size());
+
+            for (Enumeration en = certSet.getObjects(); en.hasMoreElements();)
+            {
+                ASN1Primitive obj = ((ASN1Encodable)en.nextElement()).toASN1Primitive();
+
+                if (obj instanceof ASN1Sequence)
+                {
+                    certList.add(new X509CertificateHolder(Certificate.getInstance(obj)));
+                }
+            }
+
+            return new CollectionStore(certList);
+        }
+
+        return new CollectionStore(new ArrayList());
+    }
+
+    public Store getCRLs()
+    {
+        ASN1Set crlSet = signedData.getCRLs();
+
+        if (crlSet != null)
+        {
+            List    crlList = new ArrayList(crlSet.size());
+
+            for (Enumeration en = crlSet.getObjects(); en.hasMoreElements();)
+            {
+                ASN1Primitive obj = ((ASN1Encodable)en.nextElement()).toASN1Primitive();
+
+                if (obj instanceof ASN1Sequence)
+                {
+                    crlList.add(new X509CRLHolder(CertificateList.getInstance(obj)));
+                }
+            }
+
+            return new CollectionStore(crlList);
+        }
+
+        return new CollectionStore(new ArrayList());
+    }
+
+    public Store getAttributeCertificates()
+    {
+        ASN1Set certSet = signedData.getCertificates();
+
+        if (certSet != null)
+        {
+            List    certList = new ArrayList(certSet.size());
+
+            for (Enumeration en = certSet.getObjects(); en.hasMoreElements();)
+            {
+                ASN1Primitive obj = ((ASN1Encodable)en.nextElement()).toASN1Primitive();
+
+                if (obj instanceof ASN1TaggedObject)
+                {
+                    certList.add(new X509AttributeCertificateHolder(AttributeCertificate.getInstance(((ASN1TaggedObject)obj).getObject())));
+                }
+            }
+
+            return new CollectionStore(certList);
+        }
+
+        return new CollectionStore(new ArrayList());
+    }
+
+    /**
+     * Return the a string representation of the OID associated with the
+     * encapsulated content info structure carried in the signed data.
+     * 
+     * @return the OID for the content type.
+     */
+    public String getSignedContentTypeOID()
+    {
+        return signedData.getEncapContentInfo().getContentType().getId();
+    }
+    
+    public CMSProcessable getSignedContent()
+    {
+        return signedContent;
+    }
+
+    /**
+     * return the ContentInfo
+     * @deprecated use toASN1Structure()
+     */
+    public ContentInfo getContentInfo()
+    {
+        return contentInfo;
+    }
+
+    /**
+     * return the ContentInfo
+     */
+    public ContentInfo toASN1Structure()
+    {
+        return contentInfo;
+    }
+
+    /**
+     * return the ASN.1 encoded representation of this object.
+     */
+    public byte[] getEncoded()
+        throws IOException
+    {
+        return contentInfo.getEncoded();
+    }
+    
+    /**
+     * Replace the signerinformation store associated with this
+     * CMSSignedData object with the new one passed in. You would
+     * probably only want to do this if you wanted to change the unsigned 
+     * attributes associated with a signer, or perhaps delete one.
+     * 
+     * @param signedData the signed data object to be used as a base.
+     * @param signerInformationStore the new signer information store to use.
+     * @return a new signed data object.
+     */
+    public static CMSSignedData replaceSigners(
+        CMSSignedData           signedData,
+        SignerInformationStore  signerInformationStore)
+    {
+        //
+        // copy
+        //
+        CMSSignedData   cms = new CMSSignedData(signedData);
+        
+        //
+        // replace the store
+        //
+        cms.signerInfoStore = signerInformationStore;
+
+        //
+        // replace the signers in the SignedData object
+        //
+        ASN1EncodableVector digestAlgs = new ASN1EncodableVector();
+        ASN1EncodableVector vec = new ASN1EncodableVector();
+        
+        Iterator    it = signerInformationStore.getSigners().iterator();
+        while (it.hasNext())
+        {
+            SignerInformation signer = (SignerInformation)it.next();
+            digestAlgs.add(CMSSignedHelper.INSTANCE.fixAlgID(signer.getDigestAlgorithmID()));
+            vec.add(signer.toASN1Structure());
+        }
+
+        ASN1Set             digests = new DERSet(digestAlgs);
+        ASN1Set             signers = new DERSet(vec);
+        ASN1Sequence        sD = (ASN1Sequence)signedData.signedData.toASN1Primitive();
+
+        vec = new ASN1EncodableVector();
+        
+        //
+        // signers are the last item in the sequence.
+        //
+        vec.add(sD.getObjectAt(0)); // version
+        vec.add(digests);
+
+        for (int i = 2; i != sD.size() - 1; i++)
+        {
+            vec.add(sD.getObjectAt(i));
+        }
+        
+        vec.add(signers);
+        
+        cms.signedData = SignedData.getInstance(new BERSequence(vec));
+        
+        //
+        // replace the contentInfo with the new one
+        //
+        cms.contentInfo = new ContentInfo(cms.contentInfo.getContentType(), cms.signedData);
+        
+        return cms;
+    }
+
+    /**
+     * Replace the certificate and CRL information associated with this
+     * CMSSignedData object with the new one passed in.
+     * 
+     * @param signedData the signed data object to be used as a base.
+     * @param certsAndCrls the new certificates and CRLs to be used.
+     * @return a new signed data object.
+     * @exception CMSException if there is an error processing the CertStore
+     */
+    public static CMSSignedData replaceCertificatesAndCRLs(
+        CMSSignedData   signedData,
+        CertStore       certsAndCrls)
+        throws CMSException
+    {
+        //
+        // copy
+        //
+        CMSSignedData   cms = new CMSSignedData(signedData);
+        
+        //
+        // replace the certs and crls in the SignedData object
+        //
+        ASN1Set             certs = null;
+        ASN1Set             crls = null;
+
+        try
+        {
+            ASN1Set set = CMSUtils.createBerSetFromList(CMSUtils.getCertificatesFromStore(certsAndCrls));
+
+            if (set.size() != 0)
+            {
+                certs = set;
+            }
+        }
+        catch (CertStoreException e)
+        {
+            throw new CMSException("error getting certs from certStore", e);
+        }
+
+        try
+        {
+            ASN1Set set = CMSUtils.createBerSetFromList(CMSUtils.getCRLsFromStore(certsAndCrls));
+
+            if (set.size() != 0)
+            {
+                crls = set;
+            }
+        }
+        catch (CertStoreException e)
+        {
+            throw new CMSException("error getting crls from certStore", e);
+        }
+        
+        //
+        // replace the CMS structure.
+        //
+        cms.signedData = new SignedData(signedData.signedData.getDigestAlgorithms(), 
+                                   signedData.signedData.getEncapContentInfo(),
+                                   certs,
+                                   crls,
+                                   signedData.signedData.getSignerInfos());
+        
+        //
+        // replace the contentInfo with the new one
+        //
+        cms.contentInfo = new ContentInfo(cms.contentInfo.getContentType(), cms.signedData);
+        
+        return cms;
+    }
+
+    /**
+     * Replace the certificate and CRL information associated with this
+     * CMSSignedData object with the new one passed in.
+     *
+     * @param signedData the signed data object to be used as a base.
+     * @param certificates the new certificates to be used.
+     * @param attrCerts the new attribute certificates to be used.
+     * @param crls the new CRLs to be used.
+     * @return a new signed data object.
+     * @exception CMSException if there is an error processing the CertStore
+     */
+    public static CMSSignedData replaceCertificatesAndCRLs(
+        CMSSignedData   signedData,
+        Store           certificates,
+        Store           attrCerts,
+        Store           crls)
+        throws CMSException
+    {
+        //
+        // copy
+        //
+        CMSSignedData   cms = new CMSSignedData(signedData);
+
+        //
+        // replace the certs and crls in the SignedData object
+        //
+        ASN1Set certSet = null;
+        ASN1Set crlSet = null;
+
+        if (certificates != null || attrCerts != null)
+        {
+            List certs = new ArrayList();
+
+            if (certificates != null)
+            {
+                certs.addAll(CMSUtils.getCertificatesFromStore(certificates));
+            }
+            if (attrCerts != null)
+            {
+                certs.addAll(CMSUtils.getAttributeCertificatesFromStore(attrCerts));   
+            }
+
+            ASN1Set set = CMSUtils.createBerSetFromList(certs);
+
+            if (set.size() != 0)
+            {
+                certSet = set;
+            }
+        }
+
+        if (crls != null)
+        {
+            ASN1Set set = CMSUtils.createBerSetFromList(CMSUtils.getCRLsFromStore(crls));
+
+            if (set.size() != 0)
+            {
+                crlSet = set;
+            }
+        }
+
+        //
+        // replace the CMS structure.
+        //
+        cms.signedData = new SignedData(signedData.signedData.getDigestAlgorithms(),
+                                   signedData.signedData.getEncapContentInfo(),
+                                   certSet,
+                                   crlSet,
+                                   signedData.signedData.getSignerInfos());
+
+        //
+        // replace the contentInfo with the new one
+        //
+        cms.contentInfo = new ContentInfo(cms.contentInfo.getContentType(), cms.signedData);
+
+        return cms;
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/CMSSignedDataGenerator.java b/bcpkix/src/main/java/org/bouncycastle/cms/CMSSignedDataGenerator.java
new file mode 100644
index 0000000..f50791e
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/CMSSignedDataGenerator.java
@@ -0,0 +1,786 @@
+package org.bouncycastle.cms;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.PrivateKey;
+import java.security.Provider;
+import java.security.SecureRandom;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.ASN1Set;
+import org.bouncycastle.asn1.BEROctetString;
+import org.bouncycastle.asn1.DERSet;
+import org.bouncycastle.asn1.cms.AttributeTable;
+import org.bouncycastle.asn1.cms.CMSObjectIdentifiers;
+import org.bouncycastle.asn1.cms.ContentInfo;
+import org.bouncycastle.asn1.cms.SignedData;
+import org.bouncycastle.asn1.cms.SignerInfo;
+import org.bouncycastle.cms.jcajce.JcaSignerInfoGeneratorBuilder;
+import org.bouncycastle.operator.ContentSigner;
+import org.bouncycastle.operator.OperatorCreationException;
+import org.bouncycastle.operator.bc.BcDigestCalculatorProvider;
+import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
+
+/**
+ * general class for generating a pkcs7-signature message.
+ * <p>
+ * A simple example of usage, generating a detached signature.
+ *
+ * <pre>
+ *      List             certList = new ArrayList();
+ *      CMSTypedData     msg = new CMSProcessableByteArray("Hello world!".getBytes());
+ *
+ *      certList.add(signCert);
+ *
+ *      Store           certs = new JcaCertStore(certList);
+ *
+ *      CMSSignedDataGenerator gen = new CMSSignedDataGenerator();
+ *      ContentSigner sha1Signer = new JcaContentSignerBuilder("SHA1withRSA").setProvider("BC").build(signKP.getPrivate());
+ *
+ *      gen.addSignerInfoGenerator(
+ *                new JcaSignerInfoGeneratorBuilder(
+ *                     new JcaDigestCalculatorProviderBuilder().setProvider("BC").build())
+ *                     .build(sha1Signer, signCert));
+ *
+ *      gen.addCertificates(certs);
+ *
+ *      CMSSignedData sigData = gen.generate(msg, false);
+ * </pre>
+ */
+public class CMSSignedDataGenerator
+    extends CMSSignedGenerator
+{
+    private List signerInfs = new ArrayList();
+
+    private class SignerInf
+    {
+        final PrivateKey                  key;
+        final Object                      signerIdentifier;
+        final String                      digestOID;
+        final String                      encOID;
+        final CMSAttributeTableGenerator  sAttr;
+        final CMSAttributeTableGenerator  unsAttr;
+        final AttributeTable              baseSignedTable;
+
+        SignerInf(
+            PrivateKey                 key,
+            Object                     signerIdentifier,
+            String                     digestOID,
+            String                     encOID,
+            CMSAttributeTableGenerator sAttr,
+            CMSAttributeTableGenerator unsAttr,
+            AttributeTable             baseSignedTable)
+        {
+            this.key = key;
+            this.signerIdentifier = signerIdentifier;
+            this.digestOID = digestOID;
+            this.encOID = encOID;
+            this.sAttr = sAttr;
+            this.unsAttr = unsAttr;
+            this.baseSignedTable = baseSignedTable;
+        }
+
+        SignerInfoGenerator toSignerInfoGenerator(
+            SecureRandom        random,
+            Provider sigProvider,
+            boolean             addDefaultAttributes)
+            throws IOException, CertificateEncodingException, CMSException, OperatorCreationException, NoSuchAlgorithmException
+        {
+            String              digestName = CMSSignedHelper.INSTANCE.getDigestAlgName(digestOID);
+            String              signatureName = digestName + "with" + CMSSignedHelper.INSTANCE.getEncryptionAlgName(encOID);
+
+            JcaSignerInfoGeneratorBuilder builder = new JcaSignerInfoGeneratorBuilder(new BcDigestCalculatorProvider());
+
+            if (addDefaultAttributes)
+            {
+                builder.setSignedAttributeGenerator(sAttr);
+            }
+            builder.setDirectSignature(!addDefaultAttributes);
+
+            builder.setUnsignedAttributeGenerator(unsAttr);
+
+            JcaContentSignerBuilder signerBuilder;
+
+            try
+            {
+                signerBuilder = new JcaContentSignerBuilder(signatureName).setSecureRandom(random);
+            }
+            catch (IllegalArgumentException e)
+            {
+                throw new NoSuchAlgorithmException(e.getMessage());
+            }
+
+            if (sigProvider != null)
+            {
+                signerBuilder.setProvider(sigProvider);
+            }
+
+            ContentSigner contentSigner = signerBuilder.build(key);
+            if (signerIdentifier instanceof X509Certificate)
+            {
+                return builder.build(contentSigner, (X509Certificate)signerIdentifier);
+            }
+            else
+            {
+                return builder.build(contentSigner, (byte[])signerIdentifier);
+            }
+        }
+    }
+    /**
+     * base constructor
+     */
+    public CMSSignedDataGenerator()
+    {
+    }
+
+    /**
+     * constructor allowing specific source of randomness
+     * @param rand instance of SecureRandom to use
+     */
+    public CMSSignedDataGenerator(
+        SecureRandom rand)
+    {
+        super(rand);
+    }
+
+    /**
+     * add a signer - no attributes other than the default ones will be
+     * provided here.
+     *
+     * @param key signing key to use
+     * @param cert certificate containing corresponding public key
+     * @param digestOID digest algorithm OID
+     * @deprecated use addSignerInfoGenerator
+     */
+    public void addSigner(
+        PrivateKey      key,
+        X509Certificate cert,
+        String          digestOID)
+        throws IllegalArgumentException
+    {
+        addSigner(key, cert, getEncOID(key, digestOID), digestOID);
+    }
+
+    /**
+     * add a signer, specifying the digest encryption algorithm to use - no attributes other than the default ones will be
+     * provided here.
+     *
+     * @param key signing key to use
+     * @param cert certificate containing corresponding public key
+     * @param encryptionOID digest encryption algorithm OID
+     * @param digestOID digest algorithm OID
+     * @deprecated use addSignerInfoGenerator
+     */
+    public void addSigner(
+        PrivateKey      key,
+        X509Certificate cert,
+        String          encryptionOID,
+        String          digestOID)
+        throws IllegalArgumentException
+    {
+        doAddSigner(key, cert, encryptionOID, digestOID,
+            new DefaultSignedAttributeTableGenerator(), null, null);
+    }
+
+    /**
+     * add a signer - no attributes other than the default ones will be
+     * provided here.
+     * @deprecated use addSignerInfoGenerator
+     */
+    public void addSigner(
+        PrivateKey      key,
+        byte[]          subjectKeyID,
+        String          digestOID)
+        throws IllegalArgumentException
+    {
+        addSigner(key, subjectKeyID, getEncOID(key, digestOID), digestOID);
+    }
+
+    /**
+     * add a signer, specifying the digest encryption algorithm to use - no attributes other than the default ones will be
+     * provided here.
+     * @deprecated use addSignerInfoGenerator
+     */
+    public void addSigner(
+        PrivateKey      key,
+        byte[]          subjectKeyID,
+        String          encryptionOID,
+        String          digestOID)
+        throws IllegalArgumentException
+    {
+        doAddSigner(key, subjectKeyID, encryptionOID, digestOID,
+            new DefaultSignedAttributeTableGenerator(), null, null);
+    }
+
+    /**
+     * add a signer with extra signed/unsigned attributes.
+     *
+     * @param key signing key to use
+     * @param cert certificate containing corresponding public key
+     * @param digestOID digest algorithm OID
+     * @param signedAttr table of attributes to be included in signature
+     * @param unsignedAttr table of attributes to be included as unsigned
+     * @deprecated use addSignerInfoGenerator
+     */
+    public void addSigner(
+        PrivateKey      key,
+        X509Certificate cert,
+        String          digestOID,
+        AttributeTable  signedAttr,
+        AttributeTable  unsignedAttr)
+        throws IllegalArgumentException
+    {
+        addSigner(key, cert, getEncOID(key, digestOID), digestOID, signedAttr, unsignedAttr);
+    }
+
+    /**
+     * add a signer, specifying the digest encryption algorithm, with extra signed/unsigned attributes.
+     *
+     * @param key signing key to use
+     * @param cert certificate containing corresponding public key
+     * @param encryptionOID digest encryption algorithm OID
+     * @param digestOID digest algorithm OID
+     * @param signedAttr table of attributes to be included in signature
+     * @param unsignedAttr table of attributes to be included as unsigned
+     * @deprecated use addSignerInfoGenerator
+     */
+    public void addSigner(
+        PrivateKey      key,
+        X509Certificate cert,
+        String          encryptionOID,
+        String          digestOID,
+        AttributeTable  signedAttr,
+        AttributeTable  unsignedAttr)
+        throws IllegalArgumentException
+    {
+        doAddSigner(key, cert, encryptionOID, digestOID,
+          new DefaultSignedAttributeTableGenerator(signedAttr),
+          new SimpleAttributeTableGenerator(unsignedAttr), signedAttr);
+    }
+
+    /**
+     * add a signer with extra signed/unsigned attributes.
+     *
+     * @param key signing key to use
+     * @param subjectKeyID subjectKeyID of corresponding public key
+     * @param digestOID digest algorithm OID
+     * @param signedAttr table of attributes to be included in signature
+     * @param unsignedAttr table of attributes to be included as unsigned
+     * @deprecated use addSignerInfoGenerator
+     */
+    public void addSigner(
+        PrivateKey      key,
+        byte[]          subjectKeyID,
+        String          digestOID,
+        AttributeTable  signedAttr,
+        AttributeTable  unsignedAttr)
+        throws IllegalArgumentException
+    {
+        addSigner(key, subjectKeyID, getEncOID(key, digestOID), digestOID, signedAttr,
+            unsignedAttr); 
+    }
+
+    /**
+     * add a signer, specifying the digest encryption algorithm, with extra signed/unsigned attributes.
+     *
+     * @param key signing key to use
+     * @param subjectKeyID subjectKeyID of corresponding public key
+     * @param encryptionOID digest encryption algorithm OID
+     * @param digestOID digest algorithm OID
+     * @param signedAttr table of attributes to be included in signature
+     * @param unsignedAttr table of attributes to be included as unsigned
+     * @deprecated use addSignerInfoGenerator
+     */
+    public void addSigner(
+        PrivateKey      key,
+        byte[]          subjectKeyID,
+        String          encryptionOID,
+        String          digestOID,
+        AttributeTable  signedAttr,
+        AttributeTable  unsignedAttr)
+        throws IllegalArgumentException
+    {
+        doAddSigner(key, subjectKeyID, encryptionOID, digestOID,
+            new DefaultSignedAttributeTableGenerator(signedAttr),
+            new SimpleAttributeTableGenerator(unsignedAttr), signedAttr);
+    }
+
+    /**
+     * add a signer with extra signed/unsigned attributes based on generators.
+     * @deprecated use addSignerInfoGenerator
+     */
+    public void addSigner(
+        PrivateKey                  key,
+        X509Certificate             cert,
+        String                      digestOID,
+        CMSAttributeTableGenerator  signedAttrGen,
+        CMSAttributeTableGenerator  unsignedAttrGen)
+        throws IllegalArgumentException
+    {
+        addSigner(key, cert, getEncOID(key, digestOID), digestOID, signedAttrGen, unsignedAttrGen);
+    }
+
+    /**
+     * add a signer, specifying the digest encryption algorithm, with extra signed/unsigned attributes based on generators.
+     * @deprecated use addSignerInfoGenerator
+     */
+    public void addSigner(
+        PrivateKey                  key,
+        X509Certificate             cert,
+        String                      encryptionOID,
+        String                      digestOID,
+        CMSAttributeTableGenerator  signedAttrGen,
+        CMSAttributeTableGenerator  unsignedAttrGen)
+        throws IllegalArgumentException
+    {
+        doAddSigner(key, cert, encryptionOID, digestOID, signedAttrGen,
+            unsignedAttrGen, null);
+    }
+
+    /**
+     * add a signer with extra signed/unsigned attributes based on generators.
+     * @deprecated use addSignerInfoGenerator
+     */
+    public void addSigner(
+        PrivateKey                  key,
+        byte[]                      subjectKeyID,
+        String                      digestOID,
+        CMSAttributeTableGenerator  signedAttrGen,
+        CMSAttributeTableGenerator  unsignedAttrGen)
+        throws IllegalArgumentException
+    {
+        addSigner(key, subjectKeyID, getEncOID(key, digestOID), digestOID, signedAttrGen,
+            unsignedAttrGen);
+    }
+
+    /**
+     * add a signer, including digest encryption algorithm, with extra signed/unsigned attributes based on generators.
+     * @deprecated use addSignerInfoGenerator
+     */
+    public void addSigner(
+        PrivateKey                  key,
+        byte[]                      subjectKeyID,
+        String                      encryptionOID,
+        String                      digestOID,
+        CMSAttributeTableGenerator  signedAttrGen,
+        CMSAttributeTableGenerator  unsignedAttrGen)
+        throws IllegalArgumentException
+    {
+        doAddSigner(key, subjectKeyID, encryptionOID, digestOID,
+            signedAttrGen, unsignedAttrGen, null);
+    }
+
+    private void doAddSigner(
+        PrivateKey                  key,
+        Object                      signerIdentifier,
+        String                      encryptionOID,
+        String                      digestOID,
+        CMSAttributeTableGenerator  signedAttrGen,
+        CMSAttributeTableGenerator  unsignedAttrGen,
+        AttributeTable              baseSignedTable)
+        throws IllegalArgumentException
+    {
+        signerInfs.add(new SignerInf(key, signerIdentifier, digestOID, encryptionOID,
+            signedAttrGen, unsignedAttrGen, baseSignedTable));
+    }
+
+    /**
+     * generate a signed object that for a CMS Signed Data
+     * object using the given provider.
+     * @deprecated use generate() method not taking provider.
+     */
+    public CMSSignedData generate(
+        CMSProcessable content,
+        String         sigProvider)
+        throws NoSuchAlgorithmException, NoSuchProviderException, CMSException
+    {
+        return generate(content, CMSUtils.getProvider(sigProvider));
+    }
+
+    /**
+     * generate a signed object that for a CMS Signed Data
+     * object using the given provider.
+     * @deprecated use generate() method not taking provider.
+     */
+    public CMSSignedData generate(
+        CMSProcessable content,
+        Provider       sigProvider)
+        throws NoSuchAlgorithmException, CMSException
+    {
+        return generate(content, false, sigProvider);
+    }
+
+    /**
+     * generate a signed object that for a CMS Signed Data
+     * object using the given provider - if encapsulate is true a copy
+     * of the message will be included in the signature. The content type
+     * is set according to the OID represented by the string signedContentType.
+     * @deprecated use generate(CMSTypedData, boolean)
+     */
+    public CMSSignedData generate(
+        String          eContentType,
+        CMSProcessable  content,
+        boolean         encapsulate,
+        String          sigProvider)
+        throws NoSuchAlgorithmException, NoSuchProviderException, CMSException
+    {
+        return generate(eContentType, content, encapsulate, CMSUtils.getProvider(sigProvider),
+            true);
+    }
+
+    /**
+     * generate a signed object that for a CMS Signed Data
+     * object using the given provider - if encapsulate is true a copy
+     * of the message will be included in the signature. The content type
+     * is set according to the OID represented by the string signedContentType.
+     * @deprecated use generate(CMSTypedData, boolean)
+     */
+    public CMSSignedData generate(
+        String          eContentType,
+        CMSProcessable  content,
+        boolean         encapsulate,
+        Provider        sigProvider)
+        throws NoSuchAlgorithmException, CMSException
+    {
+        return generate(eContentType, content, encapsulate, sigProvider, true);
+    }
+
+    /**
+     * Similar method to the other generate methods. The additional argument
+     * addDefaultAttributes indicates whether or not a default set of signed attributes
+     * need to be added automatically. If the argument is set to false, no
+     * attributes will get added at all.
+     * @deprecated use generate(CMSTypedData, boolean)
+     */
+    public CMSSignedData generate(
+        String                  eContentType,
+        CMSProcessable          content,
+        boolean                 encapsulate,
+        String                  sigProvider,
+        boolean                 addDefaultAttributes)
+        throws NoSuchAlgorithmException, NoSuchProviderException, CMSException
+    {
+        return generate(eContentType, content, encapsulate, CMSUtils.getProvider(sigProvider),
+            addDefaultAttributes);
+    }
+
+    /**
+     * Similar method to the other generate methods. The additional argument
+     * addDefaultAttributes indicates whether or not a default set of signed attributes
+     * need to be added automatically. If the argument is set to false, no
+     * attributes will get added at all.
+     */
+    public CMSSignedData generate(
+        String                  eContentType,
+        final CMSProcessable    content,
+        boolean                 encapsulate,
+        Provider                sigProvider,
+        boolean                 addDefaultAttributes)
+        throws NoSuchAlgorithmException, CMSException
+    {
+        boolean isCounterSignature = (eContentType == null);
+
+        final ASN1ObjectIdentifier contentTypeOID = isCounterSignature
+            ?   null
+            :   new ASN1ObjectIdentifier(eContentType);
+
+        for (Iterator it = signerInfs.iterator(); it.hasNext();)
+        {
+            SignerInf signer = (SignerInf)it.next();
+
+            try
+            {
+                signerGens.add(signer.toSignerInfoGenerator(rand, sigProvider,
+                    addDefaultAttributes));
+            }
+            catch (OperatorCreationException e)
+            {
+                throw new CMSException("exception creating signerInf", e);
+            }
+            catch (IOException e)
+            {
+                throw new CMSException("exception encoding attributes", e);
+            }
+            catch (CertificateEncodingException e)
+            {
+                throw new CMSException("error creating sid.", e);
+            }
+        }
+
+        signerInfs.clear();
+
+        if (content != null)
+        {
+            return generate(new CMSTypedData()
+            {
+                public ASN1ObjectIdentifier getContentType()
+                {
+                    return contentTypeOID;
+                }
+
+                public void write(OutputStream out)
+                    throws IOException, CMSException
+                {
+                    content.write(out);
+                }
+
+                public Object getContent()
+                {
+                    return content.getContent();
+                }
+            }, encapsulate);
+        }
+        else
+        {
+            return generate(new CMSAbsentContent(contentTypeOID), encapsulate);
+        }
+    }
+    
+    /**
+     * generate a signed object that for a CMS Signed Data
+     * object using the given provider - if encapsulate is true a copy
+     * of the message will be included in the signature with the
+     * default content type "data".
+     * @deprecated use generate(CMSTypedData, boolean)
+     */
+    public CMSSignedData generate(
+        CMSProcessable  content,
+        boolean         encapsulate,
+        String          sigProvider)
+        throws NoSuchAlgorithmException, NoSuchProviderException, CMSException
+    {
+        if (content instanceof CMSTypedData)
+        {
+            return this.generate(((CMSTypedData)content).getContentType().getId(), content, encapsulate, sigProvider);
+        }
+        else
+        {
+            return this.generate(DATA, content, encapsulate, sigProvider);
+        }
+    }
+
+    /**
+     * generate a signed object that for a CMS Signed Data
+     * object using the given provider - if encapsulate is true a copy
+     * of the message will be included in the signature with the
+     * default content type "data".
+     * @deprecated use generate(CMSTypedData, boolean)
+     */
+    public CMSSignedData generate(
+        CMSProcessable  content,
+        boolean         encapsulate,
+        Provider        sigProvider)
+        throws NoSuchAlgorithmException, CMSException
+    {
+        if (content instanceof CMSTypedData)
+        {
+            return this.generate(((CMSTypedData)content).getContentType().getId(), content, encapsulate, sigProvider);
+        }
+        else
+        {
+            return this.generate(DATA, content, encapsulate, sigProvider);
+        }
+    }
+
+    public CMSSignedData generate(
+        CMSTypedData content)
+        throws CMSException
+    {
+        return generate(content, false);
+    }
+
+    public CMSSignedData generate(
+        // FIXME Avoid accessing more than once to support CMSProcessableInputStream
+        CMSTypedData content,
+        boolean encapsulate)
+        throws CMSException
+    {
+        if (!signerInfs.isEmpty())
+        {
+            throw new IllegalStateException("this method can only be used with SignerInfoGenerator");
+        }
+
+                // TODO
+//        if (signerInfs.isEmpty())
+//        {
+//            /* RFC 3852 5.2
+//             * "In the degenerate case where there are no signers, the
+//             * EncapsulatedContentInfo value being "signed" is irrelevant.  In this
+//             * case, the content type within the EncapsulatedContentInfo value being
+//             * "signed" MUST be id-data (as defined in section 4), and the content
+//             * field of the EncapsulatedContentInfo value MUST be omitted."
+//             */
+//            if (encapsulate)
+//            {
+//                throw new IllegalArgumentException("no signers, encapsulate must be false");
+//            }
+//            if (!DATA.equals(eContentType))
+//            {
+//                throw new IllegalArgumentException("no signers, eContentType must be id-data");
+//            }
+//        }
+//
+//        if (!DATA.equals(eContentType))
+//        {
+//            /* RFC 3852 5.3
+//             * [The 'signedAttrs']...
+//             * field is optional, but it MUST be present if the content type of
+//             * the EncapsulatedContentInfo value being signed is not id-data.
+//             */
+//            // TODO signedAttrs must be present for all signers
+//        }
+
+        ASN1EncodableVector  digestAlgs = new ASN1EncodableVector();
+        ASN1EncodableVector  signerInfos = new ASN1EncodableVector();
+
+        digests.clear();  // clear the current preserved digest state
+
+        //
+        // add the precalculated SignerInfo objects.
+        //
+        for (Iterator it = _signers.iterator(); it.hasNext();)
+        {
+            SignerInformation signer = (SignerInformation)it.next();
+            digestAlgs.add(CMSSignedHelper.INSTANCE.fixAlgID(signer.getDigestAlgorithmID()));
+
+            // TODO Verify the content type and calculated digest match the precalculated SignerInfo
+            signerInfos.add(signer.toASN1Structure());
+        }
+
+        //
+        // add the SignerInfo objects
+        //
+        ASN1ObjectIdentifier contentTypeOID = content.getContentType();
+
+        ASN1OctetString octs = null;
+
+        if (content != null)
+        {
+            ByteArrayOutputStream bOut = null;
+
+            if (encapsulate)
+            {
+                bOut = new ByteArrayOutputStream();
+            }
+
+            OutputStream cOut = CMSUtils.attachSignersToOutputStream(signerGens, bOut);
+
+            // Just in case it's unencapsulated and there are no signers!
+            cOut = CMSUtils.getSafeOutputStream(cOut);
+
+            try
+            {
+                content.write(cOut);
+
+                cOut.close();
+            }
+            catch (IOException e)
+            {
+                throw new CMSException("data processing exception: " + e.getMessage(), e);
+            }
+
+            if (encapsulate)
+            {
+                octs = new BEROctetString(bOut.toByteArray());
+            }
+        }
+
+        for (Iterator it = signerGens.iterator(); it.hasNext();)
+        {
+            SignerInfoGenerator sGen = (SignerInfoGenerator)it.next();
+            SignerInfo inf = sGen.generate(contentTypeOID);
+
+            digestAlgs.add(inf.getDigestAlgorithm());
+            signerInfos.add(inf);
+
+            byte[] calcDigest = sGen.getCalculatedDigest();
+
+            if (calcDigest != null)
+            {
+                digests.put(inf.getDigestAlgorithm().getAlgorithm().getId(), calcDigest);
+            }
+        }
+
+        ASN1Set certificates = null;
+
+        if (certs.size() != 0)
+        {
+            certificates = CMSUtils.createBerSetFromList(certs);
+        }
+
+        ASN1Set certrevlist = null;
+
+        if (crls.size() != 0)
+        {
+            certrevlist = CMSUtils.createBerSetFromList(crls);
+        }
+
+        ContentInfo encInfo = new ContentInfo(contentTypeOID, octs);
+
+        SignedData  sd = new SignedData(
+                                 new DERSet(digestAlgs),
+                                 encInfo,
+                                 certificates,
+                                 certrevlist,
+                                 new DERSet(signerInfos));
+
+        ContentInfo contentInfo = new ContentInfo(
+            CMSObjectIdentifiers.signedData, sd);
+
+        return new CMSSignedData(content, contentInfo);
+    }
+
+    /**
+     * generate a set of one or more SignerInformation objects representing counter signatures on
+     * the passed in SignerInformation object.
+     *
+     * @param signer the signer to be countersigned
+     * @param sigProvider the provider to be used for counter signing.
+     * @return a store containing the signers.
+     * @deprecated use generateCounterSigners(SignerInformation)
+     */
+    public SignerInformationStore generateCounterSigners(SignerInformation signer, Provider sigProvider)
+        throws NoSuchAlgorithmException, CMSException
+    {
+        return this.generate(null, new CMSProcessableByteArray(signer.getSignature()), false, sigProvider).getSignerInfos();
+    }
+
+    /**
+     * generate a set of one or more SignerInformation objects representing counter signatures on
+     * the passed in SignerInformation object.
+     *
+     * @param signer the signer to be countersigned
+     * @param sigProvider the provider to be used for counter signing.
+     * @return a store containing the signers.
+     * @deprecated use generateCounterSigners(SignerInformation)
+     */
+    public SignerInformationStore generateCounterSigners(SignerInformation signer, String sigProvider)
+        throws NoSuchAlgorithmException, NoSuchProviderException, CMSException
+    {
+        return this.generate(null, new CMSProcessableByteArray(signer.getSignature()), false, CMSUtils.getProvider(sigProvider)).getSignerInfos();
+    }
+
+    /**
+     * generate a set of one or more SignerInformation objects representing counter signatures on
+     * the passed in SignerInformation object.
+     *
+     * @param signer the signer to be countersigned
+     * @return a store containing the signers.
+     */
+    public SignerInformationStore generateCounterSigners(SignerInformation signer)
+        throws CMSException
+    {
+        return this.generate(new CMSProcessableByteArray(null, signer.getSignature()), false).getSignerInfos();
+    }
+}
+
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/CMSSignedGenerator.java b/bcpkix/src/main/java/org/bouncycastle/cms/CMSSignedGenerator.java
new file mode 100644
index 0000000..d269345
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/CMSSignedGenerator.java
@@ -0,0 +1,295 @@
+package org.bouncycastle.cms;
+
+import java.io.IOException;
+import java.security.PrivateKey;
+import java.security.SecureRandom;
+import java.security.cert.CertStore;
+import java.security.cert.CertStoreException;
+import java.security.interfaces.DSAPrivateKey;
+import java.security.interfaces.RSAPrivateKey;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Set;
+import org.bouncycastle.asn1.DERObjectIdentifier;
+import org.bouncycastle.asn1.DERSet;
+import org.bouncycastle.asn1.DERTaggedObject;
+import org.bouncycastle.asn1.cms.AttributeTable;
+import org.bouncycastle.asn1.cms.CMSObjectIdentifiers;
+// BEGIN android-removed
+// import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers;
+// END android-removed
+import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
+import org.bouncycastle.asn1.oiw.OIWObjectIdentifiers;
+import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.bouncycastle.asn1.teletrust.TeleTrusTObjectIdentifiers;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.asn1.x509.AttributeCertificate;
+import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
+// BEGIN android-removed
+// import org.bouncycastle.jce.interfaces.GOST3410PrivateKey;
+// END android-removed
+import org.bouncycastle.util.Store;
+import org.bouncycastle.x509.X509AttributeCertificate;
+import org.bouncycastle.x509.X509Store;
+
+public class CMSSignedGenerator
+{
+    /**
+     * Default type for the signed data.
+     */
+    public static final String  DATA = CMSObjectIdentifiers.data.getId();
+    
+    public static final String  DIGEST_SHA1 = OIWObjectIdentifiers.idSHA1.getId();
+    // BEGIN android-removed
+    // public static final String  DIGEST_SHA224 = NISTObjectIdentifiers.id_sha224.getId();
+    // END android-removed
+    public static final String  DIGEST_SHA256 = NISTObjectIdentifiers.id_sha256.getId();
+    public static final String  DIGEST_SHA384 = NISTObjectIdentifiers.id_sha384.getId();
+    public static final String  DIGEST_SHA512 = NISTObjectIdentifiers.id_sha512.getId();
+    public static final String  DIGEST_MD5 = PKCSObjectIdentifiers.md5.getId();
+    // BEGIN android-removed
+    // public static final String  DIGEST_GOST3411 = CryptoProObjectIdentifiers.gostR3411.getId();
+    // public static final String  DIGEST_RIPEMD128 = TeleTrusTObjectIdentifiers.ripemd128.getId();
+    // public static final String  DIGEST_RIPEMD160 = TeleTrusTObjectIdentifiers.ripemd160.getId();
+    // public static final String  DIGEST_RIPEMD256 = TeleTrusTObjectIdentifiers.ripemd256.getId();
+    // END android-removed
+
+    public static final String  ENCRYPTION_RSA = PKCSObjectIdentifiers.rsaEncryption.getId();
+    public static final String  ENCRYPTION_DSA = X9ObjectIdentifiers.id_dsa_with_sha1.getId();
+    public static final String  ENCRYPTION_ECDSA = X9ObjectIdentifiers.ecdsa_with_SHA1.getId();
+    public static final String  ENCRYPTION_RSA_PSS = PKCSObjectIdentifiers.id_RSASSA_PSS.getId();
+    // BEGIN android-removed
+    // public static final String  ENCRYPTION_GOST3410 = CryptoProObjectIdentifiers.gostR3410_94.getId();
+    // public static final String  ENCRYPTION_ECGOST3410 = CryptoProObjectIdentifiers.gostR3410_2001.getId();
+    // END android-removed
+
+    private static final String  ENCRYPTION_ECDSA_WITH_SHA1 = X9ObjectIdentifiers.ecdsa_with_SHA1.getId();
+    // BEGIN android-removed
+    // private static final String  ENCRYPTION_ECDSA_WITH_SHA224 = X9ObjectIdentifiers.ecdsa_with_SHA224.getId();
+    // END android-removed
+    private static final String  ENCRYPTION_ECDSA_WITH_SHA256 = X9ObjectIdentifiers.ecdsa_with_SHA256.getId();
+    private static final String  ENCRYPTION_ECDSA_WITH_SHA384 = X9ObjectIdentifiers.ecdsa_with_SHA384.getId();
+    private static final String  ENCRYPTION_ECDSA_WITH_SHA512 = X9ObjectIdentifiers.ecdsa_with_SHA512.getId();
+
+    private static final Set NO_PARAMS = new HashSet();
+    private static final Map EC_ALGORITHMS = new HashMap();
+
+    static
+    {
+        NO_PARAMS.add(ENCRYPTION_DSA);
+        NO_PARAMS.add(ENCRYPTION_ECDSA);
+        NO_PARAMS.add(ENCRYPTION_ECDSA_WITH_SHA1);
+        // BEGIN android-removed
+        // NO_PARAMS.add(ENCRYPTION_ECDSA_WITH_SHA224);
+        // END android-removed
+        NO_PARAMS.add(ENCRYPTION_ECDSA_WITH_SHA256);
+        NO_PARAMS.add(ENCRYPTION_ECDSA_WITH_SHA384);
+        NO_PARAMS.add(ENCRYPTION_ECDSA_WITH_SHA512);
+
+        EC_ALGORITHMS.put(DIGEST_SHA1, ENCRYPTION_ECDSA_WITH_SHA1);
+        // BEGIN android-removed
+        // EC_ALGORITHMS.put(DIGEST_SHA224, ENCRYPTION_ECDSA_WITH_SHA224);
+        // END android-removed
+        EC_ALGORITHMS.put(DIGEST_SHA256, ENCRYPTION_ECDSA_WITH_SHA256);
+        EC_ALGORITHMS.put(DIGEST_SHA384, ENCRYPTION_ECDSA_WITH_SHA384);
+        EC_ALGORITHMS.put(DIGEST_SHA512, ENCRYPTION_ECDSA_WITH_SHA512);
+    }
+
+    protected List certs = new ArrayList();
+    protected List crls = new ArrayList();
+    protected List _signers = new ArrayList();
+    protected List signerGens = new ArrayList();
+    protected Map digests = new HashMap();
+
+    protected final SecureRandom rand;
+
+    /**
+     * base constructor
+     */
+    protected CMSSignedGenerator()
+    {
+        this(new SecureRandom());
+    }
+
+    /**
+     * constructor allowing specific source of randomness
+     * @param rand instance of SecureRandom to use
+     */
+    protected CMSSignedGenerator(
+        SecureRandom rand)
+    {
+        this.rand = rand;
+    }
+    
+    protected String getEncOID(
+        PrivateKey key,
+        String     digestOID)
+    {
+        String encOID = null;
+        
+        if (key instanceof RSAPrivateKey || "RSA".equalsIgnoreCase(key.getAlgorithm()))
+        {
+            encOID = ENCRYPTION_RSA;
+        }
+        else if (key instanceof DSAPrivateKey || "DSA".equalsIgnoreCase(key.getAlgorithm()))
+        {
+            encOID = ENCRYPTION_DSA;
+            if (!digestOID.equals(DIGEST_SHA1))
+            {
+                throw new IllegalArgumentException("can't mix DSA with anything but SHA1");
+            }
+        }
+        else if ("ECDSA".equalsIgnoreCase(key.getAlgorithm()) || "EC".equalsIgnoreCase(key.getAlgorithm()))
+        {
+            encOID = (String)EC_ALGORITHMS.get(digestOID);
+            if (encOID == null)
+            {
+                throw new IllegalArgumentException("can't mix ECDSA with anything but SHA family digests");
+            }
+        }
+        // BEGIN android-removed
+        // else if (key instanceof GOST3410PrivateKey || "GOST3410".equalsIgnoreCase(key.getAlgorithm()))
+        // {
+        //     encOID = ENCRYPTION_GOST3410;
+        // }
+        // else if ("ECGOST3410".equalsIgnoreCase(key.getAlgorithm()))
+        // {
+        //     encOID = ENCRYPTION_ECGOST3410;
+        // }
+        // END android-removed
+        
+        return encOID;
+    }
+
+    protected Map getBaseParameters(DERObjectIdentifier contentType, AlgorithmIdentifier digAlgId, byte[] hash)
+    {
+        Map param = new HashMap();
+        param.put(CMSAttributeTableGenerator.CONTENT_TYPE, contentType);
+        param.put(CMSAttributeTableGenerator.DIGEST_ALGORITHM_IDENTIFIER, digAlgId);
+        param.put(CMSAttributeTableGenerator.DIGEST,  hash.clone());
+        return param;
+    }
+
+    protected ASN1Set getAttributeSet(
+        AttributeTable attr)
+    {
+        if (attr != null)
+        {
+            return new DERSet(attr.toASN1EncodableVector());
+        }
+        
+        return null;
+    }
+
+    /**
+     * add the certificates and CRLs contained in the given CertStore
+     * to the pool that will be included in the encoded signature block.
+     * <p>
+     * Note: this assumes the CertStore will support null in the get
+     * methods.
+     * @param certStore CertStore containing the public key certificates and CRLs
+     * @throws java.security.cert.CertStoreException  if an issue occurs processing the CertStore
+     * @throws CMSException  if an issue occurse transforming data from the CertStore into the message
+     * @deprecated use addCertificates and addCRLs
+     */
+    public void addCertificatesAndCRLs(
+        CertStore certStore)
+        throws CertStoreException, CMSException
+    {
+        certs.addAll(CMSUtils.getCertificatesFromStore(certStore));
+        crls.addAll(CMSUtils.getCRLsFromStore(certStore));
+    }
+
+    public void addCertificates(
+        Store certStore)
+        throws CMSException
+    {
+        certs.addAll(CMSUtils.getCertificatesFromStore(certStore));
+    }
+
+    public void addCRLs(
+        Store crlStore)
+        throws CMSException
+    {
+        crls.addAll(CMSUtils.getCRLsFromStore(crlStore));
+    }
+
+    public void addAttributeCertificates(
+        Store attrStore)
+        throws CMSException
+    {
+        certs.addAll(CMSUtils.getAttributeCertificatesFromStore(attrStore));
+    }
+
+    /**
+     * Add the attribute certificates contained in the passed in store to the
+     * generator.
+     *
+     * @param store a store of Version 2 attribute certificates
+     * @throws CMSException if an error occurse processing the store.
+     * @deprecated use basic Store method
+     */
+    public void addAttributeCertificates(
+        X509Store store)
+        throws CMSException
+    {
+        try
+        {
+            for (Iterator it = store.getMatches(null).iterator(); it.hasNext();)
+            {
+                X509AttributeCertificate attrCert = (X509AttributeCertificate)it.next();
+
+                certs.add(new DERTaggedObject(false, 2,
+                             AttributeCertificate.getInstance(ASN1Primitive.fromByteArray(attrCert.getEncoded()))));
+            }
+        }
+        catch (IllegalArgumentException e)
+        {
+            throw new CMSException("error processing attribute certs", e);
+        }
+        catch (IOException e)
+        {
+            throw new CMSException("error processing attribute certs", e);
+        }
+    }
+
+
+    /**
+     * Add a store of precalculated signers to the generator.
+     *
+     * @param signerStore store of signers
+     */
+    public void addSigners(
+        SignerInformationStore    signerStore)
+    {
+        Iterator    it = signerStore.getSigners().iterator();
+
+        while (it.hasNext())
+        {
+            _signers.add(it.next());
+        }
+    }
+
+    public void addSignerInfoGenerator(SignerInfoGenerator infoGen)
+    {
+         signerGens.add(infoGen);
+    }
+
+    /**
+     * Return a map of oids and byte arrays representing the digests calculated on the content during
+     * the last generate.
+     *
+     * @return a map of oids (as String objects) and byte[] representing digests.
+     */
+    public Map getGeneratedDigests()
+    {
+        return new HashMap(digests);
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/CMSSignedHelper.java b/bcpkix/src/main/java/org/bouncycastle/cms/CMSSignedHelper.java
new file mode 100644
index 0000000..192704f
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/CMSSignedHelper.java
@@ -0,0 +1,423 @@
+package org.bouncycastle.cms;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.NoSuchAlgorithmException;
+import java.security.Provider;
+import java.security.cert.CRLException;
+import java.security.cert.CertStore;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.CollectionCertStoreParameters;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1Set;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERNull;
+import org.bouncycastle.asn1.DERObjectIdentifier;
+// BEGIN android-removed
+// import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers;
+// END android-removed
+import org.bouncycastle.asn1.eac.EACObjectIdentifiers;
+import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
+import org.bouncycastle.asn1.oiw.OIWObjectIdentifiers;
+import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.bouncycastle.asn1.teletrust.TeleTrusTObjectIdentifiers;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.asn1.x509.X509ObjectIdentifiers;
+import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
+import org.bouncycastle.x509.NoSuchStoreException;
+import org.bouncycastle.x509.X509CollectionStoreParameters;
+import org.bouncycastle.x509.X509Store;
+import org.bouncycastle.x509.X509V2AttributeCertificate;
+
+class CMSSignedHelper
+{
+    static final CMSSignedHelper INSTANCE = new CMSSignedHelper();
+
+    private static final Map     encryptionAlgs = new HashMap();
+    private static final Map     digestAlgs = new HashMap();
+    private static final Map     digestAliases = new HashMap();
+
+    private static void addEntries(DERObjectIdentifier alias, String digest, String encryption)
+    {
+        digestAlgs.put(alias.getId(), digest);
+        encryptionAlgs.put(alias.getId(), encryption);
+    }
+
+    static
+    {
+        // BEGIN android-removed
+        // addEntries(NISTObjectIdentifiers.dsa_with_sha224, "SHA224", "DSA");
+        // END android-removed
+        addEntries(NISTObjectIdentifiers.dsa_with_sha256, "SHA256", "DSA");
+        addEntries(NISTObjectIdentifiers.dsa_with_sha384, "SHA384", "DSA");
+        addEntries(NISTObjectIdentifiers.dsa_with_sha512, "SHA512", "DSA");
+        addEntries(OIWObjectIdentifiers.dsaWithSHA1, "SHA1", "DSA");
+        // BEGIN android-removed
+        // addEntries(OIWObjectIdentifiers.md4WithRSA, "MD4", "RSA");
+        // addEntries(OIWObjectIdentifiers.md4WithRSAEncryption, "MD4", "RSA");
+        // END android-removed
+        addEntries(OIWObjectIdentifiers.md5WithRSA, "MD5", "RSA");
+        addEntries(OIWObjectIdentifiers.sha1WithRSA, "SHA1", "RSA");
+        // BEGIN android-removed
+        // addEntries(PKCSObjectIdentifiers.md2WithRSAEncryption, "MD2", "RSA");
+        // addEntries(PKCSObjectIdentifiers.md4WithRSAEncryption, "MD4", "RSA");
+        // END android-removed
+        addEntries(PKCSObjectIdentifiers.md5WithRSAEncryption, "MD5", "RSA");
+        addEntries(PKCSObjectIdentifiers.sha1WithRSAEncryption, "SHA1", "RSA");
+        // BEGIN android-removed
+        // addEntries(PKCSObjectIdentifiers.sha224WithRSAEncryption, "SHA224", "RSA");
+        // END android-removed
+        addEntries(PKCSObjectIdentifiers.sha256WithRSAEncryption, "SHA256", "RSA");
+        addEntries(PKCSObjectIdentifiers.sha384WithRSAEncryption, "SHA384", "RSA");
+        addEntries(PKCSObjectIdentifiers.sha512WithRSAEncryption, "SHA512", "RSA");
+        addEntries(X9ObjectIdentifiers.ecdsa_with_SHA1, "SHA1", "ECDSA");
+        // BEGIN android-removed
+        // addEntries(X9ObjectIdentifiers.ecdsa_with_SHA224, "SHA224", "ECDSA");
+        // END android-removed
+        addEntries(X9ObjectIdentifiers.ecdsa_with_SHA256, "SHA256", "ECDSA");
+        addEntries(X9ObjectIdentifiers.ecdsa_with_SHA384, "SHA384", "ECDSA");
+        addEntries(X9ObjectIdentifiers.ecdsa_with_SHA512, "SHA512", "ECDSA");
+        addEntries(X9ObjectIdentifiers.id_dsa_with_sha1, "SHA1", "DSA");
+        addEntries(EACObjectIdentifiers.id_TA_ECDSA_SHA_1, "SHA1", "ECDSA");
+        // BEGIN android-removed
+        // addEntries(EACObjectIdentifiers.id_TA_ECDSA_SHA_224, "SHA224", "ECDSA");
+        // END android-removed
+        addEntries(EACObjectIdentifiers.id_TA_ECDSA_SHA_256, "SHA256", "ECDSA");
+        addEntries(EACObjectIdentifiers.id_TA_ECDSA_SHA_384, "SHA384", "ECDSA");
+        addEntries(EACObjectIdentifiers.id_TA_ECDSA_SHA_512, "SHA512", "ECDSA");
+        addEntries(EACObjectIdentifiers.id_TA_RSA_v1_5_SHA_1, "SHA1", "RSA");
+        addEntries(EACObjectIdentifiers.id_TA_RSA_v1_5_SHA_256, "SHA256", "RSA");
+        addEntries(EACObjectIdentifiers.id_TA_RSA_PSS_SHA_1, "SHA1", "RSAandMGF1");
+        addEntries(EACObjectIdentifiers.id_TA_RSA_PSS_SHA_256, "SHA256", "RSAandMGF1");
+
+        encryptionAlgs.put(X9ObjectIdentifiers.id_dsa.getId(), "DSA");
+        encryptionAlgs.put(PKCSObjectIdentifiers.rsaEncryption.getId(), "RSA");
+        encryptionAlgs.put(TeleTrusTObjectIdentifiers.teleTrusTRSAsignatureAlgorithm, "RSA");
+        encryptionAlgs.put(X509ObjectIdentifiers.id_ea_rsa.getId(), "RSA");
+        // BEGIN android-removed
+        // encryptionAlgs.put(CMSSignedDataGenerator.ENCRYPTION_RSA_PSS, "RSAandMGF1");
+        // encryptionAlgs.put(CryptoProObjectIdentifiers.gostR3410_94.getId(), "GOST3410");
+        // encryptionAlgs.put(CryptoProObjectIdentifiers.gostR3410_2001.getId(), "ECGOST3410");
+        // encryptionAlgs.put("1.3.6.1.4.1.5849.1.6.2", "ECGOST3410");
+        // encryptionAlgs.put("1.3.6.1.4.1.5849.1.1.5", "GOST3410");
+        // encryptionAlgs.put(CryptoProObjectIdentifiers.gostR3411_94_with_gostR3410_2001.getId(), "ECGOST3410");
+        // encryptionAlgs.put(CryptoProObjectIdentifiers.gostR3411_94_with_gostR3410_94.getId(), "GOST3410");
+        //
+        // digestAlgs.put(PKCSObjectIdentifiers.md2.getId(), "MD2");
+        // digestAlgs.put(PKCSObjectIdentifiers.md4.getId(), "MD4");
+        // END android-removed
+        digestAlgs.put(PKCSObjectIdentifiers.md5.getId(), "MD5");
+        digestAlgs.put(OIWObjectIdentifiers.idSHA1.getId(), "SHA1");
+        // BEGIN android-removed
+        // digestAlgs.put(NISTObjectIdentifiers.id_sha224.getId(), "SHA224");
+        // END android-removed
+        digestAlgs.put(NISTObjectIdentifiers.id_sha256.getId(), "SHA256");
+        digestAlgs.put(NISTObjectIdentifiers.id_sha384.getId(), "SHA384");
+        digestAlgs.put(NISTObjectIdentifiers.id_sha512.getId(), "SHA512");
+        // BEGIN android-removed
+        // digestAlgs.put(TeleTrusTObjectIdentifiers.ripemd128.getId(), "RIPEMD128");
+        // digestAlgs.put(TeleTrusTObjectIdentifiers.ripemd160.getId(), "RIPEMD160");
+        // digestAlgs.put(TeleTrusTObjectIdentifiers.ripemd256.getId(), "RIPEMD256");
+        // digestAlgs.put(CryptoProObjectIdentifiers.gostR3411.getId(),  "GOST3411");
+        // digestAlgs.put("1.3.6.1.4.1.5849.1.2.1",  "GOST3411");
+        // END android-removed
+
+        digestAliases.put("SHA1", new String[] { "SHA-1" });
+        // BEGIN android-removed
+        // digestAliases.put("SHA224", new String[] { "SHA-224" });
+        // END android-removed
+        digestAliases.put("SHA256", new String[] { "SHA-256" });
+        digestAliases.put("SHA384", new String[] { "SHA-384" });
+        digestAliases.put("SHA512", new String[] { "SHA-512" });
+    }
+    
+    /**
+     * Return the digest algorithm using one of the standard JCA string
+     * representations rather than the algorithm identifier (if possible).
+     */
+    String getDigestAlgName(
+        String digestAlgOID)
+    {
+        String algName = (String)digestAlgs.get(digestAlgOID);
+
+        if (algName != null)
+        {
+            return algName;
+        }
+
+        return digestAlgOID;
+    }
+
+    /**
+     * Return the digest encryption algorithm using one of the standard
+     * JCA string representations rather the the algorithm identifier (if
+     * possible).
+     */
+    String getEncryptionAlgName(
+        String encryptionAlgOID)
+    {
+        String algName = (String)encryptionAlgs.get(encryptionAlgOID);
+
+        if (algName != null)
+        {
+            return algName;
+        }
+
+        return encryptionAlgOID;
+    }
+
+
+    X509Store createAttributeStore(
+        String type,
+        Provider provider,
+        ASN1Set certSet)
+        throws NoSuchStoreException, CMSException
+    {
+        List certs = new ArrayList();
+
+        if (certSet != null)
+        {
+            Enumeration e = certSet.getObjects();
+
+            while (e.hasMoreElements())
+            {
+                try
+                {
+                    ASN1Primitive obj = ((ASN1Encodable)e.nextElement()).toASN1Primitive();
+
+                    if (obj instanceof ASN1TaggedObject)
+                    {
+                        ASN1TaggedObject tagged = (ASN1TaggedObject)obj;
+
+                        if (tagged.getTagNo() == 2)
+                        {
+                            certs.add(new X509V2AttributeCertificate(ASN1Sequence.getInstance(tagged, false).getEncoded()));
+                        }
+                    }
+                }
+                catch (IOException ex)
+                {
+                    throw new CMSException(
+                            "can't re-encode attribute certificate!", ex);
+                }
+            }
+        }
+
+        try
+        {
+            return X509Store.getInstance(
+                         "AttributeCertificate/" +type, new X509CollectionStoreParameters(certs), provider);
+        }
+        catch (IllegalArgumentException e)
+        {
+            throw new CMSException("can't setup the X509Store", e);
+        }
+    }
+
+    X509Store createCertificateStore(
+        String type,
+        Provider provider,
+        ASN1Set certSet)
+        throws NoSuchStoreException, CMSException
+    {
+        List certs = new ArrayList();
+
+        if (certSet != null)
+        {
+            addCertsFromSet(certs, certSet, provider);
+        }
+
+        try
+        {
+            return X509Store.getInstance(
+                         "Certificate/" +type, new X509CollectionStoreParameters(certs), provider);
+        }
+        catch (IllegalArgumentException e)
+        {
+            throw new CMSException("can't setup the X509Store", e);
+        }
+    }
+
+    X509Store createCRLsStore(
+        String type,
+        Provider provider,
+        ASN1Set crlSet)
+        throws NoSuchStoreException, CMSException
+    {
+        List crls = new ArrayList();
+
+        if (crlSet != null)
+        {
+            addCRLsFromSet(crls, crlSet, provider);
+        }
+
+        try
+        {
+            return X509Store.getInstance(
+                         "CRL/" +type, new X509CollectionStoreParameters(crls), provider);
+        }
+        catch (IllegalArgumentException e)
+        {
+            throw new CMSException("can't setup the X509Store", e);
+        }
+    }
+
+    CertStore createCertStore(
+        String type,
+        Provider provider,
+        ASN1Set certSet,
+        ASN1Set crlSet)
+        throws CMSException, NoSuchAlgorithmException
+    {
+        List certsAndcrls = new ArrayList();
+
+        //
+        // load the certificates and revocation lists if we have any
+        //
+
+        if (certSet != null)
+        {
+            addCertsFromSet(certsAndcrls, certSet, provider);
+        }
+
+        if (crlSet != null)
+        {
+            addCRLsFromSet(certsAndcrls, crlSet, provider);
+        }
+
+        try
+        {
+            if (provider != null)
+            {
+                return CertStore.getInstance(type, new CollectionCertStoreParameters(certsAndcrls), provider);
+            }
+            else
+            {
+                return CertStore.getInstance(type, new CollectionCertStoreParameters(certsAndcrls));
+            }
+        }
+        catch (InvalidAlgorithmParameterException e)
+        {
+            throw new CMSException("can't setup the CertStore", e);
+        }
+    }
+
+    private void addCertsFromSet(List certs, ASN1Set certSet, Provider provider)
+        throws CMSException
+    {
+        CertificateFactory cf;
+
+        try
+        {
+            if (provider != null)
+            {
+                cf = CertificateFactory.getInstance("X.509", provider);
+            }
+            else
+            {
+                cf = CertificateFactory.getInstance("X.509");
+            }
+        }
+        catch (CertificateException ex)
+        {
+            throw new CMSException("can't get certificate factory.", ex);
+        }
+        Enumeration e = certSet.getObjects();
+
+        while (e.hasMoreElements())
+        {
+            try
+            {
+                ASN1Primitive obj = ((ASN1Encodable)e.nextElement()).toASN1Primitive();
+
+                if (obj instanceof ASN1Sequence)
+                {
+                    certs.add(cf.generateCertificate(
+                        new ByteArrayInputStream(obj.getEncoded())));
+                }
+            }
+            catch (IOException ex)
+            {
+                throw new CMSException(
+                        "can't re-encode certificate!", ex);
+            }
+            catch (CertificateException ex)
+            {
+                throw new CMSException(
+                        "can't re-encode certificate!", ex);
+            }
+        }
+    }
+
+    private void addCRLsFromSet(List crls, ASN1Set certSet, Provider provider)
+        throws CMSException
+    {
+        CertificateFactory cf;
+
+        try
+        {
+            if (provider != null)
+            {
+                cf = CertificateFactory.getInstance("X.509", provider);
+            }
+            else
+            {
+                cf = CertificateFactory.getInstance("X.509");
+            }
+        }
+        catch (CertificateException ex)
+        {
+            throw new CMSException("can't get certificate factory.", ex);
+        }
+        Enumeration e = certSet.getObjects();
+
+        while (e.hasMoreElements())
+        {
+            try
+            {
+                ASN1Primitive obj = ((ASN1Encodable)e.nextElement()).toASN1Primitive();
+
+                crls.add(cf.generateCRL(
+                    new ByteArrayInputStream(obj.getEncoded())));
+            }
+            catch (IOException ex)
+            {
+                throw new CMSException("can't re-encode CRL!", ex);
+            }
+            catch (CRLException ex)
+            {
+                throw new CMSException("can't re-encode CRL!", ex);
+            }
+        }
+    }
+
+    AlgorithmIdentifier fixAlgID(AlgorithmIdentifier algId)
+    {
+        if (algId.getParameters() == null)
+        {
+            return new AlgorithmIdentifier(algId.getObjectId(), DERNull.INSTANCE);
+        }
+
+        return algId;
+    }
+
+    void setSigningEncryptionAlgorithmMapping(DERObjectIdentifier oid, String algorithmName)
+    {
+        encryptionAlgs.put(oid.getId(), algorithmName);
+    }
+
+    void setSigningDigestAlgorithmMapping(DERObjectIdentifier oid, String algorithmName)
+    {
+        digestAlgs.put(oid.getId(), algorithmName);
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/CMSSignerDigestMismatchException.java b/bcpkix/src/main/java/org/bouncycastle/cms/CMSSignerDigestMismatchException.java
new file mode 100644
index 0000000..0db54bc
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/CMSSignerDigestMismatchException.java
@@ -0,0 +1,11 @@
+package org.bouncycastle.cms;
+
+public class CMSSignerDigestMismatchException
+    extends CMSException
+{
+    public CMSSignerDigestMismatchException(
+        String msg)
+    {
+        super(msg);
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/CMSTypedData.java b/bcpkix/src/main/java/org/bouncycastle/cms/CMSTypedData.java
new file mode 100644
index 0000000..f7f0a9c
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/CMSTypedData.java
@@ -0,0 +1,9 @@
+package org.bouncycastle.cms;
+
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+
+public interface CMSTypedData
+    extends CMSProcessable
+{
+    ASN1ObjectIdentifier getContentType();
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/CMSUtils.java b/bcpkix/src/main/java/org/bouncycastle/cms/CMSUtils.java
new file mode 100644
index 0000000..75c6beb
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/CMSUtils.java
@@ -0,0 +1,337 @@
+package org.bouncycastle.cms;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.security.NoSuchProviderException;
+import java.security.Provider;
+import java.security.Security;
+import java.security.cert.CRLException;
+import java.security.cert.CertStore;
+import java.security.cert.CertStoreException;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.X509CRL;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1InputStream;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Set;
+import org.bouncycastle.asn1.BEROctetStringGenerator;
+import org.bouncycastle.asn1.BERSet;
+import org.bouncycastle.asn1.DERSet;
+import org.bouncycastle.asn1.DERTaggedObject;
+import org.bouncycastle.asn1.cms.ContentInfo;
+import org.bouncycastle.asn1.cms.IssuerAndSerialNumber;
+import org.bouncycastle.asn1.x509.CertificateList;
+import org.bouncycastle.asn1.x509.TBSCertificateStructure;
+import org.bouncycastle.asn1.x509.X509CertificateStructure;
+import org.bouncycastle.cert.X509AttributeCertificateHolder;
+import org.bouncycastle.cert.X509CRLHolder;
+import org.bouncycastle.cert.X509CertificateHolder;
+import org.bouncycastle.operator.DigestCalculator;
+import org.bouncycastle.util.Store;
+import org.bouncycastle.util.io.Streams;
+import org.bouncycastle.util.io.TeeInputStream;
+import org.bouncycastle.util.io.TeeOutputStream;
+
+class CMSUtils
+{
+    static ContentInfo readContentInfo(
+        byte[] input)
+        throws CMSException
+    {
+        // enforce limit checking as from a byte array
+        return readContentInfo(new ASN1InputStream(input));
+    }
+
+    static ContentInfo readContentInfo(
+        InputStream input)
+        throws CMSException
+    {
+        // enforce some limit checking
+        return readContentInfo(new ASN1InputStream(input));
+    } 
+
+    static List getCertificatesFromStore(CertStore certStore)
+        throws CertStoreException, CMSException
+    {
+        List certs = new ArrayList();
+
+        try
+        {
+            for (Iterator it = certStore.getCertificates(null).iterator(); it.hasNext();)
+            {
+                X509Certificate c = (X509Certificate)it.next();
+
+                certs.add(X509CertificateStructure.getInstance(
+                                                       ASN1Primitive.fromByteArray(c.getEncoded())));
+            }
+
+            return certs;
+        }
+        catch (IllegalArgumentException e)
+        {
+            throw new CMSException("error processing certs", e);
+        }
+        catch (IOException e)
+        {
+            throw new CMSException("error processing certs", e);
+        }
+        catch (CertificateEncodingException e)
+        {
+            throw new CMSException("error encoding certs", e);
+        }
+    }
+
+    static List getCertificatesFromStore(Store certStore)
+        throws CMSException
+    {
+        List certs = new ArrayList();
+
+        try
+        {
+            for (Iterator it = certStore.getMatches(null).iterator(); it.hasNext();)
+            {
+                X509CertificateHolder c = (X509CertificateHolder)it.next();
+
+                certs.add(c.toASN1Structure());
+            }
+
+            return certs;
+        }
+        catch (ClassCastException e)
+        {
+            throw new CMSException("error processing certs", e);
+        }
+    }
+
+    static List getAttributeCertificatesFromStore(Store attrStore)
+        throws CMSException
+    {
+        List certs = new ArrayList();
+
+        try
+        {
+            for (Iterator it = attrStore.getMatches(null).iterator(); it.hasNext();)
+            {
+                X509AttributeCertificateHolder attrCert = (X509AttributeCertificateHolder)it.next();
+
+                certs.add(new DERTaggedObject(false, 2, attrCert.toASN1Structure()));
+            }
+
+            return certs;
+        }
+        catch (ClassCastException e)
+        {
+            throw new CMSException("error processing certs", e);
+        }
+    }
+
+    static List getCRLsFromStore(CertStore certStore)
+        throws CertStoreException, CMSException
+    {
+        List crls = new ArrayList();
+
+        try
+        {
+            for (Iterator it = certStore.getCRLs(null).iterator(); it.hasNext();)
+            {
+                X509CRL c = (X509CRL)it.next();
+
+                crls.add(CertificateList.getInstance(ASN1Primitive.fromByteArray(c.getEncoded())));
+            }
+
+            return crls;
+        }
+        catch (IllegalArgumentException e)
+        {
+            throw new CMSException("error processing crls", e);
+        }
+        catch (IOException e)
+        {
+            throw new CMSException("error processing crls", e);
+        }
+        catch (CRLException e)
+        {
+            throw new CMSException("error encoding crls", e);
+        }
+    }
+
+    static List getCRLsFromStore(Store crlStore)
+        throws CMSException
+    {
+        List certs = new ArrayList();
+
+        try
+        {
+            for (Iterator it = crlStore.getMatches(null).iterator(); it.hasNext();)
+            {
+                X509CRLHolder c = (X509CRLHolder)it.next();
+
+                certs.add(c.toASN1Structure());
+            }
+
+            return certs;
+        }
+        catch (ClassCastException e)
+        {
+            throw new CMSException("error processing certs", e);
+        }
+    }
+
+    static ASN1Set createBerSetFromList(List derObjects)
+    {
+        ASN1EncodableVector v = new ASN1EncodableVector();
+
+        for (Iterator it = derObjects.iterator(); it.hasNext();)
+        {
+            v.add((ASN1Encodable)it.next());
+        }
+
+        return new BERSet(v);
+    }
+
+    static ASN1Set createDerSetFromList(List derObjects)
+    {
+        ASN1EncodableVector v = new ASN1EncodableVector();
+
+        for (Iterator it = derObjects.iterator(); it.hasNext();)
+        {
+            v.add((ASN1Encodable)it.next());
+        }
+
+        return new DERSet(v);
+    }
+
+    static OutputStream createBEROctetOutputStream(OutputStream s,
+            int tagNo, boolean isExplicit, int bufferSize) throws IOException
+    {
+        BEROctetStringGenerator octGen = new BEROctetStringGenerator(s, tagNo, isExplicit);
+
+        if (bufferSize != 0)
+        {
+            return octGen.getOctetOutputStream(new byte[bufferSize]);
+        }
+
+        return octGen.getOctetOutputStream();
+    }
+
+    static TBSCertificateStructure getTBSCertificateStructure(
+        X509Certificate cert)
+    {
+        try
+        {
+            return TBSCertificateStructure.getInstance(
+                ASN1Primitive.fromByteArray(cert.getTBSCertificate()));
+        }
+        catch (Exception e)
+        {
+            throw new IllegalArgumentException(
+                "can't extract TBS structure from this cert");
+        }
+    }
+
+    static IssuerAndSerialNumber getIssuerAndSerialNumber(X509Certificate cert)
+    {
+        TBSCertificateStructure tbsCert = getTBSCertificateStructure(cert);
+        return new IssuerAndSerialNumber(tbsCert.getIssuer(), tbsCert.getSerialNumber().getValue());
+    }
+
+    private static ContentInfo readContentInfo(
+        ASN1InputStream in)
+        throws CMSException
+    {
+        try
+        {
+            return ContentInfo.getInstance(in.readObject());
+        }
+        catch (IOException e)
+        {
+            throw new CMSException("IOException reading content.", e);
+        }
+        catch (ClassCastException e)
+        {
+            throw new CMSException("Malformed content.", e);
+        }
+        catch (IllegalArgumentException e)
+        {
+            throw new CMSException("Malformed content.", e);
+        }
+    }
+    
+    public static byte[] streamToByteArray(
+        InputStream in) 
+        throws IOException
+    {
+        return Streams.readAll(in);
+    }
+
+    public static byte[] streamToByteArray(
+        InputStream in,
+        int         limit)
+        throws IOException
+    {
+        return Streams.readAllLimited(in, limit);
+    }
+
+    public static Provider getProvider(String providerName)
+        throws NoSuchProviderException
+    {
+        if (providerName != null)
+        {
+            Provider prov = Security.getProvider(providerName);
+
+            if (prov != null)
+            {
+                return prov;
+            }
+
+            throw new NoSuchProviderException("provider " + providerName + " not found.");
+        }
+
+        return null; 
+    }
+
+    static InputStream attachDigestsToInputStream(Collection digests, InputStream s)
+    {
+        InputStream result = s;
+        Iterator it = digests.iterator();
+        while (it.hasNext())
+        {
+            DigestCalculator digest = (DigestCalculator)it.next();
+            result = new TeeInputStream(result, digest.getOutputStream());
+        }
+        return result;
+    }
+
+    static OutputStream attachSignersToOutputStream(Collection signers, OutputStream s)
+    {
+        OutputStream result = s;
+        Iterator it = signers.iterator();
+        while (it.hasNext())
+        {
+            SignerInfoGenerator signerGen = (SignerInfoGenerator)it.next();
+            result = getSafeTeeOutputStream(result, signerGen.getCalculatingOutputStream());
+        }
+        return result;
+    }
+
+    static OutputStream getSafeOutputStream(OutputStream s)
+    {
+        return s == null ? new NullOutputStream() : s;
+    }
+
+    static OutputStream getSafeTeeOutputStream(OutputStream s1,
+            OutputStream s2)
+    {
+        return s1 == null ? getSafeOutputStream(s2)
+                : s2 == null ? getSafeOutputStream(s1) : new TeeOutputStream(
+                        s1, s2);
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/CMSVerifierCertificateNotValidException.java b/bcpkix/src/main/java/org/bouncycastle/cms/CMSVerifierCertificateNotValidException.java
new file mode 100644
index 0000000..6bd8c0a
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/CMSVerifierCertificateNotValidException.java
@@ -0,0 +1,11 @@
+package org.bouncycastle.cms;
+
+public class CMSVerifierCertificateNotValidException
+    extends CMSException
+{
+    public CMSVerifierCertificateNotValidException(
+        String msg)
+    {
+        super(msg);
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/DefaultCMSSignatureAlgorithmNameGenerator.java b/bcpkix/src/main/java/org/bouncycastle/cms/DefaultCMSSignatureAlgorithmNameGenerator.java
new file mode 100644
index 0000000..bb20ee6
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/DefaultCMSSignatureAlgorithmNameGenerator.java
@@ -0,0 +1,174 @@
+package org.bouncycastle.cms;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+// BEGIN android-removed
+// import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers;
+// END android-removed
+import org.bouncycastle.asn1.eac.EACObjectIdentifiers;
+import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
+import org.bouncycastle.asn1.oiw.OIWObjectIdentifiers;
+import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.bouncycastle.asn1.teletrust.TeleTrusTObjectIdentifiers;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.asn1.x509.X509ObjectIdentifiers;
+import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
+
+public class DefaultCMSSignatureAlgorithmNameGenerator
+    implements CMSSignatureAlgorithmNameGenerator
+{
+    private final Map encryptionAlgs = new HashMap();
+    private final Map     digestAlgs = new HashMap();
+
+    private void addEntries(ASN1ObjectIdentifier alias, String digest, String encryption)
+    {
+        digestAlgs.put(alias, digest);
+        encryptionAlgs.put(alias, encryption);
+    }
+
+    public DefaultCMSSignatureAlgorithmNameGenerator()
+    {
+        // BEGIN android-removed
+        // addEntries(NISTObjectIdentifiers.dsa_with_sha224, "SHA224", "DSA");
+        // END android-removed
+        addEntries(NISTObjectIdentifiers.dsa_with_sha256, "SHA256", "DSA");
+        addEntries(NISTObjectIdentifiers.dsa_with_sha384, "SHA384", "DSA");
+        addEntries(NISTObjectIdentifiers.dsa_with_sha512, "SHA512", "DSA");
+        addEntries(OIWObjectIdentifiers.dsaWithSHA1, "SHA1", "DSA");
+        // BEGIN android-removed
+        // addEntries(OIWObjectIdentifiers.md4WithRSA, "MD4", "RSA");
+        // addEntries(OIWObjectIdentifiers.md4WithRSAEncryption, "MD4", "RSA");
+        // END android-removed
+        addEntries(OIWObjectIdentifiers.md5WithRSA, "MD5", "RSA");
+        addEntries(OIWObjectIdentifiers.sha1WithRSA, "SHA1", "RSA");
+        // BEGIN android-removed
+        // addEntries(PKCSObjectIdentifiers.md2WithRSAEncryption, "MD2", "RSA");
+        // addEntries(PKCSObjectIdentifiers.md4WithRSAEncryption, "MD4", "RSA");
+        // END android-removed
+        addEntries(PKCSObjectIdentifiers.md5WithRSAEncryption, "MD5", "RSA");
+        addEntries(PKCSObjectIdentifiers.sha1WithRSAEncryption, "SHA1", "RSA");
+        // BEGIN android-removed
+        // addEntries(PKCSObjectIdentifiers.sha224WithRSAEncryption, "SHA224", "RSA");
+        // END android-removed
+        addEntries(PKCSObjectIdentifiers.sha256WithRSAEncryption, "SHA256", "RSA");
+        addEntries(PKCSObjectIdentifiers.sha384WithRSAEncryption, "SHA384", "RSA");
+        addEntries(PKCSObjectIdentifiers.sha512WithRSAEncryption, "SHA512", "RSA");
+        addEntries(X9ObjectIdentifiers.ecdsa_with_SHA1, "SHA1", "ECDSA");
+        // BEGIN android-removed
+        // addEntries(X9ObjectIdentifiers.ecdsa_with_SHA224, "SHA224", "ECDSA");
+        // END android-removed
+        addEntries(X9ObjectIdentifiers.ecdsa_with_SHA256, "SHA256", "ECDSA");
+        addEntries(X9ObjectIdentifiers.ecdsa_with_SHA384, "SHA384", "ECDSA");
+        addEntries(X9ObjectIdentifiers.ecdsa_with_SHA512, "SHA512", "ECDSA");
+        addEntries(X9ObjectIdentifiers.id_dsa_with_sha1, "SHA1", "DSA");
+        addEntries(EACObjectIdentifiers.id_TA_ECDSA_SHA_1, "SHA1", "ECDSA");
+        // BEGIN android-removed
+        // addEntries(EACObjectIdentifiers.id_TA_ECDSA_SHA_224, "SHA224", "ECDSA");
+        // END android-removed
+        addEntries(EACObjectIdentifiers.id_TA_ECDSA_SHA_256, "SHA256", "ECDSA");
+        addEntries(EACObjectIdentifiers.id_TA_ECDSA_SHA_384, "SHA384", "ECDSA");
+        addEntries(EACObjectIdentifiers.id_TA_ECDSA_SHA_512, "SHA512", "ECDSA");
+        addEntries(EACObjectIdentifiers.id_TA_RSA_v1_5_SHA_1, "SHA1", "RSA");
+        addEntries(EACObjectIdentifiers.id_TA_RSA_v1_5_SHA_256, "SHA256", "RSA");
+        addEntries(EACObjectIdentifiers.id_TA_RSA_PSS_SHA_1, "SHA1", "RSAandMGF1");
+        addEntries(EACObjectIdentifiers.id_TA_RSA_PSS_SHA_256, "SHA256", "RSAandMGF1");
+
+        encryptionAlgs.put(X9ObjectIdentifiers.id_dsa, "DSA");
+        encryptionAlgs.put(PKCSObjectIdentifiers.rsaEncryption, "RSA");
+        encryptionAlgs.put(TeleTrusTObjectIdentifiers.teleTrusTRSAsignatureAlgorithm, "RSA");
+        encryptionAlgs.put(X509ObjectIdentifiers.id_ea_rsa, "RSA");
+        encryptionAlgs.put(PKCSObjectIdentifiers.id_RSASSA_PSS, "RSAandMGF1");
+        // BEGIN android-removed
+        // encryptionAlgs.put(CryptoProObjectIdentifiers.gostR3410_94, "GOST3410");
+        // encryptionAlgs.put(CryptoProObjectIdentifiers.gostR3410_2001, "ECGOST3410");
+        // encryptionAlgs.put(new ASN1ObjectIdentifier("1.3.6.1.4.1.5849.1.6.2"), "ECGOST3410");
+        // encryptionAlgs.put(new ASN1ObjectIdentifier("1.3.6.1.4.1.5849.1.1.5"), "GOST3410");
+        // encryptionAlgs.put(CryptoProObjectIdentifiers.gostR3411_94_with_gostR3410_2001, "ECGOST3410");
+        // encryptionAlgs.put(CryptoProObjectIdentifiers.gostR3411_94_with_gostR3410_94, "GOST3410");
+        //
+        // digestAlgs.put(PKCSObjectIdentifiers.md2, "MD2");
+        // digestAlgs.put(PKCSObjectIdentifiers.md4, "MD4");
+        // END android-removed
+        digestAlgs.put(PKCSObjectIdentifiers.md5, "MD5");
+        digestAlgs.put(OIWObjectIdentifiers.idSHA1, "SHA1");
+        // BEGIN android-removed
+        // digestAlgs.put(NISTObjectIdentifiers.id_sha224, "SHA224");
+        // END android-removed
+        digestAlgs.put(NISTObjectIdentifiers.id_sha256, "SHA256");
+        digestAlgs.put(NISTObjectIdentifiers.id_sha384, "SHA384");
+        digestAlgs.put(NISTObjectIdentifiers.id_sha512, "SHA512");
+        // BEGIN android-removed
+        // digestAlgs.put(TeleTrusTObjectIdentifiers.ripemd128, "RIPEMD128");
+        // digestAlgs.put(TeleTrusTObjectIdentifiers.ripemd160, "RIPEMD160");
+        // digestAlgs.put(TeleTrusTObjectIdentifiers.ripemd256, "RIPEMD256");
+        // digestAlgs.put(CryptoProObjectIdentifiers.gostR3411,  "GOST3411");
+        // digestAlgs.put(new ASN1ObjectIdentifier("1.3.6.1.4.1.5849.1.2.1"),  "GOST3411");
+        // END android-removed
+    }
+
+    /**
+     * Return the digest algorithm using one of the standard JCA string
+     * representations rather than the algorithm identifier (if possible).
+     */
+    private String getDigestAlgName(
+        ASN1ObjectIdentifier digestAlgOID)
+    {
+        String algName = (String)digestAlgs.get(digestAlgOID);
+
+        if (algName != null)
+        {
+            return algName;
+        }
+
+        return digestAlgOID.getId();
+    }
+
+    /**
+     * Return the digest encryption algorithm using one of the standard
+     * JCA string representations rather the the algorithm identifier (if
+     * possible).
+     */
+    private String getEncryptionAlgName(
+        ASN1ObjectIdentifier encryptionAlgOID)
+    {
+        String algName = (String)encryptionAlgs.get(encryptionAlgOID);
+
+        if (algName != null)
+        {
+            return algName;
+        }
+
+        return encryptionAlgOID.getId();
+    }
+
+    /**
+     * Set the mapping for the encryption algorithm used in association with a SignedData generation
+     * or interpretation.
+     *
+     * @param oid object identifier to map.
+     * @param algorithmName algorithm name to use.
+     */
+    protected void setSigningEncryptionAlgorithmMapping(ASN1ObjectIdentifier oid, String algorithmName)
+    {
+        encryptionAlgs.put(oid, algorithmName);
+    }
+
+    /**
+     * Set the mapping for the digest algorithm to use in conjunction with a SignedData generation
+     * or interpretation.
+     *
+     * @param oid object identifier to map.
+     * @param algorithmName algorithm name to use.
+     */
+    protected void setSigningDigestAlgorithmMapping(ASN1ObjectIdentifier oid, String algorithmName)
+    {
+        digestAlgs.put(oid, algorithmName);
+    }
+
+    public String getSignatureName(AlgorithmIdentifier digestAlg, AlgorithmIdentifier encryptionAlg)
+    {
+        return getDigestAlgName(digestAlg.getAlgorithm()) + "with" + getEncryptionAlgName(encryptionAlg.getAlgorithm());
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/DefaultCMSSignatureEncryptionAlgorithmFinder.java b/bcpkix/src/main/java/org/bouncycastle/cms/DefaultCMSSignatureEncryptionAlgorithmFinder.java
new file mode 100644
index 0000000..c51523a
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/DefaultCMSSignatureEncryptionAlgorithmFinder.java
@@ -0,0 +1,54 @@
+package org.bouncycastle.cms;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import org.bouncycastle.asn1.DERNull;
+import org.bouncycastle.asn1.oiw.OIWObjectIdentifiers;
+import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.bouncycastle.asn1.teletrust.TeleTrusTObjectIdentifiers;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+
+public class DefaultCMSSignatureEncryptionAlgorithmFinder
+    implements CMSSignatureEncryptionAlgorithmFinder
+{
+    private static final Set RSA_PKCS1d5 = new HashSet();
+
+    static
+    {
+        // BEGIN android-removed
+        // RSA_PKCS1d5.add(PKCSObjectIdentifiers.md2WithRSAEncryption);
+        // RSA_PKCS1d5.add(PKCSObjectIdentifiers.md4WithRSAEncryption);
+        // END android-removed
+        RSA_PKCS1d5.add(PKCSObjectIdentifiers.md5WithRSAEncryption);
+        RSA_PKCS1d5.add(PKCSObjectIdentifiers.sha1WithRSAEncryption);
+        // BEGIN android-removed
+        // RSA_PKCS1d5.add(PKCSObjectIdentifiers.sha224WithRSAEncryption);
+        // END android-removed
+        RSA_PKCS1d5.add(PKCSObjectIdentifiers.sha256WithRSAEncryption);
+        RSA_PKCS1d5.add(PKCSObjectIdentifiers.sha384WithRSAEncryption);
+        RSA_PKCS1d5.add(PKCSObjectIdentifiers.sha512WithRSAEncryption);
+        // BEGIN android-removed
+        // RSA_PKCS1d5.add(OIWObjectIdentifiers.md4WithRSAEncryption);
+        // RSA_PKCS1d5.add(OIWObjectIdentifiers.md4WithRSA);
+        // END android-removed
+        RSA_PKCS1d5.add(OIWObjectIdentifiers.md5WithRSA);
+        RSA_PKCS1d5.add(OIWObjectIdentifiers.sha1WithRSA);
+        // BEGIN android-removed
+        // RSA_PKCS1d5.add(TeleTrusTObjectIdentifiers.rsaSignatureWithripemd128);
+        // RSA_PKCS1d5.add(TeleTrusTObjectIdentifiers.rsaSignatureWithripemd160);
+        // RSA_PKCS1d5.add(TeleTrusTObjectIdentifiers.rsaSignatureWithripemd256);
+        // END android-removed
+    }
+
+    public AlgorithmIdentifier findEncryptionAlgorithm(AlgorithmIdentifier signatureAlgorithm)
+    {
+               // RFC3370 section 3.2
+        if (RSA_PKCS1d5.contains(signatureAlgorithm.getAlgorithm()))
+        {
+            return new AlgorithmIdentifier(PKCSObjectIdentifiers.rsaEncryption, DERNull.INSTANCE);
+        }
+
+        return signatureAlgorithm;
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/DefaultSignedAttributeTableGenerator.java b/bcpkix/src/main/java/org/bouncycastle/cms/DefaultSignedAttributeTableGenerator.java
new file mode 100644
index 0000000..965d121
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/DefaultSignedAttributeTableGenerator.java
@@ -0,0 +1,106 @@
+package org.bouncycastle.cms;
+
+import java.util.Date;
+import java.util.Hashtable;
+import java.util.Map;
+
+import org.bouncycastle.asn1.DERObjectIdentifier;
+import org.bouncycastle.asn1.DEROctetString;
+import org.bouncycastle.asn1.DERSet;
+import org.bouncycastle.asn1.cms.Attribute;
+import org.bouncycastle.asn1.cms.AttributeTable;
+import org.bouncycastle.asn1.cms.CMSAttributes;
+import org.bouncycastle.asn1.cms.Time;
+
+/**
+ * Default signed attributes generator.
+ */
+public class DefaultSignedAttributeTableGenerator
+    implements CMSAttributeTableGenerator
+{
+    private final Hashtable table;
+
+    /**
+     * Initialise to use all defaults
+     */
+    public DefaultSignedAttributeTableGenerator()
+    {
+        table = new Hashtable();
+    }
+
+    /**
+     * Initialise with some extra attributes or overrides.
+     *
+     * @param attributeTable initial attribute table to use.
+     */
+    public DefaultSignedAttributeTableGenerator(
+        AttributeTable attributeTable)
+    {
+        if (attributeTable != null)
+        {
+            table = attributeTable.toHashtable();
+        }
+        else
+        {
+            table = new Hashtable();
+        }
+    }
+
+    /**
+     * Create a standard attribute table from the passed in parameters - this will
+     * normally include contentType, signingTime, and messageDigest. If the constructor
+     * using an AttributeTable was used, entries in it for contentType, signingTime, and
+     * messageDigest will override the generated ones.
+     *
+     * @param parameters source parameters for table generation.
+     *
+     * @return a filled in Hashtable of attributes.
+     */
+    protected Hashtable createStandardAttributeTable(
+        Map parameters)
+    {
+        Hashtable std = (Hashtable)table.clone();
+
+        if (!std.containsKey(CMSAttributes.contentType))
+        {
+            DERObjectIdentifier contentType = (DERObjectIdentifier)
+                parameters.get(CMSAttributeTableGenerator.CONTENT_TYPE);
+
+            // contentType will be null if we're trying to generate a counter signature.
+            if (contentType != null)
+            {
+                Attribute attr = new Attribute(CMSAttributes.contentType,
+                    new DERSet(contentType));
+                std.put(attr.getAttrType(), attr);
+            }
+        }
+
+        if (!std.containsKey(CMSAttributes.signingTime))
+        {
+            Date signingTime = new Date();
+            Attribute attr = new Attribute(CMSAttributes.signingTime,
+                new DERSet(new Time(signingTime)));
+            std.put(attr.getAttrType(), attr);
+        }
+
+        if (!std.containsKey(CMSAttributes.messageDigest))
+        {
+            byte[] messageDigest = (byte[])parameters.get(
+                CMSAttributeTableGenerator.DIGEST);
+            Attribute attr = new Attribute(CMSAttributes.messageDigest,
+                new DERSet(new DEROctetString(messageDigest)));
+            std.put(attr.getAttrType(), attr);
+        }
+
+        return std;
+    }
+
+    /**
+     * @param parameters source parameters
+     * @return the populated attribute table
+     */
+    public AttributeTable getAttributes(Map parameters)
+    {
+        return new AttributeTable(createStandardAttributeTable(parameters));
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/NullOutputStream.java b/bcpkix/src/main/java/org/bouncycastle/cms/NullOutputStream.java
new file mode 100644
index 0000000..03c058a
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/NullOutputStream.java
@@ -0,0 +1,28 @@
+/**
+ * 
+ */
+package org.bouncycastle.cms;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+class NullOutputStream
+    extends OutputStream
+{
+    public void write(byte[] buf)
+        throws IOException
+    {
+        // do nothing
+    }
+
+    public void write(byte[] buf, int off, int len)
+        throws IOException
+    {
+        // do nothing
+    }
+    
+    public void write(int b) throws IOException
+    {
+        // do nothing
+    }
+}
\ No newline at end of file
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/SignerId.java b/bcpkix/src/main/java/org/bouncycastle/cms/SignerId.java
new file mode 100644
index 0000000..6b53bac
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/SignerId.java
@@ -0,0 +1,104 @@
+package org.bouncycastle.cms;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.asn1.x500.X500Name;
+import org.bouncycastle.cert.selector.X509CertificateHolderSelector;
+import org.bouncycastle.util.Selector;
+
+/**
+ * a basic index for a signer.
+ */
+public class SignerId
+    implements Selector
+{
+    private X509CertificateHolderSelector baseSelector;
+
+    private SignerId(X509CertificateHolderSelector baseSelector)
+    {
+        this.baseSelector = baseSelector;
+    }
+
+    /**
+     * Construct a signer ID with the value of a public key's subjectKeyId.
+     *
+     * @param subjectKeyId a subjectKeyId
+     */
+    public SignerId(byte[] subjectKeyId)
+    {
+        this(null, null, subjectKeyId);
+    }
+
+    /**
+     * Construct a signer ID based on the issuer and serial number of the signer's associated
+     * certificate.
+     *
+     * @param issuer the issuer of the signer's associated certificate.
+     * @param serialNumber the serial number of the signer's associated certificate.
+     */
+    public SignerId(X500Name issuer, BigInteger serialNumber)
+    {
+        this(issuer, serialNumber, null);
+    }
+
+    /**
+     * Construct a signer ID based on the issuer and serial number of the signer's associated
+     * certificate.
+     *
+     * @param issuer the issuer of the signer's associated certificate.
+     * @param serialNumber the serial number of the signer's associated certificate.
+     * @param subjectKeyId the subject key identifier to use to match the signers associated certificate.
+     */
+    public SignerId(X500Name issuer, BigInteger serialNumber, byte[] subjectKeyId)
+    {
+        this(new X509CertificateHolderSelector(issuer, serialNumber, subjectKeyId));
+    }
+
+    public X500Name getIssuer()
+    {
+        return baseSelector.getIssuer();
+    }
+
+    public BigInteger getSerialNumber()
+    {
+        return baseSelector.getSerialNumber();
+    }
+
+    public byte[] getSubjectKeyIdentifier()
+    {
+        return baseSelector.getSubjectKeyIdentifier();
+    }
+
+    public int hashCode()
+    {
+        return baseSelector.hashCode();
+    }
+
+    public boolean equals(
+        Object  o)
+    {
+        if (!(o instanceof SignerId))
+        {
+            return false;
+        }
+
+        SignerId id = (SignerId)o;
+
+        return this.baseSelector.equals(id.baseSelector);
+    }
+
+    public boolean match(Object obj)
+    {
+        if (obj instanceof SignerInformation)
+        {
+            return ((SignerInformation)obj).getSID().equals(this);
+        }
+
+        return baseSelector.match(obj);
+    }
+
+    public Object clone()
+    {
+        return new SignerId(this.baseSelector);
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/SignerInfoGenerator.java b/bcpkix/src/main/java/org/bouncycastle/cms/SignerInfoGenerator.java
new file mode 100644
index 0000000..06470c3
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/SignerInfoGenerator.java
@@ -0,0 +1,281 @@
+package org.bouncycastle.cms;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.bouncycastle.asn1.ASN1Encoding;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1Set;
+import org.bouncycastle.asn1.DERObjectIdentifier;
+import org.bouncycastle.asn1.DEROctetString;
+import org.bouncycastle.asn1.DERSet;
+import org.bouncycastle.asn1.cms.AttributeTable;
+import org.bouncycastle.asn1.cms.SignerIdentifier;
+import org.bouncycastle.asn1.cms.SignerInfo;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.cert.X509CertificateHolder;
+import org.bouncycastle.operator.ContentSigner;
+import org.bouncycastle.operator.DefaultDigestAlgorithmIdentifierFinder;
+import org.bouncycastle.operator.DigestAlgorithmIdentifierFinder;
+import org.bouncycastle.operator.DigestCalculator;
+import org.bouncycastle.operator.DigestCalculatorProvider;
+import org.bouncycastle.operator.OperatorCreationException;
+import org.bouncycastle.util.io.TeeOutputStream;
+
+public class SignerInfoGenerator
+{
+    private final SignerIdentifier signerIdentifier;
+    private final CMSAttributeTableGenerator sAttrGen;
+    private final CMSAttributeTableGenerator unsAttrGen;
+    private final ContentSigner signer;
+    private final DigestCalculator digester;
+    private final DigestAlgorithmIdentifierFinder digAlgFinder = new DefaultDigestAlgorithmIdentifierFinder();
+    private final CMSSignatureEncryptionAlgorithmFinder sigEncAlgFinder;
+
+    private byte[] calculatedDigest = null;
+    private X509CertificateHolder certHolder;
+
+    SignerInfoGenerator(
+        SignerIdentifier signerIdentifier,
+        ContentSigner signer,
+        DigestCalculatorProvider digesterProvider,
+        CMSSignatureEncryptionAlgorithmFinder sigEncAlgFinder)
+        throws OperatorCreationException
+    {
+        this(signerIdentifier, signer, digesterProvider, sigEncAlgFinder, false);
+    }
+
+    SignerInfoGenerator(
+        SignerIdentifier signerIdentifier,
+        ContentSigner signer,
+        DigestCalculatorProvider digesterProvider,
+        CMSSignatureEncryptionAlgorithmFinder sigEncAlgFinder,
+        boolean isDirectSignature)
+        throws OperatorCreationException
+    {
+        this.signerIdentifier = signerIdentifier;
+        this.signer = signer;
+
+        if (digesterProvider != null)
+        {
+            this.digester = digesterProvider.get(digAlgFinder.find(signer.getAlgorithmIdentifier()));
+        }
+        else
+        {
+            this.digester = null;
+        }
+
+        if (isDirectSignature)
+        {
+            this.sAttrGen = null;
+            this.unsAttrGen = null;
+        }
+        else
+        {
+            this.sAttrGen = new DefaultSignedAttributeTableGenerator();
+            this.unsAttrGen = null;
+        }
+
+        this.sigEncAlgFinder = sigEncAlgFinder;
+    }
+
+    public SignerInfoGenerator(
+        SignerInfoGenerator original,
+        CMSAttributeTableGenerator sAttrGen,
+        CMSAttributeTableGenerator unsAttrGen)
+    {
+        this.signerIdentifier = original.signerIdentifier;
+        this.signer = original.signer;
+        this.digester = original.digester;
+        this.sigEncAlgFinder = original.sigEncAlgFinder;
+        this.sAttrGen = sAttrGen;
+        this.unsAttrGen = unsAttrGen;
+    }
+
+    SignerInfoGenerator(
+        SignerIdentifier signerIdentifier,
+        ContentSigner signer,
+        DigestCalculatorProvider digesterProvider,
+        CMSSignatureEncryptionAlgorithmFinder sigEncAlgFinder,
+        CMSAttributeTableGenerator sAttrGen,
+        CMSAttributeTableGenerator unsAttrGen)
+        throws OperatorCreationException
+    {
+        this.signerIdentifier = signerIdentifier;
+        this.signer = signer;
+
+        if (digesterProvider != null)
+        {
+            this.digester = digesterProvider.get(digAlgFinder.find(signer.getAlgorithmIdentifier()));
+        }
+        else
+        {
+            this.digester = null;
+        }
+
+        this.sAttrGen = sAttrGen;
+        this.unsAttrGen = unsAttrGen;
+        this.sigEncAlgFinder = sigEncAlgFinder;
+    }
+
+    public boolean hasAssociatedCertificate()
+    {
+        return certHolder != null;
+    }
+
+    public X509CertificateHolder getAssociatedCertificate()
+    {
+        return certHolder;
+    }
+    
+    public AlgorithmIdentifier getDigestAlgorithm()
+    {
+        if (digester != null)
+        {
+            return digester.getAlgorithmIdentifier();
+        }
+
+        return digAlgFinder.find(signer.getAlgorithmIdentifier());
+    }
+    
+    public OutputStream getCalculatingOutputStream()
+    {
+        if (digester != null)
+        {
+            if (sAttrGen == null)
+            {
+                return new TeeOutputStream(digester.getOutputStream(), signer.getOutputStream());    
+            }
+            return digester.getOutputStream();
+        }
+        else
+        {
+            return signer.getOutputStream();
+        }
+    }
+
+    public SignerInfo generate(ASN1ObjectIdentifier contentType)
+        throws CMSException
+    {
+        try
+        {
+            /* RFC 3852 5.4
+             * The result of the message digest calculation process depends on
+             * whether the signedAttrs field is present.  When the field is absent,
+             * the result is just the message digest of the content as described
+             *
+             * above.  When the field is present, however, the result is the message
+             * digest of the complete DER encoding of the SignedAttrs value
+             * contained in the signedAttrs field.
+             */
+            ASN1Set signedAttr = null;
+
+            AlgorithmIdentifier digestAlg = null;
+
+            if (sAttrGen != null)
+            {
+                digestAlg = digester.getAlgorithmIdentifier();
+                calculatedDigest = digester.getDigest();
+                Map parameters = getBaseParameters(contentType, digester.getAlgorithmIdentifier(), calculatedDigest);
+                AttributeTable signed = sAttrGen.getAttributes(Collections.unmodifiableMap(parameters));
+
+                signedAttr = getAttributeSet(signed);
+
+                // sig must be composed from the DER encoding.
+                OutputStream sOut = signer.getOutputStream();
+
+                sOut.write(signedAttr.getEncoded(ASN1Encoding.DER));
+
+                sOut.close();
+            }
+            else
+            {
+                if (digester != null)
+                {
+                    digestAlg = digester.getAlgorithmIdentifier();
+                    calculatedDigest = digester.getDigest();
+                }
+                else
+                {
+                    digestAlg = digAlgFinder.find(signer.getAlgorithmIdentifier());
+                    calculatedDigest = null;
+                }
+            }
+
+            byte[] sigBytes = signer.getSignature();
+
+            ASN1Set unsignedAttr = null;
+            if (unsAttrGen != null)
+            {
+                Map parameters = getBaseParameters(contentType, digestAlg, calculatedDigest);
+                parameters.put(CMSAttributeTableGenerator.SIGNATURE, sigBytes.clone());
+
+                AttributeTable unsigned = unsAttrGen.getAttributes(Collections.unmodifiableMap(parameters));
+
+                unsignedAttr = getAttributeSet(unsigned);
+            }
+
+            AlgorithmIdentifier digestEncryptionAlgorithm = sigEncAlgFinder.findEncryptionAlgorithm(signer.getAlgorithmIdentifier());
+
+            return new SignerInfo(signerIdentifier, digestAlg,
+                signedAttr, digestEncryptionAlgorithm, new DEROctetString(sigBytes), unsignedAttr);
+        }
+        catch (IOException e)
+        {
+            throw new CMSException("encoding error.", e);
+        }
+    }
+
+    void setAssociatedCertificate(X509CertificateHolder certHolder)
+    {
+        this.certHolder = certHolder;
+    }
+
+    private ASN1Set getAttributeSet(
+        AttributeTable attr)
+    {
+        if (attr != null)
+        {
+            return new DERSet(attr.toASN1EncodableVector());
+        }
+
+        return null;
+    }
+
+    private Map getBaseParameters(DERObjectIdentifier contentType, AlgorithmIdentifier digAlgId, byte[] hash)
+    {
+        Map param = new HashMap();
+
+        if (contentType != null)
+        {
+            param.put(CMSAttributeTableGenerator.CONTENT_TYPE, contentType);
+        }
+
+        param.put(CMSAttributeTableGenerator.DIGEST_ALGORITHM_IDENTIFIER, digAlgId);
+        param.put(CMSAttributeTableGenerator.DIGEST,  hash.clone());
+        return param;
+    }
+
+    public byte[] getCalculatedDigest()
+    {
+        if (calculatedDigest != null)
+        {
+            return (byte[])calculatedDigest.clone();
+        }
+
+        return null;
+    }
+
+    public CMSAttributeTableGenerator getSignedAttributeTableGenerator()
+    {
+        return sAttrGen;
+    }
+
+    public CMSAttributeTableGenerator getUnsignedAttributeTableGenerator()
+    {
+        return unsAttrGen;
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/SignerInfoGeneratorBuilder.java b/bcpkix/src/main/java/org/bouncycastle/cms/SignerInfoGeneratorBuilder.java
new file mode 100644
index 0000000..7a47a2f
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/SignerInfoGeneratorBuilder.java
@@ -0,0 +1,139 @@
+package org.bouncycastle.cms;
+
+import org.bouncycastle.asn1.DEROctetString;
+import org.bouncycastle.asn1.cms.IssuerAndSerialNumber;
+import org.bouncycastle.asn1.cms.SignerIdentifier;
+import org.bouncycastle.cert.X509CertificateHolder;
+import org.bouncycastle.operator.ContentSigner;
+import org.bouncycastle.operator.DigestCalculatorProvider;
+import org.bouncycastle.operator.OperatorCreationException;
+
+/**
+ * Builder for SignerInfo generator objects.
+ */
+public class SignerInfoGeneratorBuilder
+{
+    private DigestCalculatorProvider digestProvider;
+    private boolean directSignature;
+    private CMSAttributeTableGenerator signedGen;
+    private CMSAttributeTableGenerator unsignedGen;
+    private CMSSignatureEncryptionAlgorithmFinder sigEncAlgFinder;
+
+    /**
+     *  Base constructor.
+     *
+     * @param digestProvider  a provider of digest calculators for the algorithms required in the signature and attribute calculations.
+     */
+    public SignerInfoGeneratorBuilder(DigestCalculatorProvider digestProvider)
+    {
+        this(digestProvider, new DefaultCMSSignatureEncryptionAlgorithmFinder());
+    }
+
+        /**
+     *  Base constructor.
+     *
+     * @param digestProvider  a provider of digest calculators for the algorithms required in the signature and attribute calculations.
+     */
+    public SignerInfoGeneratorBuilder(DigestCalculatorProvider digestProvider, CMSSignatureEncryptionAlgorithmFinder sigEncAlgFinder)
+    {
+        this.digestProvider = digestProvider;
+        this.sigEncAlgFinder = sigEncAlgFinder;
+    }
+
+    /**
+     * If the passed in flag is true, the signer signature will be based on the data, not
+     * a collection of signed attributes, and no signed attributes will be included.
+     *
+     * @return the builder object
+     */
+    public SignerInfoGeneratorBuilder setDirectSignature(boolean hasNoSignedAttributes)
+    {
+        this.directSignature = hasNoSignedAttributes;
+
+        return this;
+    }
+
+    /**
+     *  Provide a custom signed attribute generator.
+     *
+     * @param signedGen a generator of signed attributes.
+     * @return the builder object
+     */
+    public SignerInfoGeneratorBuilder setSignedAttributeGenerator(CMSAttributeTableGenerator signedGen)
+    {
+        this.signedGen = signedGen;
+
+        return this;
+    }
+
+    /**
+     * Provide a generator of unsigned attributes.
+     *
+     * @param unsignedGen  a generator for signed attributes.
+     * @return the builder object
+     */
+    public SignerInfoGeneratorBuilder setUnsignedAttributeGenerator(CMSAttributeTableGenerator unsignedGen)
+    {
+        this.unsignedGen = unsignedGen;
+
+        return this;
+    }
+
+    /**
+     * Build a generator with the passed in certHolder issuer and serial number as the signerIdentifier.
+     *
+     * @param contentSigner  operator for generating the final signature in the SignerInfo with.
+     * @param certHolder  carrier for the X.509 certificate related to the contentSigner.
+     * @return  a SignerInfoGenerator
+     * @throws OperatorCreationException   if the generator cannot be built.
+     */
+    public SignerInfoGenerator build(ContentSigner contentSigner, X509CertificateHolder certHolder)
+        throws OperatorCreationException
+    {
+        SignerIdentifier sigId = new SignerIdentifier(new IssuerAndSerialNumber(certHolder.toASN1Structure()));
+
+        SignerInfoGenerator sigInfoGen = createGenerator(contentSigner, sigId);
+
+        sigInfoGen.setAssociatedCertificate(certHolder);
+
+        return sigInfoGen;
+    }
+
+    /**
+     * Build a generator with the passed in subjectKeyIdentifier as the signerIdentifier. If used  you should
+     * try to follow the calculation described in RFC 5280 section 4.2.1.2.
+     *
+     * @param contentSigner  operator for generating the final signature in the SignerInfo with.
+     * @param subjectKeyIdentifier    key identifier to identify the public key for verifying the signature.
+     * @return  a SignerInfoGenerator
+     * @throws OperatorCreationException if the generator cannot be built.
+     */
+    public SignerInfoGenerator build(ContentSigner contentSigner, byte[] subjectKeyIdentifier)
+        throws OperatorCreationException
+    {
+        SignerIdentifier sigId = new SignerIdentifier(new DEROctetString(subjectKeyIdentifier));
+
+        return createGenerator(contentSigner, sigId);
+    }
+
+    private SignerInfoGenerator createGenerator(ContentSigner contentSigner, SignerIdentifier sigId)
+        throws OperatorCreationException
+    {
+        if (directSignature)
+        {
+            return new SignerInfoGenerator(sigId, contentSigner, digestProvider, sigEncAlgFinder, true);
+        }
+
+        if (signedGen != null || unsignedGen != null)
+        {
+            if (signedGen == null)
+            {
+                signedGen = new DefaultSignedAttributeTableGenerator();
+            }
+
+            return new SignerInfoGenerator(sigId, contentSigner, digestProvider, sigEncAlgFinder, signedGen, unsignedGen);
+        }
+        
+        return new SignerInfoGenerator(sigId, contentSigner, digestProvider, sigEncAlgFinder);
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/SignerInformation.java b/bcpkix/src/main/java/org/bouncycastle/cms/SignerInformation.java
new file mode 100644
index 0000000..4526a2e
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/SignerInformation.java
@@ -0,0 +1,777 @@
+package org.bouncycastle.cms;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.Provider;
+import java.security.PublicKey;
+import java.security.cert.CertificateExpiredException;
+import java.security.cert.CertificateNotYetValidException;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.List;
+
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Encoding;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Set;
+import org.bouncycastle.asn1.DERObjectIdentifier;
+import org.bouncycastle.asn1.DERSet;
+import org.bouncycastle.asn1.cms.Attribute;
+import org.bouncycastle.asn1.cms.AttributeTable;
+import org.bouncycastle.asn1.cms.CMSAttributes;
+import org.bouncycastle.asn1.cms.IssuerAndSerialNumber;
+import org.bouncycastle.asn1.cms.SignerIdentifier;
+import org.bouncycastle.asn1.cms.SignerInfo;
+import org.bouncycastle.asn1.cms.Time;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.asn1.x509.DigestInfo;
+import org.bouncycastle.cert.X509CertificateHolder;
+import org.bouncycastle.cms.jcajce.JcaSignerInfoVerifierBuilder;
+import org.bouncycastle.cms.jcajce.JcaSimpleSignerInfoVerifierBuilder;
+import org.bouncycastle.operator.ContentVerifier;
+import org.bouncycastle.operator.DigestCalculator;
+import org.bouncycastle.operator.OperatorCreationException;
+import org.bouncycastle.operator.RawContentVerifier;
+import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder;
+import org.bouncycastle.util.Arrays;
+
+/**
+ * an expanded SignerInfo block from a CMS Signed message
+ */
+public class SignerInformation
+{
+    private SignerId                sid;
+    private SignerInfo              info;
+    private AlgorithmIdentifier     digestAlgorithm;
+    private AlgorithmIdentifier     encryptionAlgorithm;
+    private final ASN1Set           signedAttributeSet;
+    private final ASN1Set           unsignedAttributeSet;
+    private CMSProcessable          content;
+    private byte[]                  signature;
+    private ASN1ObjectIdentifier    contentType;
+    private byte[]                  resultDigest;
+
+    // Derived
+    private AttributeTable          signedAttributeValues;
+    private AttributeTable          unsignedAttributeValues;
+    private boolean                 isCounterSignature;
+
+    SignerInformation(
+        SignerInfo          info,
+        ASN1ObjectIdentifier contentType,
+        CMSProcessable      content,
+        byte[]              resultDigest)
+    {
+        this.info = info;
+        this.contentType = contentType;
+        this.isCounterSignature = contentType == null;
+
+        SignerIdentifier   s = info.getSID();
+
+        if (s.isTagged())
+        {
+            ASN1OctetString octs = ASN1OctetString.getInstance(s.getId());
+
+            sid = new SignerId(octs.getOctets());
+        }
+        else
+        {
+            IssuerAndSerialNumber   iAnds = IssuerAndSerialNumber.getInstance(s.getId());
+
+            sid = new SignerId(iAnds.getName(), iAnds.getSerialNumber().getValue());
+        }
+
+        this.digestAlgorithm = info.getDigestAlgorithm();
+        this.signedAttributeSet = info.getAuthenticatedAttributes();
+        this.unsignedAttributeSet = info.getUnauthenticatedAttributes();
+        this.encryptionAlgorithm = info.getDigestEncryptionAlgorithm();
+        this.signature = info.getEncryptedDigest().getOctets();
+
+        this.content = content;
+        this.resultDigest = resultDigest;
+    }
+
+    public boolean isCounterSignature()
+    {
+        return isCounterSignature;
+    }
+
+    public ASN1ObjectIdentifier getContentType()
+    {
+        return this.contentType;
+    }
+
+    private byte[] encodeObj(
+        ASN1Encodable    obj)
+        throws IOException
+    {
+        if (obj != null)
+        {
+            return obj.toASN1Primitive().getEncoded();
+        }
+
+        return null;
+    }
+
+    public SignerId getSID()
+    {
+        return sid;
+    }
+
+    /**
+     * return the version number for this objects underlying SignerInfo structure.
+     */
+    public int getVersion()
+    {
+        return info.getVersion().getValue().intValue();
+    }
+
+    public AlgorithmIdentifier getDigestAlgorithmID()
+    {
+        return digestAlgorithm;
+    }
+
+    /**
+     * return the object identifier for the signature.
+     */
+    public String getDigestAlgOID()
+    {
+        return digestAlgorithm.getObjectId().getId();
+    }
+
+    /**
+     * return the signature parameters, or null if there aren't any.
+     */
+    public byte[] getDigestAlgParams()
+    {
+        try
+        {
+            return encodeObj(digestAlgorithm.getParameters());
+        }
+        catch (Exception e)
+        {
+            throw new RuntimeException("exception getting digest parameters " + e);
+        }
+    }
+
+    /**
+     * return the content digest that was calculated during verification.
+     */
+    public byte[] getContentDigest()
+    {
+        if (resultDigest == null)
+        {
+            throw new IllegalStateException("method can only be called after verify.");
+        }
+        
+        return (byte[])resultDigest.clone();
+    }
+    
+    /**
+     * return the object identifier for the signature.
+     */
+    public String getEncryptionAlgOID()
+    {
+        return encryptionAlgorithm.getObjectId().getId();
+    }
+
+    /**
+     * return the signature/encryption algorithm parameters, or null if
+     * there aren't any.
+     */
+    public byte[] getEncryptionAlgParams()
+    {
+        try
+        {
+            return encodeObj(encryptionAlgorithm.getParameters());
+        }
+        catch (Exception e)
+        {
+            throw new RuntimeException("exception getting encryption parameters " + e);
+        }
+    }  
+
+    /**
+     * return a table of the signed attributes - indexed by
+     * the OID of the attribute.
+     */
+    public AttributeTable getSignedAttributes()
+    {
+        if (signedAttributeSet != null && signedAttributeValues == null)
+        {
+            signedAttributeValues = new AttributeTable(signedAttributeSet);
+        }
+
+        return signedAttributeValues;
+    }
+
+    /**
+     * return a table of the unsigned attributes indexed by
+     * the OID of the attribute.
+     */
+    public AttributeTable getUnsignedAttributes()
+    {
+        if (unsignedAttributeSet != null && unsignedAttributeValues == null)
+        {
+            unsignedAttributeValues = new AttributeTable(unsignedAttributeSet);
+        }
+
+        return unsignedAttributeValues;
+    }
+
+    /**
+     * return the encoded signature
+     */
+    public byte[] getSignature()
+    {
+        return (byte[])signature.clone();
+    }
+
+    /**
+     * Return a SignerInformationStore containing the counter signatures attached to this
+     * signer. If no counter signatures are present an empty store is returned.
+     */
+    public SignerInformationStore getCounterSignatures()
+    {
+        // TODO There are several checks implied by the RFC3852 comments that are missing
+
+        /*
+        The countersignature attribute MUST be an unsigned attribute; it MUST
+        NOT be a signed attribute, an authenticated attribute, an
+        unauthenticated attribute, or an unprotected attribute.
+        */        
+        AttributeTable unsignedAttributeTable = getUnsignedAttributes();
+        if (unsignedAttributeTable == null)
+        {
+            return new SignerInformationStore(new ArrayList(0));
+        }
+
+        List counterSignatures = new ArrayList();
+
+        /*
+        The UnsignedAttributes syntax is defined as a SET OF Attributes.  The
+        UnsignedAttributes in a signerInfo may include multiple instances of
+        the countersignature attribute.
+        */
+        ASN1EncodableVector allCSAttrs = unsignedAttributeTable.getAll(CMSAttributes.counterSignature);
+
+        for (int i = 0; i < allCSAttrs.size(); ++i)
+        {
+            Attribute counterSignatureAttribute = (Attribute)allCSAttrs.get(i);            
+
+            /*
+            A countersignature attribute can have multiple attribute values.  The
+            syntax is defined as a SET OF AttributeValue, and there MUST be one
+            or more instances of AttributeValue present.
+            */
+            ASN1Set values = counterSignatureAttribute.getAttrValues();
+            if (values.size() < 1)
+            {
+                // TODO Throw an appropriate exception?
+            }
+
+            for (Enumeration en = values.getObjects(); en.hasMoreElements();)
+            {
+                /*
+                Countersignature values have the same meaning as SignerInfo values
+                for ordinary signatures, except that:
+
+                   1. The signedAttributes field MUST NOT contain a content-type
+                      attribute; there is no content type for countersignatures.
+
+                   2. The signedAttributes field MUST contain a message-digest
+                      attribute if it contains any other attributes.
+
+                   3. The input to the message-digesting process is the contents
+                      octets of the DER encoding of the signatureValue field of the
+                      SignerInfo value with which the attribute is associated.
+                */
+                SignerInfo si = SignerInfo.getInstance(en.nextElement());
+
+                counterSignatures.add(new SignerInformation(si, null, new CMSProcessableByteArray(getSignature()), null));
+            }
+        }
+
+        return new SignerInformationStore(counterSignatures);
+    }
+    
+    /**
+     * return the DER encoding of the signed attributes.
+     * @throws IOException if an encoding error occurs.
+     */
+    public byte[] getEncodedSignedAttributes()
+        throws IOException
+    {
+        if (signedAttributeSet != null)
+        {
+            return signedAttributeSet.getEncoded();
+        }
+
+        return null;
+    }
+
+    /**
+     * @deprecated
+     */
+    private boolean doVerify(
+        PublicKey       key,
+        Provider        sigProvider)
+        throws CMSException, NoSuchAlgorithmException
+    {
+        try
+        {
+            SignerInformationVerifier verifier;
+
+            if (sigProvider != null)
+            {
+                if (!sigProvider.getName().equalsIgnoreCase("BC"))
+                {
+                    verifier = new JcaSignerInfoVerifierBuilder(new JcaDigestCalculatorProviderBuilder().build()).setProvider(sigProvider).build(key);
+                }
+                else
+                {
+                    verifier = new JcaSimpleSignerInfoVerifierBuilder().setProvider(sigProvider).build(key);
+                }
+            }
+            else
+            {
+                verifier = new JcaSimpleSignerInfoVerifierBuilder().build(key);
+            }
+
+            return doVerify(verifier);
+        }
+        catch (OperatorCreationException e)
+        {
+            throw new CMSException("unable to create verifier: " + e.getMessage(), e);
+        }
+    }
+
+    private boolean doVerify(
+        SignerInformationVerifier verifier)
+        throws CMSException
+    {
+        String          encName = CMSSignedHelper.INSTANCE.getEncryptionAlgName(this.getEncryptionAlgOID());
+
+        try
+        {
+            if (resultDigest == null)
+            {
+                DigestCalculator calc = verifier.getDigestCalculator(this.getDigestAlgorithmID());
+                if (content != null)
+                {
+                    OutputStream      digOut = calc.getOutputStream();
+
+                    content.write(digOut);
+
+                    digOut.close();
+                }
+                else if (signedAttributeSet == null)
+                {
+                    // TODO Get rid of this exception and just treat content==null as empty not missing?
+                    throw new CMSException("data not encapsulated in signature - use detached constructor.");
+                }
+
+                resultDigest = calc.getDigest();
+            }
+        }
+        catch (IOException e)
+        {
+            throw new CMSException("can't process mime object to create signature.", e);
+        }
+        catch (OperatorCreationException e)
+        {
+            throw new CMSException("can't create digest calculator: " + e.getMessage(), e);
+        }
+
+        // RFC 3852 11.1 Check the content-type attribute is correct
+        {
+            ASN1Primitive validContentType = getSingleValuedSignedAttribute(
+                CMSAttributes.contentType, "content-type");
+            if (validContentType == null)
+            {
+                if (!isCounterSignature && signedAttributeSet != null)
+                {
+                    throw new CMSException("The content-type attribute type MUST be present whenever signed attributes are present in signed-data");
+                }
+            }
+            else
+            {
+                if (isCounterSignature)
+                {
+                    throw new CMSException("[For counter signatures,] the signedAttributes field MUST NOT contain a content-type attribute");
+                }
+
+                if (!(validContentType instanceof DERObjectIdentifier))
+                {
+                    throw new CMSException("content-type attribute value not of ASN.1 type 'OBJECT IDENTIFIER'");
+                }
+
+                DERObjectIdentifier signedContentType = (DERObjectIdentifier)validContentType;
+
+                if (!signedContentType.equals(contentType))
+                {
+                    throw new CMSException("content-type attribute value does not match eContentType");
+                }
+            }
+        }
+
+        // RFC 3852 11.2 Check the message-digest attribute is correct
+        {
+            ASN1Primitive validMessageDigest = getSingleValuedSignedAttribute(
+                CMSAttributes.messageDigest, "message-digest");
+            if (validMessageDigest == null)
+            {
+                if (signedAttributeSet != null)
+                {
+                    throw new CMSException("the message-digest signed attribute type MUST be present when there are any signed attributes present");
+                }
+            }
+            else
+            {
+                if (!(validMessageDigest instanceof ASN1OctetString))
+                {
+                    throw new CMSException("message-digest attribute value not of ASN.1 type 'OCTET STRING'");
+                }
+
+                ASN1OctetString signedMessageDigest = (ASN1OctetString)validMessageDigest;
+
+                if (!Arrays.constantTimeAreEqual(resultDigest, signedMessageDigest.getOctets()))
+                {
+                    throw new CMSSignerDigestMismatchException("message-digest attribute value does not match calculated value");
+                }
+            }
+        }
+
+        // RFC 3852 11.4 Validate countersignature attribute(s)
+        {
+            AttributeTable signedAttrTable = this.getSignedAttributes();
+            if (signedAttrTable != null
+                && signedAttrTable.getAll(CMSAttributes.counterSignature).size() > 0)
+            {
+                throw new CMSException("A countersignature attribute MUST NOT be a signed attribute");
+            }
+
+            AttributeTable unsignedAttrTable = this.getUnsignedAttributes();
+            if (unsignedAttrTable != null)
+            {
+                ASN1EncodableVector csAttrs = unsignedAttrTable.getAll(CMSAttributes.counterSignature);
+                for (int i = 0; i < csAttrs.size(); ++i)
+                {
+                    Attribute csAttr = (Attribute)csAttrs.get(i);
+                    if (csAttr.getAttrValues().size() < 1)
+                    {
+                        throw new CMSException("A countersignature attribute MUST contain at least one AttributeValue");
+                    }
+
+                    // Note: We don't recursively validate the countersignature value
+                }
+            }
+        }
+
+        try
+        {
+            ContentVerifier contentVerifier = verifier.getContentVerifier(encryptionAlgorithm, info.getDigestAlgorithm());
+            OutputStream sigOut = contentVerifier.getOutputStream();
+
+            if (signedAttributeSet == null)
+            {
+                if (resultDigest != null)
+                {
+                    if (contentVerifier instanceof RawContentVerifier)
+                    {           
+                        RawContentVerifier rawVerifier = (RawContentVerifier)contentVerifier;
+
+                        if (encName.equals("RSA"))
+                        {
+                            DigestInfo digInfo = new DigestInfo(digestAlgorithm, resultDigest);
+
+                            return rawVerifier.verify(digInfo.getEncoded(ASN1Encoding.DER), this.getSignature());
+                        }
+
+                        return rawVerifier.verify(resultDigest, this.getSignature());
+                    }
+
+                    throw new CMSException("verifier unable to process raw signature");
+                }
+                else if (content != null)
+                {
+                    // TODO Use raw signature of the hash value instead
+                    content.write(sigOut);
+                }
+            }
+            else
+            {
+                sigOut.write(this.getEncodedSignedAttributes());
+            }
+
+            sigOut.close();
+
+            return contentVerifier.verify(this.getSignature());
+        }
+        catch (IOException e)
+        {
+            throw new CMSException("can't process mime object to create signature.", e);
+        }
+        catch (OperatorCreationException e)
+        {
+            throw new CMSException("can't create content verifier: " + e.getMessage(), e);
+        }
+    }
+
+    /**
+     * verify that the given public key successfully handles and confirms the
+     * signature associated with this signer.
+     * @deprecated use verify(ContentVerifierProvider)
+     */
+    public boolean verify(
+        PublicKey   key,
+        String      sigProvider)
+        throws NoSuchAlgorithmException, NoSuchProviderException, CMSException
+    {
+        return verify(key, CMSUtils.getProvider(sigProvider));
+    }
+
+    /**
+     * verify that the given public key successfully handles and confirms the
+     * signature associated with this signer
+     * @deprecated use verify(ContentVerifierProvider)
+     */
+    public boolean verify(
+        PublicKey   key,
+        Provider    sigProvider)
+        throws NoSuchAlgorithmException, NoSuchProviderException, CMSException
+    {
+        // Optional, but still need to validate if present
+        getSigningTime();
+
+        return doVerify(key, sigProvider);
+    }
+
+    /**
+     * verify that the given certificate successfully handles and confirms
+     * the signature associated with this signer and, if a signingTime
+     * attribute is available, that the certificate was valid at the time the
+     * signature was generated.
+     * @deprecated use verify(ContentVerifierProvider)
+     */
+    public boolean verify(
+        X509Certificate cert,
+        String          sigProvider)
+        throws NoSuchAlgorithmException, NoSuchProviderException,
+            CertificateExpiredException, CertificateNotYetValidException,
+            CMSException
+    {
+        return verify(cert, CMSUtils.getProvider(sigProvider));
+    }
+
+    /**
+     * verify that the given certificate successfully handles and confirms
+     * the signature associated with this signer and, if a signingTime
+     * attribute is available, that the certificate was valid at the time the
+     * signature was generated.
+     * @deprecated use verify(ContentVerifierProvider)
+     */
+    public boolean verify(
+        X509Certificate cert,
+        Provider        sigProvider)
+        throws NoSuchAlgorithmException,
+            CertificateExpiredException, CertificateNotYetValidException,
+            CMSException
+    {
+        Time signingTime = getSigningTime();
+        if (signingTime != null)
+        {
+            cert.checkValidity(signingTime.getDate());
+        }
+
+        return doVerify(cert.getPublicKey(), sigProvider); 
+    }
+
+    /**
+     * Verify that the given verifier can successfully verify the signature on
+     * this SignerInformation object.
+     *
+     * @param verifier a suitably configured SignerInformationVerifier.
+     * @return true if the signer information is verified, false otherwise.
+     * @throws org.bouncycastle.cms.CMSVerifierCertificateNotValidException if the provider has an associated certificate and the certificate is not valid at the time given as the SignerInfo's signing time.
+     * @throws org.bouncycastle.cms.CMSException if the verifier is unable to create a ContentVerifiers or DigestCalculators.
+     */
+    public boolean verify(SignerInformationVerifier verifier)
+        throws CMSException
+    {
+        Time signingTime = getSigningTime();   // has to be validated if present.
+
+        if (verifier.hasAssociatedCertificate())
+        {
+            if (signingTime != null)
+            {
+                X509CertificateHolder dcv = verifier.getAssociatedCertificate();
+
+                if (!dcv.isValidOn(signingTime.getDate()))
+                {
+                    throw new CMSVerifierCertificateNotValidException("verifier not valid at signingTime");
+                }
+            }
+        }
+
+        return doVerify(verifier);
+    }
+
+    /**
+     * Return the base ASN.1 CMS structure that this object contains.
+     * 
+     * @return an object containing a CMS SignerInfo structure.
+     * @deprecated use toASN1Structure()
+     */
+    public SignerInfo toSignerInfo()
+    {
+        return info;
+    }
+
+    /**
+     * Return the underlying ASN.1 object defining this SignerInformation object.
+     *
+     * @return a SignerInfo.
+     */
+    public SignerInfo toASN1Structure()
+    {
+        return info;
+    }
+
+    private ASN1Primitive getSingleValuedSignedAttribute(
+        ASN1ObjectIdentifier attrOID, String printableName)
+        throws CMSException
+    {
+        AttributeTable unsignedAttrTable = this.getUnsignedAttributes();
+        if (unsignedAttrTable != null
+            && unsignedAttrTable.getAll(attrOID).size() > 0)
+        {
+            throw new CMSException("The " + printableName
+                + " attribute MUST NOT be an unsigned attribute");
+        }
+
+        AttributeTable signedAttrTable = this.getSignedAttributes();
+        if (signedAttrTable == null)
+        {
+            return null;
+        }
+
+        ASN1EncodableVector v = signedAttrTable.getAll(attrOID);
+        switch (v.size())
+        {
+            case 0:
+                return null;
+            case 1:
+            {
+                Attribute t = (Attribute)v.get(0);
+                ASN1Set attrValues = t.getAttrValues();
+                if (attrValues.size() != 1)
+                {
+                    throw new CMSException("A " + printableName
+                        + " attribute MUST have a single attribute value");
+                }
+
+                return attrValues.getObjectAt(0).toASN1Primitive();
+            }
+            default:
+                throw new CMSException("The SignedAttributes in a signerInfo MUST NOT include multiple instances of the "
+                    + printableName + " attribute");
+        }
+    }
+
+    private Time getSigningTime() throws CMSException
+    {
+        ASN1Primitive validSigningTime = getSingleValuedSignedAttribute(
+            CMSAttributes.signingTime, "signing-time");
+
+        if (validSigningTime == null)
+        {
+            return null;
+        }
+
+        try
+        {
+            return Time.getInstance(validSigningTime);
+        }
+        catch (IllegalArgumentException e)
+        {
+            throw new CMSException("signing-time attribute value not a valid 'Time' structure");
+        }
+    }
+
+    /**
+     * Return a signer information object with the passed in unsigned
+     * attributes replacing the ones that are current associated with
+     * the object passed in.
+     * 
+     * @param signerInformation the signerInfo to be used as the basis.
+     * @param unsignedAttributes the unsigned attributes to add.
+     * @return a copy of the original SignerInformationObject with the changed attributes.
+     */
+    public static SignerInformation replaceUnsignedAttributes(
+        SignerInformation   signerInformation,
+        AttributeTable      unsignedAttributes)
+    {
+        SignerInfo  sInfo = signerInformation.info;
+        ASN1Set     unsignedAttr = null;
+        
+        if (unsignedAttributes != null)
+        {
+            unsignedAttr = new DERSet(unsignedAttributes.toASN1EncodableVector());
+        }
+        
+        return new SignerInformation(
+                new SignerInfo(sInfo.getSID(), sInfo.getDigestAlgorithm(),
+                    sInfo.getAuthenticatedAttributes(), sInfo.getDigestEncryptionAlgorithm(), sInfo.getEncryptedDigest(), unsignedAttr),
+                    signerInformation.contentType, signerInformation.content, null);
+    }
+
+    /**
+     * Return a signer information object with passed in SignerInformationStore representing counter
+     * signatures attached as an unsigned attribute.
+     *
+     * @param signerInformation the signerInfo to be used as the basis.
+     * @param counterSigners signer info objects carrying counter signature.
+     * @return a copy of the original SignerInformationObject with the changed attributes.
+     */
+    public static SignerInformation addCounterSigners(
+        SignerInformation        signerInformation,
+        SignerInformationStore   counterSigners)
+    {
+        // TODO Perform checks from RFC 3852 11.4
+
+        SignerInfo          sInfo = signerInformation.info;
+        AttributeTable      unsignedAttr = signerInformation.getUnsignedAttributes();
+        ASN1EncodableVector v;
+
+        if (unsignedAttr != null)
+        {
+            v = unsignedAttr.toASN1EncodableVector();
+        }
+        else
+        {
+            v = new ASN1EncodableVector();
+        }
+
+        ASN1EncodableVector sigs = new ASN1EncodableVector();
+
+        for (Iterator it = counterSigners.getSigners().iterator(); it.hasNext();)
+        {
+            sigs.add(((SignerInformation)it.next()).toSignerInfo());
+        }
+
+        v.add(new Attribute(CMSAttributes.counterSignature, new DERSet(sigs)));
+
+        return new SignerInformation(
+                new SignerInfo(sInfo.getSID(), sInfo.getDigestAlgorithm(),
+                    sInfo.getAuthenticatedAttributes(), sInfo.getDigestEncryptionAlgorithm(), sInfo.getEncryptedDigest(), new DERSet(v)),
+                    signerInformation.contentType, signerInformation.content, null);
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/SignerInformationStore.java b/bcpkix/src/main/java/org/bouncycastle/cms/SignerInformationStore.java
new file mode 100644
index 0000000..70a8727
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/SignerInformationStore.java
@@ -0,0 +1,109 @@
+package org.bouncycastle.cms;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+public class SignerInformationStore
+{
+    private ArrayList all = new ArrayList();
+    private Map table = new HashMap();
+
+    public SignerInformationStore(
+        Collection  signerInfos)
+    {
+        Iterator    it = signerInfos.iterator();
+
+        while (it.hasNext())
+        {
+            SignerInformation   signer = (SignerInformation)it.next();
+            SignerId            sid = signer.getSID();
+
+            List list = (ArrayList)table.get(sid);
+            if (list == null)
+            {
+                list = new ArrayList(1);
+                table.put(sid, list);
+            }
+
+            list.add(signer);
+        }
+
+        this.all = new ArrayList(signerInfos);
+    }
+
+    /**
+     * Return the first SignerInformation object that matches the
+     * passed in selector. Null if there are no matches.
+     * 
+     * @param selector to identify a signer
+     * @return a single SignerInformation object. Null if none matches.
+     */
+    public SignerInformation get(
+        SignerId        selector)
+    {
+        Collection list = getSigners(selector);
+
+        return list.size() == 0 ? null : (SignerInformation) list.iterator().next();
+    }
+
+    /**
+     * Return the number of signers in the collection.
+     * 
+     * @return number of signers identified.
+     */
+    public int size()
+    {
+        return all.size();
+    }
+
+    /**
+     * Return all signers in the collection
+     * 
+     * @return a collection of signers.
+     */
+    public Collection getSigners()
+    {
+        return new ArrayList(all);
+    }
+
+    /**
+     * Return possible empty collection with signers matching the passed in SignerId
+     * 
+     * @param selector a signer id to select against.
+     * @return a collection of SignerInformation objects.
+     */
+    public Collection getSigners(
+        SignerId selector)
+    {
+        if (selector.getIssuer() != null && selector.getSubjectKeyIdentifier() != null)
+        {
+            List results = new ArrayList();
+
+            Collection match1 = getSigners(new SignerId(selector.getIssuer(), selector.getSerialNumber()));
+
+            if (match1 != null)
+            {
+                results.addAll(match1);
+            }
+
+            Collection match2 = getSigners(new SignerId(selector.getSubjectKeyIdentifier()));
+
+            if (match2 != null)
+            {
+                results.addAll(match2);
+            }
+
+            return results;
+        }
+        else
+        {
+            List list = (ArrayList)table.get(selector);
+
+            return list == null ? new ArrayList() : new ArrayList(list);
+        }
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/SignerInformationVerifier.java b/bcpkix/src/main/java/org/bouncycastle/cms/SignerInformationVerifier.java
new file mode 100644
index 0000000..ada4d0e
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/SignerInformationVerifier.java
@@ -0,0 +1,50 @@
+package org.bouncycastle.cms;
+
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.cert.X509CertificateHolder;
+import org.bouncycastle.operator.ContentVerifier;
+import org.bouncycastle.operator.ContentVerifierProvider;
+import org.bouncycastle.operator.DigestCalculator;
+import org.bouncycastle.operator.DigestCalculatorProvider;
+import org.bouncycastle.operator.OperatorCreationException;
+import org.bouncycastle.operator.SignatureAlgorithmIdentifierFinder;
+
+public class SignerInformationVerifier
+{
+    private ContentVerifierProvider verifierProvider;
+    private DigestCalculatorProvider digestProvider;
+    private SignatureAlgorithmIdentifierFinder sigAlgorithmFinder;
+    private CMSSignatureAlgorithmNameGenerator sigNameGenerator;
+
+    public SignerInformationVerifier(CMSSignatureAlgorithmNameGenerator sigNameGenerator, SignatureAlgorithmIdentifierFinder sigAlgorithmFinder, ContentVerifierProvider verifierProvider, DigestCalculatorProvider digestProvider)
+    {
+        this.sigNameGenerator = sigNameGenerator;
+        this.sigAlgorithmFinder = sigAlgorithmFinder;
+        this.verifierProvider = verifierProvider;
+        this.digestProvider = digestProvider;
+    }
+
+    public boolean hasAssociatedCertificate()
+    {
+        return verifierProvider.hasAssociatedCertificate();
+    }
+
+    public X509CertificateHolder getAssociatedCertificate()
+    {
+        return verifierProvider.getAssociatedCertificate();
+    }
+
+    public ContentVerifier getContentVerifier(AlgorithmIdentifier signingAlgorithm, AlgorithmIdentifier digestAlgorithm)
+        throws OperatorCreationException
+    {
+        String          signatureName = sigNameGenerator.getSignatureName(digestAlgorithm, signingAlgorithm);
+
+        return verifierProvider.get(sigAlgorithmFinder.find(signatureName));
+    }
+
+    public DigestCalculator getDigestCalculator(AlgorithmIdentifier algorithmIdentifier)
+        throws OperatorCreationException
+    {
+        return digestProvider.get(algorithmIdentifier);
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/SimpleAttributeTableGenerator.java b/bcpkix/src/main/java/org/bouncycastle/cms/SimpleAttributeTableGenerator.java
new file mode 100644
index 0000000..f182431
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/SimpleAttributeTableGenerator.java
@@ -0,0 +1,25 @@
+package org.bouncycastle.cms;
+
+import org.bouncycastle.asn1.cms.AttributeTable;
+
+import java.util.Map;
+
+/**
+ * Basic generator that just returns a preconstructed attribute table
+ */
+public class SimpleAttributeTableGenerator
+    implements CMSAttributeTableGenerator
+{
+    private final AttributeTable attributes;
+
+    public SimpleAttributeTableGenerator(
+        AttributeTable attributes)
+    {
+        this.attributes = attributes;
+    }
+
+    public AttributeTable getAttributes(Map parameters)
+    {
+        return attributes;
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/JcaSignerInfoGeneratorBuilder.java b/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/JcaSignerInfoGeneratorBuilder.java
new file mode 100644
index 0000000..4a0e7ca
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/JcaSignerInfoGeneratorBuilder.java
@@ -0,0 +1,68 @@
+package org.bouncycastle.cms.jcajce;
+
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.X509Certificate;
+
+import org.bouncycastle.cert.X509CertificateHolder;
+import org.bouncycastle.cert.jcajce.JcaX509CertificateHolder;
+import org.bouncycastle.cms.CMSAttributeTableGenerator;
+import org.bouncycastle.cms.SignerInfoGenerator;
+import org.bouncycastle.cms.SignerInfoGeneratorBuilder;
+import org.bouncycastle.operator.ContentSigner;
+import org.bouncycastle.operator.DigestCalculatorProvider;
+import org.bouncycastle.operator.OperatorCreationException;
+
+public class JcaSignerInfoGeneratorBuilder
+{
+    private SignerInfoGeneratorBuilder builder;
+
+    public JcaSignerInfoGeneratorBuilder(DigestCalculatorProvider digestProvider)
+    {
+        builder = new SignerInfoGeneratorBuilder(digestProvider);
+    }
+
+    /**
+     * If the passed in flag is true, the signer signature will be based on the data, not
+     * a collection of signed attributes, and no signed attributes will be included.
+     *
+     * @return the builder object
+     */
+    public JcaSignerInfoGeneratorBuilder setDirectSignature(boolean hasNoSignedAttributes)
+    {
+        builder.setDirectSignature(hasNoSignedAttributes);
+
+        return this;
+    }
+
+    public JcaSignerInfoGeneratorBuilder setSignedAttributeGenerator(CMSAttributeTableGenerator signedGen)
+    {
+        builder.setSignedAttributeGenerator(signedGen);
+
+        return this;
+    }
+
+    public JcaSignerInfoGeneratorBuilder setUnsignedAttributeGenerator(CMSAttributeTableGenerator unsignedGen)
+    {
+        builder.setUnsignedAttributeGenerator(unsignedGen);
+
+        return this;
+    }
+
+    public SignerInfoGenerator build(ContentSigner contentSigner, X509CertificateHolder certHolder)
+        throws OperatorCreationException
+    {
+        return builder.build(contentSigner, certHolder);
+    }
+
+    public SignerInfoGenerator build(ContentSigner contentSigner, byte[] keyIdentifier)
+        throws OperatorCreationException
+    {
+        return builder.build(contentSigner, keyIdentifier);
+    }
+
+    public SignerInfoGenerator build(ContentSigner contentSigner, X509Certificate certificate)
+        throws OperatorCreationException, CertificateEncodingException
+    {
+        return this.build(contentSigner, new JcaX509CertificateHolder(certificate));
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/JcaSignerInfoVerifierBuilder.java b/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/JcaSignerInfoVerifierBuilder.java
new file mode 100644
index 0000000..a805839
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/JcaSignerInfoVerifierBuilder.java
@@ -0,0 +1,180 @@
+package org.bouncycastle.cms.jcajce;
+
+import java.security.Provider;
+import java.security.PublicKey;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+
+import org.bouncycastle.cert.X509CertificateHolder;
+import org.bouncycastle.cms.CMSSignatureAlgorithmNameGenerator;
+import org.bouncycastle.cms.DefaultCMSSignatureAlgorithmNameGenerator;
+import org.bouncycastle.cms.SignerInformationVerifier;
+import org.bouncycastle.operator.ContentVerifierProvider;
+import org.bouncycastle.operator.DefaultSignatureAlgorithmIdentifierFinder;
+import org.bouncycastle.operator.DigestCalculatorProvider;
+import org.bouncycastle.operator.OperatorCreationException;
+import org.bouncycastle.operator.SignatureAlgorithmIdentifierFinder;
+import org.bouncycastle.operator.jcajce.JcaContentVerifierProviderBuilder;
+import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder;
+
+public class JcaSignerInfoVerifierBuilder
+{
+    private Helper helper = new Helper();
+    private DigestCalculatorProvider digestProvider;
+    private CMSSignatureAlgorithmNameGenerator sigAlgNameGen = new DefaultCMSSignatureAlgorithmNameGenerator();
+    private SignatureAlgorithmIdentifierFinder sigAlgIDFinder = new DefaultSignatureAlgorithmIdentifierFinder();
+
+    public JcaSignerInfoVerifierBuilder(DigestCalculatorProvider digestProvider)
+    {
+        this.digestProvider = digestProvider;
+    }
+
+    public JcaSignerInfoVerifierBuilder setProvider(Provider provider)
+    {
+        this.helper = new ProviderHelper(provider);
+
+        return this;
+    }
+
+    public JcaSignerInfoVerifierBuilder setProvider(String providerName)
+    {
+        this.helper = new NamedHelper(providerName);
+
+        return this;
+    }
+
+    /**
+     * Override the default signature algorithm name generator.
+     *
+     * @param sigAlgNameGen the algorithm name generator to use.
+     * @return the current builder.
+     */
+    public JcaSignerInfoVerifierBuilder setSignatureAlgorithmNameGenerator(CMSSignatureAlgorithmNameGenerator sigAlgNameGen)
+    {
+        this.sigAlgNameGen = sigAlgNameGen;
+
+        return this;
+    }
+
+    public JcaSignerInfoVerifierBuilder setSignatureAlgorithmFinder(SignatureAlgorithmIdentifierFinder sigAlgIDFinder)
+    {
+        this.sigAlgIDFinder = sigAlgIDFinder;
+
+        return this;
+    }
+
+    public SignerInformationVerifier build(X509CertificateHolder certHolder)
+        throws OperatorCreationException, CertificateException
+    {
+        return new SignerInformationVerifier(sigAlgNameGen, sigAlgIDFinder, helper.createContentVerifierProvider(certHolder), digestProvider);
+    }
+
+    public SignerInformationVerifier build(X509Certificate certificate)
+        throws OperatorCreationException
+    {
+        return new SignerInformationVerifier(sigAlgNameGen, sigAlgIDFinder, helper.createContentVerifierProvider(certificate), digestProvider);
+    }
+
+    public SignerInformationVerifier build(PublicKey pubKey)
+        throws OperatorCreationException
+    {
+        return new SignerInformationVerifier(sigAlgNameGen, sigAlgIDFinder, helper.createContentVerifierProvider(pubKey), digestProvider);
+    }
+
+    private class Helper
+    {
+        ContentVerifierProvider createContentVerifierProvider(PublicKey publicKey)
+            throws OperatorCreationException
+        {
+            return new JcaContentVerifierProviderBuilder().build(publicKey);
+        }
+
+        ContentVerifierProvider createContentVerifierProvider(X509Certificate certificate)
+            throws OperatorCreationException
+        {
+            return new JcaContentVerifierProviderBuilder().build(certificate);
+        }
+
+        ContentVerifierProvider createContentVerifierProvider(X509CertificateHolder certHolder)
+            throws OperatorCreationException, CertificateException
+        {
+            return new JcaContentVerifierProviderBuilder().build(certHolder);
+        }
+
+        DigestCalculatorProvider createDigestCalculatorProvider()
+            throws OperatorCreationException
+        {
+            return new JcaDigestCalculatorProviderBuilder().build();
+        }
+    }
+
+    private class NamedHelper
+        extends Helper
+    {
+        private final String providerName;
+
+        public NamedHelper(String providerName)
+        {
+            this.providerName = providerName;
+        }
+
+        ContentVerifierProvider createContentVerifierProvider(PublicKey publicKey)
+            throws OperatorCreationException
+        {
+            return new JcaContentVerifierProviderBuilder().setProvider(providerName).build(publicKey);
+        }
+
+        ContentVerifierProvider createContentVerifierProvider(X509Certificate certificate)
+            throws OperatorCreationException
+        {
+            return new JcaContentVerifierProviderBuilder().setProvider(providerName).build(certificate);
+        }
+
+        DigestCalculatorProvider createDigestCalculatorProvider()
+            throws OperatorCreationException
+        {
+            return new JcaDigestCalculatorProviderBuilder().setProvider(providerName).build();
+        }
+
+        ContentVerifierProvider createContentVerifierProvider(X509CertificateHolder certHolder)
+            throws OperatorCreationException, CertificateException
+        {
+            return new JcaContentVerifierProviderBuilder().setProvider(providerName).build(certHolder);
+        }
+    }
+
+    private class ProviderHelper
+        extends Helper
+    {
+        private final Provider provider;
+
+        public ProviderHelper(Provider provider)
+        {
+            this.provider = provider;
+        }
+
+        ContentVerifierProvider createContentVerifierProvider(PublicKey publicKey)
+            throws OperatorCreationException
+        {
+            return new JcaContentVerifierProviderBuilder().setProvider(provider).build(publicKey);
+        }
+
+        ContentVerifierProvider createContentVerifierProvider(X509Certificate certificate)
+            throws OperatorCreationException
+        {
+            return new JcaContentVerifierProviderBuilder().setProvider(provider).build(certificate);
+        }
+
+        DigestCalculatorProvider createDigestCalculatorProvider()
+            throws OperatorCreationException
+        {
+            return new JcaDigestCalculatorProviderBuilder().setProvider(provider).build();
+        }
+
+        ContentVerifierProvider createContentVerifierProvider(X509CertificateHolder certHolder)
+            throws OperatorCreationException, CertificateException
+        {
+            return new JcaContentVerifierProviderBuilder().setProvider(provider).build(certHolder);
+        }
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/JcaSimpleSignerInfoVerifierBuilder.java b/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/JcaSimpleSignerInfoVerifierBuilder.java
new file mode 100644
index 0000000..441f27d
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/JcaSimpleSignerInfoVerifierBuilder.java
@@ -0,0 +1,150 @@
+package org.bouncycastle.cms.jcajce;
+
+import java.security.Provider;
+import java.security.PublicKey;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+
+import org.bouncycastle.cert.X509CertificateHolder;
+import org.bouncycastle.cms.DefaultCMSSignatureAlgorithmNameGenerator;
+import org.bouncycastle.cms.SignerInformationVerifier;
+import org.bouncycastle.operator.ContentVerifierProvider;
+import org.bouncycastle.operator.DefaultSignatureAlgorithmIdentifierFinder;
+import org.bouncycastle.operator.DigestCalculatorProvider;
+import org.bouncycastle.operator.OperatorCreationException;
+import org.bouncycastle.operator.jcajce.JcaContentVerifierProviderBuilder;
+import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder;
+
+public class JcaSimpleSignerInfoVerifierBuilder
+{
+    private Helper helper = new Helper();
+
+    public JcaSimpleSignerInfoVerifierBuilder setProvider(Provider provider)
+    {
+        this.helper = new ProviderHelper(provider);
+
+        return this;
+    }
+
+    public JcaSimpleSignerInfoVerifierBuilder setProvider(String providerName)
+    {
+        this.helper = new NamedHelper(providerName);
+
+        return this;
+    }
+
+    public SignerInformationVerifier build(X509CertificateHolder certHolder)
+        throws OperatorCreationException, CertificateException
+    {
+        return new SignerInformationVerifier(new DefaultCMSSignatureAlgorithmNameGenerator(), new DefaultSignatureAlgorithmIdentifierFinder(), helper.createContentVerifierProvider(certHolder), helper.createDigestCalculatorProvider());
+    }
+
+    public SignerInformationVerifier build(X509Certificate certificate)
+        throws OperatorCreationException
+    {
+        return new SignerInformationVerifier(new DefaultCMSSignatureAlgorithmNameGenerator(), new DefaultSignatureAlgorithmIdentifierFinder(), helper.createContentVerifierProvider(certificate), helper.createDigestCalculatorProvider());
+    }
+
+    public SignerInformationVerifier build(PublicKey pubKey)
+        throws OperatorCreationException
+    {
+        return new SignerInformationVerifier(new DefaultCMSSignatureAlgorithmNameGenerator(), new DefaultSignatureAlgorithmIdentifierFinder(), helper.createContentVerifierProvider(pubKey), helper.createDigestCalculatorProvider());
+    }
+
+    private class Helper
+    {
+        ContentVerifierProvider createContentVerifierProvider(PublicKey publicKey)
+            throws OperatorCreationException
+        {
+            return new JcaContentVerifierProviderBuilder().build(publicKey);
+        }
+
+        ContentVerifierProvider createContentVerifierProvider(X509Certificate certificate)
+            throws OperatorCreationException
+        {
+            return new JcaContentVerifierProviderBuilder().build(certificate);
+        }
+
+        ContentVerifierProvider createContentVerifierProvider(X509CertificateHolder certHolder)
+            throws OperatorCreationException, CertificateException
+        {
+            return new JcaContentVerifierProviderBuilder().build(certHolder);
+        }
+
+        DigestCalculatorProvider createDigestCalculatorProvider()
+            throws OperatorCreationException
+        {
+            return new JcaDigestCalculatorProviderBuilder().build();
+        }
+    }
+
+    private class NamedHelper
+        extends Helper
+    {
+        private final String providerName;
+
+        public NamedHelper(String providerName)
+        {
+            this.providerName = providerName;
+        }
+
+        ContentVerifierProvider createContentVerifierProvider(PublicKey publicKey)
+            throws OperatorCreationException
+        {
+            return new JcaContentVerifierProviderBuilder().setProvider(providerName).build(publicKey);
+        }
+
+        ContentVerifierProvider createContentVerifierProvider(X509Certificate certificate)
+            throws OperatorCreationException
+        {
+            return new JcaContentVerifierProviderBuilder().setProvider(providerName).build(certificate);
+        }
+
+        DigestCalculatorProvider createDigestCalculatorProvider()
+            throws OperatorCreationException
+        {
+            return new JcaDigestCalculatorProviderBuilder().setProvider(providerName).build();
+        }
+
+        ContentVerifierProvider createContentVerifierProvider(X509CertificateHolder certHolder)
+            throws OperatorCreationException, CertificateException
+        {
+            return new JcaContentVerifierProviderBuilder().setProvider(providerName).build(certHolder);
+        }
+    }
+
+    private class ProviderHelper
+        extends Helper
+    {
+        private final Provider provider;
+
+        public ProviderHelper(Provider provider)
+        {
+            this.provider = provider;
+        }
+
+        ContentVerifierProvider createContentVerifierProvider(PublicKey publicKey)
+            throws OperatorCreationException
+        {
+            return new JcaContentVerifierProviderBuilder().setProvider(provider).build(publicKey);
+        }
+
+        ContentVerifierProvider createContentVerifierProvider(X509Certificate certificate)
+            throws OperatorCreationException
+        {
+            return new JcaContentVerifierProviderBuilder().setProvider(provider).build(certificate);
+        }
+
+        DigestCalculatorProvider createDigestCalculatorProvider()
+            throws OperatorCreationException
+        {
+            return new JcaDigestCalculatorProviderBuilder().setProvider(provider).build();
+        }
+
+        ContentVerifierProvider createContentVerifierProvider(X509CertificateHolder certHolder)
+            throws OperatorCreationException, CertificateException
+        {
+            return new JcaContentVerifierProviderBuilder().setProvider(provider).build(certHolder);
+        }
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/operator/ContentSigner.java b/bcpkix/src/main/java/org/bouncycastle/operator/ContentSigner.java
new file mode 100644
index 0000000..fadef60
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/operator/ContentSigner.java
@@ -0,0 +1,27 @@
+package org.bouncycastle.operator;
+
+import java.io.OutputStream;
+
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+
+public interface ContentSigner
+{
+    AlgorithmIdentifier getAlgorithmIdentifier();
+
+    /**
+     * Returns a stream that will accept data for the purpose of calculating
+     * a signature. Use org.bouncycastle.util.io.TeeOutputStream if you want to accumulate
+     * the data on the fly as well.
+     *
+     * @return an OutputStream
+     */
+    OutputStream getOutputStream();
+
+    /**
+     * Returns a signature based on the current data written to the stream, since the
+     * start or the last call to getSignature().
+     *
+     * @return bytes representing the signature.
+     */
+    byte[] getSignature();
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/operator/ContentVerifier.java b/bcpkix/src/main/java/org/bouncycastle/operator/ContentVerifier.java
new file mode 100644
index 0000000..54d9ef1
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/operator/ContentVerifier.java
@@ -0,0 +1,31 @@
+package org.bouncycastle.operator;
+
+import java.io.OutputStream;
+
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+
+public interface ContentVerifier
+{
+    /**
+     * Return the algorithm identifier describing the signature
+     * algorithm and parameters this expander supports.
+     *
+     * @return algorithm oid and parameters.
+     */
+    AlgorithmIdentifier getAlgorithmIdentifier();
+
+    /**
+     * Returns a stream that will accept data for the purpose of calculating
+     * a signature for later verification. Use org.bouncycastle.util.io.TeeOutputStream if you want to accumulate
+     * the data on the fly as well.
+     *
+     * @return an OutputStream
+     */
+    OutputStream getOutputStream();
+
+    /**
+     * @param expected expected value of the signature on the data.
+     * @return true if the signature verifies, false otherwise
+     */
+    boolean verify(byte[] expected);
+}
\ No newline at end of file
diff --git a/bcpkix/src/main/java/org/bouncycastle/operator/ContentVerifierProvider.java b/bcpkix/src/main/java/org/bouncycastle/operator/ContentVerifierProvider.java
new file mode 100644
index 0000000..9594382
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/operator/ContentVerifierProvider.java
@@ -0,0 +1,34 @@
+package org.bouncycastle.operator;
+
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.cert.X509CertificateHolder;
+
+/**
+ * General interface for providers of ContentVerifier objects.
+ */
+public interface ContentVerifierProvider
+{
+    /**
+     * Return whether or not this verifier has a certificate associated with it.
+     *
+     * @return true if there is an associated certificate, false otherwise.
+     */
+    boolean hasAssociatedCertificate();
+
+    /**
+     * Return the associated certificate if there is one.
+     *
+     * @return a holder containing the associated certificate if there is one, null if there is not.
+     */
+    X509CertificateHolder getAssociatedCertificate();
+
+    /**
+     * Return a ContentVerifier that matches the passed in algorithm identifier,
+     *
+     * @param verifierAlgorithmIdentifier the algorithm and parameters required.
+     * @return a matching ContentVerifier
+     * @throws OperatorCreationException if the required ContentVerifier cannot be created.
+     */
+    ContentVerifier get(AlgorithmIdentifier verifierAlgorithmIdentifier)
+        throws OperatorCreationException;
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/operator/DefaultDigestAlgorithmIdentifierFinder.java b/bcpkix/src/main/java/org/bouncycastle/operator/DefaultDigestAlgorithmIdentifierFinder.java
new file mode 100644
index 0000000..82a43a0
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/operator/DefaultDigestAlgorithmIdentifierFinder.java
@@ -0,0 +1,121 @@
+package org.bouncycastle.operator;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.DERNull;
+// BEGIN android-removed
+// import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers;
+// END android-removed
+import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
+import org.bouncycastle.asn1.oiw.OIWObjectIdentifiers;
+import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.bouncycastle.asn1.pkcs.RSASSAPSSparams;
+import org.bouncycastle.asn1.teletrust.TeleTrusTObjectIdentifiers;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
+
+public class DefaultDigestAlgorithmIdentifierFinder
+    implements DigestAlgorithmIdentifierFinder
+{
+    private static Map digestOids = new HashMap();
+    private static Map digestNameToOids = new HashMap();
+
+    static
+    {
+        //
+        // digests
+        //
+        // BEGIN android-removed
+        // digestOids.put(OIWObjectIdentifiers.md4WithRSAEncryption, PKCSObjectIdentifiers.md4);
+        // digestOids.put(OIWObjectIdentifiers.md4WithRSA, PKCSObjectIdentifiers.md4);
+        // END android-removed
+        digestOids.put(OIWObjectIdentifiers.sha1WithRSA, OIWObjectIdentifiers.idSHA1);
+
+        // BEGIN android-removed
+        // digestOids.put(PKCSObjectIdentifiers.sha224WithRSAEncryption, NISTObjectIdentifiers.id_sha224);
+        // END android-removed
+        digestOids.put(PKCSObjectIdentifiers.sha256WithRSAEncryption, NISTObjectIdentifiers.id_sha256);
+        digestOids.put(PKCSObjectIdentifiers.sha384WithRSAEncryption, NISTObjectIdentifiers.id_sha384);
+        digestOids.put(PKCSObjectIdentifiers.sha512WithRSAEncryption, NISTObjectIdentifiers.id_sha512);
+        // BEGIN android-removed
+        // digestOids.put(PKCSObjectIdentifiers.md2WithRSAEncryption, PKCSObjectIdentifiers.md2);
+        // digestOids.put(PKCSObjectIdentifiers.md4WithRSAEncryption, PKCSObjectIdentifiers.md4);
+        // END android-removed
+        digestOids.put(PKCSObjectIdentifiers.md5WithRSAEncryption, PKCSObjectIdentifiers.md5);
+        digestOids.put(PKCSObjectIdentifiers.sha1WithRSAEncryption, OIWObjectIdentifiers.idSHA1);
+
+        digestOids.put(X9ObjectIdentifiers.ecdsa_with_SHA1, OIWObjectIdentifiers.idSHA1);
+        // BEGIN android-removed
+        // digestOids.put(X9ObjectIdentifiers.ecdsa_with_SHA224, NISTObjectIdentifiers.id_sha224);
+        // END android-removed
+        digestOids.put(X9ObjectIdentifiers.ecdsa_with_SHA256, NISTObjectIdentifiers.id_sha256);
+        digestOids.put(X9ObjectIdentifiers.ecdsa_with_SHA384, NISTObjectIdentifiers.id_sha384);
+        digestOids.put(X9ObjectIdentifiers.ecdsa_with_SHA512, NISTObjectIdentifiers.id_sha512);
+        digestOids.put(X9ObjectIdentifiers.id_dsa_with_sha1, OIWObjectIdentifiers.idSHA1);
+
+        // BEGIN android-removed
+        // digestOids.put(NISTObjectIdentifiers.dsa_with_sha224, NISTObjectIdentifiers.id_sha224);
+        // END android-removed
+        digestOids.put(NISTObjectIdentifiers.dsa_with_sha256, NISTObjectIdentifiers.id_sha256);
+        digestOids.put(NISTObjectIdentifiers.dsa_with_sha384, NISTObjectIdentifiers.id_sha384);
+        digestOids.put(NISTObjectIdentifiers.dsa_with_sha512, NISTObjectIdentifiers.id_sha512);
+
+        // BEGIN android-removed
+        // digestOids.put(TeleTrusTObjectIdentifiers.rsaSignatureWithripemd128, TeleTrusTObjectIdentifiers.ripemd128);
+        // digestOids.put(TeleTrusTObjectIdentifiers.rsaSignatureWithripemd160, TeleTrusTObjectIdentifiers.ripemd160);
+        // digestOids.put(TeleTrusTObjectIdentifiers.rsaSignatureWithripemd256, TeleTrusTObjectIdentifiers.ripemd256);
+        //
+        // digestOids.put(CryptoProObjectIdentifiers.gostR3411_94_with_gostR3410_94, CryptoProObjectIdentifiers.gostR3411);
+        // digestOids.put(CryptoProObjectIdentifiers.gostR3411_94_with_gostR3410_2001, CryptoProObjectIdentifiers.gostR3411);
+        // END android-removed
+
+        digestNameToOids.put("SHA-1", OIWObjectIdentifiers.idSHA1);
+        // BEGIN android-removed
+        // digestNameToOids.put("SHA-224", NISTObjectIdentifiers.id_sha224);
+        // END android-removed
+        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("GOST3411", CryptoProObjectIdentifiers.gostR3411);
+        //
+        // digestNameToOids.put("MD2", PKCSObjectIdentifiers.md2);
+        // digestNameToOids.put("MD4", PKCSObjectIdentifiers.md4);
+        // END android-removed
+        digestNameToOids.put("MD5", PKCSObjectIdentifiers.md5);
+
+        // BEGIN android-removed
+        // digestNameToOids.put("RIPEMD128", TeleTrusTObjectIdentifiers.ripemd128);
+        // digestNameToOids.put("RIPEMD160", TeleTrusTObjectIdentifiers.ripemd160);
+        // digestNameToOids.put("RIPEMD256", TeleTrusTObjectIdentifiers.ripemd256);
+        // END android-removed
+    }
+
+    public AlgorithmIdentifier find(AlgorithmIdentifier sigAlgId)
+    {
+        AlgorithmIdentifier digAlgId;
+
+        if (sigAlgId.getAlgorithm().equals(PKCSObjectIdentifiers.id_RSASSA_PSS))
+        {
+            digAlgId = ((RSASSAPSSparams)sigAlgId.getParameters()).getHashAlgorithm();
+        }
+        else
+        {
+            // BEGIN android-changed
+            digAlgId = new AlgorithmIdentifier((ASN1ObjectIdentifier)digestOids.get(sigAlgId.getAlgorithm()), DERNull.INSTANCE);
+            // END android-changed
+        }
+
+        return digAlgId;
+    }
+
+    public AlgorithmIdentifier find(String digAlgName)
+    {
+        // BEGIN android-changed
+        return new AlgorithmIdentifier((ASN1ObjectIdentifier)digestNameToOids.get(digAlgName), DERNull.INSTANCE);
+        // END android-changed
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/operator/DefaultSignatureAlgorithmIdentifierFinder.java b/bcpkix/src/main/java/org/bouncycastle/operator/DefaultSignatureAlgorithmIdentifierFinder.java
new file mode 100644
index 0000000..be3567f
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/operator/DefaultSignatureAlgorithmIdentifierFinder.java
@@ -0,0 +1,262 @@
+package org.bouncycastle.operator;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.DERNull;
+// BEGIN android-removed
+// import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers;
+// END android-removed
+import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
+import org.bouncycastle.asn1.oiw.OIWObjectIdentifiers;
+import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.bouncycastle.asn1.pkcs.RSASSAPSSparams;
+import org.bouncycastle.asn1.teletrust.TeleTrusTObjectIdentifiers;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
+import org.bouncycastle.util.Strings;
+
+public class DefaultSignatureAlgorithmIdentifierFinder
+    implements SignatureAlgorithmIdentifierFinder
+{
+    private static Map algorithms = new HashMap();
+    private static Set noParams = new HashSet();
+    private static Map params = new HashMap();
+    private static Set pkcs15RsaEncryption = new HashSet();
+    private static Map digestOids = new HashMap();
+
+    private static final ASN1ObjectIdentifier ENCRYPTION_RSA = PKCSObjectIdentifiers.rsaEncryption;
+    private static final ASN1ObjectIdentifier ENCRYPTION_DSA = X9ObjectIdentifiers.id_dsa_with_sha1;
+    private static final ASN1ObjectIdentifier ENCRYPTION_ECDSA = X9ObjectIdentifiers.ecdsa_with_SHA1;
+    private static final ASN1ObjectIdentifier ENCRYPTION_RSA_PSS = PKCSObjectIdentifiers.id_RSASSA_PSS;
+    // BEGIN android-removed
+    // private static final ASN1ObjectIdentifier ENCRYPTION_GOST3410 = CryptoProObjectIdentifiers.gostR3410_94;
+    // private static final ASN1ObjectIdentifier ENCRYPTION_ECGOST3410 = CryptoProObjectIdentifiers.gostR3410_2001;
+    // END android-removed
+
+    static
+    {
+        // BEGIN android-removed
+        // algorithms.put("MD2WITHRSAENCRYPTION", PKCSObjectIdentifiers.md2WithRSAEncryption);
+        // algorithms.put("MD2WITHRSA", PKCSObjectIdentifiers.md2WithRSAEncryption);
+        // END android-removed
+        algorithms.put("MD5WITHRSAENCRYPTION", PKCSObjectIdentifiers.md5WithRSAEncryption);
+        algorithms.put("MD5WITHRSA", PKCSObjectIdentifiers.md5WithRSAEncryption);
+        algorithms.put("SHA1WITHRSAENCRYPTION", PKCSObjectIdentifiers.sha1WithRSAEncryption);
+        algorithms.put("SHA1WITHRSA", PKCSObjectIdentifiers.sha1WithRSAEncryption);
+        // BEGIN android-removed
+        // algorithms.put("SHA224WITHRSAENCRYPTION", PKCSObjectIdentifiers.sha224WithRSAEncryption);
+        // algorithms.put("SHA224WITHRSA", PKCSObjectIdentifiers.sha224WithRSAEncryption);
+        // END android-removed
+        algorithms.put("SHA256WITHRSAENCRYPTION", PKCSObjectIdentifiers.sha256WithRSAEncryption);
+        algorithms.put("SHA256WITHRSA", PKCSObjectIdentifiers.sha256WithRSAEncryption);
+        algorithms.put("SHA384WITHRSAENCRYPTION", PKCSObjectIdentifiers.sha384WithRSAEncryption);
+        algorithms.put("SHA384WITHRSA", PKCSObjectIdentifiers.sha384WithRSAEncryption);
+        algorithms.put("SHA512WITHRSAENCRYPTION", PKCSObjectIdentifiers.sha512WithRSAEncryption);
+        algorithms.put("SHA512WITHRSA", PKCSObjectIdentifiers.sha512WithRSAEncryption);
+        algorithms.put("SHA1WITHRSAANDMGF1", PKCSObjectIdentifiers.id_RSASSA_PSS);
+        // BEGIN android-removed
+        // algorithms.put("SHA224WITHRSAANDMGF1", PKCSObjectIdentifiers.id_RSASSA_PSS);
+        // END android-removed
+        algorithms.put("SHA256WITHRSAANDMGF1", PKCSObjectIdentifiers.id_RSASSA_PSS);
+        algorithms.put("SHA384WITHRSAANDMGF1", PKCSObjectIdentifiers.id_RSASSA_PSS);
+        algorithms.put("SHA512WITHRSAANDMGF1", PKCSObjectIdentifiers.id_RSASSA_PSS);
+        // BEGIN android-removed
+        // algorithms.put("RIPEMD160WITHRSAENCRYPTION", TeleTrusTObjectIdentifiers.rsaSignatureWithripemd160);
+        // algorithms.put("RIPEMD160WITHRSA", TeleTrusTObjectIdentifiers.rsaSignatureWithripemd160);
+        // algorithms.put("RIPEMD128WITHRSAENCRYPTION", TeleTrusTObjectIdentifiers.rsaSignatureWithripemd128);
+        // algorithms.put("RIPEMD128WITHRSA", TeleTrusTObjectIdentifiers.rsaSignatureWithripemd128);
+        // algorithms.put("RIPEMD256WITHRSAENCRYPTION", TeleTrusTObjectIdentifiers.rsaSignatureWithripemd256);
+        // algorithms.put("RIPEMD256WITHRSA", TeleTrusTObjectIdentifiers.rsaSignatureWithripemd256);
+        // END android-removed
+        algorithms.put("SHA1WITHDSA", X9ObjectIdentifiers.id_dsa_with_sha1);
+        algorithms.put("DSAWITHSHA1", X9ObjectIdentifiers.id_dsa_with_sha1);
+        // BEGIN android-removed
+        // algorithms.put("SHA224WITHDSA", NISTObjectIdentifiers.dsa_with_sha224);
+        // END android-removed
+        algorithms.put("SHA256WITHDSA", NISTObjectIdentifiers.dsa_with_sha256);
+        algorithms.put("SHA384WITHDSA", NISTObjectIdentifiers.dsa_with_sha384);
+        algorithms.put("SHA512WITHDSA", NISTObjectIdentifiers.dsa_with_sha512);
+        algorithms.put("SHA1WITHECDSA", X9ObjectIdentifiers.ecdsa_with_SHA1);
+        algorithms.put("ECDSAWITHSHA1", X9ObjectIdentifiers.ecdsa_with_SHA1);
+        // BEGIN android-removed
+        // algorithms.put("SHA224WITHECDSA", X9ObjectIdentifiers.ecdsa_with_SHA224);
+        // END android-removed
+        algorithms.put("SHA256WITHECDSA", X9ObjectIdentifiers.ecdsa_with_SHA256);
+        algorithms.put("SHA384WITHECDSA", X9ObjectIdentifiers.ecdsa_with_SHA384);
+        algorithms.put("SHA512WITHECDSA", X9ObjectIdentifiers.ecdsa_with_SHA512);
+        // BEGIN android-removed
+        // algorithms.put("GOST3411WITHGOST3410", CryptoProObjectIdentifiers.gostR3411_94_with_gostR3410_94);
+        // algorithms.put("GOST3411WITHGOST3410-94", CryptoProObjectIdentifiers.gostR3411_94_with_gostR3410_94);
+        // algorithms.put("GOST3411WITHECGOST3410", CryptoProObjectIdentifiers.gostR3411_94_with_gostR3410_2001);
+        // algorithms.put("GOST3411WITHECGOST3410-2001", CryptoProObjectIdentifiers.gostR3411_94_with_gostR3410_2001);
+        // algorithms.put("GOST3411WITHGOST3410-2001", CryptoProObjectIdentifiers.gostR3411_94_with_gostR3410_2001);
+        // END android-removed
+               
+        //
+        // According to RFC 3279, the ASN.1 encoding SHALL (id-dsa-with-sha1) or MUST (ecdsa-with-SHA*) omit the parameters field.
+        // The parameters field SHALL be NULL for RSA based signature algorithms.
+        //
+        noParams.add(X9ObjectIdentifiers.ecdsa_with_SHA1);
+        // BEGIN android-removed
+        // noParams.add(X9ObjectIdentifiers.ecdsa_with_SHA224);
+        // END android-removed
+        noParams.add(X9ObjectIdentifiers.ecdsa_with_SHA256);
+        noParams.add(X9ObjectIdentifiers.ecdsa_with_SHA384);
+        noParams.add(X9ObjectIdentifiers.ecdsa_with_SHA512);
+        noParams.add(X9ObjectIdentifiers.id_dsa_with_sha1);
+        // BEGIN android-removed
+        // noParams.add(NISTObjectIdentifiers.dsa_with_sha224);
+        // END android-removed
+        noParams.add(NISTObjectIdentifiers.dsa_with_sha256);
+        noParams.add(NISTObjectIdentifiers.dsa_with_sha384);
+        noParams.add(NISTObjectIdentifiers.dsa_with_sha512);
+
+        //
+        // RFC 4491
+        //
+        // BEGIN android-removed
+        // noParams.add(CryptoProObjectIdentifiers.gostR3411_94_with_gostR3410_94);
+        // noParams.add(CryptoProObjectIdentifiers.gostR3411_94_with_gostR3410_2001);
+        // END android-removed
+
+        //
+        // PKCS 1.5 encrypted  algorithms
+        //
+        pkcs15RsaEncryption.add(PKCSObjectIdentifiers.sha1WithRSAEncryption);
+        // BEGIN android-removed
+        // pkcs15RsaEncryption.add(PKCSObjectIdentifiers.sha224WithRSAEncryption);
+        // END android-removed
+        pkcs15RsaEncryption.add(PKCSObjectIdentifiers.sha256WithRSAEncryption);
+        pkcs15RsaEncryption.add(PKCSObjectIdentifiers.sha384WithRSAEncryption);
+        pkcs15RsaEncryption.add(PKCSObjectIdentifiers.sha512WithRSAEncryption);
+        // BEGIN android-removed
+        // pkcs15RsaEncryption.add(TeleTrusTObjectIdentifiers.rsaSignatureWithripemd128);
+        // pkcs15RsaEncryption.add(TeleTrusTObjectIdentifiers.rsaSignatureWithripemd160);
+        // pkcs15RsaEncryption.add(TeleTrusTObjectIdentifiers.rsaSignatureWithripemd256);
+        // END android-removed
+
+        //
+        // explicit params
+        //
+        // BEGIN android-changed
+        AlgorithmIdentifier sha1AlgId = new AlgorithmIdentifier(OIWObjectIdentifiers.idSHA1, DERNull.INSTANCE);
+        // END android-changed
+        params.put("SHA1WITHRSAANDMGF1", createPSSParams(sha1AlgId, 20));
+
+        // BEGIN android-removed
+        // // BEGIN android-changed
+        // AlgorithmIdentifier sha224AlgId = new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha224, DERNull.INSTANCE);
+        // // END android-changed
+        // params.put("SHA224WITHRSAANDMGF1", createPSSParams(sha224AlgId, 28));
+        // END android-removed
+
+        // BEGIN android-changed
+        AlgorithmIdentifier sha256AlgId = new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha256, DERNull.INSTANCE);
+        // END android-changed
+        params.put("SHA256WITHRSAANDMGF1", createPSSParams(sha256AlgId, 32));
+
+        // BEGIN android-changed
+        AlgorithmIdentifier sha384AlgId = new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha384, DERNull.INSTANCE);
+        // END android-changed
+        params.put("SHA384WITHRSAANDMGF1", createPSSParams(sha384AlgId, 48));
+
+        // BEGIN android-changed
+        AlgorithmIdentifier sha512AlgId = new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha512, DERNull.INSTANCE);
+        // END android-changed
+        params.put("SHA512WITHRSAANDMGF1", createPSSParams(sha512AlgId, 64));
+
+        //
+        // digests
+        //
+        // BEGIN android-removed
+        // digestOids.put(PKCSObjectIdentifiers.sha224WithRSAEncryption, NISTObjectIdentifiers.id_sha224);
+        // END android-removed
+        digestOids.put(PKCSObjectIdentifiers.sha256WithRSAEncryption, NISTObjectIdentifiers.id_sha256);
+        digestOids.put(PKCSObjectIdentifiers.sha384WithRSAEncryption, NISTObjectIdentifiers.id_sha384);
+        digestOids.put(PKCSObjectIdentifiers.sha512WithRSAEncryption, NISTObjectIdentifiers.id_sha512);
+        // BEGIN android-removed
+        // digestOids.put(PKCSObjectIdentifiers.md2WithRSAEncryption, PKCSObjectIdentifiers.md2);
+        // digestOids.put(PKCSObjectIdentifiers.md4WithRSAEncryption, PKCSObjectIdentifiers.md4);
+        // END android-removed
+        digestOids.put(PKCSObjectIdentifiers.md5WithRSAEncryption, PKCSObjectIdentifiers.md5);
+        digestOids.put(PKCSObjectIdentifiers.sha1WithRSAEncryption, OIWObjectIdentifiers.idSHA1);
+        // BEGIN android-removed
+        // digestOids.put(TeleTrusTObjectIdentifiers.rsaSignatureWithripemd128, TeleTrusTObjectIdentifiers.ripemd128);
+        // digestOids.put(TeleTrusTObjectIdentifiers.rsaSignatureWithripemd160, TeleTrusTObjectIdentifiers.ripemd160);
+        // digestOids.put(TeleTrusTObjectIdentifiers.rsaSignatureWithripemd256, TeleTrusTObjectIdentifiers.ripemd256);
+        // digestOids.put(CryptoProObjectIdentifiers.gostR3411_94_with_gostR3410_94, CryptoProObjectIdentifiers.gostR3411);
+        // digestOids.put(CryptoProObjectIdentifiers.gostR3411_94_with_gostR3410_2001, CryptoProObjectIdentifiers.gostR3411);
+        // END android-removed
+    }
+
+    private static AlgorithmIdentifier generate(String signatureAlgorithm)
+    {
+        AlgorithmIdentifier sigAlgId;
+        AlgorithmIdentifier encAlgId;
+        AlgorithmIdentifier digAlgId;
+
+        String algorithmName = Strings.toUpperCase(signatureAlgorithm);
+        ASN1ObjectIdentifier sigOID = (ASN1ObjectIdentifier)algorithms.get(algorithmName);
+        if (sigOID == null)
+        {
+            throw new IllegalArgumentException("Unknown signature type requested: " + algorithmName);
+        }
+
+        if (noParams.contains(sigOID))
+        {
+            sigAlgId = new AlgorithmIdentifier(sigOID);
+        }
+        else if (params.containsKey(algorithmName))
+        {
+            sigAlgId = new AlgorithmIdentifier(sigOID, (ASN1Encodable)params.get(algorithmName));
+        }
+        else
+        {
+            sigAlgId = new AlgorithmIdentifier(sigOID, DERNull.INSTANCE);
+        }
+
+        if (pkcs15RsaEncryption.contains(sigOID))
+        {
+            // BEGIN android-changed
+            encAlgId = new AlgorithmIdentifier(PKCSObjectIdentifiers.rsaEncryption, DERNull.INSTANCE);
+            // END android-changed
+        }
+        else
+        {
+            encAlgId = sigAlgId;
+        }
+
+        if (sigAlgId.getAlgorithm().equals(PKCSObjectIdentifiers.id_RSASSA_PSS))
+        {
+            digAlgId = ((RSASSAPSSparams)sigAlgId.getParameters()).getHashAlgorithm();
+        }
+        else
+        {
+            // BEGIN android-changed
+            digAlgId = new AlgorithmIdentifier((ASN1ObjectIdentifier)digestOids.get(sigOID), DERNull.INSTANCE);
+            // END android-changed
+        }
+
+        return sigAlgId;
+    }
+
+    private static RSASSAPSSparams createPSSParams(AlgorithmIdentifier hashAlgId, int saltSize)
+    {
+        return new RSASSAPSSparams(
+            hashAlgId,
+            new AlgorithmIdentifier(PKCSObjectIdentifiers.id_mgf1, hashAlgId),
+            new ASN1Integer(saltSize),
+            new ASN1Integer(1));
+    }
+
+    public AlgorithmIdentifier find(String sigAlgName)
+    {
+        return generate(sigAlgName);
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/operator/DigestAlgorithmIdentifierFinder.java b/bcpkix/src/main/java/org/bouncycastle/operator/DigestAlgorithmIdentifierFinder.java
new file mode 100644
index 0000000..b2d57c6
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/operator/DigestAlgorithmIdentifierFinder.java
@@ -0,0 +1,24 @@
+package org.bouncycastle.operator;
+
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+
+public interface DigestAlgorithmIdentifierFinder
+{
+    /**
+     * Find the digest algorithm identifier that matches with
+     * the passed in signature algorithm identifier.
+     *
+     * @param sigAlgId the signature algorithm of interest.
+     * @return an algorithm identifier for the corresponding digest.
+     */
+    AlgorithmIdentifier find(AlgorithmIdentifier sigAlgId);
+
+    /**
+     * Find the algorithm identifier that matches with
+     * the passed in digest name.
+     *
+     * @param digAlgName the name of the digest algorithm of interest.
+     * @return an algorithm identifier for the digest signature.
+     */
+    AlgorithmIdentifier find(String digAlgName);
+}
\ No newline at end of file
diff --git a/bcpkix/src/main/java/org/bouncycastle/operator/DigestCalculator.java b/bcpkix/src/main/java/org/bouncycastle/operator/DigestCalculator.java
new file mode 100644
index 0000000..203e876
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/operator/DigestCalculator.java
@@ -0,0 +1,36 @@
+package org.bouncycastle.operator;
+
+import java.io.OutputStream;
+
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+
+/**
+ * General interface for an operator that is able to calculate a digest from
+ * a stream of output.
+ */
+public interface DigestCalculator
+{
+    /**
+     * Return the algorithm identifier representing the digest implemented by
+     * this calculator.
+     *
+     * @return algorithm id and parameters.
+     */
+    AlgorithmIdentifier getAlgorithmIdentifier();
+
+    /**
+     * Returns a stream that will accept data for the purpose of calculating
+     * a digest. Use org.bouncycastle.util.io.TeeOutputStream if you want to accumulate
+     * the data on the fly as well.
+     *
+     * @return an OutputStream
+     */
+    OutputStream getOutputStream();
+
+    /**
+     * Return the digest calculated on what has been written to the calculator's output stream.
+     *
+     * @return a digest.
+     */
+    byte[] getDigest();
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/operator/DigestCalculatorProvider.java b/bcpkix/src/main/java/org/bouncycastle/operator/DigestCalculatorProvider.java
new file mode 100644
index 0000000..2365270
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/operator/DigestCalculatorProvider.java
@@ -0,0 +1,9 @@
+package org.bouncycastle.operator;
+
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+
+public interface DigestCalculatorProvider
+{
+    DigestCalculator get(AlgorithmIdentifier digestAlgorithmIdentifier)
+        throws OperatorCreationException;
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/operator/OperatorCreationException.java b/bcpkix/src/main/java/org/bouncycastle/operator/OperatorCreationException.java
new file mode 100644
index 0000000..06d3fa0
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/operator/OperatorCreationException.java
@@ -0,0 +1,15 @@
+package org.bouncycastle.operator;
+
+public class OperatorCreationException
+    extends OperatorException
+{
+    public OperatorCreationException(String msg, Throwable cause)
+    {
+        super(msg, cause);
+    }
+
+    public OperatorCreationException(String msg)
+    {
+        super(msg);
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/operator/OperatorException.java b/bcpkix/src/main/java/org/bouncycastle/operator/OperatorException.java
new file mode 100644
index 0000000..a214652
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/operator/OperatorException.java
@@ -0,0 +1,24 @@
+package org.bouncycastle.operator;
+
+public class OperatorException
+    extends Exception
+{
+    private Throwable cause;
+
+    public OperatorException(String msg, Throwable cause)
+    {
+        super(msg);
+
+        this.cause = cause;
+    }
+
+    public OperatorException(String msg)
+    {
+        super(msg);
+    }
+
+    public Throwable getCause()
+    {
+        return cause;
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/operator/OperatorStreamException.java b/bcpkix/src/main/java/org/bouncycastle/operator/OperatorStreamException.java
new file mode 100644
index 0000000..a4534eb
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/operator/OperatorStreamException.java
@@ -0,0 +1,21 @@
+package org.bouncycastle.operator;
+
+import java.io.IOException;
+
+public class OperatorStreamException
+    extends IOException
+{
+    private Throwable cause;
+
+    public OperatorStreamException(String msg, Throwable cause)
+    {
+        super(msg);
+
+        this.cause = cause;
+    }
+
+    public Throwable getCause()
+    {
+        return cause; 
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/operator/RawContentVerifier.java b/bcpkix/src/main/java/org/bouncycastle/operator/RawContentVerifier.java
new file mode 100644
index 0000000..447a27b
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/operator/RawContentVerifier.java
@@ -0,0 +1,17 @@
+package org.bouncycastle.operator;
+
+/**
+ * Interface for ContentVerifiers that also support raw signatures that can be
+ * verified using the digest of the calculated data.
+ */
+public interface RawContentVerifier
+{
+    /**
+     * Verify that the expected signature value was derived from the passed in digest.
+     *
+     * @param digest digest calculated from the content.
+     * @param expected expected value of the signature
+     * @return true if the expected signature is derived from the digest, false otherwise.
+     */
+    boolean verify(byte[] digest, byte[] expected);
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/operator/RuntimeOperatorException.java b/bcpkix/src/main/java/org/bouncycastle/operator/RuntimeOperatorException.java
new file mode 100644
index 0000000..2918b4d
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/operator/RuntimeOperatorException.java
@@ -0,0 +1,19 @@
+package org.bouncycastle.operator;
+
+public class RuntimeOperatorException
+    extends RuntimeException
+{
+    private Throwable cause;
+
+    public RuntimeOperatorException(String msg, Throwable cause)
+    {
+        super(msg);
+
+        this.cause = cause;
+    }
+
+    public Throwable getCause()
+    {
+        return cause;
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/operator/SignatureAlgorithmIdentifierFinder.java b/bcpkix/src/main/java/org/bouncycastle/operator/SignatureAlgorithmIdentifierFinder.java
new file mode 100644
index 0000000..87521dd
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/operator/SignatureAlgorithmIdentifierFinder.java
@@ -0,0 +1,15 @@
+package org.bouncycastle.operator;
+
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+
+public interface SignatureAlgorithmIdentifierFinder
+{
+    /**
+     * Find the signature algorithm identifier that matches with
+     * the passed in signature algorithm name.
+     *
+     * @param sigAlgName the name of the signature algorithm of interest.
+     * @return an algorithm identifier for the corresponding signature.
+     */
+    AlgorithmIdentifier find(String sigAlgName);
+}
\ No newline at end of file
diff --git a/bcpkix/src/main/java/org/bouncycastle/operator/bc/BcDigestCalculatorProvider.java b/bcpkix/src/main/java/org/bouncycastle/operator/bc/BcDigestCalculatorProvider.java
new file mode 100644
index 0000000..233b31b
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/operator/bc/BcDigestCalculatorProvider.java
@@ -0,0 +1,78 @@
+package org.bouncycastle.operator.bc;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.operator.DigestCalculator;
+import org.bouncycastle.operator.DigestCalculatorProvider;
+import org.bouncycastle.operator.OperatorCreationException;
+
+public class BcDigestCalculatorProvider
+    implements DigestCalculatorProvider
+{
+    public DigestCalculator get(final AlgorithmIdentifier algorithm)
+        throws OperatorCreationException
+    {
+        Digest dig = BcUtil.createDigest(algorithm);
+
+        final DigestOutputStream stream = new DigestOutputStream(dig);
+
+        return new DigestCalculator()
+        {
+            public AlgorithmIdentifier getAlgorithmIdentifier()
+            {
+                return algorithm;
+            }
+
+            public OutputStream getOutputStream()
+            {
+                return stream;
+            }
+
+            public byte[] getDigest()
+            {
+                return stream.getDigest();
+            }
+        };
+    }
+
+    private class DigestOutputStream
+        extends OutputStream
+    {
+        private Digest dig;
+
+        DigestOutputStream(Digest dig)
+        {
+            this.dig = dig;
+        }
+
+        public void write(byte[] bytes, int off, int len)
+            throws IOException
+        {
+            dig.update(bytes, off, len);
+        }
+
+        public void write(byte[] bytes)
+            throws IOException
+        {
+            dig.update(bytes, 0, bytes.length);
+        }
+
+        public void write(int b)
+            throws IOException
+        {
+            dig.update((byte)b);
+        }
+
+        byte[] getDigest()
+        {
+            byte[] d = new byte[dig.getDigestSize()];
+
+            dig.doFinal(d, 0);
+
+            return d;
+        }
+    }
+}
\ No newline at end of file
diff --git a/bcpkix/src/main/java/org/bouncycastle/operator/bc/BcUtil.java b/bcpkix/src/main/java/org/bouncycastle/operator/bc/BcUtil.java
new file mode 100644
index 0000000..368c1f3
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/operator/bc/BcUtil.java
@@ -0,0 +1,98 @@
+package org.bouncycastle.operator.bc;
+
+// BEGIN android-removed
+// import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers;
+// END android-removed
+import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
+import org.bouncycastle.asn1.oiw.OIWObjectIdentifiers;
+import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.bouncycastle.asn1.teletrust.TeleTrusTObjectIdentifiers;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.crypto.Digest;
+// BEGIN android-removed
+// import org.bouncycastle.crypto.digests.GOST3411Digest;
+// import org.bouncycastle.crypto.digests.MD2Digest;
+// import org.bouncycastle.crypto.digests.MD4Digest;
+// END android-removed
+import org.bouncycastle.crypto.digests.MD5Digest;
+// BEGIN android-removed
+// import org.bouncycastle.crypto.digests.RIPEMD128Digest;
+// import org.bouncycastle.crypto.digests.RIPEMD160Digest;
+// import org.bouncycastle.crypto.digests.RIPEMD256Digest;
+// END android-removed
+import org.bouncycastle.crypto.digests.SHA1Digest;
+// BEGIN android-removed
+// import org.bouncycastle.crypto.digests.SHA224Digest;
+// END android-removed
+import org.bouncycastle.crypto.digests.SHA256Digest;
+import org.bouncycastle.crypto.digests.SHA384Digest;
+import org.bouncycastle.crypto.digests.SHA512Digest;
+import org.bouncycastle.operator.OperatorCreationException;
+
+class BcUtil
+{
+    static Digest createDigest(AlgorithmIdentifier digAlg)
+        throws OperatorCreationException
+    {
+        Digest dig;
+
+        if (digAlg.getAlgorithm().equals(OIWObjectIdentifiers.idSHA1))
+        {
+            dig = new SHA1Digest();
+        }
+        // BEGIN android-removed
+        // else if (digAlg.getAlgorithm().equals(NISTObjectIdentifiers.id_sha224))
+        // {
+        //     dig = new SHA224Digest();
+        // }
+        // END android-removed
+        else if (digAlg.getAlgorithm().equals(NISTObjectIdentifiers.id_sha256))
+        {
+            dig = new SHA256Digest();
+        }
+        else if (digAlg.getAlgorithm().equals(NISTObjectIdentifiers.id_sha384))
+        {
+            dig = new SHA384Digest();
+        }
+        else if (digAlg.getAlgorithm().equals(NISTObjectIdentifiers.id_sha512))
+        {
+            dig = new SHA512Digest();
+        }
+        else if (digAlg.getAlgorithm().equals(PKCSObjectIdentifiers.md5))
+        {
+            dig = new MD5Digest();
+        }
+        // BEGIN android-removed
+        // else if (digAlg.getAlgorithm().equals(PKCSObjectIdentifiers.md4))
+        // {
+        //     dig = new MD4Digest();
+        // }
+        // else if (digAlg.getAlgorithm().equals(PKCSObjectIdentifiers.md2))
+        // {
+        //     dig = new MD2Digest();
+        // }
+        // else if (digAlg.getAlgorithm().equals(CryptoProObjectIdentifiers.gostR3411))
+        // {
+        //     dig = new GOST3411Digest();
+        // }
+        // else if (digAlg.getAlgorithm().equals(TeleTrusTObjectIdentifiers.ripemd128))
+        // {
+        //     dig = new RIPEMD128Digest();
+        // }
+        // else if (digAlg.getAlgorithm().equals(TeleTrusTObjectIdentifiers.ripemd160))
+        // {
+        //     dig = new RIPEMD160Digest();
+        // }
+        // else if (digAlg.getAlgorithm().equals(TeleTrusTObjectIdentifiers.ripemd256))
+        // {
+        //     dig = new RIPEMD256Digest();
+        // }
+        // END android-removed
+        else
+        {
+            throw new OperatorCreationException("cannot recognise digest");
+        }
+
+        return dig;
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/operator/jcajce/JcaContentSignerBuilder.java b/bcpkix/src/main/java/org/bouncycastle/operator/jcajce/JcaContentSignerBuilder.java
new file mode 100644
index 0000000..04885c0
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/operator/jcajce/JcaContentSignerBuilder.java
@@ -0,0 +1,160 @@
+package org.bouncycastle.operator.jcajce;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.security.GeneralSecurityException;
+import java.security.PrivateKey;
+import java.security.Provider;
+import java.security.SecureRandom;
+import java.security.Signature;
+import java.security.SignatureException;
+
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.jcajce.DefaultJcaJceHelper;
+import org.bouncycastle.jcajce.NamedJcaJceHelper;
+import org.bouncycastle.jcajce.ProviderJcaJceHelper;
+import org.bouncycastle.operator.ContentSigner;
+import org.bouncycastle.operator.DefaultSignatureAlgorithmIdentifierFinder;
+import org.bouncycastle.operator.OperatorCreationException;
+import org.bouncycastle.operator.OperatorStreamException;
+import org.bouncycastle.operator.RuntimeOperatorException;
+
+public class JcaContentSignerBuilder
+{
+    private OperatorHelper helper = new OperatorHelper(new DefaultJcaJceHelper());
+    private SecureRandom random;
+    private String signatureAlgorithm;
+    private AlgorithmIdentifier sigAlgId;
+
+    public JcaContentSignerBuilder(String signatureAlgorithm)
+    {
+        this.signatureAlgorithm = signatureAlgorithm;
+        this.sigAlgId = new DefaultSignatureAlgorithmIdentifierFinder().find(signatureAlgorithm);
+    }
+
+    public JcaContentSignerBuilder setProvider(Provider provider)
+    {
+        this.helper = new OperatorHelper(new ProviderJcaJceHelper(provider));
+
+        return this;
+    }
+
+    public JcaContentSignerBuilder setProvider(String providerName)
+    {
+        this.helper = new OperatorHelper(new NamedJcaJceHelper(providerName));
+
+        return this;
+    }
+
+    public JcaContentSignerBuilder setSecureRandom(SecureRandom random)
+    {
+        this.random = random;
+
+        return this;
+    }
+
+    public ContentSigner build(PrivateKey privateKey)
+        throws OperatorCreationException
+    {
+        try
+        {
+            final Signature sig = helper.createSignature(sigAlgId);
+
+            if (random != null)
+            {
+                sig.initSign(privateKey, random);
+            }
+            else
+            {
+                sig.initSign(privateKey);
+            }
+
+            return new ContentSigner()
+            {
+                private SignatureOutputStream stream = new SignatureOutputStream(sig);
+
+                public AlgorithmIdentifier getAlgorithmIdentifier()
+                {
+                    return sigAlgId;
+                }
+
+                public OutputStream getOutputStream()
+                {
+                    return stream;
+                }
+
+                public byte[] getSignature()
+                {
+                    try
+                    {
+                        return stream.getSignature();
+                    }
+                    catch (SignatureException e)
+                    {
+                        throw new RuntimeOperatorException("exception obtaining signature: " + e.getMessage(), e);
+                    }
+                }
+            };
+        }
+        catch (GeneralSecurityException e)
+        {
+            throw new OperatorCreationException("cannot create signer: " + e.getMessage(), e);
+        }
+    }
+
+    private class SignatureOutputStream
+        extends OutputStream
+    {
+        private Signature sig;
+
+        SignatureOutputStream(Signature sig)
+        {
+            this.sig = sig;
+        }
+
+        public void write(byte[] bytes, int off, int len)
+            throws IOException
+        {
+            try
+            {
+                sig.update(bytes, off, len);
+            }
+            catch (SignatureException e)
+            {
+                throw new OperatorStreamException("exception in content signer: " + e.getMessage(), e);
+            }
+        }
+
+        public void write(byte[] bytes)
+            throws IOException
+        {
+            try
+            {
+                sig.update(bytes);
+            }
+            catch (SignatureException e)
+            {
+                throw new OperatorStreamException("exception in content signer: " + e.getMessage(), e);
+            }
+        }
+
+        public void write(int b)
+            throws IOException
+        {
+            try
+            {
+                sig.update((byte)b);
+            }
+            catch (SignatureException e)
+            {
+                throw new OperatorStreamException("exception in content signer: " + e.getMessage(), e);
+            }
+        }
+
+        byte[] getSignature()
+            throws SignatureException
+        {
+            return sig.sign();
+        }
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/operator/jcajce/JcaContentVerifierProviderBuilder.java b/bcpkix/src/main/java/org/bouncycastle/operator/jcajce/JcaContentVerifierProviderBuilder.java
new file mode 100644
index 0000000..56c3771
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/operator/jcajce/JcaContentVerifierProviderBuilder.java
@@ -0,0 +1,305 @@
+package org.bouncycastle.operator.jcajce;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.security.GeneralSecurityException;
+import java.security.Provider;
+import java.security.PublicKey;
+import java.security.Signature;
+import java.security.SignatureException;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.cert.X509CertificateHolder;
+import org.bouncycastle.cert.jcajce.JcaX509CertificateHolder;
+import org.bouncycastle.jcajce.DefaultJcaJceHelper;
+import org.bouncycastle.jcajce.NamedJcaJceHelper;
+import org.bouncycastle.jcajce.ProviderJcaJceHelper;
+import org.bouncycastle.operator.ContentVerifier;
+import org.bouncycastle.operator.ContentVerifierProvider;
+import org.bouncycastle.operator.OperatorCreationException;
+import org.bouncycastle.operator.OperatorStreamException;
+import org.bouncycastle.operator.RawContentVerifier;
+import org.bouncycastle.operator.RuntimeOperatorException;
+
+public class JcaContentVerifierProviderBuilder
+{
+    private OperatorHelper helper = new OperatorHelper(new DefaultJcaJceHelper());
+
+    public JcaContentVerifierProviderBuilder()
+    {
+    }
+
+    public JcaContentVerifierProviderBuilder setProvider(Provider provider)
+    {
+        this.helper = new OperatorHelper(new ProviderJcaJceHelper(provider));
+
+        return this;
+    }
+
+    public JcaContentVerifierProviderBuilder setProvider(String providerName)
+    {
+        this.helper = new OperatorHelper(new NamedJcaJceHelper(providerName));
+
+        return this;
+    }
+
+    public ContentVerifierProvider build(X509CertificateHolder certHolder)
+        throws OperatorCreationException, CertificateException
+    {
+        return build(helper.convertCertificate(certHolder));
+    }
+
+    public ContentVerifierProvider build(final X509Certificate certificate)
+        throws OperatorCreationException
+    {
+        final X509CertificateHolder certHolder;
+
+        try
+        {
+            certHolder = new JcaX509CertificateHolder(certificate);
+        }
+        catch (CertificateEncodingException e)
+        {
+            throw new OperatorCreationException("cannot process certificate: " + e.getMessage(), e);
+        }
+
+        return new ContentVerifierProvider()
+        {
+            private SignatureOutputStream stream;
+
+            public boolean hasAssociatedCertificate()
+            {
+                return true;
+            }
+
+            public X509CertificateHolder getAssociatedCertificate()
+            {
+                return certHolder;
+            }
+
+            public ContentVerifier get(AlgorithmIdentifier algorithm)
+                throws OperatorCreationException
+            {
+                try
+                {
+                    Signature sig = helper.createSignature(algorithm);
+
+                    sig.initVerify(certificate.getPublicKey());
+
+                    stream = new SignatureOutputStream(sig);
+                }
+                catch (GeneralSecurityException e)
+                {
+                    throw new OperatorCreationException("exception on setup: " + e, e);
+                }
+
+                Signature rawSig = createRawSig(algorithm, certificate.getPublicKey());
+
+                if (rawSig != null)
+                {
+                    return new RawSigVerifier(algorithm, stream, rawSig);
+                }
+                else
+                {
+                    return new SigVerifier(algorithm, stream);
+                }
+            }
+        };
+    }
+
+    public ContentVerifierProvider build(final PublicKey publicKey)
+        throws OperatorCreationException
+    {
+        return new ContentVerifierProvider()
+        {
+            public boolean hasAssociatedCertificate()
+            {
+                return false;
+            }
+
+            public X509CertificateHolder getAssociatedCertificate()
+            {
+                return null;
+            }
+
+            public ContentVerifier get(AlgorithmIdentifier algorithm)
+                throws OperatorCreationException
+            {
+                SignatureOutputStream stream = createSignatureStream(algorithm, publicKey);
+
+                Signature rawSig = createRawSig(algorithm, publicKey);
+
+                if (rawSig != null)
+                {
+                    return new RawSigVerifier(algorithm, stream, rawSig);
+                }
+                else
+                {
+                    return new SigVerifier(algorithm, stream);
+                }
+            }
+        };
+    }
+
+    private SignatureOutputStream createSignatureStream(AlgorithmIdentifier algorithm, PublicKey publicKey)
+        throws OperatorCreationException
+    {
+        try
+        {
+            Signature sig = helper.createSignature(algorithm);
+
+            sig.initVerify(publicKey);
+
+            return new SignatureOutputStream(sig);
+        }
+        catch (GeneralSecurityException e)
+        {
+            throw new OperatorCreationException("exception on setup: " + e, e);
+        }
+    }
+
+    private Signature createRawSig(AlgorithmIdentifier algorithm, PublicKey publicKey)
+    {
+        Signature rawSig;
+        try
+        {
+            rawSig = helper.createRawSignature(algorithm);
+
+            if (rawSig != null)
+            {
+                rawSig.initVerify(publicKey);
+            }
+        }
+        catch (Exception e)
+        {
+            rawSig = null;
+        }
+        return rawSig;
+    }
+
+    private class SigVerifier
+        implements ContentVerifier
+    {
+        private SignatureOutputStream stream;
+        private AlgorithmIdentifier algorithm;
+
+        SigVerifier(AlgorithmIdentifier algorithm, SignatureOutputStream stream)
+        {
+            this.algorithm = algorithm;
+            this.stream = stream;
+        }
+
+        public AlgorithmIdentifier getAlgorithmIdentifier()
+        {
+            return algorithm;
+        }
+
+        public OutputStream getOutputStream()
+        {
+            if (stream == null)
+            {
+                throw new IllegalStateException("verifier not initialised");
+            }
+
+            return stream;
+        }
+
+        public boolean verify(byte[] expected)
+        {
+            try
+            {
+                return stream.verify(expected);
+            }
+            catch (SignatureException e)
+            {
+                throw new RuntimeOperatorException("exception obtaining signature: " + e.getMessage(), e);
+            }
+        }
+    }
+
+    private class RawSigVerifier
+        extends SigVerifier
+        implements RawContentVerifier
+    {
+        private Signature rawSignature;
+
+        RawSigVerifier(AlgorithmIdentifier algorithm, SignatureOutputStream stream, Signature rawSignature)
+        {
+            super(algorithm, stream);
+            this.rawSignature = rawSignature;
+        }
+
+        public boolean verify(byte[] digest, byte[] expected)
+        {
+            try
+            {
+                rawSignature.update(digest);
+
+                return rawSignature.verify(expected);
+            }
+            catch (SignatureException e)
+            {
+                throw new RuntimeOperatorException("exception obtaining raw signature: " + e.getMessage(), e);
+            }
+        }
+    }
+
+    private class SignatureOutputStream
+        extends OutputStream
+    {
+        private Signature sig;
+
+        SignatureOutputStream(Signature sig)
+        {
+            this.sig = sig;
+        }
+
+        public void write(byte[] bytes, int off, int len)
+            throws IOException
+        {
+            try
+            {
+                sig.update(bytes, off, len);
+            }
+            catch (SignatureException e)
+            {
+                throw new OperatorStreamException("exception in content signer: " + e.getMessage(), e);
+            }
+        }
+
+        public void write(byte[] bytes)
+            throws IOException
+        {
+            try
+            {
+                sig.update(bytes);
+            }
+            catch (SignatureException e)
+            {
+                throw new OperatorStreamException("exception in content signer: " + e.getMessage(), e);
+            }
+        }
+
+        public void write(int b)
+            throws IOException
+        {
+            try
+            {
+                sig.update((byte)b);
+            }
+            catch (SignatureException e)
+            {
+                throw new OperatorStreamException("exception in content signer: " + e.getMessage(), e);
+            }
+        }
+
+        boolean verify(byte[] expected)
+            throws SignatureException
+        {
+            return sig.verify(expected);
+        }
+    }
+}
\ No newline at end of file
diff --git a/bcpkix/src/main/java/org/bouncycastle/operator/jcajce/JcaDigestCalculatorProviderBuilder.java b/bcpkix/src/main/java/org/bouncycastle/operator/jcajce/JcaDigestCalculatorProviderBuilder.java
new file mode 100644
index 0000000..6f59cd0
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/operator/jcajce/JcaDigestCalculatorProviderBuilder.java
@@ -0,0 +1,114 @@
+package org.bouncycastle.operator.jcajce;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.security.GeneralSecurityException;
+import java.security.MessageDigest;
+import java.security.Provider;
+
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.jcajce.DefaultJcaJceHelper;
+import org.bouncycastle.jcajce.NamedJcaJceHelper;
+import org.bouncycastle.jcajce.ProviderJcaJceHelper;
+import org.bouncycastle.operator.DigestCalculator;
+import org.bouncycastle.operator.DigestCalculatorProvider;
+import org.bouncycastle.operator.OperatorCreationException;
+
+public class JcaDigestCalculatorProviderBuilder
+{
+    private OperatorHelper helper = new OperatorHelper(new DefaultJcaJceHelper());
+
+    public JcaDigestCalculatorProviderBuilder()
+    {
+    }
+
+    public JcaDigestCalculatorProviderBuilder setProvider(Provider provider)
+    {
+        this.helper = new OperatorHelper(new ProviderJcaJceHelper(provider));
+
+        return this;
+    }
+
+    public JcaDigestCalculatorProviderBuilder setProvider(String providerName)
+    {
+        this.helper = new OperatorHelper(new NamedJcaJceHelper(providerName));
+
+        return this;
+    }
+
+    public DigestCalculatorProvider build()
+        throws OperatorCreationException
+    {
+        return new DigestCalculatorProvider()
+        {
+            public DigestCalculator get(final AlgorithmIdentifier algorithm)
+                throws OperatorCreationException
+            {
+                final DigestOutputStream stream;
+
+                try
+                {
+                    MessageDigest dig = helper.createDigest(algorithm);
+
+                    stream = new DigestOutputStream(dig);
+                }
+                catch (GeneralSecurityException e)
+                {
+                    throw new OperatorCreationException("exception on setup: " + e, e);
+                }
+
+                return new DigestCalculator()
+                {
+                    public AlgorithmIdentifier getAlgorithmIdentifier()
+                    {
+                        return algorithm;
+                    }
+                    
+                    public OutputStream getOutputStream()
+                    {
+                        return stream;
+                    }
+
+                    public byte[] getDigest()
+                    {
+                        return stream.getDigest();
+                    }
+                };
+            }
+        };
+    }
+
+    private class DigestOutputStream
+        extends OutputStream
+    {
+        private MessageDigest dig;
+
+        DigestOutputStream(MessageDigest dig)
+        {
+            this.dig = dig;
+        }
+
+        public void write(byte[] bytes, int off, int len)
+            throws IOException
+        {
+            dig.update(bytes, off, len);
+        }
+
+        public void write(byte[] bytes)
+            throws IOException
+        {
+           dig.update(bytes);
+        }
+
+        public void write(int b)
+            throws IOException
+        {
+           dig.update((byte)b);
+        }
+
+        byte[] getDigest()
+        {
+            return dig.digest();
+        }
+    }
+}
\ No newline at end of file
diff --git a/bcpkix/src/main/java/org/bouncycastle/operator/jcajce/OperatorHelper.java b/bcpkix/src/main/java/org/bouncycastle/operator/jcajce/OperatorHelper.java
new file mode 100644
index 0000000..28221f4
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/operator/jcajce/OperatorHelper.java
@@ -0,0 +1,408 @@
+package org.bouncycastle.operator.jcajce;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.security.AlgorithmParameters;
+import java.security.GeneralSecurityException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.Signature;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.security.spec.PSSParameterSpec;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.crypto.Cipher;
+
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.DERNull;
+// BEGIN android-removed
+// import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers;
+// END android-removed
+import org.bouncycastle.asn1.kisa.KISAObjectIdentifiers;
+import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
+import org.bouncycastle.asn1.ntt.NTTObjectIdentifiers;
+import org.bouncycastle.asn1.oiw.OIWObjectIdentifiers;
+import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.bouncycastle.asn1.pkcs.RSASSAPSSparams;
+import org.bouncycastle.asn1.teletrust.TeleTrusTObjectIdentifiers;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
+import org.bouncycastle.cert.X509CertificateHolder;
+import org.bouncycastle.jcajce.JcaJceHelper;
+import org.bouncycastle.operator.OperatorCreationException;
+
+class OperatorHelper
+{
+    private static final Map oids = new HashMap();
+    private static final Map asymmetricWrapperAlgNames = new HashMap();
+    private static final Map symmetricWrapperAlgNames = new HashMap();
+    private static final Map symmetricKeyAlgNames = new HashMap();
+
+    static
+    {
+        //
+        // reverse mappings
+        //
+        oids.put(new ASN1ObjectIdentifier("1.2.840.113549.1.1.5"), "SHA1WITHRSA");
+        // BEGIN android-removed
+        // oids.put(PKCSObjectIdentifiers.sha224WithRSAEncryption, "SHA224WITHRSA");
+        // END android-removed
+        oids.put(PKCSObjectIdentifiers.sha256WithRSAEncryption, "SHA256WITHRSA");
+        oids.put(PKCSObjectIdentifiers.sha384WithRSAEncryption, "SHA384WITHRSA");
+        oids.put(PKCSObjectIdentifiers.sha512WithRSAEncryption, "SHA512WITHRSA");
+        // BEGIN android-removed
+        // oids.put(CryptoProObjectIdentifiers.gostR3411_94_with_gostR3410_94, "GOST3411WITHGOST3410");
+        // oids.put(CryptoProObjectIdentifiers.gostR3411_94_with_gostR3410_2001, "GOST3411WITHECGOST3410");
+        // END android-removed
+
+        oids.put(new ASN1ObjectIdentifier("1.2.840.113549.1.1.4"), "MD5WITHRSA");
+        // BEGIN android-removed
+        // oids.put(new ASN1ObjectIdentifier("1.2.840.113549.1.1.2"), "MD2WITHRSA");
+        // END android-removed
+        oids.put(new ASN1ObjectIdentifier("1.2.840.10040.4.3"), "SHA1WITHDSA");
+        oids.put(X9ObjectIdentifiers.ecdsa_with_SHA1, "SHA1WITHECDSA");
+        // BEGIN android-removed
+        // oids.put(X9ObjectIdentifiers.ecdsa_with_SHA224, "SHA224WITHECDSA");
+        // END android-removed
+        oids.put(X9ObjectIdentifiers.ecdsa_with_SHA256, "SHA256WITHECDSA");
+        oids.put(X9ObjectIdentifiers.ecdsa_with_SHA384, "SHA384WITHECDSA");
+        oids.put(X9ObjectIdentifiers.ecdsa_with_SHA512, "SHA512WITHECDSA");
+        oids.put(OIWObjectIdentifiers.sha1WithRSA, "SHA1WITHRSA");
+        oids.put(OIWObjectIdentifiers.dsaWithSHA1, "SHA1WITHDSA");
+        // BEGIN android-removed
+        // oids.put(NISTObjectIdentifiers.dsa_with_sha224, "SHA224WITHDSA");
+        // END android-removed
+        oids.put(NISTObjectIdentifiers.dsa_with_sha256, "SHA256WITHDSA");
+
+        asymmetricWrapperAlgNames.put(PKCSObjectIdentifiers.rsaEncryption, "RSA/ECB/PKCS1Padding");
+
+        symmetricWrapperAlgNames.put(PKCSObjectIdentifiers.id_alg_CMS3DESwrap, "DESEDEWrap");
+        symmetricWrapperAlgNames.put(PKCSObjectIdentifiers.id_alg_CMSRC2wrap, "RC2Wrap");
+        symmetricWrapperAlgNames.put(NISTObjectIdentifiers.id_aes128_wrap, "AESWrap");
+        symmetricWrapperAlgNames.put(NISTObjectIdentifiers.id_aes192_wrap, "AESWrap");
+        symmetricWrapperAlgNames.put(NISTObjectIdentifiers.id_aes256_wrap, "AESWrap");
+        symmetricWrapperAlgNames.put(NTTObjectIdentifiers.id_camellia128_wrap, "CamelliaWrap");
+        symmetricWrapperAlgNames.put(NTTObjectIdentifiers.id_camellia192_wrap, "CamelliaWrap");
+        symmetricWrapperAlgNames.put(NTTObjectIdentifiers.id_camellia256_wrap, "CamelliaWrap");
+        symmetricWrapperAlgNames.put(KISAObjectIdentifiers.id_npki_app_cmsSeed_wrap, "SEEDWrap");
+        symmetricWrapperAlgNames.put(PKCSObjectIdentifiers.des_EDE3_CBC, "DESede");
+
+        symmetricKeyAlgNames.put(NISTObjectIdentifiers.aes, "AES");
+        symmetricKeyAlgNames.put(NISTObjectIdentifiers.id_aes128_CBC, "AES");
+        symmetricKeyAlgNames.put(NISTObjectIdentifiers.id_aes192_CBC, "AES");
+        symmetricKeyAlgNames.put(NISTObjectIdentifiers.id_aes256_CBC, "AES");
+        symmetricKeyAlgNames.put(PKCSObjectIdentifiers.des_EDE3_CBC, "DESede");
+        symmetricKeyAlgNames.put(PKCSObjectIdentifiers.RC2_CBC, "RC2");
+    }
+
+    private JcaJceHelper helper;
+
+    OperatorHelper(JcaJceHelper helper)
+    {
+        this.helper = helper;
+    }
+
+    Cipher createAsymmetricWrapper(ASN1ObjectIdentifier algorithm, Map extraAlgNames)
+        throws OperatorCreationException
+    {
+        try
+        {
+            String cipherName = null;
+
+            if (!extraAlgNames.isEmpty())
+            {
+                cipherName = (String)extraAlgNames.get(algorithm);
+            }
+
+            if (cipherName == null)
+            {
+                cipherName = (String)asymmetricWrapperAlgNames.get(algorithm);
+            }
+
+            if (cipherName != null)
+            {
+                try
+                {
+                    // this is reversed as the Sun policy files now allow unlimited strength RSA
+                    return helper.createCipher(cipherName);
+                }
+                catch (NoSuchAlgorithmException e)
+                {
+                    // try alternate for RSA
+                    if (cipherName.equals("RSA/ECB/PKCS1Padding"))
+                    {
+                        try
+                        {
+                            return helper.createCipher("RSA/NONE/PKCS1Padding");
+                        }
+                        catch (NoSuchAlgorithmException ex)
+                        {
+                            // Ignore
+                        }
+                    }
+                    // Ignore
+                }
+            }
+
+            return helper.createCipher(algorithm.getId());
+        }
+        catch (GeneralSecurityException e)
+        {
+            throw new OperatorCreationException("cannot create cipher: " + e.getMessage(), e);
+        }
+    }
+
+    Cipher createSymmetricWrapper(ASN1ObjectIdentifier algorithm)
+        throws OperatorCreationException
+    {
+        try
+        {
+            String cipherName = (String)symmetricWrapperAlgNames.get(algorithm);
+
+            if (cipherName != null)
+            {
+                try
+                {
+                    // this is reversed as the Sun policy files now allow unlimited strength RSA
+                    return helper.createCipher(cipherName);
+                }
+                catch (NoSuchAlgorithmException e)
+                {
+                    // Ignore
+                }
+            }
+            return helper.createCipher(algorithm.getId());
+        }
+        catch (GeneralSecurityException e)
+        {
+            throw new OperatorCreationException("cannot create cipher: " + e.getMessage(), e);
+        }
+    }
+
+    MessageDigest createDigest(AlgorithmIdentifier digAlgId)
+        throws GeneralSecurityException
+    {
+        MessageDigest dig;
+
+        try
+        {
+            dig = helper.createDigest(getDigestAlgName(digAlgId.getAlgorithm()));
+        }
+        catch (NoSuchAlgorithmException e)
+        {
+            //
+            // try an alternate
+            //
+            if (oids.get(digAlgId.getAlgorithm()) != null)
+            {
+                String  digestAlgorithm = (String)oids.get(digAlgId.getAlgorithm());
+
+                dig = helper.createDigest(digestAlgorithm);
+            }
+            else
+            {
+                throw e;
+            }
+        }
+
+        return dig;
+    }
+
+    Signature createSignature(AlgorithmIdentifier sigAlgId)
+        throws GeneralSecurityException
+    {
+        Signature   sig;
+
+        try
+        {
+            sig = helper.createSignature(getSignatureName(sigAlgId));
+        }
+        catch (NoSuchAlgorithmException e)
+        {
+            //
+            // try an alternate
+            //
+            if (oids.get(sigAlgId.getAlgorithm()) != null)
+            {
+                String  signatureAlgorithm = (String)oids.get(sigAlgId.getAlgorithm());
+
+                sig = helper.createSignature(signatureAlgorithm);
+            }
+            else
+            {
+                throw e;
+            }
+        }
+
+        return sig;
+    }
+
+    public Signature createRawSignature(AlgorithmIdentifier algorithm)
+    {
+        Signature   sig;
+
+        try
+        {
+            String algName = getSignatureName(algorithm);
+
+            algName = "NONE" + algName.substring(algName.indexOf("WITH"));
+
+            sig = helper.createSignature(algName);
+
+            // RFC 4056
+            // When the id-RSASSA-PSS algorithm identifier is used for a signature,
+            // the AlgorithmIdentifier parameters field MUST contain RSASSA-PSS-params.
+            if (algorithm.getAlgorithm().equals(PKCSObjectIdentifiers.id_RSASSA_PSS))
+            {
+                AlgorithmParameters params = helper.createAlgorithmParameters(algName);
+
+                params.init(algorithm.getParameters().toASN1Primitive().getEncoded(), "ASN.1");
+
+                PSSParameterSpec spec = (PSSParameterSpec)params.getParameterSpec(PSSParameterSpec.class);
+                sig.setParameter(spec);
+            }
+        }
+        catch (Exception e)
+        {
+            return null;
+        }
+
+        return sig;
+    }
+
+    private static String getSignatureName(
+        AlgorithmIdentifier sigAlgId)
+    {
+        ASN1Encodable params = sigAlgId.getParameters();
+
+        if (params != null && !DERNull.INSTANCE.equals(params))
+        {
+            if (sigAlgId.getAlgorithm().equals(PKCSObjectIdentifiers.id_RSASSA_PSS))
+            {
+                RSASSAPSSparams rsaParams = RSASSAPSSparams.getInstance(params);
+                return getDigestAlgName(rsaParams.getHashAlgorithm().getAlgorithm()) + "WITHRSAANDMGF1";
+            }
+        }
+
+        if (oids.containsKey(sigAlgId.getAlgorithm()))
+        {
+            return (String)oids.get(sigAlgId.getAlgorithm());
+        }
+
+        return sigAlgId.getAlgorithm().getId();
+    }
+
+    private static String getDigestAlgName(
+        ASN1ObjectIdentifier digestAlgOID)
+    {
+        if (PKCSObjectIdentifiers.md5.equals(digestAlgOID))
+        {
+            return "MD5";
+        }
+        else if (OIWObjectIdentifiers.idSHA1.equals(digestAlgOID))
+        {
+            return "SHA1";
+        }
+        // BEGIN android-removed
+        // else if (NISTObjectIdentifiers.id_sha224.equals(digestAlgOID))
+        // {
+        //     return "SHA224";
+        // }
+        // END android-removed
+        else if (NISTObjectIdentifiers.id_sha256.equals(digestAlgOID))
+        {
+            return "SHA256";
+        }
+        else if (NISTObjectIdentifiers.id_sha384.equals(digestAlgOID))
+        {
+            return "SHA384";
+        }
+        else if (NISTObjectIdentifiers.id_sha512.equals(digestAlgOID))
+        {
+            return "SHA512";
+        }
+        // BEGIN android-removed
+        // else if (TeleTrusTObjectIdentifiers.ripemd128.equals(digestAlgOID))
+        // {
+        //     return "RIPEMD128";
+        // }
+        // else if (TeleTrusTObjectIdentifiers.ripemd160.equals(digestAlgOID))
+        // {
+        //     return "RIPEMD160";
+        // }
+        // else if (TeleTrusTObjectIdentifiers.ripemd256.equals(digestAlgOID))
+        // {
+        //     return "RIPEMD256";
+        // }
+        // else if (CryptoProObjectIdentifiers.gostR3411.equals(digestAlgOID))
+        // {
+        //     return "GOST3411";
+        // }
+        // END android-removed
+        else
+        {
+            return digestAlgOID.getId();
+        }
+    }
+
+    public X509Certificate convertCertificate(X509CertificateHolder certHolder)
+        throws CertificateException
+    {
+
+        try
+        {
+            CertificateFactory certFact = helper.createCertificateFactory("X.509");
+
+            return (X509Certificate)certFact.generateCertificate(new ByteArrayInputStream(certHolder.getEncoded()));
+        }
+        catch (IOException e)
+        {
+            throw new OpCertificateException("cannot get encoded form of certificate: " + e.getMessage(), e);
+        }
+        catch (NoSuchAlgorithmException e)
+        {
+            throw new OpCertificateException("cannot create certificate factory: " + e.getMessage(), e);
+        }
+        catch (NoSuchProviderException e)
+        {
+            throw new OpCertificateException("cannot find factory provider: " + e.getMessage(), e);
+        }
+    }
+
+    // TODO: put somewhere public so cause easily accessed
+    private static class OpCertificateException
+        extends CertificateException
+    {
+        private Throwable cause;
+
+        public OpCertificateException(String msg, Throwable cause)
+        {
+            super(msg);
+
+            this.cause = cause;
+        }
+
+        public Throwable getCause()
+        {
+            return cause;
+        }
+    }
+
+    String getKeyAlgorithmName(ASN1ObjectIdentifier oid)
+    {
+
+        String name = (String)symmetricKeyAlgNames.get(oid);
+
+        if (name != null)
+        {
+            return name;
+        }
+
+        return oid.getId();
+    }
+}
diff --git a/src/main/java/org/bouncycastle/asn1/ASN1ApplicationSpecificParser.java b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1ApplicationSpecificParser.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/ASN1ApplicationSpecificParser.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/ASN1ApplicationSpecificParser.java
diff --git a/src/main/java/org/bouncycastle/asn1/ASN1Boolean.java b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1Boolean.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/ASN1Boolean.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/ASN1Boolean.java
diff --git a/src/main/java/org/bouncycastle/asn1/ASN1Choice.java b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1Choice.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/ASN1Choice.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/ASN1Choice.java
diff --git a/src/main/java/org/bouncycastle/asn1/ASN1Encodable.java b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1Encodable.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/ASN1Encodable.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/ASN1Encodable.java
diff --git a/src/main/java/org/bouncycastle/asn1/ASN1EncodableVector.java b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1EncodableVector.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/ASN1EncodableVector.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/ASN1EncodableVector.java
diff --git a/src/main/java/org/bouncycastle/asn1/ASN1Encoding.java b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1Encoding.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/ASN1Encoding.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/ASN1Encoding.java
diff --git a/src/main/java/org/bouncycastle/asn1/ASN1Enumerated.java b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1Enumerated.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/ASN1Enumerated.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/ASN1Enumerated.java
diff --git a/src/main/java/org/bouncycastle/asn1/ASN1Exception.java b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1Exception.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/ASN1Exception.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/ASN1Exception.java
diff --git a/src/main/java/org/bouncycastle/asn1/ASN1GeneralizedTime.java b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1GeneralizedTime.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/ASN1GeneralizedTime.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/ASN1GeneralizedTime.java
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/ASN1Generator.java b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1Generator.java
new file mode 100644
index 0000000..50cb705
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1Generator.java
@@ -0,0 +1,15 @@
+package org.bouncycastle.asn1;
+
+import java.io.OutputStream;
+
+public abstract class ASN1Generator
+{
+    protected OutputStream _out;
+    
+    public ASN1Generator(OutputStream out)
+    {
+        _out = out;
+    }
+    
+    public abstract OutputStream getRawOutputStream();
+}
diff --git a/src/main/java/org/bouncycastle/asn1/ASN1InputStream.java b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1InputStream.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/ASN1InputStream.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/ASN1InputStream.java
diff --git a/src/main/java/org/bouncycastle/asn1/ASN1Integer.java b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1Integer.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/ASN1Integer.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/ASN1Integer.java
diff --git a/src/main/java/org/bouncycastle/asn1/ASN1Null.java b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1Null.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/ASN1Null.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/ASN1Null.java
diff --git a/src/main/java/org/bouncycastle/asn1/ASN1Object.java b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1Object.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/ASN1Object.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/ASN1Object.java
diff --git a/src/main/java/org/bouncycastle/asn1/ASN1ObjectIdentifier.java b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1ObjectIdentifier.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/ASN1ObjectIdentifier.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/ASN1ObjectIdentifier.java
diff --git a/src/main/java/org/bouncycastle/asn1/ASN1OctetString.java b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1OctetString.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/ASN1OctetString.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/ASN1OctetString.java
diff --git a/src/main/java/org/bouncycastle/asn1/ASN1OctetStringParser.java b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1OctetStringParser.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/ASN1OctetStringParser.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/ASN1OctetStringParser.java
diff --git a/src/main/java/org/bouncycastle/asn1/ASN1OutputStream.java b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1OutputStream.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/ASN1OutputStream.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/ASN1OutputStream.java
diff --git a/src/main/java/org/bouncycastle/asn1/ASN1ParsingException.java b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1ParsingException.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/ASN1ParsingException.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/ASN1ParsingException.java
diff --git a/src/main/java/org/bouncycastle/asn1/ASN1Primitive.java b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1Primitive.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/ASN1Primitive.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/ASN1Primitive.java
diff --git a/src/main/java/org/bouncycastle/asn1/ASN1Sequence.java b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1Sequence.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/ASN1Sequence.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/ASN1Sequence.java
diff --git a/src/main/java/org/bouncycastle/asn1/ASN1SequenceParser.java b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1SequenceParser.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/ASN1SequenceParser.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/ASN1SequenceParser.java
diff --git a/src/main/java/org/bouncycastle/asn1/ASN1Set.java b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1Set.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/ASN1Set.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/ASN1Set.java
diff --git a/src/main/java/org/bouncycastle/asn1/ASN1SetParser.java b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1SetParser.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/ASN1SetParser.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/ASN1SetParser.java
diff --git a/src/main/java/org/bouncycastle/asn1/ASN1StreamParser.java b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1StreamParser.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/ASN1StreamParser.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/ASN1StreamParser.java
diff --git a/src/main/java/org/bouncycastle/asn1/ASN1String.java b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1String.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/ASN1String.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/ASN1String.java
diff --git a/src/main/java/org/bouncycastle/asn1/ASN1TaggedObject.java b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1TaggedObject.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/ASN1TaggedObject.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/ASN1TaggedObject.java
diff --git a/src/main/java/org/bouncycastle/asn1/ASN1TaggedObjectParser.java b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1TaggedObjectParser.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/ASN1TaggedObjectParser.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/ASN1TaggedObjectParser.java
diff --git a/src/main/java/org/bouncycastle/asn1/ASN1UTCTime.java b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1UTCTime.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/ASN1UTCTime.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/ASN1UTCTime.java
diff --git a/src/main/java/org/bouncycastle/asn1/BERApplicationSpecific.java b/bcprov/src/main/java/org/bouncycastle/asn1/BERApplicationSpecific.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/BERApplicationSpecific.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/BERApplicationSpecific.java
diff --git a/src/main/java/org/bouncycastle/asn1/BERApplicationSpecificParser.java b/bcprov/src/main/java/org/bouncycastle/asn1/BERApplicationSpecificParser.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/BERApplicationSpecificParser.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/BERApplicationSpecificParser.java
diff --git a/src/main/java/org/bouncycastle/asn1/BERConstructedOctetString.java b/bcprov/src/main/java/org/bouncycastle/asn1/BERConstructedOctetString.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/BERConstructedOctetString.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/BERConstructedOctetString.java
diff --git a/src/main/java/org/bouncycastle/asn1/BERFactory.java b/bcprov/src/main/java/org/bouncycastle/asn1/BERFactory.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/BERFactory.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/BERFactory.java
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/BERGenerator.java b/bcprov/src/main/java/org/bouncycastle/asn1/BERGenerator.java
new file mode 100644
index 0000000..ef7f9a3
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/BERGenerator.java
@@ -0,0 +1,100 @@
+package org.bouncycastle.asn1;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+public class BERGenerator
+    extends ASN1Generator
+{
+    private boolean      _tagged = false;
+    private boolean      _isExplicit;
+    private int          _tagNo;
+    
+    protected BERGenerator(
+        OutputStream out)
+    {
+        super(out);
+    }
+
+    public BERGenerator(
+        OutputStream out,
+        int tagNo,
+        boolean isExplicit) 
+    {
+        super(out);
+        
+        _tagged = true;
+        _isExplicit = isExplicit;
+        _tagNo = tagNo;
+    }
+
+    public OutputStream getRawOutputStream()
+    {
+        return _out;
+    }
+    
+    private void writeHdr(
+        int tag)
+        throws IOException
+    {
+        _out.write(tag);
+        _out.write(0x80);
+    }
+    
+    protected void writeBERHeader(
+        int tag) 
+        throws IOException
+    {
+        if (_tagged)
+        {
+            int tagNum = _tagNo | BERTags.TAGGED;
+
+            if (_isExplicit)
+            {
+                writeHdr(tagNum | BERTags.CONSTRUCTED);
+                writeHdr(tag);
+            }
+            else
+            {   
+                if ((tag & BERTags.CONSTRUCTED) != 0)
+                {
+                    writeHdr(tagNum | BERTags.CONSTRUCTED);
+                }
+                else
+                {
+                    writeHdr(tagNum);
+                }
+            }
+        }
+        else
+        {
+            writeHdr(tag);
+        }
+    }
+    
+    protected void writeBERBody(
+        InputStream contentStream)
+        throws IOException
+    {
+        int ch;
+        
+        while ((ch = contentStream.read()) >= 0)
+        {
+            _out.write(ch);
+        }
+    }
+
+    protected void writeBEREnd()
+        throws IOException
+    {
+        _out.write(0x00);
+        _out.write(0x00);
+        
+        if (_tagged && _isExplicit)  // write extra end for tag header
+        {
+            _out.write(0x00);
+            _out.write(0x00);
+        }
+    }
+}
diff --git a/src/main/java/org/bouncycastle/asn1/BEROctetString.java b/bcprov/src/main/java/org/bouncycastle/asn1/BEROctetString.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/BEROctetString.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/BEROctetString.java
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/BEROctetStringGenerator.java b/bcprov/src/main/java/org/bouncycastle/asn1/BEROctetStringGenerator.java
new file mode 100644
index 0000000..b8df94a
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/BEROctetStringGenerator.java
@@ -0,0 +1,102 @@
+package org.bouncycastle.asn1;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+public class BEROctetStringGenerator
+    extends BERGenerator
+{
+    public BEROctetStringGenerator(OutputStream out) 
+        throws IOException
+    {
+        super(out);
+        
+        writeBERHeader(BERTags.CONSTRUCTED | BERTags.OCTET_STRING);
+    }
+
+    public BEROctetStringGenerator(
+        OutputStream out,
+        int tagNo,
+        boolean isExplicit) 
+        throws IOException
+    {
+        super(out, tagNo, isExplicit);
+        
+        writeBERHeader(BERTags.CONSTRUCTED | BERTags.OCTET_STRING);
+    }
+    
+    public OutputStream getOctetOutputStream()
+    {
+        return getOctetOutputStream(new byte[1000]); // limit for CER encoding.
+    }
+
+    public OutputStream getOctetOutputStream(
+        byte[] buf)
+    {
+        return new BufferedBEROctetStream(buf);
+    }
+   
+    private class BufferedBEROctetStream
+        extends OutputStream
+    {
+        private byte[] _buf;
+        private int    _off;
+        private DEROutputStream _derOut;
+
+        BufferedBEROctetStream(
+            byte[] buf)
+        {
+            _buf = buf;
+            _off = 0;
+            _derOut = new DEROutputStream(_out);
+        }
+        
+        public void write(
+            int b)
+            throws IOException
+        {
+            _buf[_off++] = (byte)b;
+
+            if (_off == _buf.length)
+            {
+                DEROctetString.encode(_derOut, _buf);
+                _off = 0;
+            }
+        }
+
+        public void write(byte[] b, int off, int len) throws IOException
+        {
+            while (len > 0)
+            {
+                int numToCopy = Math.min(len, _buf.length - _off);
+                System.arraycopy(b, off, _buf, _off, numToCopy);
+
+                _off += numToCopy;
+                if (_off < _buf.length)
+                {
+                    break;
+                }
+
+                DEROctetString.encode(_derOut, _buf);
+                _off = 0;
+
+                off += numToCopy;
+                len -= numToCopy;
+            }
+        }
+
+        public void close() 
+            throws IOException
+        {
+            if (_off != 0)
+            {
+                byte[] bytes = new byte[_off];
+                System.arraycopy(_buf, 0, bytes, 0, _off);
+                
+                DEROctetString.encode(_derOut, bytes);
+            }
+            
+             writeBEREnd();
+        }
+    }
+}
diff --git a/src/main/java/org/bouncycastle/asn1/BEROctetStringParser.java b/bcprov/src/main/java/org/bouncycastle/asn1/BEROctetStringParser.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/BEROctetStringParser.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/BEROctetStringParser.java
diff --git a/src/main/java/org/bouncycastle/asn1/BEROutputStream.java b/bcprov/src/main/java/org/bouncycastle/asn1/BEROutputStream.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/BEROutputStream.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/BEROutputStream.java
diff --git a/src/main/java/org/bouncycastle/asn1/BERSequence.java b/bcprov/src/main/java/org/bouncycastle/asn1/BERSequence.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/BERSequence.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/BERSequence.java
diff --git a/src/main/java/org/bouncycastle/asn1/BERSequenceParser.java b/bcprov/src/main/java/org/bouncycastle/asn1/BERSequenceParser.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/BERSequenceParser.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/BERSequenceParser.java
diff --git a/src/main/java/org/bouncycastle/asn1/BERSet.java b/bcprov/src/main/java/org/bouncycastle/asn1/BERSet.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/BERSet.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/BERSet.java
diff --git a/src/main/java/org/bouncycastle/asn1/BERSetParser.java b/bcprov/src/main/java/org/bouncycastle/asn1/BERSetParser.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/BERSetParser.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/BERSetParser.java
diff --git a/src/main/java/org/bouncycastle/asn1/BERTaggedObject.java b/bcprov/src/main/java/org/bouncycastle/asn1/BERTaggedObject.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/BERTaggedObject.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/BERTaggedObject.java
diff --git a/src/main/java/org/bouncycastle/asn1/BERTaggedObjectParser.java b/bcprov/src/main/java/org/bouncycastle/asn1/BERTaggedObjectParser.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/BERTaggedObjectParser.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/BERTaggedObjectParser.java
diff --git a/src/main/java/org/bouncycastle/asn1/BERTags.java b/bcprov/src/main/java/org/bouncycastle/asn1/BERTags.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/BERTags.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/BERTags.java
diff --git a/src/main/java/org/bouncycastle/asn1/ConstructedOctetStream.java b/bcprov/src/main/java/org/bouncycastle/asn1/ConstructedOctetStream.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/ConstructedOctetStream.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/ConstructedOctetStream.java
diff --git a/src/main/java/org/bouncycastle/asn1/DERApplicationSpecific.java b/bcprov/src/main/java/org/bouncycastle/asn1/DERApplicationSpecific.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/DERApplicationSpecific.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/DERApplicationSpecific.java
diff --git a/src/main/java/org/bouncycastle/asn1/DERBMPString.java b/bcprov/src/main/java/org/bouncycastle/asn1/DERBMPString.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/DERBMPString.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/DERBMPString.java
diff --git a/src/main/java/org/bouncycastle/asn1/DERBitString.java b/bcprov/src/main/java/org/bouncycastle/asn1/DERBitString.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/DERBitString.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/DERBitString.java
diff --git a/src/main/java/org/bouncycastle/asn1/DERBoolean.java b/bcprov/src/main/java/org/bouncycastle/asn1/DERBoolean.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/DERBoolean.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/DERBoolean.java
diff --git a/src/main/java/org/bouncycastle/asn1/DEREncodableVector.java b/bcprov/src/main/java/org/bouncycastle/asn1/DEREncodableVector.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/DEREncodableVector.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/DEREncodableVector.java
diff --git a/src/main/java/org/bouncycastle/asn1/DEREnumerated.java b/bcprov/src/main/java/org/bouncycastle/asn1/DEREnumerated.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/DEREnumerated.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/DEREnumerated.java
diff --git a/src/main/java/org/bouncycastle/asn1/DERExternal.java b/bcprov/src/main/java/org/bouncycastle/asn1/DERExternal.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/DERExternal.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/DERExternal.java
diff --git a/src/main/java/org/bouncycastle/asn1/DERExternalParser.java b/bcprov/src/main/java/org/bouncycastle/asn1/DERExternalParser.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/DERExternalParser.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/DERExternalParser.java
diff --git a/src/main/java/org/bouncycastle/asn1/DERFactory.java b/bcprov/src/main/java/org/bouncycastle/asn1/DERFactory.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/DERFactory.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/DERFactory.java
diff --git a/src/main/java/org/bouncycastle/asn1/DERGeneralString.java b/bcprov/src/main/java/org/bouncycastle/asn1/DERGeneralString.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/DERGeneralString.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/DERGeneralString.java
diff --git a/src/main/java/org/bouncycastle/asn1/DERGeneralizedTime.java b/bcprov/src/main/java/org/bouncycastle/asn1/DERGeneralizedTime.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/DERGeneralizedTime.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/DERGeneralizedTime.java
diff --git a/src/main/java/org/bouncycastle/asn1/DERIA5String.java b/bcprov/src/main/java/org/bouncycastle/asn1/DERIA5String.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/DERIA5String.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/DERIA5String.java
diff --git a/src/main/java/org/bouncycastle/asn1/DERInteger.java b/bcprov/src/main/java/org/bouncycastle/asn1/DERInteger.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/DERInteger.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/DERInteger.java
diff --git a/src/main/java/org/bouncycastle/asn1/DERNull.java b/bcprov/src/main/java/org/bouncycastle/asn1/DERNull.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/DERNull.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/DERNull.java
diff --git a/src/main/java/org/bouncycastle/asn1/DERNumericString.java b/bcprov/src/main/java/org/bouncycastle/asn1/DERNumericString.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/DERNumericString.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/DERNumericString.java
diff --git a/src/main/java/org/bouncycastle/asn1/DERObjectIdentifier.java b/bcprov/src/main/java/org/bouncycastle/asn1/DERObjectIdentifier.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/DERObjectIdentifier.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/DERObjectIdentifier.java
diff --git a/src/main/java/org/bouncycastle/asn1/DEROctetString.java b/bcprov/src/main/java/org/bouncycastle/asn1/DEROctetString.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/DEROctetString.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/DEROctetString.java
diff --git a/src/main/java/org/bouncycastle/asn1/DEROctetStringParser.java b/bcprov/src/main/java/org/bouncycastle/asn1/DEROctetStringParser.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/DEROctetStringParser.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/DEROctetStringParser.java
diff --git a/src/main/java/org/bouncycastle/asn1/DEROutputStream.java b/bcprov/src/main/java/org/bouncycastle/asn1/DEROutputStream.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/DEROutputStream.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/DEROutputStream.java
diff --git a/src/main/java/org/bouncycastle/asn1/DERPrintableString.java b/bcprov/src/main/java/org/bouncycastle/asn1/DERPrintableString.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/DERPrintableString.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/DERPrintableString.java
diff --git a/src/main/java/org/bouncycastle/asn1/DERSequence.java b/bcprov/src/main/java/org/bouncycastle/asn1/DERSequence.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/DERSequence.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/DERSequence.java
diff --git a/src/main/java/org/bouncycastle/asn1/DERSequenceParser.java b/bcprov/src/main/java/org/bouncycastle/asn1/DERSequenceParser.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/DERSequenceParser.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/DERSequenceParser.java
diff --git a/src/main/java/org/bouncycastle/asn1/DERSet.java b/bcprov/src/main/java/org/bouncycastle/asn1/DERSet.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/DERSet.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/DERSet.java
diff --git a/src/main/java/org/bouncycastle/asn1/DERSetParser.java b/bcprov/src/main/java/org/bouncycastle/asn1/DERSetParser.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/DERSetParser.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/DERSetParser.java
diff --git a/src/main/java/org/bouncycastle/asn1/DERT61String.java b/bcprov/src/main/java/org/bouncycastle/asn1/DERT61String.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/DERT61String.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/DERT61String.java
diff --git a/src/main/java/org/bouncycastle/asn1/DERTaggedObject.java b/bcprov/src/main/java/org/bouncycastle/asn1/DERTaggedObject.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/DERTaggedObject.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/DERTaggedObject.java
diff --git a/src/main/java/org/bouncycastle/asn1/DERTags.java b/bcprov/src/main/java/org/bouncycastle/asn1/DERTags.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/DERTags.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/DERTags.java
diff --git a/src/main/java/org/bouncycastle/asn1/DERUTCTime.java b/bcprov/src/main/java/org/bouncycastle/asn1/DERUTCTime.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/DERUTCTime.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/DERUTCTime.java
diff --git a/src/main/java/org/bouncycastle/asn1/DERUTF8String.java b/bcprov/src/main/java/org/bouncycastle/asn1/DERUTF8String.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/DERUTF8String.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/DERUTF8String.java
diff --git a/src/main/java/org/bouncycastle/asn1/DERUniversalString.java b/bcprov/src/main/java/org/bouncycastle/asn1/DERUniversalString.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/DERUniversalString.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/DERUniversalString.java
diff --git a/src/main/java/org/bouncycastle/asn1/DERVisibleString.java b/bcprov/src/main/java/org/bouncycastle/asn1/DERVisibleString.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/DERVisibleString.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/DERVisibleString.java
diff --git a/src/main/java/org/bouncycastle/asn1/DLOutputStream.java b/bcprov/src/main/java/org/bouncycastle/asn1/DLOutputStream.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/DLOutputStream.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/DLOutputStream.java
diff --git a/src/main/java/org/bouncycastle/asn1/DLSequence.java b/bcprov/src/main/java/org/bouncycastle/asn1/DLSequence.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/DLSequence.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/DLSequence.java
diff --git a/src/main/java/org/bouncycastle/asn1/DLSet.java b/bcprov/src/main/java/org/bouncycastle/asn1/DLSet.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/DLSet.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/DLSet.java
diff --git a/src/main/java/org/bouncycastle/asn1/DLTaggedObject.java b/bcprov/src/main/java/org/bouncycastle/asn1/DLTaggedObject.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/DLTaggedObject.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/DLTaggedObject.java
diff --git a/src/main/java/org/bouncycastle/asn1/DefiniteLengthInputStream.java b/bcprov/src/main/java/org/bouncycastle/asn1/DefiniteLengthInputStream.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/DefiniteLengthInputStream.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/DefiniteLengthInputStream.java
diff --git a/src/main/java/org/bouncycastle/asn1/InMemoryRepresentable.java b/bcprov/src/main/java/org/bouncycastle/asn1/InMemoryRepresentable.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/InMemoryRepresentable.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/InMemoryRepresentable.java
diff --git a/src/main/java/org/bouncycastle/asn1/IndefiniteLengthInputStream.java b/bcprov/src/main/java/org/bouncycastle/asn1/IndefiniteLengthInputStream.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/IndefiniteLengthInputStream.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/IndefiniteLengthInputStream.java
diff --git a/src/main/java/org/bouncycastle/asn1/LazyConstructionEnumeration.java b/bcprov/src/main/java/org/bouncycastle/asn1/LazyConstructionEnumeration.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/LazyConstructionEnumeration.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/LazyConstructionEnumeration.java
diff --git a/src/main/java/org/bouncycastle/asn1/LazyEncodedSequence.java b/bcprov/src/main/java/org/bouncycastle/asn1/LazyEncodedSequence.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/LazyEncodedSequence.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/LazyEncodedSequence.java
diff --git a/src/main/java/org/bouncycastle/asn1/LimitedInputStream.java b/bcprov/src/main/java/org/bouncycastle/asn1/LimitedInputStream.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/LimitedInputStream.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/LimitedInputStream.java
diff --git a/src/main/java/org/bouncycastle/asn1/OIDTokenizer.java b/bcprov/src/main/java/org/bouncycastle/asn1/OIDTokenizer.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/OIDTokenizer.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/OIDTokenizer.java
diff --git a/src/main/java/org/bouncycastle/asn1/StreamUtil.java b/bcprov/src/main/java/org/bouncycastle/asn1/StreamUtil.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/StreamUtil.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/StreamUtil.java
diff --git a/src/main/java/org/bouncycastle/asn1/bc/BCObjectIdentifiers.java b/bcprov/src/main/java/org/bouncycastle/asn1/bc/BCObjectIdentifiers.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/bc/BCObjectIdentifiers.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/bc/BCObjectIdentifiers.java
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cms/Attribute.java b/bcprov/src/main/java/org/bouncycastle/asn1/cms/Attribute.java
new file mode 100644
index 0000000..b5a2f34
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/cms/Attribute.java
@@ -0,0 +1,100 @@
+package org.bouncycastle.asn1.cms;
+
+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.DERObjectIdentifier;
+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 instanceof Attribute)
+        {
+            return (Attribute)o;
+        }
+        
+        if (o != null)
+        {
+            return new Attribute(ASN1Sequence.getInstance(o));
+        }
+
+        return null;
+    }
+    
+    private Attribute(
+        ASN1Sequence seq)
+    {
+        attrType = (ASN1ObjectIdentifier)seq.getObjectAt(0);
+        attrValues = (ASN1Set)seq.getObjectAt(1);
+    }
+
+    /**
+     * @deprecated use ASN1ObjectIdentifier
+     */
+    public Attribute(
+        DERObjectIdentifier attrType,
+        ASN1Set             attrValues)
+    {
+        this.attrType = new ASN1ObjectIdentifier(attrType.getId());
+        this.attrValues = attrValues;
+    }
+
+    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/cms/AttributeTable.java b/bcprov/src/main/java/org/bouncycastle/asn1/cms/AttributeTable.java
new file mode 100644
index 0000000..4c88c7b
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/cms/AttributeTable.java
@@ -0,0 +1,248 @@
+package org.bouncycastle.asn1.cms;
+
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Vector;
+
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1Set;
+import org.bouncycastle.asn1.DERObjectIdentifier;
+import org.bouncycastle.asn1.DERSet;
+
+public class AttributeTable
+{
+    private Hashtable attributes = new Hashtable();
+
+    public AttributeTable(
+        Hashtable  attrs)
+    {
+        attributes = copyTable(attrs);
+    }
+
+    public AttributeTable(
+        ASN1EncodableVector v)
+    {
+        for (int i = 0; i != v.size(); i++)
+        {
+            Attribute   a = Attribute.getInstance(v.get(i));
+
+            addAttribute(a.getAttrType(), a);
+        }
+    }
+
+    public AttributeTable(
+        ASN1Set    s)
+    {
+        for (int i = 0; i != s.size(); i++)
+        {
+            Attribute   a = Attribute.getInstance(s.getObjectAt(i));
+
+            addAttribute(a.getAttrType(), a);
+        }
+    }
+
+    public AttributeTable(
+        Attributes    attrs)
+    {
+        this(ASN1Set.getInstance(attrs.toASN1Primitive()));
+    }
+
+    private void addAttribute(
+        ASN1ObjectIdentifier oid,
+        Attribute           a)
+    {
+        Object value = attributes.get(oid);
+        
+        if (value == null)
+        {
+            attributes.put(oid, a);
+        }
+        else
+        {
+            Vector v;
+            
+            if (value instanceof Attribute)
+            {
+                v = new Vector();
+                
+                v.addElement(value);
+                v.addElement(a);
+            }
+            else
+            {
+                v = (Vector)value;
+            
+                v.addElement(a);
+            }
+            
+            attributes.put(oid, v);
+        }
+    }
+
+    /**
+     * @deprecated use ASN1ObjectIdentifier
+     */
+    public Attribute get(DERObjectIdentifier oid)
+    {
+        return get(new ASN1ObjectIdentifier(oid.getId()));
+    }
+
+    /**
+     * Return the first attribute matching the OBJECT IDENTIFIER oid.
+     * 
+     * @param oid type of attribute required.
+     * @return first attribute found of type oid.
+     */
+    public Attribute get(
+        ASN1ObjectIdentifier oid)
+    {
+        Object value = attributes.get(oid);
+        
+        if (value instanceof Vector)
+        {
+            return (Attribute)((Vector)value).elementAt(0);
+        }
+        
+        return (Attribute)value;
+    }
+
+     /**
+     * @deprecated use ASN1ObjectIdentifier
+     */
+    public ASN1EncodableVector getAll(DERObjectIdentifier oid)
+    {
+        return getAll(new ASN1ObjectIdentifier(oid.getId()));
+    }
+
+    /**
+     * Return all the attributes matching the OBJECT IDENTIFIER oid. The vector will be 
+     * empty if there are no attributes of the required type present.
+     * 
+     * @param oid type of attribute required.
+     * @return a vector of all the attributes found of type oid.
+     */
+    public ASN1EncodableVector getAll(
+        ASN1ObjectIdentifier oid)
+    {
+        ASN1EncodableVector v = new ASN1EncodableVector();
+        
+        Object value = attributes.get(oid);
+        
+        if (value instanceof Vector)
+        {
+            Enumeration e = ((Vector)value).elements();
+            
+            while (e.hasMoreElements())
+            {
+                v.add((Attribute)e.nextElement());
+            }
+        }
+        else if (value != null)
+        {
+            v.add((Attribute)value);
+        }
+        
+        return v;
+    }
+
+    public int size()
+    {
+        int size = 0;
+
+        for (Enumeration en = attributes.elements(); en.hasMoreElements();)
+        {
+            Object o = en.nextElement();
+
+            if (o instanceof Vector)
+            {
+                size += ((Vector)o).size();
+            }
+            else
+            {
+                size++;
+            }
+        }
+
+        return size;
+    }
+
+    public Hashtable toHashtable()
+    {
+        return copyTable(attributes);
+    }
+    
+    public ASN1EncodableVector toASN1EncodableVector()
+    {
+        ASN1EncodableVector  v = new ASN1EncodableVector();
+        Enumeration          e = attributes.elements();
+        
+        while (e.hasMoreElements())
+        {
+            Object value = e.nextElement();
+            
+            if (value instanceof Vector)
+            {
+                Enumeration en = ((Vector)value).elements();
+                
+                while (en.hasMoreElements())
+                {
+                    v.add(Attribute.getInstance(en.nextElement()));
+                }
+            }
+            else
+            {
+                v.add(Attribute.getInstance(value));
+            }
+        }
+        
+        return v;
+    }
+
+    public Attributes toASN1Structure()
+    {
+        return new Attributes(this.toASN1EncodableVector());
+    }
+
+    private Hashtable copyTable(
+        Hashtable in)
+    {
+        Hashtable   out = new Hashtable();
+        Enumeration e = in.keys();
+        
+        while (e.hasMoreElements())
+        {
+            Object key = e.nextElement();
+            
+            out.put(key, in.get(key));
+        }
+        
+        return out;
+    }
+
+    /**
+     * Return a new table with the passed in attribute added.
+     *
+     * @param attrType
+     * @param attrValue
+     * @return
+     */
+    public AttributeTable add(ASN1ObjectIdentifier attrType, ASN1Encodable attrValue)
+    {
+        AttributeTable newTable = new AttributeTable(attributes);
+
+        newTable.addAttribute(attrType, new Attribute(attrType, new DERSet(attrValue)));
+
+        return newTable;
+    }
+
+    public AttributeTable remove(ASN1ObjectIdentifier attrType)
+    {
+        AttributeTable newTable = new AttributeTable(attributes);
+
+        newTable.attributes.remove(attrType);
+
+        return newTable;
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cms/Attributes.java b/bcprov/src/main/java/org/bouncycastle/asn1/cms/Attributes.java
new file mode 100644
index 0000000..0c5a518
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/cms/Attributes.java
@@ -0,0 +1,49 @@
+package org.bouncycastle.asn1.cms;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Set;
+import org.bouncycastle.asn1.BERSet;
+
+public class Attributes
+    extends ASN1Object
+{
+    private ASN1Set attributes;
+
+    private Attributes(ASN1Set set)
+    {
+        attributes = set;
+    }
+
+    public Attributes(ASN1EncodableVector v)
+    {
+        attributes = new BERSet(v);
+    }
+
+    public static Attributes getInstance(Object obj)
+    {
+        if (obj instanceof Attributes)
+        {
+            return (Attributes)obj;
+        }
+        else if (obj != null)
+        {
+            return new Attributes(ASN1Set.getInstance(obj));
+        }
+
+        return null;
+    }
+
+    /**
+     * <pre>
+     * Attributes ::=
+     *   SET SIZE(1..MAX) OF Attribute -- according to RFC 5652
+     * </pre>
+     * @return
+     */
+    public ASN1Primitive toASN1Primitive()
+    {
+        return attributes;
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cms/CMSAttributes.java b/bcprov/src/main/java/org/bouncycastle/asn1/cms/CMSAttributes.java
new file mode 100644
index 0000000..5e97324
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/cms/CMSAttributes.java
@@ -0,0 +1,13 @@
+package org.bouncycastle.asn1.cms;
+
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+
+public interface CMSAttributes
+{
+    public static final ASN1ObjectIdentifier  contentType = PKCSObjectIdentifiers.pkcs_9_at_contentType;
+    public static final ASN1ObjectIdentifier  messageDigest = PKCSObjectIdentifiers.pkcs_9_at_messageDigest;
+    public static final ASN1ObjectIdentifier  signingTime = PKCSObjectIdentifiers.pkcs_9_at_signingTime;
+    public static final ASN1ObjectIdentifier  counterSignature = PKCSObjectIdentifiers.pkcs_9_at_counterSignature;
+    public static final ASN1ObjectIdentifier  contentHint = PKCSObjectIdentifiers.id_aa_contentHint;
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cms/CMSObjectIdentifiers.java b/bcprov/src/main/java/org/bouncycastle/asn1/cms/CMSObjectIdentifiers.java
new file mode 100644
index 0000000..e8f4541
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/cms/CMSObjectIdentifiers.java
@@ -0,0 +1,18 @@
+package org.bouncycastle.asn1.cms;
+
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+
+public interface CMSObjectIdentifiers
+{
+    static final ASN1ObjectIdentifier    data = PKCSObjectIdentifiers.data;
+    static final ASN1ObjectIdentifier    signedData = PKCSObjectIdentifiers.signedData;
+    static final ASN1ObjectIdentifier    envelopedData = PKCSObjectIdentifiers.envelopedData;
+    static final ASN1ObjectIdentifier    signedAndEnvelopedData = PKCSObjectIdentifiers.signedAndEnvelopedData;
+    static final ASN1ObjectIdentifier    digestedData = PKCSObjectIdentifiers.digestedData;
+    static final ASN1ObjectIdentifier    encryptedData = PKCSObjectIdentifiers.encryptedData;
+    static final ASN1ObjectIdentifier    authenticatedData = PKCSObjectIdentifiers.id_ct_authData;
+    static final ASN1ObjectIdentifier    compressedData = PKCSObjectIdentifiers.id_ct_compressedData;
+    static final ASN1ObjectIdentifier    authEnvelopedData = PKCSObjectIdentifiers.id_ct_authEnvelopedData;
+    static final ASN1ObjectIdentifier    timestampedData = PKCSObjectIdentifiers.id_ct_timestampedData;
+}
diff --git a/src/main/java/org/bouncycastle/asn1/cms/ContentInfo.java b/bcprov/src/main/java/org/bouncycastle/asn1/cms/ContentInfo.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/cms/ContentInfo.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/cms/ContentInfo.java
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cms/IssuerAndSerialNumber.java b/bcprov/src/main/java/org/bouncycastle/asn1/cms/IssuerAndSerialNumber.java
new file mode 100644
index 0000000..29348d2
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/cms/IssuerAndSerialNumber.java
@@ -0,0 +1,107 @@
+package org.bouncycastle.asn1.cms;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.x500.X500Name;
+import org.bouncycastle.asn1.x509.Certificate;
+import org.bouncycastle.asn1.x509.X509CertificateStructure;
+import org.bouncycastle.asn1.x509.X509Name;
+
+public class IssuerAndSerialNumber
+    extends ASN1Object
+{
+    private X500Name    name;
+    private ASN1Integer  serialNumber;
+
+    public static IssuerAndSerialNumber getInstance(
+        Object  obj)
+    {
+        if (obj instanceof IssuerAndSerialNumber)
+        {
+            return (IssuerAndSerialNumber)obj;
+        }
+        else if (obj != null)
+        {
+            return new IssuerAndSerialNumber(ASN1Sequence.getInstance(obj));
+        }
+
+        return null;
+    }
+
+    public IssuerAndSerialNumber(
+        ASN1Sequence    seq)
+    {
+        this.name = X500Name.getInstance(seq.getObjectAt(0));
+        this.serialNumber = (ASN1Integer)seq.getObjectAt(1);
+    }
+
+    public IssuerAndSerialNumber(
+        Certificate certificate)
+    {
+        this.name = certificate.getIssuer();
+        this.serialNumber = certificate.getSerialNumber();
+    }
+
+    public IssuerAndSerialNumber(
+        X509CertificateStructure certificate)
+    {
+        this.name = certificate.getIssuer();
+        this.serialNumber = certificate.getSerialNumber();
+    }
+
+    public IssuerAndSerialNumber(
+        X500Name name,
+        BigInteger  serialNumber)
+    {
+        this.name = name;
+        this.serialNumber = new ASN1Integer(serialNumber);
+    }
+
+    /**
+     * @deprecated use X500Name constructor
+     */
+    public IssuerAndSerialNumber(
+        X509Name    name,
+        BigInteger  serialNumber)
+    {
+        this.name = X500Name.getInstance(name);
+        this.serialNumber = new ASN1Integer(serialNumber);
+    }
+
+    /**
+     * @deprecated use X500Name constructor
+     */
+    public IssuerAndSerialNumber(
+        X509Name    name,
+        ASN1Integer  serialNumber)
+    {
+        this.name = X500Name.getInstance(name);
+        this.serialNumber = serialNumber;
+    }
+
+    public X500Name getName()
+    {
+        return name;
+    }
+
+    public ASN1Integer getSerialNumber()
+    {
+        return serialNumber;
+    }
+
+    public ASN1Primitive toASN1Primitive()
+    {
+        ASN1EncodableVector    v = new ASN1EncodableVector();
+
+        v.add(name);
+        v.add(serialNumber);
+
+        return new DERSequence(v);
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cms/SignedData.java b/bcprov/src/main/java/org/bouncycastle/asn1/cms/SignedData.java
new file mode 100644
index 0000000..a0a34ef
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/cms/SignedData.java
@@ -0,0 +1,302 @@
+package org.bouncycastle.asn1.cms;
+
+import java.util.Enumeration;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1Set;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.BERSequence;
+import org.bouncycastle.asn1.BERSet;
+import org.bouncycastle.asn1.BERTaggedObject;
+import org.bouncycastle.asn1.DERTaggedObject;
+
+/**
+ * a signed data object.
+ */
+public class SignedData
+    extends ASN1Object
+{
+    private ASN1Integer version;
+    private ASN1Set     digestAlgorithms;
+    private ContentInfo contentInfo;
+    private ASN1Set     certificates;
+    private ASN1Set     crls;
+    private ASN1Set     signerInfos;
+    private boolean certsBer;
+    private boolean        crlsBer;
+
+    public static SignedData getInstance(
+        Object  o)
+    {
+        if (o instanceof SignedData)
+        {
+            return (SignedData)o;
+        }
+        else if (o != null)
+        {
+            return new SignedData(ASN1Sequence.getInstance(o));
+        }
+
+        return null;
+    }
+
+    public SignedData(
+        ASN1Set     digestAlgorithms,
+        ContentInfo contentInfo,
+        ASN1Set     certificates,
+        ASN1Set     crls,
+        ASN1Set     signerInfos)
+    {
+        this.version = calculateVersion(contentInfo.getContentType(), certificates, crls, signerInfos);
+        this.digestAlgorithms = digestAlgorithms;
+        this.contentInfo = contentInfo;
+        this.certificates = certificates;
+        this.crls = crls;
+        this.signerInfos = signerInfos;
+        this.crlsBer = crls instanceof BERSet;
+        this.certsBer = certificates instanceof BERSet;
+    }
+
+
+    // RFC3852, section 5.1:
+    // IF ((certificates is present) AND
+    //    (any certificates with a type of other are present)) OR
+    //    ((crls is present) AND
+    //    (any crls with a type of other are present))
+    // THEN version MUST be 5
+    // ELSE
+    //    IF (certificates is present) AND
+    //       (any version 2 attribute certificates are present)
+    //    THEN version MUST be 4
+    //    ELSE
+    //       IF ((certificates is present) AND
+    //          (any version 1 attribute certificates are present)) OR
+    //          (any SignerInfo structures are version 3) OR
+    //          (encapContentInfo eContentType is other than id-data)
+    //       THEN version MUST be 3
+    //       ELSE version MUST be 1
+    //
+    private ASN1Integer calculateVersion(
+        ASN1ObjectIdentifier contentOid,
+        ASN1Set certs,
+        ASN1Set crls,
+        ASN1Set signerInfs)
+    {
+        boolean otherCert = false;
+        boolean otherCrl = false;
+        boolean attrCertV1Found = false;
+        boolean attrCertV2Found = false;
+
+        if (certs != null)
+        {
+            for (Enumeration en = certs.getObjects(); en.hasMoreElements();)
+            {
+                Object obj = en.nextElement();
+                if (obj instanceof ASN1TaggedObject)
+                {
+                    ASN1TaggedObject tagged = ASN1TaggedObject.getInstance(obj);
+
+                    if (tagged.getTagNo() == 1)
+                    {
+                        attrCertV1Found = true;
+                    }
+                    else if (tagged.getTagNo() == 2)
+                    {
+                        attrCertV2Found = true;
+                    }
+                    else if (tagged.getTagNo() == 3)
+                    {
+                        otherCert = true;
+                    }
+                }
+            }
+        }
+
+        if (otherCert)
+        {
+            return new ASN1Integer(5);
+        }
+
+        if (crls != null)         // no need to check if otherCert is true
+        {
+            for (Enumeration en = crls.getObjects(); en.hasMoreElements();)
+            {
+                Object obj = en.nextElement();
+                if (obj instanceof ASN1TaggedObject)
+                {
+                    otherCrl = true;
+                }
+            }
+        }
+
+        if (otherCrl)
+        {
+            return new ASN1Integer(5);
+        }
+
+        if (attrCertV2Found)
+        {
+            return new ASN1Integer(4);
+        }
+
+        if (attrCertV1Found)
+        {
+            return new ASN1Integer(3);
+        }
+
+        if (checkForVersion3(signerInfs))
+        {
+            return new ASN1Integer(3);
+        }
+
+        if (!CMSObjectIdentifiers.data.equals(contentOid))
+        {
+            return new ASN1Integer(3);
+        }
+
+        return new ASN1Integer(1);
+    }
+
+    private boolean checkForVersion3(ASN1Set signerInfs)
+    {
+        for (Enumeration e = signerInfs.getObjects(); e.hasMoreElements();)
+        {
+            SignerInfo s = SignerInfo.getInstance(e.nextElement());
+
+            if (s.getVersion().getValue().intValue() == 3)
+            {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    private SignedData(
+        ASN1Sequence seq)
+    {
+        Enumeration     e = seq.getObjects();
+
+        version = ASN1Integer.getInstance(e.nextElement());
+        digestAlgorithms = ((ASN1Set)e.nextElement());
+        contentInfo = ContentInfo.getInstance(e.nextElement());
+
+        while (e.hasMoreElements())
+        {
+            ASN1Primitive o = (ASN1Primitive)e.nextElement();
+
+            //
+            // an interesting feature of SignedData is that there appear
+            // to be varying implementations...
+            // for the moment we ignore anything which doesn't fit.
+            //
+            if (o instanceof ASN1TaggedObject)
+            {
+                ASN1TaggedObject tagged = (ASN1TaggedObject)o;
+
+                switch (tagged.getTagNo())
+                {
+                case 0:
+                    certsBer = tagged instanceof BERTaggedObject;
+                    certificates = ASN1Set.getInstance(tagged, false);
+                    break;
+                case 1:
+                    crlsBer = tagged instanceof BERTaggedObject;
+                    crls = ASN1Set.getInstance(tagged, false);
+                    break;
+                default:
+                    throw new IllegalArgumentException("unknown tag value " + tagged.getTagNo());
+                }
+            }
+            else
+            {
+                signerInfos = (ASN1Set)o;
+            }
+        }
+    }
+
+    public ASN1Integer getVersion()
+    {
+        return version;
+    }
+
+    public ASN1Set getDigestAlgorithms()
+    {
+        return digestAlgorithms;
+    }
+
+    public ContentInfo getEncapContentInfo()
+    {
+        return contentInfo;
+    }
+
+    public ASN1Set getCertificates()
+    {
+        return certificates;
+    }
+
+    public ASN1Set getCRLs()
+    {
+        return crls;
+    }
+
+    public ASN1Set getSignerInfos()
+    {
+        return signerInfos;
+    }
+
+    /**
+     * Produce an object suitable for an ASN1OutputStream.
+     * <pre>
+     * SignedData ::= SEQUENCE {
+     *     version CMSVersion,
+     *     digestAlgorithms DigestAlgorithmIdentifiers,
+     *     encapContentInfo EncapsulatedContentInfo,
+     *     certificates [0] IMPLICIT CertificateSet OPTIONAL,
+     *     crls [1] IMPLICIT CertificateRevocationLists OPTIONAL,
+     *     signerInfos SignerInfos
+     *   }
+     * </pre>
+     */
+    public ASN1Primitive toASN1Primitive()
+    {
+        ASN1EncodableVector  v = new ASN1EncodableVector();
+
+        v.add(version);
+        v.add(digestAlgorithms);
+        v.add(contentInfo);
+
+        if (certificates != null)
+        {
+            if (certsBer)
+            {
+                v.add(new BERTaggedObject(false, 0, certificates));
+            }
+            else
+            {
+                v.add(new DERTaggedObject(false, 0, certificates));
+            }
+        }
+
+        if (crls != null)
+        {
+            if (crlsBer)
+            {
+                v.add(new BERTaggedObject(false, 1, crls));
+            }
+            else
+            {
+                v.add(new DERTaggedObject(false, 1, crls));
+            }
+        }
+
+        v.add(signerInfos);
+
+        return new BERSequence(v);
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cms/SignerIdentifier.java b/bcprov/src/main/java/org/bouncycastle/asn1/cms/SignerIdentifier.java
new file mode 100644
index 0000000..37b6b31
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/cms/SignerIdentifier.java
@@ -0,0 +1,98 @@
+package org.bouncycastle.asn1.cms;
+
+import org.bouncycastle.asn1.ASN1Choice;
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERTaggedObject;
+
+public class SignerIdentifier
+    extends ASN1Object
+    implements ASN1Choice
+{
+    private ASN1Encodable id;
+    
+    public SignerIdentifier(
+        IssuerAndSerialNumber id)
+    {
+        this.id = id;
+    }
+    
+    public SignerIdentifier(
+        ASN1OctetString id)
+    {
+        this.id = new DERTaggedObject(false, 0, id);
+    }
+    
+    public SignerIdentifier(
+        ASN1Primitive id)
+    {
+        this.id = id;
+    }
+    
+    /**
+     * return a SignerIdentifier object from the given object.
+     *
+     * @param o the object we want converted.
+     * @exception IllegalArgumentException if the object cannot be converted.
+     */
+    public static SignerIdentifier getInstance(
+        Object o)
+    {
+        if (o == null || o instanceof SignerIdentifier)
+        {
+            return (SignerIdentifier)o;
+        }
+        
+        if (o instanceof IssuerAndSerialNumber)
+        {
+            return new SignerIdentifier((IssuerAndSerialNumber)o);
+        }
+        
+        if (o instanceof ASN1OctetString)
+        {
+            return new SignerIdentifier((ASN1OctetString)o);
+        }
+        
+        if (o instanceof ASN1Primitive)
+        {
+            return new SignerIdentifier((ASN1Primitive)o);
+        }
+        
+        throw new IllegalArgumentException(
+             "Illegal object in SignerIdentifier: " + o.getClass().getName());
+    } 
+
+    public boolean isTagged()
+    {
+        return (id instanceof ASN1TaggedObject);
+    }
+
+    public ASN1Encodable getId()
+    {
+        if (id instanceof ASN1TaggedObject)
+        {
+            return ASN1OctetString.getInstance((ASN1TaggedObject)id, false);
+        }
+
+        return id;
+    }
+
+    /** 
+     * Produce an object suitable for an ASN1OutputStream.
+     * <pre>
+     * SignerIdentifier ::= CHOICE {
+     *     issuerAndSerialNumber IssuerAndSerialNumber,
+     *     subjectKeyIdentifier [0] SubjectKeyIdentifier 
+     * }
+     *
+     * SubjectKeyIdentifier ::= OCTET STRING
+     * </pre>
+     */
+    public ASN1Primitive toASN1Primitive()
+    {
+        return id.toASN1Primitive();
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cms/SignerInfo.java b/bcprov/src/main/java/org/bouncycastle/asn1/cms/SignerInfo.java
new file mode 100644
index 0000000..0727b68
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/cms/SignerInfo.java
@@ -0,0 +1,183 @@
+package org.bouncycastle.asn1.cms;
+
+import java.util.Enumeration;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1Set;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DEROctetString;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.DERTaggedObject;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+
+public class SignerInfo
+    extends ASN1Object
+{
+    private ASN1Integer              version;
+    private SignerIdentifier        sid;
+    private AlgorithmIdentifier     digAlgorithm;
+    private ASN1Set                 authenticatedAttributes;
+    private AlgorithmIdentifier     digEncryptionAlgorithm;
+    private ASN1OctetString         encryptedDigest;
+    private ASN1Set                 unauthenticatedAttributes;
+
+    public static SignerInfo getInstance(
+        Object  o)
+        throws IllegalArgumentException
+    {
+        if (o == null || o instanceof SignerInfo)
+        {
+            return (SignerInfo)o;
+        }
+        else if (o instanceof ASN1Sequence)
+        {
+            return new SignerInfo((ASN1Sequence)o);
+        }
+
+        throw new IllegalArgumentException("unknown object in factory: " + o.getClass().getName());
+    }
+
+    public SignerInfo(
+        SignerIdentifier        sid,
+        AlgorithmIdentifier     digAlgorithm,
+        ASN1Set                 authenticatedAttributes,
+        AlgorithmIdentifier     digEncryptionAlgorithm,
+        ASN1OctetString         encryptedDigest,
+        ASN1Set                 unauthenticatedAttributes)
+    {
+        if (sid.isTagged())
+        {
+            this.version = new ASN1Integer(3);
+        }
+        else
+        {
+            this.version = new ASN1Integer(1);
+        }
+
+        this.sid = sid;
+        this.digAlgorithm = digAlgorithm;
+        this.authenticatedAttributes = authenticatedAttributes;
+        this.digEncryptionAlgorithm = digEncryptionAlgorithm;
+        this.encryptedDigest = encryptedDigest;
+        this.unauthenticatedAttributes = unauthenticatedAttributes;
+    }
+
+    public SignerInfo(
+        ASN1Sequence seq)
+    {
+        Enumeration     e = seq.getObjects();
+
+        version = (ASN1Integer)e.nextElement();
+        sid = SignerIdentifier.getInstance(e.nextElement());
+        digAlgorithm = AlgorithmIdentifier.getInstance(e.nextElement());
+
+        Object obj = e.nextElement();
+
+        if (obj instanceof ASN1TaggedObject)
+        {
+            authenticatedAttributes = ASN1Set.getInstance((ASN1TaggedObject)obj, false);
+
+            digEncryptionAlgorithm = AlgorithmIdentifier.getInstance(e.nextElement());
+        }
+        else
+        {
+            authenticatedAttributes = null;
+            digEncryptionAlgorithm = AlgorithmIdentifier.getInstance(obj);
+        }
+
+        encryptedDigest = DEROctetString.getInstance(e.nextElement());
+
+        if (e.hasMoreElements())
+        {
+            unauthenticatedAttributes = ASN1Set.getInstance((ASN1TaggedObject)e.nextElement(), false);
+        }
+        else
+        {
+            unauthenticatedAttributes = null;
+        }
+    }
+
+    public ASN1Integer getVersion()
+    {
+        return version;
+    }
+
+    public SignerIdentifier getSID()
+    {
+        return sid;
+    }
+
+    public ASN1Set getAuthenticatedAttributes()
+    {
+        return authenticatedAttributes;
+    }
+
+    public AlgorithmIdentifier getDigestAlgorithm()
+    {
+        return digAlgorithm;
+    }
+
+    public ASN1OctetString getEncryptedDigest()
+    {
+        return encryptedDigest;
+    }
+
+    public AlgorithmIdentifier getDigestEncryptionAlgorithm()
+    {
+        return digEncryptionAlgorithm;
+    }
+
+    public ASN1Set getUnauthenticatedAttributes()
+    {
+        return unauthenticatedAttributes;
+    }
+
+    /**
+     * Produce an object suitable for an ASN1OutputStream.
+     * <pre>
+     *  SignerInfo ::= SEQUENCE {
+     *      version Version,
+     *      SignerIdentifier sid,
+     *      digestAlgorithm DigestAlgorithmIdentifier,
+     *      authenticatedAttributes [0] IMPLICIT Attributes OPTIONAL,
+     *      digestEncryptionAlgorithm DigestEncryptionAlgorithmIdentifier,
+     *      encryptedDigest EncryptedDigest,
+     *      unauthenticatedAttributes [1] IMPLICIT Attributes OPTIONAL
+     *  }
+     *
+     *  EncryptedDigest ::= OCTET STRING
+     *
+     *  DigestAlgorithmIdentifier ::= AlgorithmIdentifier
+     *
+     *  DigestEncryptionAlgorithmIdentifier ::= AlgorithmIdentifier
+     * </pre>
+     */
+    public ASN1Primitive toASN1Primitive()
+    {
+        ASN1EncodableVector v = new ASN1EncodableVector();
+
+        v.add(version);
+        v.add(sid);
+        v.add(digAlgorithm);
+
+        if (authenticatedAttributes != null)
+        {
+            v.add(new DERTaggedObject(false, 0, authenticatedAttributes));
+        }
+
+        v.add(digEncryptionAlgorithm);
+        v.add(encryptedDigest);
+
+        if (unauthenticatedAttributes != null)
+        {
+            v.add(new DERTaggedObject(false, 1, unauthenticatedAttributes));
+        }
+
+        return new DERSequence(v);
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cms/Time.java b/bcprov/src/main/java/org/bouncycastle/asn1/cms/Time.java
new file mode 100644
index 0000000..2087248
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/cms/Time.java
@@ -0,0 +1,128 @@
+package org.bouncycastle.asn1.cms;
+
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.SimpleTimeZone;
+
+import org.bouncycastle.asn1.ASN1Choice;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERGeneralizedTime;
+import org.bouncycastle.asn1.DERUTCTime;
+
+public class Time
+    extends ASN1Object
+    implements ASN1Choice
+{
+    ASN1Primitive time;
+
+    public static Time getInstance(
+        ASN1TaggedObject obj,
+        boolean          explicit)
+    {
+        return getInstance(obj.getObject());
+    }
+
+    public Time(
+        ASN1Primitive   time)
+    {
+        if (!(time instanceof DERUTCTime)
+            && !(time instanceof DERGeneralizedTime))
+        {
+            throw new IllegalArgumentException("unknown object passed to Time");
+        }
+
+        this.time = time; 
+    }
+
+    /**
+     * creates a time object from a given date - if the date is between 1950
+     * and 2049 a UTCTime object is generated, otherwise a GeneralizedTime
+     * is used.
+     */
+    public Time(
+        Date    date)
+    {
+        SimpleTimeZone      tz = new SimpleTimeZone(0, "Z");
+        SimpleDateFormat    dateF = new SimpleDateFormat("yyyyMMddHHmmss");
+
+        dateF.setTimeZone(tz);
+
+        String  d = dateF.format(date) + "Z";
+        int     year = Integer.parseInt(d.substring(0, 4));
+
+        if (year < 1950 || year > 2049)
+        {
+            time = new DERGeneralizedTime(d);
+        }
+        else
+        {
+            time = new DERUTCTime(d.substring(2));
+        }
+    }
+
+    public static Time getInstance(
+        Object  obj)
+    {
+        if (obj == null || obj instanceof Time)
+        {
+            return (Time)obj;
+        }
+        else if (obj instanceof DERUTCTime)
+        {
+            return new Time((DERUTCTime)obj);
+        }
+        else if (obj instanceof DERGeneralizedTime)
+        {
+            return new Time((DERGeneralizedTime)obj);
+        }
+
+        throw new IllegalArgumentException("unknown object in factory: " + obj.getClass().getName());
+    }
+
+    public String getTime()
+    {
+        if (time instanceof DERUTCTime)
+        {
+            return ((DERUTCTime)time).getAdjustedTime();
+        }
+        else
+        {
+            return ((DERGeneralizedTime)time).getTime();
+        }
+    }
+
+    public Date getDate()
+    {
+        try
+        {
+            if (time instanceof DERUTCTime)
+            {
+                return ((DERUTCTime)time).getAdjustedDate();
+            }
+            else
+            {
+                return ((DERGeneralizedTime)time).getDate();
+            }
+        }
+        catch (ParseException e)
+        {         // this should never happen
+            throw new IllegalStateException("invalid date string: " + e.getMessage());
+        }
+    }
+
+    /**
+     * Produce an object suitable for an ASN1OutputStream.
+     * <pre>
+     * Time ::= CHOICE {
+     *             utcTime        UTCTime,
+     *             generalTime    GeneralizedTime }
+     * </pre>
+     */
+    public ASN1Primitive toASN1Primitive()
+    {
+        return time;
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/eac/EACObjectIdentifiers.java b/bcprov/src/main/java/org/bouncycastle/asn1/eac/EACObjectIdentifiers.java
new file mode 100644
index 0000000..bef8620
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/eac/EACObjectIdentifiers.java
@@ -0,0 +1,55 @@
+package org.bouncycastle.asn1.eac;
+
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+
+public interface EACObjectIdentifiers
+{
+    // bsi-de OBJECT IDENTIFIER ::= {
+    //         itu-t(0) identified-organization(4) etsi(0)
+    //         reserved(127) etsi-identified-organization(0) 7
+    //     }
+    static final ASN1ObjectIdentifier    bsi_de      = new ASN1ObjectIdentifier("0.4.0.127.0.7");
+
+    // id-PK OBJECT IDENTIFIER ::= {
+    //         bsi-de protocols(2) smartcard(2) 1
+    //     }
+    static final ASN1ObjectIdentifier    id_PK = bsi_de.branch("2.2.1");
+
+    static final ASN1ObjectIdentifier    id_PK_DH = id_PK.branch("1");
+    static final ASN1ObjectIdentifier    id_PK_ECDH = id_PK.branch("2");
+
+    // id-CA OBJECT IDENTIFIER ::= {
+    //         bsi-de protocols(2) smartcard(2) 3
+    //     }
+    static final ASN1ObjectIdentifier    id_CA = bsi_de.branch("2.2.3");
+    static final ASN1ObjectIdentifier    id_CA_DH = id_CA.branch("1");
+    static final ASN1ObjectIdentifier    id_CA_DH_3DES_CBC_CBC = id_CA_DH.branch("1");
+    static final ASN1ObjectIdentifier    id_CA_ECDH = id_CA.branch("2");
+    static final ASN1ObjectIdentifier    id_CA_ECDH_3DES_CBC_CBC = id_CA_ECDH.branch("1");
+
+    //
+    // id-TA OBJECT IDENTIFIER ::= {
+    //     bsi-de protocols(2) smartcard(2) 2
+    // }
+    static final ASN1ObjectIdentifier    id_TA = bsi_de.branch("2.2.2");
+
+    static final ASN1ObjectIdentifier    id_TA_RSA = id_TA.branch("1");
+    static final ASN1ObjectIdentifier    id_TA_RSA_v1_5_SHA_1 = id_TA_RSA .branch("1");
+    static final ASN1ObjectIdentifier    id_TA_RSA_v1_5_SHA_256 = id_TA_RSA.branch("2");
+    static final ASN1ObjectIdentifier    id_TA_RSA_PSS_SHA_1 = id_TA_RSA.branch("3");
+    static final ASN1ObjectIdentifier    id_TA_RSA_PSS_SHA_256 = id_TA_RSA.branch("4");
+    static final ASN1ObjectIdentifier    id_TA_RSA_v1_5_SHA_512 = id_TA_RSA.branch("5");
+    static final ASN1ObjectIdentifier    id_TA_RSA_PSS_SHA_512 = id_TA_RSA.branch("6");
+    static final ASN1ObjectIdentifier    id_TA_ECDSA = id_TA.branch("2");
+    static final ASN1ObjectIdentifier    id_TA_ECDSA_SHA_1 = id_TA_ECDSA.branch("1");
+    static final ASN1ObjectIdentifier    id_TA_ECDSA_SHA_224 = id_TA_ECDSA.branch("2");
+    static final ASN1ObjectIdentifier    id_TA_ECDSA_SHA_256 = id_TA_ECDSA.branch("3");
+    static final ASN1ObjectIdentifier    id_TA_ECDSA_SHA_384 = id_TA_ECDSA.branch("4");
+    static final ASN1ObjectIdentifier    id_TA_ECDSA_SHA_512 = id_TA_ECDSA.branch("5");
+
+    /**
+     * id-EAC-ePassport OBJECT IDENTIFIER ::= {
+     * bsi-de applications(3) mrtd(1) roles(2) 1}
+     */
+    static final ASN1ObjectIdentifier id_EAC_ePassport = bsi_de.branch("3.1.2.1");
+}
diff --git a/src/main/java/org/bouncycastle/asn1/iana/IANAObjectIdentifiers.java b/bcprov/src/main/java/org/bouncycastle/asn1/iana/IANAObjectIdentifiers.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/iana/IANAObjectIdentifiers.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/iana/IANAObjectIdentifiers.java
diff --git a/src/main/java/org/bouncycastle/asn1/isismtt/ISISMTTObjectIdentifiers.java b/bcprov/src/main/java/org/bouncycastle/asn1/isismtt/ISISMTTObjectIdentifiers.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/isismtt/ISISMTTObjectIdentifiers.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/isismtt/ISISMTTObjectIdentifiers.java
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/kisa/KISAObjectIdentifiers.java b/bcprov/src/main/java/org/bouncycastle/asn1/kisa/KISAObjectIdentifiers.java
new file mode 100644
index 0000000..73e0c58
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/kisa/KISAObjectIdentifiers.java
@@ -0,0 +1,9 @@
+package org.bouncycastle.asn1.kisa;
+
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+
+public interface KISAObjectIdentifiers
+{
+    public static final ASN1ObjectIdentifier id_seedCBC = new ASN1ObjectIdentifier("1.2.410.200004.1.4");
+    public static final ASN1ObjectIdentifier id_npki_app_cmsSeed_wrap = new ASN1ObjectIdentifier("1.2.410.200004.7.1.1.1");
+}
diff --git a/src/main/java/org/bouncycastle/asn1/misc/MiscObjectIdentifiers.java b/bcprov/src/main/java/org/bouncycastle/asn1/misc/MiscObjectIdentifiers.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/misc/MiscObjectIdentifiers.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/misc/MiscObjectIdentifiers.java
diff --git a/src/main/java/org/bouncycastle/asn1/misc/NetscapeCertType.java b/bcprov/src/main/java/org/bouncycastle/asn1/misc/NetscapeCertType.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/misc/NetscapeCertType.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/misc/NetscapeCertType.java
diff --git a/src/main/java/org/bouncycastle/asn1/misc/NetscapeRevocationURL.java b/bcprov/src/main/java/org/bouncycastle/asn1/misc/NetscapeRevocationURL.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/misc/NetscapeRevocationURL.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/misc/NetscapeRevocationURL.java
diff --git a/src/main/java/org/bouncycastle/asn1/misc/VerisignCzagExtension.java b/bcprov/src/main/java/org/bouncycastle/asn1/misc/VerisignCzagExtension.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/misc/VerisignCzagExtension.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/misc/VerisignCzagExtension.java
diff --git a/src/main/java/org/bouncycastle/asn1/nist/NISTNamedCurves.java b/bcprov/src/main/java/org/bouncycastle/asn1/nist/NISTNamedCurves.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/nist/NISTNamedCurves.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/nist/NISTNamedCurves.java
diff --git a/src/main/java/org/bouncycastle/asn1/nist/NISTObjectIdentifiers.java b/bcprov/src/main/java/org/bouncycastle/asn1/nist/NISTObjectIdentifiers.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/nist/NISTObjectIdentifiers.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/nist/NISTObjectIdentifiers.java
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/ntt/NTTObjectIdentifiers.java b/bcprov/src/main/java/org/bouncycastle/asn1/ntt/NTTObjectIdentifiers.java
new file mode 100644
index 0000000..2e4132a
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/ntt/NTTObjectIdentifiers.java
@@ -0,0 +1,17 @@
+package org.bouncycastle.asn1.ntt;
+
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+
+/**
+ * From RFC 3657
+ */
+public interface NTTObjectIdentifiers
+{
+    public static final ASN1ObjectIdentifier id_camellia128_cbc = new ASN1ObjectIdentifier("1.2.392.200011.61.1.1.1.2");
+    public static final ASN1ObjectIdentifier id_camellia192_cbc = new ASN1ObjectIdentifier("1.2.392.200011.61.1.1.1.3");
+    public static final ASN1ObjectIdentifier id_camellia256_cbc = new ASN1ObjectIdentifier("1.2.392.200011.61.1.1.1.4");
+
+    public static final ASN1ObjectIdentifier id_camellia128_wrap = new ASN1ObjectIdentifier("1.2.392.200011.61.1.1.3.2");
+    public static final ASN1ObjectIdentifier id_camellia192_wrap = new ASN1ObjectIdentifier("1.2.392.200011.61.1.1.3.3");
+    public static final ASN1ObjectIdentifier id_camellia256_wrap = new ASN1ObjectIdentifier("1.2.392.200011.61.1.1.3.4");
+}
diff --git a/src/main/java/org/bouncycastle/asn1/oiw/OIWObjectIdentifiers.java b/bcprov/src/main/java/org/bouncycastle/asn1/oiw/OIWObjectIdentifiers.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/oiw/OIWObjectIdentifiers.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/oiw/OIWObjectIdentifiers.java
diff --git a/src/main/java/org/bouncycastle/asn1/pkcs/AuthenticatedSafe.java b/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/AuthenticatedSafe.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/pkcs/AuthenticatedSafe.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/pkcs/AuthenticatedSafe.java
diff --git a/src/main/java/org/bouncycastle/asn1/pkcs/CRLBag.java b/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/CRLBag.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/pkcs/CRLBag.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/pkcs/CRLBag.java
diff --git a/src/main/java/org/bouncycastle/asn1/pkcs/CertBag.java b/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/CertBag.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/pkcs/CertBag.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/pkcs/CertBag.java
diff --git a/src/main/java/org/bouncycastle/asn1/pkcs/CertificationRequest.java b/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/CertificationRequest.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/pkcs/CertificationRequest.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/pkcs/CertificationRequest.java
diff --git a/src/main/java/org/bouncycastle/asn1/pkcs/CertificationRequestInfo.java b/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/CertificationRequestInfo.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/pkcs/CertificationRequestInfo.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/pkcs/CertificationRequestInfo.java
diff --git a/src/main/java/org/bouncycastle/asn1/pkcs/ContentInfo.java b/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/ContentInfo.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/pkcs/ContentInfo.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/pkcs/ContentInfo.java
diff --git a/src/main/java/org/bouncycastle/asn1/pkcs/DHParameter.java b/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/DHParameter.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/pkcs/DHParameter.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/pkcs/DHParameter.java
diff --git a/src/main/java/org/bouncycastle/asn1/pkcs/EncryptedData.java b/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/EncryptedData.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/pkcs/EncryptedData.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/pkcs/EncryptedData.java
diff --git a/src/main/java/org/bouncycastle/asn1/pkcs/EncryptedPrivateKeyInfo.java b/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/EncryptedPrivateKeyInfo.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/pkcs/EncryptedPrivateKeyInfo.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/pkcs/EncryptedPrivateKeyInfo.java
diff --git a/src/main/java/org/bouncycastle/asn1/pkcs/EncryptionScheme.java b/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/EncryptionScheme.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/pkcs/EncryptionScheme.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/pkcs/EncryptionScheme.java
diff --git a/src/main/java/org/bouncycastle/asn1/pkcs/IssuerAndSerialNumber.java b/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/IssuerAndSerialNumber.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/pkcs/IssuerAndSerialNumber.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/pkcs/IssuerAndSerialNumber.java
diff --git a/src/main/java/org/bouncycastle/asn1/pkcs/KeyDerivationFunc.java b/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/KeyDerivationFunc.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/pkcs/KeyDerivationFunc.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/pkcs/KeyDerivationFunc.java
diff --git a/src/main/java/org/bouncycastle/asn1/pkcs/MacData.java b/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/MacData.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/pkcs/MacData.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/pkcs/MacData.java
diff --git a/src/main/java/org/bouncycastle/asn1/pkcs/PBEParameter.java b/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/PBEParameter.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/pkcs/PBEParameter.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/pkcs/PBEParameter.java
diff --git a/src/main/java/org/bouncycastle/asn1/pkcs/PBES2Algorithms.java b/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/PBES2Algorithms.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/pkcs/PBES2Algorithms.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/pkcs/PBES2Algorithms.java
diff --git a/src/main/java/org/bouncycastle/asn1/pkcs/PBES2Parameters.java b/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/PBES2Parameters.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/pkcs/PBES2Parameters.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/pkcs/PBES2Parameters.java
diff --git a/src/main/java/org/bouncycastle/asn1/pkcs/PBKDF2Params.java b/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/PBKDF2Params.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/pkcs/PBKDF2Params.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/pkcs/PBKDF2Params.java
diff --git a/src/main/java/org/bouncycastle/asn1/pkcs/PKCS12PBEParams.java b/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/PKCS12PBEParams.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/pkcs/PKCS12PBEParams.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/pkcs/PKCS12PBEParams.java
diff --git a/src/main/java/org/bouncycastle/asn1/pkcs/PKCSObjectIdentifiers.java b/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/PKCSObjectIdentifiers.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/pkcs/PKCSObjectIdentifiers.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/pkcs/PKCSObjectIdentifiers.java
diff --git a/src/main/java/org/bouncycastle/asn1/pkcs/Pfx.java b/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/Pfx.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/pkcs/Pfx.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/pkcs/Pfx.java
diff --git a/src/main/java/org/bouncycastle/asn1/pkcs/PrivateKeyInfo.java b/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/PrivateKeyInfo.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/pkcs/PrivateKeyInfo.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/pkcs/PrivateKeyInfo.java
diff --git a/src/main/java/org/bouncycastle/asn1/pkcs/RSAESOAEPparams.java b/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/RSAESOAEPparams.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/pkcs/RSAESOAEPparams.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/pkcs/RSAESOAEPparams.java
diff --git a/src/main/java/org/bouncycastle/asn1/pkcs/RSAPrivateKey.java b/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/RSAPrivateKey.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/pkcs/RSAPrivateKey.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/pkcs/RSAPrivateKey.java
diff --git a/src/main/java/org/bouncycastle/asn1/pkcs/RSAPrivateKeyStructure.java b/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/RSAPrivateKeyStructure.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/pkcs/RSAPrivateKeyStructure.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/pkcs/RSAPrivateKeyStructure.java
diff --git a/src/main/java/org/bouncycastle/asn1/pkcs/RSAPublicKey.java b/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/RSAPublicKey.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/pkcs/RSAPublicKey.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/pkcs/RSAPublicKey.java
diff --git a/src/main/java/org/bouncycastle/asn1/pkcs/RSASSAPSSparams.java b/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/RSASSAPSSparams.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/pkcs/RSASSAPSSparams.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/pkcs/RSASSAPSSparams.java
diff --git a/src/main/java/org/bouncycastle/asn1/pkcs/SafeBag.java b/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/SafeBag.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/pkcs/SafeBag.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/pkcs/SafeBag.java
diff --git a/src/main/java/org/bouncycastle/asn1/pkcs/SignedData.java b/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/SignedData.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/pkcs/SignedData.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/pkcs/SignedData.java
diff --git a/src/main/java/org/bouncycastle/asn1/sec/ECPrivateKey.java b/bcprov/src/main/java/org/bouncycastle/asn1/sec/ECPrivateKey.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/sec/ECPrivateKey.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/sec/ECPrivateKey.java
diff --git a/src/main/java/org/bouncycastle/asn1/sec/ECPrivateKeyStructure.java b/bcprov/src/main/java/org/bouncycastle/asn1/sec/ECPrivateKeyStructure.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/sec/ECPrivateKeyStructure.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/sec/ECPrivateKeyStructure.java
diff --git a/src/main/java/org/bouncycastle/asn1/sec/SECNamedCurves.java b/bcprov/src/main/java/org/bouncycastle/asn1/sec/SECNamedCurves.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/sec/SECNamedCurves.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/sec/SECNamedCurves.java
diff --git a/src/main/java/org/bouncycastle/asn1/sec/SECObjectIdentifiers.java b/bcprov/src/main/java/org/bouncycastle/asn1/sec/SECObjectIdentifiers.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/sec/SECObjectIdentifiers.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/sec/SECObjectIdentifiers.java
diff --git a/src/main/java/org/bouncycastle/asn1/teletrust/TeleTrusTObjectIdentifiers.java b/bcprov/src/main/java/org/bouncycastle/asn1/teletrust/TeleTrusTObjectIdentifiers.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/teletrust/TeleTrusTObjectIdentifiers.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/teletrust/TeleTrusTObjectIdentifiers.java
diff --git a/src/main/java/org/bouncycastle/asn1/util/ASN1Dump.java b/bcprov/src/main/java/org/bouncycastle/asn1/util/ASN1Dump.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/util/ASN1Dump.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/util/ASN1Dump.java
diff --git a/src/main/java/org/bouncycastle/asn1/x500/AttributeTypeAndValue.java b/bcprov/src/main/java/org/bouncycastle/asn1/x500/AttributeTypeAndValue.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/x500/AttributeTypeAndValue.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/x500/AttributeTypeAndValue.java
diff --git a/src/main/java/org/bouncycastle/asn1/x500/DirectoryString.java b/bcprov/src/main/java/org/bouncycastle/asn1/x500/DirectoryString.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/x500/DirectoryString.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/x500/DirectoryString.java
diff --git a/src/main/java/org/bouncycastle/asn1/x500/RDN.java b/bcprov/src/main/java/org/bouncycastle/asn1/x500/RDN.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/x500/RDN.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/x500/RDN.java
diff --git a/src/main/java/org/bouncycastle/asn1/x500/X500Name.java b/bcprov/src/main/java/org/bouncycastle/asn1/x500/X500Name.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/x500/X500Name.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/x500/X500Name.java
diff --git a/src/main/java/org/bouncycastle/asn1/x500/X500NameBuilder.java b/bcprov/src/main/java/org/bouncycastle/asn1/x500/X500NameBuilder.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/x500/X500NameBuilder.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/x500/X500NameBuilder.java
diff --git a/src/main/java/org/bouncycastle/asn1/x500/X500NameStyle.java b/bcprov/src/main/java/org/bouncycastle/asn1/x500/X500NameStyle.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/x500/X500NameStyle.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/x500/X500NameStyle.java
diff --git a/src/main/java/org/bouncycastle/asn1/x500/style/BCStrictStyle.java b/bcprov/src/main/java/org/bouncycastle/asn1/x500/style/BCStrictStyle.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/x500/style/BCStrictStyle.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/x500/style/BCStrictStyle.java
diff --git a/src/main/java/org/bouncycastle/asn1/x500/style/BCStyle.java b/bcprov/src/main/java/org/bouncycastle/asn1/x500/style/BCStyle.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/x500/style/BCStyle.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/x500/style/BCStyle.java
diff --git a/src/main/java/org/bouncycastle/asn1/x500/style/IETFUtils.java b/bcprov/src/main/java/org/bouncycastle/asn1/x500/style/IETFUtils.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/x500/style/IETFUtils.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/x500/style/IETFUtils.java
diff --git a/src/main/java/org/bouncycastle/asn1/x500/style/RFC4519Style.java b/bcprov/src/main/java/org/bouncycastle/asn1/x500/style/RFC4519Style.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/x500/style/RFC4519Style.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/x500/style/RFC4519Style.java
diff --git a/src/main/java/org/bouncycastle/asn1/x500/style/X500NameTokenizer.java b/bcprov/src/main/java/org/bouncycastle/asn1/x500/style/X500NameTokenizer.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/x500/style/X500NameTokenizer.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/x500/style/X500NameTokenizer.java
diff --git a/src/main/java/org/bouncycastle/asn1/x509/AlgorithmIdentifier.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/AlgorithmIdentifier.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/x509/AlgorithmIdentifier.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/x509/AlgorithmIdentifier.java
diff --git a/src/main/java/org/bouncycastle/asn1/x509/AttCertIssuer.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/AttCertIssuer.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/x509/AttCertIssuer.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/x509/AttCertIssuer.java
diff --git a/src/main/java/org/bouncycastle/asn1/x509/AttCertValidityPeriod.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/AttCertValidityPeriod.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/x509/AttCertValidityPeriod.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/x509/AttCertValidityPeriod.java
diff --git a/src/main/java/org/bouncycastle/asn1/x509/Attribute.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/Attribute.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/x509/Attribute.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/x509/Attribute.java
diff --git a/src/main/java/org/bouncycastle/asn1/x509/AttributeCertificate.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/AttributeCertificate.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/x509/AttributeCertificate.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/x509/AttributeCertificate.java
diff --git a/src/main/java/org/bouncycastle/asn1/x509/AttributeCertificateInfo.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/AttributeCertificateInfo.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/x509/AttributeCertificateInfo.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/x509/AttributeCertificateInfo.java
diff --git a/src/main/java/org/bouncycastle/asn1/x509/AuthorityKeyIdentifier.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/AuthorityKeyIdentifier.java
similarity index 97%
rename from src/main/java/org/bouncycastle/asn1/x509/AuthorityKeyIdentifier.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/x509/AuthorityKeyIdentifier.java
index 9a73dd6..84ef3da 100644
--- a/src/main/java/org/bouncycastle/asn1/x509/AuthorityKeyIdentifier.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/x509/AuthorityKeyIdentifier.java
@@ -15,7 +15,7 @@
 import org.bouncycastle.asn1.DERTaggedObject;
 import org.bouncycastle.crypto.Digest;
 // BEGIN android-changed
-import org.bouncycastle.crypto.digests.OpenSSLDigest;
+import org.bouncycastle.crypto.digests.AndroidDigestFactory;
 // END android-changed
 
 /**
@@ -104,7 +104,7 @@
         SubjectPublicKeyInfo    spki)
     {
         // BEGIN android-changed
-        Digest  digest = new OpenSSLDigest.SHA1();
+        Digest  digest = AndroidDigestFactory.getSHA1();
         // END android-changed
         byte[]  resBuf = new byte[digest.getDigestSize()];
 
@@ -124,7 +124,7 @@
         BigInteger              serialNumber)
     {
         // BEGIN android-changed
-        Digest  digest = new OpenSSLDigest.SHA1();
+        Digest  digest = AndroidDigestFactory.getSHA1();
         // END android-changed
         byte[]  resBuf = new byte[digest.getDigestSize()];
 
diff --git a/src/main/java/org/bouncycastle/asn1/x509/BasicConstraints.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/BasicConstraints.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/x509/BasicConstraints.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/x509/BasicConstraints.java
diff --git a/src/main/java/org/bouncycastle/asn1/x509/CRLDistPoint.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/CRLDistPoint.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/x509/CRLDistPoint.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/x509/CRLDistPoint.java
diff --git a/src/main/java/org/bouncycastle/asn1/x509/CRLNumber.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/CRLNumber.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/x509/CRLNumber.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/x509/CRLNumber.java
diff --git a/src/main/java/org/bouncycastle/asn1/x509/CRLReason.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/CRLReason.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/x509/CRLReason.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/x509/CRLReason.java
diff --git a/src/main/java/org/bouncycastle/asn1/x509/Certificate.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/Certificate.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/x509/Certificate.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/x509/Certificate.java
diff --git a/src/main/java/org/bouncycastle/asn1/x509/CertificateList.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/CertificateList.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/x509/CertificateList.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/x509/CertificateList.java
diff --git a/src/main/java/org/bouncycastle/asn1/x509/DSAParameter.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/DSAParameter.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/x509/DSAParameter.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/x509/DSAParameter.java
diff --git a/src/main/java/org/bouncycastle/asn1/x509/DigestInfo.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/DigestInfo.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/x509/DigestInfo.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/x509/DigestInfo.java
diff --git a/src/main/java/org/bouncycastle/asn1/x509/DistributionPoint.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/DistributionPoint.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/x509/DistributionPoint.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/x509/DistributionPoint.java
diff --git a/src/main/java/org/bouncycastle/asn1/x509/DistributionPointName.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/DistributionPointName.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/x509/DistributionPointName.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/x509/DistributionPointName.java
diff --git a/src/main/java/org/bouncycastle/asn1/x509/ExtendedKeyUsage.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/ExtendedKeyUsage.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/x509/ExtendedKeyUsage.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/x509/ExtendedKeyUsage.java
diff --git a/src/main/java/org/bouncycastle/asn1/x509/Extension.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/Extension.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/x509/Extension.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/x509/Extension.java
diff --git a/src/main/java/org/bouncycastle/asn1/x509/Extensions.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/Extensions.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/x509/Extensions.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/x509/Extensions.java
diff --git a/src/main/java/org/bouncycastle/asn1/x509/ExtensionsGenerator.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/ExtensionsGenerator.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/x509/ExtensionsGenerator.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/x509/ExtensionsGenerator.java
diff --git a/src/main/java/org/bouncycastle/asn1/x509/GeneralName.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/GeneralName.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/x509/GeneralName.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/x509/GeneralName.java
diff --git a/src/main/java/org/bouncycastle/asn1/x509/GeneralNames.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/GeneralNames.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/x509/GeneralNames.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/x509/GeneralNames.java
diff --git a/src/main/java/org/bouncycastle/asn1/x509/GeneralSubtree.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/GeneralSubtree.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/x509/GeneralSubtree.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/x509/GeneralSubtree.java
diff --git a/src/main/java/org/bouncycastle/asn1/x509/Holder.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/Holder.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/x509/Holder.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/x509/Holder.java
diff --git a/src/main/java/org/bouncycastle/asn1/x509/IssuerSerial.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/IssuerSerial.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/x509/IssuerSerial.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/x509/IssuerSerial.java
diff --git a/src/main/java/org/bouncycastle/asn1/x509/IssuingDistributionPoint.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/IssuingDistributionPoint.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/x509/IssuingDistributionPoint.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/x509/IssuingDistributionPoint.java
diff --git a/src/main/java/org/bouncycastle/asn1/x509/KeyPurposeId.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/KeyPurposeId.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/x509/KeyPurposeId.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/x509/KeyPurposeId.java
diff --git a/src/main/java/org/bouncycastle/asn1/x509/KeyUsage.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/KeyUsage.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/x509/KeyUsage.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/x509/KeyUsage.java
diff --git a/src/main/java/org/bouncycastle/asn1/x509/NameConstraints.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/NameConstraints.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/x509/NameConstraints.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/x509/NameConstraints.java
diff --git a/src/main/java/org/bouncycastle/asn1/x509/ObjectDigestInfo.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/ObjectDigestInfo.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/x509/ObjectDigestInfo.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/x509/ObjectDigestInfo.java
diff --git a/src/main/java/org/bouncycastle/asn1/x509/PolicyInformation.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/PolicyInformation.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/x509/PolicyInformation.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/x509/PolicyInformation.java
diff --git a/src/main/java/org/bouncycastle/asn1/x509/RSAPublicKeyStructure.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/RSAPublicKeyStructure.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/x509/RSAPublicKeyStructure.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/x509/RSAPublicKeyStructure.java
diff --git a/src/main/java/org/bouncycastle/asn1/x509/ReasonFlags.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/ReasonFlags.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/x509/ReasonFlags.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/x509/ReasonFlags.java
diff --git a/src/main/java/org/bouncycastle/asn1/x509/SubjectKeyIdentifier.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/SubjectKeyIdentifier.java
similarity index 96%
rename from src/main/java/org/bouncycastle/asn1/x509/SubjectKeyIdentifier.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/x509/SubjectKeyIdentifier.java
index 29af48f..e56d89f 100644
--- a/src/main/java/org/bouncycastle/asn1/x509/SubjectKeyIdentifier.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/x509/SubjectKeyIdentifier.java
@@ -7,7 +7,7 @@
 import org.bouncycastle.asn1.DEROctetString;
 import org.bouncycastle.crypto.Digest;
 // BEGIN android-changed
-import org.bouncycastle.crypto.digests.OpenSSLDigest;
+import org.bouncycastle.crypto.digests.AndroidDigestFactory;
 // END android-changed
 
 /**
@@ -122,7 +122,7 @@
     private static byte[] getDigest(SubjectPublicKeyInfo spki)
     {
         // BEGIN android-changed
-        Digest digest = new OpenSSLDigest.SHA1();
+        Digest digest = AndroidDigestFactory.getSHA1();
         // END android-changed
         byte[]  resBuf = new byte[digest.getDigestSize()];
 
diff --git a/src/main/java/org/bouncycastle/asn1/x509/SubjectPublicKeyInfo.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/SubjectPublicKeyInfo.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/x509/SubjectPublicKeyInfo.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/x509/SubjectPublicKeyInfo.java
diff --git a/src/main/java/org/bouncycastle/asn1/x509/TBSCertList.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/TBSCertList.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/x509/TBSCertList.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/x509/TBSCertList.java
diff --git a/src/main/java/org/bouncycastle/asn1/x509/TBSCertificate.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/TBSCertificate.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/x509/TBSCertificate.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/x509/TBSCertificate.java
diff --git a/src/main/java/org/bouncycastle/asn1/x509/TBSCertificateStructure.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/TBSCertificateStructure.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/x509/TBSCertificateStructure.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/x509/TBSCertificateStructure.java
diff --git a/src/main/java/org/bouncycastle/asn1/x509/Time.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/Time.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/x509/Time.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/x509/Time.java
diff --git a/src/main/java/org/bouncycastle/asn1/x509/V1TBSCertificateGenerator.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/V1TBSCertificateGenerator.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/x509/V1TBSCertificateGenerator.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/x509/V1TBSCertificateGenerator.java
diff --git a/src/main/java/org/bouncycastle/asn1/x509/V2Form.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/V2Form.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/x509/V2Form.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/x509/V2Form.java
diff --git a/src/main/java/org/bouncycastle/asn1/x509/V3TBSCertificateGenerator.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/V3TBSCertificateGenerator.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/x509/V3TBSCertificateGenerator.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/x509/V3TBSCertificateGenerator.java
diff --git a/src/main/java/org/bouncycastle/asn1/x509/X509CertificateStructure.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/X509CertificateStructure.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/x509/X509CertificateStructure.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/x509/X509CertificateStructure.java
diff --git a/src/main/java/org/bouncycastle/asn1/x509/X509DefaultEntryConverter.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/X509DefaultEntryConverter.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/x509/X509DefaultEntryConverter.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/x509/X509DefaultEntryConverter.java
diff --git a/src/main/java/org/bouncycastle/asn1/x509/X509Extension.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/X509Extension.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/x509/X509Extension.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/x509/X509Extension.java
diff --git a/src/main/java/org/bouncycastle/asn1/x509/X509Extensions.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/X509Extensions.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/x509/X509Extensions.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/x509/X509Extensions.java
diff --git a/src/main/java/org/bouncycastle/asn1/x509/X509ExtensionsGenerator.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/X509ExtensionsGenerator.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/x509/X509ExtensionsGenerator.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/x509/X509ExtensionsGenerator.java
diff --git a/src/main/java/org/bouncycastle/asn1/x509/X509Name.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/X509Name.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/x509/X509Name.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/x509/X509Name.java
diff --git a/src/main/java/org/bouncycastle/asn1/x509/X509NameEntryConverter.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/X509NameEntryConverter.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/x509/X509NameEntryConverter.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/x509/X509NameEntryConverter.java
diff --git a/src/main/java/org/bouncycastle/asn1/x509/X509NameTokenizer.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/X509NameTokenizer.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/x509/X509NameTokenizer.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/x509/X509NameTokenizer.java
diff --git a/src/main/java/org/bouncycastle/asn1/x509/X509ObjectIdentifiers.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/X509ObjectIdentifiers.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/x509/X509ObjectIdentifiers.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/x509/X509ObjectIdentifiers.java
diff --git a/src/main/java/org/bouncycastle/asn1/x9/DHDomainParameters.java b/bcprov/src/main/java/org/bouncycastle/asn1/x9/DHDomainParameters.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/x9/DHDomainParameters.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/x9/DHDomainParameters.java
diff --git a/src/main/java/org/bouncycastle/asn1/x9/DHPublicKey.java b/bcprov/src/main/java/org/bouncycastle/asn1/x9/DHPublicKey.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/x9/DHPublicKey.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/x9/DHPublicKey.java
diff --git a/src/main/java/org/bouncycastle/asn1/x9/DHValidationParms.java b/bcprov/src/main/java/org/bouncycastle/asn1/x9/DHValidationParms.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/x9/DHValidationParms.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/x9/DHValidationParms.java
diff --git a/src/main/java/org/bouncycastle/asn1/x9/X962NamedCurves.java b/bcprov/src/main/java/org/bouncycastle/asn1/x9/X962NamedCurves.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/x9/X962NamedCurves.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/x9/X962NamedCurves.java
diff --git a/src/main/java/org/bouncycastle/asn1/x9/X962Parameters.java b/bcprov/src/main/java/org/bouncycastle/asn1/x9/X962Parameters.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/x9/X962Parameters.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/x9/X962Parameters.java
diff --git a/src/main/java/org/bouncycastle/asn1/x9/X9Curve.java b/bcprov/src/main/java/org/bouncycastle/asn1/x9/X9Curve.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/x9/X9Curve.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/x9/X9Curve.java
diff --git a/src/main/java/org/bouncycastle/asn1/x9/X9ECParameters.java b/bcprov/src/main/java/org/bouncycastle/asn1/x9/X9ECParameters.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/x9/X9ECParameters.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/x9/X9ECParameters.java
diff --git a/src/main/java/org/bouncycastle/asn1/x9/X9ECParametersHolder.java b/bcprov/src/main/java/org/bouncycastle/asn1/x9/X9ECParametersHolder.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/x9/X9ECParametersHolder.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/x9/X9ECParametersHolder.java
diff --git a/src/main/java/org/bouncycastle/asn1/x9/X9ECPoint.java b/bcprov/src/main/java/org/bouncycastle/asn1/x9/X9ECPoint.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/x9/X9ECPoint.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/x9/X9ECPoint.java
diff --git a/src/main/java/org/bouncycastle/asn1/x9/X9FieldElement.java b/bcprov/src/main/java/org/bouncycastle/asn1/x9/X9FieldElement.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/x9/X9FieldElement.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/x9/X9FieldElement.java
diff --git a/src/main/java/org/bouncycastle/asn1/x9/X9FieldID.java b/bcprov/src/main/java/org/bouncycastle/asn1/x9/X9FieldID.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/x9/X9FieldID.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/x9/X9FieldID.java
diff --git a/src/main/java/org/bouncycastle/asn1/x9/X9IntegerConverter.java b/bcprov/src/main/java/org/bouncycastle/asn1/x9/X9IntegerConverter.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/x9/X9IntegerConverter.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/x9/X9IntegerConverter.java
diff --git a/src/main/java/org/bouncycastle/asn1/x9/X9ObjectIdentifiers.java b/bcprov/src/main/java/org/bouncycastle/asn1/x9/X9ObjectIdentifiers.java
similarity index 100%
rename from src/main/java/org/bouncycastle/asn1/x9/X9ObjectIdentifiers.java
rename to bcprov/src/main/java/org/bouncycastle/asn1/x9/X9ObjectIdentifiers.java
diff --git a/src/main/java/org/bouncycastle/crypto/AsymmetricBlockCipher.java b/bcprov/src/main/java/org/bouncycastle/crypto/AsymmetricBlockCipher.java
similarity index 100%
rename from src/main/java/org/bouncycastle/crypto/AsymmetricBlockCipher.java
rename to bcprov/src/main/java/org/bouncycastle/crypto/AsymmetricBlockCipher.java
diff --git a/src/main/java/org/bouncycastle/crypto/AsymmetricCipherKeyPair.java b/bcprov/src/main/java/org/bouncycastle/crypto/AsymmetricCipherKeyPair.java
similarity index 100%
rename from src/main/java/org/bouncycastle/crypto/AsymmetricCipherKeyPair.java
rename to bcprov/src/main/java/org/bouncycastle/crypto/AsymmetricCipherKeyPair.java
diff --git a/src/main/java/org/bouncycastle/crypto/AsymmetricCipherKeyPairGenerator.java b/bcprov/src/main/java/org/bouncycastle/crypto/AsymmetricCipherKeyPairGenerator.java
similarity index 100%
rename from src/main/java/org/bouncycastle/crypto/AsymmetricCipherKeyPairGenerator.java
rename to bcprov/src/main/java/org/bouncycastle/crypto/AsymmetricCipherKeyPairGenerator.java
diff --git a/src/main/java/org/bouncycastle/crypto/BasicAgreement.java b/bcprov/src/main/java/org/bouncycastle/crypto/BasicAgreement.java
similarity index 100%
rename from src/main/java/org/bouncycastle/crypto/BasicAgreement.java
rename to bcprov/src/main/java/org/bouncycastle/crypto/BasicAgreement.java
diff --git a/src/main/java/org/bouncycastle/crypto/BlockCipher.java b/bcprov/src/main/java/org/bouncycastle/crypto/BlockCipher.java
similarity index 100%
rename from src/main/java/org/bouncycastle/crypto/BlockCipher.java
rename to bcprov/src/main/java/org/bouncycastle/crypto/BlockCipher.java
diff --git a/src/main/java/org/bouncycastle/crypto/BufferedBlockCipher.java b/bcprov/src/main/java/org/bouncycastle/crypto/BufferedBlockCipher.java
similarity index 100%
rename from src/main/java/org/bouncycastle/crypto/BufferedBlockCipher.java
rename to bcprov/src/main/java/org/bouncycastle/crypto/BufferedBlockCipher.java
diff --git a/src/main/java/org/bouncycastle/crypto/CipherKeyGenerator.java b/bcprov/src/main/java/org/bouncycastle/crypto/CipherKeyGenerator.java
similarity index 100%
rename from src/main/java/org/bouncycastle/crypto/CipherKeyGenerator.java
rename to bcprov/src/main/java/org/bouncycastle/crypto/CipherKeyGenerator.java
diff --git a/src/main/java/org/bouncycastle/crypto/CipherParameters.java b/bcprov/src/main/java/org/bouncycastle/crypto/CipherParameters.java
similarity index 100%
rename from src/main/java/org/bouncycastle/crypto/CipherParameters.java
rename to bcprov/src/main/java/org/bouncycastle/crypto/CipherParameters.java
diff --git a/src/main/java/org/bouncycastle/crypto/CryptoException.java b/bcprov/src/main/java/org/bouncycastle/crypto/CryptoException.java
similarity index 100%
rename from src/main/java/org/bouncycastle/crypto/CryptoException.java
rename to bcprov/src/main/java/org/bouncycastle/crypto/CryptoException.java
diff --git a/src/main/java/org/bouncycastle/crypto/DSA.java b/bcprov/src/main/java/org/bouncycastle/crypto/DSA.java
similarity index 100%
rename from src/main/java/org/bouncycastle/crypto/DSA.java
rename to bcprov/src/main/java/org/bouncycastle/crypto/DSA.java
diff --git a/src/main/java/org/bouncycastle/crypto/DataLengthException.java b/bcprov/src/main/java/org/bouncycastle/crypto/DataLengthException.java
similarity index 100%
rename from src/main/java/org/bouncycastle/crypto/DataLengthException.java
rename to bcprov/src/main/java/org/bouncycastle/crypto/DataLengthException.java
diff --git a/src/main/java/org/bouncycastle/crypto/DerivationFunction.java b/bcprov/src/main/java/org/bouncycastle/crypto/DerivationFunction.java
similarity index 100%
rename from src/main/java/org/bouncycastle/crypto/DerivationFunction.java
rename to bcprov/src/main/java/org/bouncycastle/crypto/DerivationFunction.java
diff --git a/src/main/java/org/bouncycastle/crypto/DerivationParameters.java b/bcprov/src/main/java/org/bouncycastle/crypto/DerivationParameters.java
similarity index 100%
rename from src/main/java/org/bouncycastle/crypto/DerivationParameters.java
rename to bcprov/src/main/java/org/bouncycastle/crypto/DerivationParameters.java
diff --git a/src/main/java/org/bouncycastle/crypto/Digest.java b/bcprov/src/main/java/org/bouncycastle/crypto/Digest.java
similarity index 100%
rename from src/main/java/org/bouncycastle/crypto/Digest.java
rename to bcprov/src/main/java/org/bouncycastle/crypto/Digest.java
diff --git a/src/main/java/org/bouncycastle/crypto/ExtendedDigest.java b/bcprov/src/main/java/org/bouncycastle/crypto/ExtendedDigest.java
similarity index 100%
rename from src/main/java/org/bouncycastle/crypto/ExtendedDigest.java
rename to bcprov/src/main/java/org/bouncycastle/crypto/ExtendedDigest.java
diff --git a/src/main/java/org/bouncycastle/crypto/InvalidCipherTextException.java b/bcprov/src/main/java/org/bouncycastle/crypto/InvalidCipherTextException.java
similarity index 100%
rename from src/main/java/org/bouncycastle/crypto/InvalidCipherTextException.java
rename to bcprov/src/main/java/org/bouncycastle/crypto/InvalidCipherTextException.java
diff --git a/src/main/java/org/bouncycastle/crypto/KeyGenerationParameters.java b/bcprov/src/main/java/org/bouncycastle/crypto/KeyGenerationParameters.java
similarity index 100%
rename from src/main/java/org/bouncycastle/crypto/KeyGenerationParameters.java
rename to bcprov/src/main/java/org/bouncycastle/crypto/KeyGenerationParameters.java
diff --git a/src/main/java/org/bouncycastle/crypto/Mac.java b/bcprov/src/main/java/org/bouncycastle/crypto/Mac.java
similarity index 100%
rename from src/main/java/org/bouncycastle/crypto/Mac.java
rename to bcprov/src/main/java/org/bouncycastle/crypto/Mac.java
diff --git a/src/main/java/org/bouncycastle/crypto/PBEParametersGenerator.java b/bcprov/src/main/java/org/bouncycastle/crypto/PBEParametersGenerator.java
similarity index 100%
rename from src/main/java/org/bouncycastle/crypto/PBEParametersGenerator.java
rename to bcprov/src/main/java/org/bouncycastle/crypto/PBEParametersGenerator.java
diff --git a/src/main/java/org/bouncycastle/crypto/RuntimeCryptoException.java b/bcprov/src/main/java/org/bouncycastle/crypto/RuntimeCryptoException.java
similarity index 100%
rename from src/main/java/org/bouncycastle/crypto/RuntimeCryptoException.java
rename to bcprov/src/main/java/org/bouncycastle/crypto/RuntimeCryptoException.java
diff --git a/src/main/java/org/bouncycastle/crypto/Signer.java b/bcprov/src/main/java/org/bouncycastle/crypto/Signer.java
similarity index 100%
rename from src/main/java/org/bouncycastle/crypto/Signer.java
rename to bcprov/src/main/java/org/bouncycastle/crypto/Signer.java
diff --git a/src/main/java/org/bouncycastle/crypto/SignerWithRecovery.java b/bcprov/src/main/java/org/bouncycastle/crypto/SignerWithRecovery.java
similarity index 100%
rename from src/main/java/org/bouncycastle/crypto/SignerWithRecovery.java
rename to bcprov/src/main/java/org/bouncycastle/crypto/SignerWithRecovery.java
diff --git a/src/main/java/org/bouncycastle/crypto/StreamBlockCipher.java b/bcprov/src/main/java/org/bouncycastle/crypto/StreamBlockCipher.java
similarity index 100%
rename from src/main/java/org/bouncycastle/crypto/StreamBlockCipher.java
rename to bcprov/src/main/java/org/bouncycastle/crypto/StreamBlockCipher.java
diff --git a/src/main/java/org/bouncycastle/crypto/StreamCipher.java b/bcprov/src/main/java/org/bouncycastle/crypto/StreamCipher.java
similarity index 100%
rename from src/main/java/org/bouncycastle/crypto/StreamCipher.java
rename to bcprov/src/main/java/org/bouncycastle/crypto/StreamCipher.java
diff --git a/src/main/java/org/bouncycastle/crypto/Wrapper.java b/bcprov/src/main/java/org/bouncycastle/crypto/Wrapper.java
similarity index 100%
rename from src/main/java/org/bouncycastle/crypto/Wrapper.java
rename to bcprov/src/main/java/org/bouncycastle/crypto/Wrapper.java
diff --git a/src/main/java/org/bouncycastle/crypto/agreement/DHBasicAgreement.java b/bcprov/src/main/java/org/bouncycastle/crypto/agreement/DHBasicAgreement.java
similarity index 100%
rename from src/main/java/org/bouncycastle/crypto/agreement/DHBasicAgreement.java
rename to bcprov/src/main/java/org/bouncycastle/crypto/agreement/DHBasicAgreement.java
diff --git a/src/main/java/org/bouncycastle/crypto/agreement/ECDHBasicAgreement.java b/bcprov/src/main/java/org/bouncycastle/crypto/agreement/ECDHBasicAgreement.java
similarity index 100%
rename from src/main/java/org/bouncycastle/crypto/agreement/ECDHBasicAgreement.java
rename to bcprov/src/main/java/org/bouncycastle/crypto/agreement/ECDHBasicAgreement.java
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/digests/AndroidDigestFactory.java b/bcprov/src/main/java/org/bouncycastle/crypto/digests/AndroidDigestFactory.java
new file mode 100644
index 0000000..1a82a46
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/digests/AndroidDigestFactory.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.bouncycastle.crypto.digests;
+
+import org.bouncycastle.crypto.Digest;
+
+/**
+ * Level of indirection to let us select OpenSSLDigest implementations
+ * for libcore but fallback to BouncyCastle ones on the RI.
+ */
+public final class AndroidDigestFactory {
+    private static final String OpenSSLFactoryClassName
+            = AndroidDigestFactory.class.getName() + "OpenSSL";
+    private static final String BouncyCastleFactoryClassName
+            = AndroidDigestFactory.class.getName() + "BouncyCastle";
+
+    private static final AndroidDigestFactoryInterface FACTORY;
+    static {
+        Class factoryImplementationClass;
+        try {
+            factoryImplementationClass = Class.forName(OpenSSLFactoryClassName);
+        } catch (ClassNotFoundException e1) {
+            try {
+                factoryImplementationClass = Class.forName(BouncyCastleFactoryClassName);
+            } catch (ClassNotFoundException e2) {
+                throw new AssertionError("Failed to find AndroidDigestFactoryInterface "
+                                         + "implementation. Looked for "
+                                         + OpenSSLFactoryClassName + " and "
+                                         + BouncyCastleFactoryClassName);
+            }
+        }
+        if (!AndroidDigestFactoryInterface.class.isAssignableFrom(factoryImplementationClass)) {
+            throw new AssertionError(factoryImplementationClass
+                                     + "does not implement AndroidDigestFactoryInterface");
+        }
+        try {
+            FACTORY = (AndroidDigestFactoryInterface) factoryImplementationClass.newInstance();
+        } catch (InstantiationException e) {
+            throw new AssertionError(e);
+        } catch (IllegalAccessException e) {
+            throw new AssertionError(e);
+        }
+    }
+
+    public static Digest getMD5() {
+        return FACTORY.getMD5();
+    }
+
+    public static Digest getSHA1() {
+        return FACTORY.getSHA1();
+    }
+
+    public static Digest getSHA256() {
+        return FACTORY.getSHA256();
+    }
+
+    public static Digest getSHA384() {
+        return FACTORY.getSHA384();
+    }
+
+    public static Digest getSHA512() {
+        return FACTORY.getSHA512();
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/digests/AndroidDigestFactoryBouncyCastle.java b/bcprov/src/main/java/org/bouncycastle/crypto/digests/AndroidDigestFactoryBouncyCastle.java
new file mode 100644
index 0000000..47d3852
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/digests/AndroidDigestFactoryBouncyCastle.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.bouncycastle.crypto.digests;
+
+import org.bouncycastle.crypto.Digest;
+
+public class AndroidDigestFactoryBouncyCastle implements AndroidDigestFactoryInterface {
+    public Digest getMD5() {
+        return new MD5Digest();
+    }
+    public Digest getSHA1() {
+        return new SHA1Digest();
+    }
+    public Digest getSHA256() {
+        return new SHA256Digest();
+    }
+    public Digest getSHA384() {
+        return new SHA384Digest();
+    }
+    public Digest getSHA512() {
+        return new SHA512Digest();
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/digests/AndroidDigestFactoryInterface.java b/bcprov/src/main/java/org/bouncycastle/crypto/digests/AndroidDigestFactoryInterface.java
new file mode 100644
index 0000000..e2e020b
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/digests/AndroidDigestFactoryInterface.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.bouncycastle.crypto.digests;
+
+import org.bouncycastle.crypto.Digest;
+
+interface AndroidDigestFactoryInterface {
+    public Digest getMD5();
+    public Digest getSHA1();
+    public Digest getSHA256();
+    public Digest getSHA384();
+    public Digest getSHA512();
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/digests/AndroidDigestFactoryOpenSSL.java b/bcprov/src/main/java/org/bouncycastle/crypto/digests/AndroidDigestFactoryOpenSSL.java
new file mode 100644
index 0000000..cd07bd9
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/digests/AndroidDigestFactoryOpenSSL.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.bouncycastle.crypto.digests;
+
+import org.bouncycastle.crypto.Digest;
+
+public class AndroidDigestFactoryOpenSSL implements AndroidDigestFactoryInterface {
+    public Digest getMD5() {
+        return new OpenSSLDigest.MD5();
+    }
+    public Digest getSHA1() {
+        return new OpenSSLDigest.SHA1();
+    }
+    public Digest getSHA256() {
+        return new OpenSSLDigest.SHA256();
+    }
+    public Digest getSHA384() {
+        return new OpenSSLDigest.SHA384();
+    }
+    public Digest getSHA512() {
+        return new OpenSSLDigest.SHA512();
+    }
+}
diff --git a/src/main/java/org/bouncycastle/crypto/digests/GeneralDigest.java b/bcprov/src/main/java/org/bouncycastle/crypto/digests/GeneralDigest.java
similarity index 100%
rename from src/main/java/org/bouncycastle/crypto/digests/GeneralDigest.java
rename to bcprov/src/main/java/org/bouncycastle/crypto/digests/GeneralDigest.java
diff --git a/src/main/java/org/bouncycastle/crypto/digests/LongDigest.java b/bcprov/src/main/java/org/bouncycastle/crypto/digests/LongDigest.java
similarity index 100%
rename from src/main/java/org/bouncycastle/crypto/digests/LongDigest.java
rename to bcprov/src/main/java/org/bouncycastle/crypto/digests/LongDigest.java
diff --git a/src/main/java/org/bouncycastle/crypto/digests/MD5Digest.java b/bcprov/src/main/java/org/bouncycastle/crypto/digests/MD5Digest.java
similarity index 100%
rename from src/main/java/org/bouncycastle/crypto/digests/MD5Digest.java
rename to bcprov/src/main/java/org/bouncycastle/crypto/digests/MD5Digest.java
diff --git a/src/main/java/org/bouncycastle/crypto/digests/NullDigest.java b/bcprov/src/main/java/org/bouncycastle/crypto/digests/NullDigest.java
similarity index 100%
rename from src/main/java/org/bouncycastle/crypto/digests/NullDigest.java
rename to bcprov/src/main/java/org/bouncycastle/crypto/digests/NullDigest.java
diff --git a/src/main/java/org/bouncycastle/crypto/digests/OpenSSLDigest.java b/bcprov/src/main/java/org/bouncycastle/crypto/digests/OpenSSLDigest.java
similarity index 100%
rename from src/main/java/org/bouncycastle/crypto/digests/OpenSSLDigest.java
rename to bcprov/src/main/java/org/bouncycastle/crypto/digests/OpenSSLDigest.java
diff --git a/src/main/java/org/bouncycastle/crypto/digests/SHA1Digest.java b/bcprov/src/main/java/org/bouncycastle/crypto/digests/SHA1Digest.java
similarity index 100%
rename from src/main/java/org/bouncycastle/crypto/digests/SHA1Digest.java
rename to bcprov/src/main/java/org/bouncycastle/crypto/digests/SHA1Digest.java
diff --git a/src/main/java/org/bouncycastle/crypto/digests/SHA256Digest.java b/bcprov/src/main/java/org/bouncycastle/crypto/digests/SHA256Digest.java
similarity index 100%
rename from src/main/java/org/bouncycastle/crypto/digests/SHA256Digest.java
rename to bcprov/src/main/java/org/bouncycastle/crypto/digests/SHA256Digest.java
diff --git a/src/main/java/org/bouncycastle/crypto/digests/SHA384Digest.java b/bcprov/src/main/java/org/bouncycastle/crypto/digests/SHA384Digest.java
similarity index 100%
rename from src/main/java/org/bouncycastle/crypto/digests/SHA384Digest.java
rename to bcprov/src/main/java/org/bouncycastle/crypto/digests/SHA384Digest.java
diff --git a/src/main/java/org/bouncycastle/crypto/digests/SHA512Digest.java b/bcprov/src/main/java/org/bouncycastle/crypto/digests/SHA512Digest.java
similarity index 100%
rename from src/main/java/org/bouncycastle/crypto/digests/SHA512Digest.java
rename to bcprov/src/main/java/org/bouncycastle/crypto/digests/SHA512Digest.java
diff --git a/src/main/java/org/bouncycastle/crypto/encodings/OAEPEncoding.java b/bcprov/src/main/java/org/bouncycastle/crypto/encodings/OAEPEncoding.java
similarity index 98%
rename from src/main/java/org/bouncycastle/crypto/encodings/OAEPEncoding.java
rename to bcprov/src/main/java/org/bouncycastle/crypto/encodings/OAEPEncoding.java
index fb11fc5..c4719cf 100644
--- a/src/main/java/org/bouncycastle/crypto/encodings/OAEPEncoding.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/encodings/OAEPEncoding.java
@@ -5,7 +5,7 @@
 import org.bouncycastle.crypto.Digest;
 import org.bouncycastle.crypto.InvalidCipherTextException;
 // BEGIN android-changed
-import org.bouncycastle.crypto.digests.OpenSSLDigest;
+import org.bouncycastle.crypto.digests.AndroidDigestFactory;
 // END android-changed
 import org.bouncycastle.crypto.params.ParametersWithRandom;
 
@@ -29,7 +29,7 @@
         AsymmetricBlockCipher   cipher)
     {
         // BEGIN android-changed
-        this(cipher, new OpenSSLDigest.SHA1(), null);
+        this(cipher, AndroidDigestFactory.getSHA1(), null);
         // END android-changed
     }
     
diff --git a/src/main/java/org/bouncycastle/crypto/encodings/PKCS1Encoding.java b/bcprov/src/main/java/org/bouncycastle/crypto/encodings/PKCS1Encoding.java
similarity index 100%
rename from src/main/java/org/bouncycastle/crypto/encodings/PKCS1Encoding.java
rename to bcprov/src/main/java/org/bouncycastle/crypto/encodings/PKCS1Encoding.java
diff --git a/src/main/java/org/bouncycastle/crypto/engines/AESEngine.java b/bcprov/src/main/java/org/bouncycastle/crypto/engines/AESEngine.java
similarity index 100%
rename from src/main/java/org/bouncycastle/crypto/engines/AESEngine.java
rename to bcprov/src/main/java/org/bouncycastle/crypto/engines/AESEngine.java
diff --git a/src/main/java/org/bouncycastle/crypto/engines/AESFastEngine.java b/bcprov/src/main/java/org/bouncycastle/crypto/engines/AESFastEngine.java
similarity index 100%
rename from src/main/java/org/bouncycastle/crypto/engines/AESFastEngine.java
rename to bcprov/src/main/java/org/bouncycastle/crypto/engines/AESFastEngine.java
diff --git a/src/main/java/org/bouncycastle/crypto/engines/AESWrapEngine.java b/bcprov/src/main/java/org/bouncycastle/crypto/engines/AESWrapEngine.java
similarity index 100%
rename from src/main/java/org/bouncycastle/crypto/engines/AESWrapEngine.java
rename to bcprov/src/main/java/org/bouncycastle/crypto/engines/AESWrapEngine.java
diff --git a/src/main/java/org/bouncycastle/crypto/engines/BlowfishEngine.java b/bcprov/src/main/java/org/bouncycastle/crypto/engines/BlowfishEngine.java
similarity index 100%
rename from src/main/java/org/bouncycastle/crypto/engines/BlowfishEngine.java
rename to bcprov/src/main/java/org/bouncycastle/crypto/engines/BlowfishEngine.java
diff --git a/src/main/java/org/bouncycastle/crypto/engines/DESEngine.java b/bcprov/src/main/java/org/bouncycastle/crypto/engines/DESEngine.java
similarity index 100%
rename from src/main/java/org/bouncycastle/crypto/engines/DESEngine.java
rename to bcprov/src/main/java/org/bouncycastle/crypto/engines/DESEngine.java
diff --git a/src/main/java/org/bouncycastle/crypto/engines/DESedeEngine.java b/bcprov/src/main/java/org/bouncycastle/crypto/engines/DESedeEngine.java
similarity index 100%
rename from src/main/java/org/bouncycastle/crypto/engines/DESedeEngine.java
rename to bcprov/src/main/java/org/bouncycastle/crypto/engines/DESedeEngine.java
diff --git a/src/main/java/org/bouncycastle/crypto/engines/DESedeWrapEngine.java b/bcprov/src/main/java/org/bouncycastle/crypto/engines/DESedeWrapEngine.java
similarity index 98%
rename from src/main/java/org/bouncycastle/crypto/engines/DESedeWrapEngine.java
rename to bcprov/src/main/java/org/bouncycastle/crypto/engines/DESedeWrapEngine.java
index be2d09b..d0c04f2 100644
--- a/src/main/java/org/bouncycastle/crypto/engines/DESedeWrapEngine.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/engines/DESedeWrapEngine.java
@@ -7,7 +7,7 @@
 import org.bouncycastle.crypto.InvalidCipherTextException;
 import org.bouncycastle.crypto.Wrapper;
 // BEGIN android-changed
-import org.bouncycastle.crypto.digests.OpenSSLDigest;
+import org.bouncycastle.crypto.digests.AndroidDigestFactory;
 // END android-changed
 import org.bouncycastle.crypto.modes.CBCBlockCipher;
 import org.bouncycastle.crypto.params.KeyParameter;
@@ -55,7 +55,7 @@
     // checksum digest
     //
     // BEGIN android-changed
-    Digest  sha1 = new OpenSSLDigest.SHA1();
+    Digest  sha1 = AndroidDigestFactory.getSHA1();
     // END android-changed
     byte[]  digest = new byte[20];
 
diff --git a/src/main/java/org/bouncycastle/crypto/engines/RC2Engine.java b/bcprov/src/main/java/org/bouncycastle/crypto/engines/RC2Engine.java
similarity index 100%
rename from src/main/java/org/bouncycastle/crypto/engines/RC2Engine.java
rename to bcprov/src/main/java/org/bouncycastle/crypto/engines/RC2Engine.java
diff --git a/src/main/java/org/bouncycastle/crypto/engines/RC4Engine.java b/bcprov/src/main/java/org/bouncycastle/crypto/engines/RC4Engine.java
similarity index 100%
rename from src/main/java/org/bouncycastle/crypto/engines/RC4Engine.java
rename to bcprov/src/main/java/org/bouncycastle/crypto/engines/RC4Engine.java
diff --git a/src/main/java/org/bouncycastle/crypto/engines/RFC3394WrapEngine.java b/bcprov/src/main/java/org/bouncycastle/crypto/engines/RFC3394WrapEngine.java
similarity index 100%
rename from src/main/java/org/bouncycastle/crypto/engines/RFC3394WrapEngine.java
rename to bcprov/src/main/java/org/bouncycastle/crypto/engines/RFC3394WrapEngine.java
diff --git a/src/main/java/org/bouncycastle/crypto/engines/RSABlindedEngine.java b/bcprov/src/main/java/org/bouncycastle/crypto/engines/RSABlindedEngine.java
similarity index 100%
rename from src/main/java/org/bouncycastle/crypto/engines/RSABlindedEngine.java
rename to bcprov/src/main/java/org/bouncycastle/crypto/engines/RSABlindedEngine.java
diff --git a/src/main/java/org/bouncycastle/crypto/engines/RSACoreEngine.java b/bcprov/src/main/java/org/bouncycastle/crypto/engines/RSACoreEngine.java
similarity index 100%
rename from src/main/java/org/bouncycastle/crypto/engines/RSACoreEngine.java
rename to bcprov/src/main/java/org/bouncycastle/crypto/engines/RSACoreEngine.java
diff --git a/src/main/java/org/bouncycastle/crypto/engines/TwofishEngine.java b/bcprov/src/main/java/org/bouncycastle/crypto/engines/TwofishEngine.java
similarity index 100%
rename from src/main/java/org/bouncycastle/crypto/engines/TwofishEngine.java
rename to bcprov/src/main/java/org/bouncycastle/crypto/engines/TwofishEngine.java
diff --git a/src/main/java/org/bouncycastle/crypto/generators/DESKeyGenerator.java b/bcprov/src/main/java/org/bouncycastle/crypto/generators/DESKeyGenerator.java
similarity index 100%
rename from src/main/java/org/bouncycastle/crypto/generators/DESKeyGenerator.java
rename to bcprov/src/main/java/org/bouncycastle/crypto/generators/DESKeyGenerator.java
diff --git a/src/main/java/org/bouncycastle/crypto/generators/DESedeKeyGenerator.java b/bcprov/src/main/java/org/bouncycastle/crypto/generators/DESedeKeyGenerator.java
similarity index 100%
rename from src/main/java/org/bouncycastle/crypto/generators/DESedeKeyGenerator.java
rename to bcprov/src/main/java/org/bouncycastle/crypto/generators/DESedeKeyGenerator.java
diff --git a/src/main/java/org/bouncycastle/crypto/generators/DHBasicKeyPairGenerator.java b/bcprov/src/main/java/org/bouncycastle/crypto/generators/DHBasicKeyPairGenerator.java
similarity index 100%
rename from src/main/java/org/bouncycastle/crypto/generators/DHBasicKeyPairGenerator.java
rename to bcprov/src/main/java/org/bouncycastle/crypto/generators/DHBasicKeyPairGenerator.java
diff --git a/src/main/java/org/bouncycastle/crypto/generators/DHKeyGeneratorHelper.java b/bcprov/src/main/java/org/bouncycastle/crypto/generators/DHKeyGeneratorHelper.java
similarity index 100%
rename from src/main/java/org/bouncycastle/crypto/generators/DHKeyGeneratorHelper.java
rename to bcprov/src/main/java/org/bouncycastle/crypto/generators/DHKeyGeneratorHelper.java
diff --git a/src/main/java/org/bouncycastle/crypto/generators/DHParametersGenerator.java b/bcprov/src/main/java/org/bouncycastle/crypto/generators/DHParametersGenerator.java
similarity index 100%
rename from src/main/java/org/bouncycastle/crypto/generators/DHParametersGenerator.java
rename to bcprov/src/main/java/org/bouncycastle/crypto/generators/DHParametersGenerator.java
diff --git a/src/main/java/org/bouncycastle/crypto/generators/DHParametersHelper.java b/bcprov/src/main/java/org/bouncycastle/crypto/generators/DHParametersHelper.java
similarity index 100%
rename from src/main/java/org/bouncycastle/crypto/generators/DHParametersHelper.java
rename to bcprov/src/main/java/org/bouncycastle/crypto/generators/DHParametersHelper.java
diff --git a/src/main/java/org/bouncycastle/crypto/generators/DSAKeyPairGenerator.java b/bcprov/src/main/java/org/bouncycastle/crypto/generators/DSAKeyPairGenerator.java
similarity index 100%
rename from src/main/java/org/bouncycastle/crypto/generators/DSAKeyPairGenerator.java
rename to bcprov/src/main/java/org/bouncycastle/crypto/generators/DSAKeyPairGenerator.java
diff --git a/src/main/java/org/bouncycastle/crypto/generators/DSAParametersGenerator.java b/bcprov/src/main/java/org/bouncycastle/crypto/generators/DSAParametersGenerator.java
similarity index 98%
rename from src/main/java/org/bouncycastle/crypto/generators/DSAParametersGenerator.java
rename to bcprov/src/main/java/org/bouncycastle/crypto/generators/DSAParametersGenerator.java
index f05f3d7..98dd0f7 100644
--- a/src/main/java/org/bouncycastle/crypto/generators/DSAParametersGenerator.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/generators/DSAParametersGenerator.java
@@ -2,7 +2,7 @@
 
 import org.bouncycastle.crypto.Digest;
 // BEGIN android-changed
-import org.bouncycastle.crypto.digests.OpenSSLDigest;
+import org.bouncycastle.crypto.digests.AndroidDigestFactory;
 // END android-changed
 import org.bouncycastle.crypto.params.DSAParameters;
 import org.bouncycastle.crypto.params.DSAValidationParameters;
@@ -77,7 +77,7 @@
         byte[]          part2 = new byte[20];
         byte[]          u = new byte[20];
         // BEGIN android-changed
-        Digest          sha1 = new OpenSSLDigest.SHA1();
+        Digest          sha1 = AndroidDigestFactory.getSHA1();
         // END android-changed
         int             n = (L - 1) / 160;
         byte[]          w = new byte[L / 8];
@@ -170,7 +170,7 @@
 // A.1.1.2 Generation of the Probable Primes p and q Using an Approved Hash Function
         // FIXME This should be configurable (digest size in bits must be >= N)
         // BEGIN android-changed
-        Digest d = new OpenSSLDigest.SHA256();
+        Digest d = AndroidDigestFactory.getSHA256();
         // END android-changed
         int outlen = d.getDigestSize() * 8;
 
diff --git a/src/main/java/org/bouncycastle/crypto/generators/ECKeyPairGenerator.java b/bcprov/src/main/java/org/bouncycastle/crypto/generators/ECKeyPairGenerator.java
similarity index 100%
rename from src/main/java/org/bouncycastle/crypto/generators/ECKeyPairGenerator.java
rename to bcprov/src/main/java/org/bouncycastle/crypto/generators/ECKeyPairGenerator.java
diff --git a/src/main/java/org/bouncycastle/crypto/generators/OpenSSLPBEParametersGenerator.java b/bcprov/src/main/java/org/bouncycastle/crypto/generators/OpenSSLPBEParametersGenerator.java
similarity index 96%
rename from src/main/java/org/bouncycastle/crypto/generators/OpenSSLPBEParametersGenerator.java
rename to bcprov/src/main/java/org/bouncycastle/crypto/generators/OpenSSLPBEParametersGenerator.java
index 6999653..79ec0f2 100644
--- a/src/main/java/org/bouncycastle/crypto/generators/OpenSSLPBEParametersGenerator.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/generators/OpenSSLPBEParametersGenerator.java
@@ -4,7 +4,7 @@
 import org.bouncycastle.crypto.Digest;
 import org.bouncycastle.crypto.PBEParametersGenerator;
 // BEGIN android-changed
-import org.bouncycastle.crypto.digests.OpenSSLDigest;
+import org.bouncycastle.crypto.digests.AndroidDigestFactory;
 // END android-changed
 import org.bouncycastle.crypto.params.KeyParameter;
 import org.bouncycastle.crypto.params.ParametersWithIV;
@@ -20,7 +20,7 @@
     extends PBEParametersGenerator
 {
     // BEGIN android-changed
-    private Digest  digest = new OpenSSLDigest.MD5();
+    private Digest  digest = AndroidDigestFactory.getMD5();
     // END android-changed
 
     /**
diff --git a/src/main/java/org/bouncycastle/crypto/generators/PKCS12ParametersGenerator.java b/bcprov/src/main/java/org/bouncycastle/crypto/generators/PKCS12ParametersGenerator.java
similarity index 100%
rename from src/main/java/org/bouncycastle/crypto/generators/PKCS12ParametersGenerator.java
rename to bcprov/src/main/java/org/bouncycastle/crypto/generators/PKCS12ParametersGenerator.java
diff --git a/src/main/java/org/bouncycastle/crypto/generators/PKCS5S1ParametersGenerator.java b/bcprov/src/main/java/org/bouncycastle/crypto/generators/PKCS5S1ParametersGenerator.java
similarity index 100%
rename from src/main/java/org/bouncycastle/crypto/generators/PKCS5S1ParametersGenerator.java
rename to bcprov/src/main/java/org/bouncycastle/crypto/generators/PKCS5S1ParametersGenerator.java
diff --git a/src/main/java/org/bouncycastle/crypto/generators/PKCS5S2ParametersGenerator.java b/bcprov/src/main/java/org/bouncycastle/crypto/generators/PKCS5S2ParametersGenerator.java
similarity index 97%
rename from src/main/java/org/bouncycastle/crypto/generators/PKCS5S2ParametersGenerator.java
rename to bcprov/src/main/java/org/bouncycastle/crypto/generators/PKCS5S2ParametersGenerator.java
index 236ae2e..a6d87b9 100644
--- a/src/main/java/org/bouncycastle/crypto/generators/PKCS5S2ParametersGenerator.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/generators/PKCS5S2ParametersGenerator.java
@@ -5,7 +5,7 @@
 import org.bouncycastle.crypto.Mac;
 import org.bouncycastle.crypto.PBEParametersGenerator;
 // BEGIN android-changed
-import org.bouncycastle.crypto.digests.OpenSSLDigest;
+import org.bouncycastle.crypto.digests.AndroidDigestFactory;
 // END android-changed
 import org.bouncycastle.crypto.macs.HMac;
 import org.bouncycastle.crypto.params.KeyParameter;
@@ -30,7 +30,7 @@
     public PKCS5S2ParametersGenerator()
     {
         // BEGIN android-changed
-    	this(new OpenSSLDigest.SHA1());
+    	this(AndroidDigestFactory.getSHA1());
         // END android-changed
     }
 
diff --git a/src/main/java/org/bouncycastle/crypto/generators/RSAKeyPairGenerator.java b/bcprov/src/main/java/org/bouncycastle/crypto/generators/RSAKeyPairGenerator.java
similarity index 100%
rename from src/main/java/org/bouncycastle/crypto/generators/RSAKeyPairGenerator.java
rename to bcprov/src/main/java/org/bouncycastle/crypto/generators/RSAKeyPairGenerator.java
diff --git a/src/main/java/org/bouncycastle/crypto/io/CipherInputStream.java b/bcprov/src/main/java/org/bouncycastle/crypto/io/CipherInputStream.java
similarity index 100%
rename from src/main/java/org/bouncycastle/crypto/io/CipherInputStream.java
rename to bcprov/src/main/java/org/bouncycastle/crypto/io/CipherInputStream.java
diff --git a/src/main/java/org/bouncycastle/crypto/io/CipherOutputStream.java b/bcprov/src/main/java/org/bouncycastle/crypto/io/CipherOutputStream.java
similarity index 100%
rename from src/main/java/org/bouncycastle/crypto/io/CipherOutputStream.java
rename to bcprov/src/main/java/org/bouncycastle/crypto/io/CipherOutputStream.java
diff --git a/src/main/java/org/bouncycastle/crypto/io/DigestInputStream.java b/bcprov/src/main/java/org/bouncycastle/crypto/io/DigestInputStream.java
similarity index 100%
rename from src/main/java/org/bouncycastle/crypto/io/DigestInputStream.java
rename to bcprov/src/main/java/org/bouncycastle/crypto/io/DigestInputStream.java
diff --git a/src/main/java/org/bouncycastle/crypto/io/DigestOutputStream.java b/bcprov/src/main/java/org/bouncycastle/crypto/io/DigestOutputStream.java
similarity index 100%
rename from src/main/java/org/bouncycastle/crypto/io/DigestOutputStream.java
rename to bcprov/src/main/java/org/bouncycastle/crypto/io/DigestOutputStream.java
diff --git a/src/main/java/org/bouncycastle/crypto/io/MacInputStream.java b/bcprov/src/main/java/org/bouncycastle/crypto/io/MacInputStream.java
similarity index 100%
rename from src/main/java/org/bouncycastle/crypto/io/MacInputStream.java
rename to bcprov/src/main/java/org/bouncycastle/crypto/io/MacInputStream.java
diff --git a/src/main/java/org/bouncycastle/crypto/io/MacOutputStream.java b/bcprov/src/main/java/org/bouncycastle/crypto/io/MacOutputStream.java
similarity index 100%
rename from src/main/java/org/bouncycastle/crypto/io/MacOutputStream.java
rename to bcprov/src/main/java/org/bouncycastle/crypto/io/MacOutputStream.java
diff --git a/src/main/java/org/bouncycastle/crypto/macs/CBCBlockCipherMac.java b/bcprov/src/main/java/org/bouncycastle/crypto/macs/CBCBlockCipherMac.java
similarity index 100%
rename from src/main/java/org/bouncycastle/crypto/macs/CBCBlockCipherMac.java
rename to bcprov/src/main/java/org/bouncycastle/crypto/macs/CBCBlockCipherMac.java
diff --git a/src/main/java/org/bouncycastle/crypto/macs/HMac.java b/bcprov/src/main/java/org/bouncycastle/crypto/macs/HMac.java
similarity index 100%
rename from src/main/java/org/bouncycastle/crypto/macs/HMac.java
rename to bcprov/src/main/java/org/bouncycastle/crypto/macs/HMac.java
diff --git a/src/main/java/org/bouncycastle/crypto/modes/AEADBlockCipher.java b/bcprov/src/main/java/org/bouncycastle/crypto/modes/AEADBlockCipher.java
similarity index 100%
rename from src/main/java/org/bouncycastle/crypto/modes/AEADBlockCipher.java
rename to bcprov/src/main/java/org/bouncycastle/crypto/modes/AEADBlockCipher.java
diff --git a/src/main/java/org/bouncycastle/crypto/modes/CBCBlockCipher.java b/bcprov/src/main/java/org/bouncycastle/crypto/modes/CBCBlockCipher.java
similarity index 100%
rename from src/main/java/org/bouncycastle/crypto/modes/CBCBlockCipher.java
rename to bcprov/src/main/java/org/bouncycastle/crypto/modes/CBCBlockCipher.java
diff --git a/src/main/java/org/bouncycastle/crypto/modes/CCMBlockCipher.java b/bcprov/src/main/java/org/bouncycastle/crypto/modes/CCMBlockCipher.java
similarity index 100%
rename from src/main/java/org/bouncycastle/crypto/modes/CCMBlockCipher.java
rename to bcprov/src/main/java/org/bouncycastle/crypto/modes/CCMBlockCipher.java
diff --git a/src/main/java/org/bouncycastle/crypto/modes/CFBBlockCipher.java b/bcprov/src/main/java/org/bouncycastle/crypto/modes/CFBBlockCipher.java
similarity index 100%
rename from src/main/java/org/bouncycastle/crypto/modes/CFBBlockCipher.java
rename to bcprov/src/main/java/org/bouncycastle/crypto/modes/CFBBlockCipher.java
diff --git a/src/main/java/org/bouncycastle/crypto/modes/CTSBlockCipher.java b/bcprov/src/main/java/org/bouncycastle/crypto/modes/CTSBlockCipher.java
similarity index 100%
rename from src/main/java/org/bouncycastle/crypto/modes/CTSBlockCipher.java
rename to bcprov/src/main/java/org/bouncycastle/crypto/modes/CTSBlockCipher.java
diff --git a/src/main/java/org/bouncycastle/crypto/modes/GCMBlockCipher.java b/bcprov/src/main/java/org/bouncycastle/crypto/modes/GCMBlockCipher.java
similarity index 100%
rename from src/main/java/org/bouncycastle/crypto/modes/GCMBlockCipher.java
rename to bcprov/src/main/java/org/bouncycastle/crypto/modes/GCMBlockCipher.java
diff --git a/src/main/java/org/bouncycastle/crypto/modes/OFBBlockCipher.java b/bcprov/src/main/java/org/bouncycastle/crypto/modes/OFBBlockCipher.java
similarity index 100%
rename from src/main/java/org/bouncycastle/crypto/modes/OFBBlockCipher.java
rename to bcprov/src/main/java/org/bouncycastle/crypto/modes/OFBBlockCipher.java
diff --git a/src/main/java/org/bouncycastle/crypto/modes/SICBlockCipher.java b/bcprov/src/main/java/org/bouncycastle/crypto/modes/SICBlockCipher.java
similarity index 100%
rename from src/main/java/org/bouncycastle/crypto/modes/SICBlockCipher.java
rename to bcprov/src/main/java/org/bouncycastle/crypto/modes/SICBlockCipher.java
diff --git a/src/main/java/org/bouncycastle/crypto/modes/gcm/GCMMultiplier.java b/bcprov/src/main/java/org/bouncycastle/crypto/modes/gcm/GCMMultiplier.java
similarity index 100%
rename from src/main/java/org/bouncycastle/crypto/modes/gcm/GCMMultiplier.java
rename to bcprov/src/main/java/org/bouncycastle/crypto/modes/gcm/GCMMultiplier.java
diff --git a/src/main/java/org/bouncycastle/crypto/modes/gcm/GCMUtil.java b/bcprov/src/main/java/org/bouncycastle/crypto/modes/gcm/GCMUtil.java
similarity index 100%
rename from src/main/java/org/bouncycastle/crypto/modes/gcm/GCMUtil.java
rename to bcprov/src/main/java/org/bouncycastle/crypto/modes/gcm/GCMUtil.java
diff --git a/src/main/java/org/bouncycastle/crypto/modes/gcm/Tables8kGCMMultiplier.java b/bcprov/src/main/java/org/bouncycastle/crypto/modes/gcm/Tables8kGCMMultiplier.java
similarity index 100%
rename from src/main/java/org/bouncycastle/crypto/modes/gcm/Tables8kGCMMultiplier.java
rename to bcprov/src/main/java/org/bouncycastle/crypto/modes/gcm/Tables8kGCMMultiplier.java
diff --git a/src/main/java/org/bouncycastle/crypto/paddings/BlockCipherPadding.java b/bcprov/src/main/java/org/bouncycastle/crypto/paddings/BlockCipherPadding.java
similarity index 100%
rename from src/main/java/org/bouncycastle/crypto/paddings/BlockCipherPadding.java
rename to bcprov/src/main/java/org/bouncycastle/crypto/paddings/BlockCipherPadding.java
diff --git a/src/main/java/org/bouncycastle/crypto/paddings/ISO10126d2Padding.java b/bcprov/src/main/java/org/bouncycastle/crypto/paddings/ISO10126d2Padding.java
similarity index 100%
rename from src/main/java/org/bouncycastle/crypto/paddings/ISO10126d2Padding.java
rename to bcprov/src/main/java/org/bouncycastle/crypto/paddings/ISO10126d2Padding.java
diff --git a/src/main/java/org/bouncycastle/crypto/paddings/ISO7816d4Padding.java b/bcprov/src/main/java/org/bouncycastle/crypto/paddings/ISO7816d4Padding.java
similarity index 100%
rename from src/main/java/org/bouncycastle/crypto/paddings/ISO7816d4Padding.java
rename to bcprov/src/main/java/org/bouncycastle/crypto/paddings/ISO7816d4Padding.java
diff --git a/src/main/java/org/bouncycastle/crypto/paddings/PKCS7Padding.java b/bcprov/src/main/java/org/bouncycastle/crypto/paddings/PKCS7Padding.java
similarity index 100%
rename from src/main/java/org/bouncycastle/crypto/paddings/PKCS7Padding.java
rename to bcprov/src/main/java/org/bouncycastle/crypto/paddings/PKCS7Padding.java
diff --git a/src/main/java/org/bouncycastle/crypto/paddings/PaddedBufferedBlockCipher.java b/bcprov/src/main/java/org/bouncycastle/crypto/paddings/PaddedBufferedBlockCipher.java
similarity index 100%
rename from src/main/java/org/bouncycastle/crypto/paddings/PaddedBufferedBlockCipher.java
rename to bcprov/src/main/java/org/bouncycastle/crypto/paddings/PaddedBufferedBlockCipher.java
diff --git a/src/main/java/org/bouncycastle/crypto/paddings/TBCPadding.java b/bcprov/src/main/java/org/bouncycastle/crypto/paddings/TBCPadding.java
similarity index 100%
rename from src/main/java/org/bouncycastle/crypto/paddings/TBCPadding.java
rename to bcprov/src/main/java/org/bouncycastle/crypto/paddings/TBCPadding.java
diff --git a/src/main/java/org/bouncycastle/crypto/paddings/X923Padding.java b/bcprov/src/main/java/org/bouncycastle/crypto/paddings/X923Padding.java
similarity index 100%
rename from src/main/java/org/bouncycastle/crypto/paddings/X923Padding.java
rename to bcprov/src/main/java/org/bouncycastle/crypto/paddings/X923Padding.java
diff --git a/src/main/java/org/bouncycastle/crypto/paddings/ZeroBytePadding.java b/bcprov/src/main/java/org/bouncycastle/crypto/paddings/ZeroBytePadding.java
similarity index 100%
rename from src/main/java/org/bouncycastle/crypto/paddings/ZeroBytePadding.java
rename to bcprov/src/main/java/org/bouncycastle/crypto/paddings/ZeroBytePadding.java
diff --git a/src/main/java/org/bouncycastle/crypto/params/AEADParameters.java b/bcprov/src/main/java/org/bouncycastle/crypto/params/AEADParameters.java
similarity index 100%
rename from src/main/java/org/bouncycastle/crypto/params/AEADParameters.java
rename to bcprov/src/main/java/org/bouncycastle/crypto/params/AEADParameters.java
diff --git a/src/main/java/org/bouncycastle/crypto/params/AsymmetricKeyParameter.java b/bcprov/src/main/java/org/bouncycastle/crypto/params/AsymmetricKeyParameter.java
similarity index 100%
rename from src/main/java/org/bouncycastle/crypto/params/AsymmetricKeyParameter.java
rename to bcprov/src/main/java/org/bouncycastle/crypto/params/AsymmetricKeyParameter.java
diff --git a/src/main/java/org/bouncycastle/crypto/params/DESParameters.java b/bcprov/src/main/java/org/bouncycastle/crypto/params/DESParameters.java
similarity index 100%
rename from src/main/java/org/bouncycastle/crypto/params/DESParameters.java
rename to bcprov/src/main/java/org/bouncycastle/crypto/params/DESParameters.java
diff --git a/src/main/java/org/bouncycastle/crypto/params/DESedeParameters.java b/bcprov/src/main/java/org/bouncycastle/crypto/params/DESedeParameters.java
similarity index 100%
rename from src/main/java/org/bouncycastle/crypto/params/DESedeParameters.java
rename to bcprov/src/main/java/org/bouncycastle/crypto/params/DESedeParameters.java
diff --git a/src/main/java/org/bouncycastle/crypto/params/DHKeyGenerationParameters.java b/bcprov/src/main/java/org/bouncycastle/crypto/params/DHKeyGenerationParameters.java
similarity index 100%
rename from src/main/java/org/bouncycastle/crypto/params/DHKeyGenerationParameters.java
rename to bcprov/src/main/java/org/bouncycastle/crypto/params/DHKeyGenerationParameters.java
diff --git a/src/main/java/org/bouncycastle/crypto/params/DHKeyParameters.java b/bcprov/src/main/java/org/bouncycastle/crypto/params/DHKeyParameters.java
similarity index 100%
rename from src/main/java/org/bouncycastle/crypto/params/DHKeyParameters.java
rename to bcprov/src/main/java/org/bouncycastle/crypto/params/DHKeyParameters.java
diff --git a/src/main/java/org/bouncycastle/crypto/params/DHParameters.java b/bcprov/src/main/java/org/bouncycastle/crypto/params/DHParameters.java
similarity index 100%
rename from src/main/java/org/bouncycastle/crypto/params/DHParameters.java
rename to bcprov/src/main/java/org/bouncycastle/crypto/params/DHParameters.java
diff --git a/src/main/java/org/bouncycastle/crypto/params/DHPrivateKeyParameters.java b/bcprov/src/main/java/org/bouncycastle/crypto/params/DHPrivateKeyParameters.java
similarity index 100%
rename from src/main/java/org/bouncycastle/crypto/params/DHPrivateKeyParameters.java
rename to bcprov/src/main/java/org/bouncycastle/crypto/params/DHPrivateKeyParameters.java
diff --git a/src/main/java/org/bouncycastle/crypto/params/DHPublicKeyParameters.java b/bcprov/src/main/java/org/bouncycastle/crypto/params/DHPublicKeyParameters.java
similarity index 100%
rename from src/main/java/org/bouncycastle/crypto/params/DHPublicKeyParameters.java
rename to bcprov/src/main/java/org/bouncycastle/crypto/params/DHPublicKeyParameters.java
diff --git a/src/main/java/org/bouncycastle/crypto/params/DHValidationParameters.java b/bcprov/src/main/java/org/bouncycastle/crypto/params/DHValidationParameters.java
similarity index 100%
rename from src/main/java/org/bouncycastle/crypto/params/DHValidationParameters.java
rename to bcprov/src/main/java/org/bouncycastle/crypto/params/DHValidationParameters.java
diff --git a/src/main/java/org/bouncycastle/crypto/params/DSAKeyGenerationParameters.java b/bcprov/src/main/java/org/bouncycastle/crypto/params/DSAKeyGenerationParameters.java
similarity index 100%
rename from src/main/java/org/bouncycastle/crypto/params/DSAKeyGenerationParameters.java
rename to bcprov/src/main/java/org/bouncycastle/crypto/params/DSAKeyGenerationParameters.java
diff --git a/src/main/java/org/bouncycastle/crypto/params/DSAKeyParameters.java b/bcprov/src/main/java/org/bouncycastle/crypto/params/DSAKeyParameters.java
similarity index 100%
rename from src/main/java/org/bouncycastle/crypto/params/DSAKeyParameters.java
rename to bcprov/src/main/java/org/bouncycastle/crypto/params/DSAKeyParameters.java
diff --git a/src/main/java/org/bouncycastle/crypto/params/DSAParameters.java b/bcprov/src/main/java/org/bouncycastle/crypto/params/DSAParameters.java
similarity index 100%
rename from src/main/java/org/bouncycastle/crypto/params/DSAParameters.java
rename to bcprov/src/main/java/org/bouncycastle/crypto/params/DSAParameters.java
diff --git a/src/main/java/org/bouncycastle/crypto/params/DSAPrivateKeyParameters.java b/bcprov/src/main/java/org/bouncycastle/crypto/params/DSAPrivateKeyParameters.java
similarity index 100%
rename from src/main/java/org/bouncycastle/crypto/params/DSAPrivateKeyParameters.java
rename to bcprov/src/main/java/org/bouncycastle/crypto/params/DSAPrivateKeyParameters.java
diff --git a/src/main/java/org/bouncycastle/crypto/params/DSAPublicKeyParameters.java b/bcprov/src/main/java/org/bouncycastle/crypto/params/DSAPublicKeyParameters.java
similarity index 100%
rename from src/main/java/org/bouncycastle/crypto/params/DSAPublicKeyParameters.java
rename to bcprov/src/main/java/org/bouncycastle/crypto/params/DSAPublicKeyParameters.java
diff --git a/src/main/java/org/bouncycastle/crypto/params/DSAValidationParameters.java b/bcprov/src/main/java/org/bouncycastle/crypto/params/DSAValidationParameters.java
similarity index 100%
rename from src/main/java/org/bouncycastle/crypto/params/DSAValidationParameters.java
rename to bcprov/src/main/java/org/bouncycastle/crypto/params/DSAValidationParameters.java
diff --git a/src/main/java/org/bouncycastle/crypto/params/ECDomainParameters.java b/bcprov/src/main/java/org/bouncycastle/crypto/params/ECDomainParameters.java
similarity index 100%
rename from src/main/java/org/bouncycastle/crypto/params/ECDomainParameters.java
rename to bcprov/src/main/java/org/bouncycastle/crypto/params/ECDomainParameters.java
diff --git a/src/main/java/org/bouncycastle/crypto/params/ECKeyGenerationParameters.java b/bcprov/src/main/java/org/bouncycastle/crypto/params/ECKeyGenerationParameters.java
similarity index 100%
rename from src/main/java/org/bouncycastle/crypto/params/ECKeyGenerationParameters.java
rename to bcprov/src/main/java/org/bouncycastle/crypto/params/ECKeyGenerationParameters.java
diff --git a/src/main/java/org/bouncycastle/crypto/params/ECKeyParameters.java b/bcprov/src/main/java/org/bouncycastle/crypto/params/ECKeyParameters.java
similarity index 100%
rename from src/main/java/org/bouncycastle/crypto/params/ECKeyParameters.java
rename to bcprov/src/main/java/org/bouncycastle/crypto/params/ECKeyParameters.java
diff --git a/src/main/java/org/bouncycastle/crypto/params/ECPrivateKeyParameters.java b/bcprov/src/main/java/org/bouncycastle/crypto/params/ECPrivateKeyParameters.java
similarity index 100%
rename from src/main/java/org/bouncycastle/crypto/params/ECPrivateKeyParameters.java
rename to bcprov/src/main/java/org/bouncycastle/crypto/params/ECPrivateKeyParameters.java
diff --git a/src/main/java/org/bouncycastle/crypto/params/ECPublicKeyParameters.java b/bcprov/src/main/java/org/bouncycastle/crypto/params/ECPublicKeyParameters.java
similarity index 100%
rename from src/main/java/org/bouncycastle/crypto/params/ECPublicKeyParameters.java
rename to bcprov/src/main/java/org/bouncycastle/crypto/params/ECPublicKeyParameters.java
diff --git a/src/main/java/org/bouncycastle/crypto/params/KeyParameter.java b/bcprov/src/main/java/org/bouncycastle/crypto/params/KeyParameter.java
similarity index 100%
rename from src/main/java/org/bouncycastle/crypto/params/KeyParameter.java
rename to bcprov/src/main/java/org/bouncycastle/crypto/params/KeyParameter.java
diff --git a/src/main/java/org/bouncycastle/crypto/params/ParametersWithIV.java b/bcprov/src/main/java/org/bouncycastle/crypto/params/ParametersWithIV.java
similarity index 100%
rename from src/main/java/org/bouncycastle/crypto/params/ParametersWithIV.java
rename to bcprov/src/main/java/org/bouncycastle/crypto/params/ParametersWithIV.java
diff --git a/src/main/java/org/bouncycastle/crypto/params/ParametersWithRandom.java b/bcprov/src/main/java/org/bouncycastle/crypto/params/ParametersWithRandom.java
similarity index 100%
rename from src/main/java/org/bouncycastle/crypto/params/ParametersWithRandom.java
rename to bcprov/src/main/java/org/bouncycastle/crypto/params/ParametersWithRandom.java
diff --git a/src/main/java/org/bouncycastle/crypto/params/RC2Parameters.java b/bcprov/src/main/java/org/bouncycastle/crypto/params/RC2Parameters.java
similarity index 100%
rename from src/main/java/org/bouncycastle/crypto/params/RC2Parameters.java
rename to bcprov/src/main/java/org/bouncycastle/crypto/params/RC2Parameters.java
diff --git a/src/main/java/org/bouncycastle/crypto/params/RSAKeyGenerationParameters.java b/bcprov/src/main/java/org/bouncycastle/crypto/params/RSAKeyGenerationParameters.java
similarity index 100%
rename from src/main/java/org/bouncycastle/crypto/params/RSAKeyGenerationParameters.java
rename to bcprov/src/main/java/org/bouncycastle/crypto/params/RSAKeyGenerationParameters.java
diff --git a/src/main/java/org/bouncycastle/crypto/params/RSAKeyParameters.java b/bcprov/src/main/java/org/bouncycastle/crypto/params/RSAKeyParameters.java
similarity index 100%
rename from src/main/java/org/bouncycastle/crypto/params/RSAKeyParameters.java
rename to bcprov/src/main/java/org/bouncycastle/crypto/params/RSAKeyParameters.java
diff --git a/src/main/java/org/bouncycastle/crypto/params/RSAPrivateCrtKeyParameters.java b/bcprov/src/main/java/org/bouncycastle/crypto/params/RSAPrivateCrtKeyParameters.java
similarity index 100%
rename from src/main/java/org/bouncycastle/crypto/params/RSAPrivateCrtKeyParameters.java
rename to bcprov/src/main/java/org/bouncycastle/crypto/params/RSAPrivateCrtKeyParameters.java
diff --git a/src/main/java/org/bouncycastle/crypto/signers/DSASigner.java b/bcprov/src/main/java/org/bouncycastle/crypto/signers/DSASigner.java
similarity index 100%
rename from src/main/java/org/bouncycastle/crypto/signers/DSASigner.java
rename to bcprov/src/main/java/org/bouncycastle/crypto/signers/DSASigner.java
diff --git a/src/main/java/org/bouncycastle/crypto/signers/ECDSASigner.java b/bcprov/src/main/java/org/bouncycastle/crypto/signers/ECDSASigner.java
similarity index 100%
rename from src/main/java/org/bouncycastle/crypto/signers/ECDSASigner.java
rename to bcprov/src/main/java/org/bouncycastle/crypto/signers/ECDSASigner.java
diff --git a/src/main/java/org/bouncycastle/crypto/signers/RSADigestSigner.java b/bcprov/src/main/java/org/bouncycastle/crypto/signers/RSADigestSigner.java
similarity index 100%
rename from src/main/java/org/bouncycastle/crypto/signers/RSADigestSigner.java
rename to bcprov/src/main/java/org/bouncycastle/crypto/signers/RSADigestSigner.java
diff --git a/src/main/java/org/bouncycastle/crypto/util/Pack.java b/bcprov/src/main/java/org/bouncycastle/crypto/util/Pack.java
similarity index 100%
rename from src/main/java/org/bouncycastle/crypto/util/Pack.java
rename to bcprov/src/main/java/org/bouncycastle/crypto/util/Pack.java
diff --git